Browse Source

ConsistentHashRouteLoadBalance

master
tangmingyou 4 years ago
parent
commit
8befc9bbf4
  1. 7
      im-client/pom.xml
  2. 309
      im-client/src/main/java/net/sopod/soim/client/cmd/console/ConsoleBox.java
  3. 24
      im-client/src/main/java/net/sopod/soim/client/cmd/console/ConsoleBoxKeyHandler.java
  4. 41
      im-client/src/main/java/net/sopod/soim/client/cmd/console/handlers/ConsoleBoxPasswordHandler.java
  5. 72
      im-client/src/main/java/net/sopod/soim/client/cmd/console/handlers/JvmKeyHandler.java
  6. 11
      im-client/src/main/java/net/sopod/soim/client/handler/msg/ResOnlineUserListHandler.java
  7. 47
      im-client/src/main/java/net/sopod/soim/client/logger/Logger.java
  8. 241
      im-client/src/test/java/net/sopod/soim/client/prettyconsole/ConsoleBoxTest.java
  9. 18
      im-entry/src/main/java/net/sopod/soim/entry/worker/Worker.java
  10. 6
      im-service-api/im-router-api/pom.xml
  11. 26
      im-service-api/im-router-api/src/main/java/net/sopod/soim/router/api/route/ConsistentHashRoute.java
  12. 6
      im-service/im-router/src/main/resources/application.yml

7
im-client/pom.xml

@ -47,6 +47,13 @@
<artifactId>unirest-java</artifactId> <artifactId>unirest-java</artifactId>
<version>3.13.6</version> <version>3.13.6</version>
</dependency> </dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

309
im-client/src/main/java/net/sopod/soim/client/cmd/console/ConsoleBox.java

@ -0,0 +1,309 @@
package net.sopod.soim.client.cmd.console;
import com.google.common.base.*;
import com.google.common.collect.Iterables;
import net.sopod.soim.client.cmd.console.handlers.ConsoleBoxPasswordHandler;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
* represents a console display box in the system console (or any PrintStream).
* used to display friendly technical information
* @author Willie Scholtz
*/
public final class ConsoleBox {
/**
* the character to use in box corners
*/
private String boxChar = " + ";
/**
* the character to use to pad strings with
*/
private char padChar = ' ';
/**
* the character used for the box's right and left sides
*/
private String endChar = " | ";
/**
* the character used for the box's top, title and bottom sides
*/
private String borderChar = "-";
/**
* the character used for representing black in a color
*/
private String blackChar = " ";
/**
* the character used for representing white in a color
*/
private String whiteChar = "#";
/**
* the character used for representing aliasing of fonts.
* actually other colors, but since our image is only black and white
* it will represent a shade, which in this case is aliasing
*/
private String aliasChar = " ";
/**
* the key value separator for names and values
*/
private String keyValueSeparator = " : ";
private final List<ConsoleBoxKeyHandler> handlers;
private final StringBuilder builder;
private boolean content;
private final int width;
/**
* creates a new instance of a ConsoleBox
* @param boxWidth the width of the box (in character count)
* @param title the initial title of the box, leave blank for none
*/
public ConsoleBox(int boxWidth, String title) {
this.width = boxWidth;
this.builder = new StringBuilder();
this.handlers = new ArrayList<ConsoleBoxKeyHandler>();
// add default password handler
this.handlers.add(new ConsoleBoxPasswordHandler());
if (!Strings.isNullOrEmpty(title)) {
this.title(title);
}
}
/*
* creates a new instance of a ConsoleBox with no title
* @param boxWidth the width of the box (in character count)
*/
public ConsoleBox(int boxWidth) {
this(boxWidth, null);
}
/**
* pads a string from both sides
* @param string the string to pad
* @param pad the padding character
* @param length the length to pad
* @return the padded string
*/
private String padBoth(String string, String pad, int length) {
int right = (length - string.length()) / 2 + string.length();
String result = Strings.padEnd(string, right, pad.toCharArray()[0]);
return Strings.padStart(result, length, pad.toCharArray()[0]);
}
/**
* configures the box characters, note that sensible defaults are already set.
* @param cornerChar the character to use in box corners
* @param padChar the character to use to pad strings with
* @param sideChar the character used for the box's right and left sides
* @param borderChar the character used for the box's top, title and bottom sides
* @return the current instance
*/
public ConsoleBox setBoxCharacters(String cornerChar, char padChar, String sideChar, String borderChar) {
this.boxChar = Preconditions.checkNotNull(cornerChar);
this.padChar = Preconditions.checkNotNull(padChar);
this.endChar = Preconditions.checkNotNull(sideChar);
this.borderChar = Preconditions.checkNotNull(borderChar);
return this;
}
/**
* configures the box's ASCII characters, note that sensible defaults are already set.
* @param blackChar the character used for representing black in a color
* @param whiteChar the character used for representing white in a color
* @param aliasChar the character used for representing aliasing of fonts.
* actually other colors, but since our image is only black and white
* it will represent a shade, which in this case is aliasing.
* @return the current instance
*/
public ConsoleBox setAsciiCharacters(String blackChar, String whiteChar, String aliasChar) {
this.blackChar = Preconditions.checkNotNull(blackChar);
this.whiteChar = Preconditions.checkNotNull(whiteChar);
this.aliasChar = Preconditions.checkNotNull(aliasChar);
return this;
}
/**
* configures the box's key-value separator.
* @param keyValueSeparator the character used for representing black in a color
* actually other colors, but since our image is only black and white
* it will represent a shade, which in this case is aliasing.
* @return the current instance
*/
public ConsoleBox setKeyValueSeparator(String keyValueSeparator) {
this.keyValueSeparator = Preconditions.checkNotNull(keyValueSeparator);
return this;
}
/**
* adds a new key-value handler for this ConsoleBox
* @param handler the handler to use
* @return the current instance
*/
public ConsoleBox handler(ConsoleBoxKeyHandler handler) {
this.handlers.add(handler);
return this;
}
/**
* builds and writes this box to the specified output stream
* @param output
*/
public void build(PrintStream output) {
this.title("");
output.println(this.builder.toString());
}
/**
* adds a title section to the console box
* @param title the title to use
* @return the current box
*/
public ConsoleBox title(String title) {
this.builder.append("\n").append(this.boxChar).append(padBoth(title,
this.borderChar, this.width)).append(this.boxChar);
return this;
}
/**
* adds an empty line section to the console box
* @return the current box
*/
public ConsoleBox empty() {
this.builder.append("\n").append(this.boxChar).append(
padBoth("", " ", this.width)).append(this.boxChar);
return this;
}
/**
* generates and writes the specified text as an ASCII image into this box
* @param text the text to write as ASCII
* @param invert should the ASCII colors be inverted?
* @return the current box
*/
public ConsoleBox ascii(String text, boolean invert) {
final BufferedImage image = new BufferedImage(this.width,
32, BufferedImage.TYPE_INT_RGB);
final Graphics graphics = image.getGraphics();
final Graphics2D g2d = (Graphics2D) graphics;
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
Font textFont = new Font("Dialog", Font.BOLD, 22);
FontMetrics textMetrics = g2d.getFontMetrics(textFont);
g2d.setFont(textFont);
int tX = (image.getWidth() / 2) - (textMetrics.stringWidth(text) / 2);
int tY = (image.getHeight() / 2) + (textMetrics.getHeight() / 2) - 5;
g2d.drawString(text, tX, tY);
g2d.drawRenderedImage(image, null);
g2d.dispose();
final int iHeight = image.getHeight();
final int iWidth = image.getWidth();
final String bChar = invert ? this.whiteChar : this.blackChar;
final String wChar = invert ? this.blackChar : this.whiteChar;
for (int y = 0; y < iHeight; y++) {
final StringBuilder sb = new StringBuilder();
for (int x = 0; x < iWidth; x++) {
final int rgbColor = image.getRGB(x, y);
sb.append(rgbColor == -16777216 ? bChar : rgbColor == -1 ? wChar : aliasChar);
}
if (sb.toString().trim().isEmpty()) {
continue;
}
this.builder.append("\n").append(this.endChar)
.append(sb).append(this.endChar);
}
return this;
}
/**
* adds a informational line to the console box,
* automatically splitting large values
* @param key the name of the value to display
* @param value the value of this line
* @return the current box
*/
public ConsoleBox line(String key, String value) {
key = Strings.isNullOrEmpty(key) ? "null" : key;
value = Strings.isNullOrEmpty(value) ? "" : value;
// get the key length
final int kL = key.length();
final int kSl = this.keyValueSeparator.length();
// calculate remaining box space for the value
final int ths = (this.width - kL - kSl);
Preconditions.checkState(ths > -1, "key[" + key + "] is to long "
+ "for box with a " + width + " width!");
// \n | the_key_length_in_spaces
final String joinOn = ("\n" + this.endChar + Strings.padEnd("",
kL + kSl, this.padChar));
// get key handlers and modify if neccessary
for (ConsoleBoxKeyHandler handler : this.handlers) {
if (handler.shouldHandle(key)) {
value = handler.handleValue(key, value);
// don't break, possibilitty of multiple handlers
}
}
// if a key handler returns null, a key should be skipped
if (value != null) {
// split the string on either length or new lines
Iterable<String> splitted = Splitter.on(Pattern
.compile("(?<=\\G.{" + ths + "})|\\n")).split(value);
// add the value + end characters (multiple lines)
String formatted = Joiner.on(joinOn).join(
Iterables.transform(splitted, new Function<String, String>() {
@Override
public String apply(String input) {
return Strings.padEnd(input, ths, ' ') + endChar;
}
}));
// write completed line to builder
this.builder.append("\n").append(this.endChar).append(key)
.append(this.keyValueSeparator).append(formatted);
this.content = true;
}
return this;
}
/**
* @return true if {@link #line(java.lang.String, java.lang.String)}
* has been called at least once
*/
public boolean hasContent() {
return content;
}
}

24
im-client/src/main/java/net/sopod/soim/client/cmd/console/ConsoleBoxKeyHandler.java

@ -0,0 +1,24 @@
package net.sopod.soim.client.cmd.console;
/**
* interface for defining handlers for certain console box keys
* @author Willie Scholtz
*/
public interface ConsoleBoxKeyHandler {
/**
* determines if the specified key should be handled by this key handler
* @param key the key to check
* @return true if this key should be handled by this key handler
*/
public boolean shouldHandle(final String key);
/**
* if this handler is set to handle the specified key it, will execute this method to handle it's value
* @param key the key being handled
* @param value the original value to handle, note that console box automatically handles new lines and long lines
* @return the formatted value for ConsoleBox, or null if the whole line should be skipped
*/
public String handleValue(final String key, final String value);
}

41
im-client/src/main/java/net/sopod/soim/client/cmd/console/handlers/ConsoleBoxPasswordHandler.java

@ -0,0 +1,41 @@
package net.sopod.soim.client.cmd.console.handlers;
import net.sopod.soim.client.cmd.console.ConsoleBoxKeyHandler;
import java.util.regex.Pattern;
/**
* ensures no passwords are present in the ConsoleBox
* @author Willie Scholtz
*/
public class ConsoleBoxPasswordHandler implements ConsoleBoxKeyHandler {
/**
* pattern defining which attributes should be blurred
*/
private static final Pattern IGNORE_ATT_PATT = Pattern
.compile("pass(word)?", Pattern.CASE_INSENSITIVE);
/**
* replacement value for sensitive data
*/
private static final String SAFE_REPLACEMENT = "*****";
/**
* determines if a certain parameter is safe to transmit over the wire
* @param key the name of the parameter that will be sent
* @return true if the parameter may be sent
*/
private static boolean safeToTransmit(final String key) {
return !IGNORE_ATT_PATT.matcher(key).find();
}
@Override
public boolean shouldHandle(String key) {
return !safeToTransmit(key);
}
@Override
public String handleValue(String key, String value) {
return SAFE_REPLACEMENT;
}
}

72
im-client/src/main/java/net/sopod/soim/client/cmd/console/handlers/JvmKeyHandler.java

@ -0,0 +1,72 @@
package net.sopod.soim.client.cmd.console.handlers;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import net.sopod.soim.client.cmd.console.ConsoleBoxKeyHandler;
/**
* ensures that JVM system properties are handled correctly by consolebox
* @author Willie Scholtz
*/
public class JvmKeyHandler implements ConsoleBoxKeyHandler {
private static final boolean WINDOWS = System.getProperty("os.name").indexOf("Windows") > -1;
/**
* keys that should be converted to new lines
*/
private static final String[] CLASSPATHS = new String[] {
"java.library.path", "java.class.path", "sun.boot.class.path"
};
/**
* keys that should be skipped
*/
private static final String[] SKIPS = new String[] {
"line.seperator"
};
@Override
public boolean shouldHandle(String key) {
for (String string : CLASSPATHS) {
if (string.equals(key)) {
return true;
}
}
for (String string : SKIPS) {
if (string.equals(key)) {
return true;
}
}
return false;
}
@Override
public String handleValue(String key, String value) {
for (String string : CLASSPATHS) {
if (string.equals(key)) {
value = Joiner.on("\n").join(Iterables.transform(Splitter.on(WINDOWS ? ";" : ":").split(value),
new Function<String, String>() {
int item = 0;
@Override
public String apply(String input) {
return "[" + ++item + "] " + input;
}
}));
}
}
for (String string : SKIPS) {
if (string.equals(key)) {
return null;
}
}
return value;
}
}

11
im-client/src/main/java/net/sopod/soim/client/handler/msg/ResOnlineUserListHandler.java

@ -5,6 +5,9 @@ import net.sopod.soim.client.logger.Logger;
import net.sopod.soim.client.session.MessageHandler; import net.sopod.soim.client.session.MessageHandler;
import net.sopod.soim.data.msg.user.UserGroup; import net.sopod.soim.data.msg.user.UserGroup;
import java.util.List;
import java.util.stream.Collectors;
/** /**
* ResOnlineUserListHandler * ResOnlineUserListHandler
* *
@ -16,10 +19,10 @@ public class ResOnlineUserListHandler implements MessageHandler<UserGroup.ResOnl
@Override @Override
public void handleMsg(UserGroup.ResOnlineUserList msg) { public void handleMsg(UserGroup.ResOnlineUserList msg) {
Logger.info("-------在线用户--------"); List<String> userLines = msg.getUsersList().stream()
for (UserGroup.UserInfo user : msg.getUsersList()) { .map(user -> user.getUid() + " | " + user.getAccount())
Logger.info("{} | {}", user.getUid(), user.getAccount()); .collect(Collectors.toList());
} Logger.logList("在线用户", userLines);
} }
} }

47
im-client/src/main/java/net/sopod/soim/client/logger/Logger.java

@ -1,6 +1,12 @@
package net.sopod.soim.client.logger; package net.sopod.soim.client.logger;
import com.google.common.base.Joiner;
import net.sopod.soim.client.cmd.CmdStarter; import net.sopod.soim.client.cmd.CmdStarter;
import net.sopod.soim.client.cmd.console.ConsoleBox;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/** /**
* Logger * Logger
@ -10,30 +16,51 @@ import net.sopod.soim.client.cmd.CmdStarter;
*/ */
public class Logger { public class Logger {
private static int consoleWidth = 60;
public static void info(String msg, Object...args) { public static void info(String msg, Object...args) {
log("[info] " + msg, args); log("info ", msg, args);
} }
public static void error(String msg, Object...args) { public static void error(String msg, Object...args) {
log("[error] " + msg, args); log("error ", msg, args);
} }
public static void log(String msg, Object...args) { /**
* TODO ConsoleBox 中文尺寸处理
*/
public static void log(String key, String msg, Object...args) {
msg = msg.replaceAll("\\{}", "%s"); msg = msg.replaceAll("\\{}", "%s");
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
args[i] = String.valueOf(args[i]); args[i] = String.valueOf(args[i]);
} }
if ("main".equals(Thread.currentThread().getName())) { // if ("main".equals(Thread.currentThread().getName())) {
System.out.println(String.format(msg, args)); // System.out.println(String.format(msg, args));
} else { // } else {
// 其他线程异步打印 // // 其他线程异步打印
System.out.println(System.lineSeparator() + String.format(msg, args)); // System.out.println(System.lineSeparator() + String.format(msg, args));
CmdStarter.printPre(); // CmdStarter.printPre();
} // }
ConsoleBox box = new ConsoleBox(consoleWidth);
box.title("提示");
box.line(key, String.format(msg, args));
box.build(System.out);
}
public static void logList(String key, List<String> lines) {
ConsoleBox box = new ConsoleBox(consoleWidth);
box.title("");
String line = Joiner.on("\n").join(lines);
box.line(key, "\n" + line);
box.build(System.out);
} }
public static void pre(String pre) { public static void pre(String pre) {
System.out.print(pre); System.out.print(pre);
} }
public static void main(String[] args) {
System.out.println(Joiner.on(System.lineSeparator()).join(Arrays.asList("a", "b", "c")));
}
} }

241
im-client/src/test/java/net/sopod/soim/client/prettyconsole/ConsoleBoxTest.java

@ -0,0 +1,241 @@
package net.sopod.soim.client.prettyconsole;
import com.google.common.base.Charsets;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import net.sopod.soim.client.cmd.console.ConsoleBox;
import net.sopod.soim.client.cmd.console.ConsoleBoxKeyHandler;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Unit test for testing console box
*/
public class ConsoleBoxTest extends TestCase {
/**
* Create the test case
* @param testName name of the test case
*/
public ConsoleBoxTest(String testName) {
super(testName);
}
/**
* @return the suite of tests being tested
*/
public static Test suite() {
return new TestSuite(ConsoleBoxTest.class);
}
private ConsoleBox basicBox(int width) {
ConsoleBox box = new ConsoleBox(width);
box.title("TEST");
return box;
}
private String getBox(ConsoleBox box) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
box.build(new PrintStream(baos));
String actual = null;
try {
actual = baos.toString(Charsets.UTF_8.name());
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(ConsoleBoxTest.class.getName()).log(
Level.SEVERE, "failed to get UTF-8", ex);
}
System.out.println(actual);
return actual;
}
/**
* @throws java.io.UnsupportedEncodingException
*/
public void testCreation() throws UnsupportedEncodingException {
ConsoleBox box = this.basicBox(10);
box.line("a", "1");
final String actual = this.getBox(box);
final String expected = "\n" +
" + ---TEST--- + \n" +
" | a : 1 | \n" +
" + ---------- + \r\n";
assertEquals("basic box build", expected, actual);
}
public void testOverflow() throws UnsupportedEncodingException {
ConsoleBox box = this.basicBox(10);
box.line("a", "123456789987654321");
final String actual = this.getBox(box);
final String expected = "\n" +
" + ---TEST--- + \n" +
" | a : 123456 | \n" +
" | 789987 | \n" +
" | 654321 | \n" +
" | | \n" +
" + ---------- + \r\n";
assertEquals("overflow in content", expected, actual);
}
public void testNewLineOverflow() throws UnsupportedEncodingException {
ConsoleBox box = this.basicBox(10);
box.line("a", "1\n2\n3\n4\n5\n6\n7\n8\n9\n12345679abcdefg");
final String actual = this.getBox(box);
final String expected = "\n" +
" + ---TEST--- + \n" +
" | a : 1 | \n" +
" | 2 | \n" +
" | 3 | \n" +
" | 4 | \n" +
" | 5 | \n" +
" | 6 | \n" +
" | 7 | \n" +
" | 8 | \n" +
" | 9 | \n" +
" | 123456 | \n" +
" | 79abcd | \n" +
" | efg | \n" +
" + ---------- + \r\n";
assertEquals("overflow in content with new lines", expected, actual);
}
public void testDefaultPasswordHandling() throws UnsupportedEncodingException {
ConsoleBox box = this.basicBox(20);
box.line("password", "supercoolpassword");
box.line("pass", "mysecret");
final String actual = this.getBox(box);
final String expected = "\n" +
" + --------TEST-------- + \n" +
" | password : ***** | \n" +
" | pass : ***** | \n" +
" + -------------------- + \r\n";
assertEquals("default password handling", expected, actual);
}
public void testLinesAndTitles() throws UnsupportedEncodingException {
ConsoleBox box = this.basicBox(25);
box.line("keyOne", "valueOne");
box.line("keyTwo", "valueTwo");
box.title("TITLE");
box.line("keyThree", "valueThree");
box.line("keyFour", "valueFour");
final String actual = this.getBox(box);
final String expected = "\n" +
" + -----------TEST---------- + \n" +
" | keyOne : valueOne | \n" +
" | keyTwo : valueTwo | \n" +
" + ----------TITLE---------- + \n" +
" | keyThree : valueThree | \n" +
" | keyFour : valueFour | \n" +
" + ------------------------- + \r\n";
assertEquals("with lines and titles", expected, actual);
}
public void testAscii() throws UnsupportedEncodingException {
ConsoleBox box = this.basicBox(60);
box.line("keyOne", "valueOne");
box.line("keyTwo", "valueTwo");
box.title("TITLE");
box.line("keyThree", "valueThree");
box.line("keyFour", "valueFour");
box.title("");
box.ascii("ASCII", false);
final String actual = this.getBox(box);
final String expected = "\n" +
" + ----------------------------TEST---------------------------- + \n" +
" | keyOne : valueOne | \n" +
" | keyTwo : valueTwo | \n" +
" + ----------------------------TITLE--------------------------- + \n" +
" | keyThree : valueThree | \n" +
" | keyFour : valueFour | \n" +
" + ------------------------------------------------------------ + \n" +
" | ### ### ### | \n" +
" | #### ######## ####### ### ### | \n" +
" | ##### ########## ########## ### ### | \n" +
" | ## ## ## ## ## ## ### ### | \n" +
" | ## # ## ## ## ### ### | \n" +
" | ## ## ## ## ### ### | \n" +
" | ## ## ### ## ### ### | \n" +
" | ## # ###### ## ### ### | \n" +
" | ## ## ### ## ### ### | \n" +
" | ######### ## ## ### ### | \n" +
" | ########### ## ## ## ### ### | \n" +
" | ########### ## ## ## ### ### | \n" +
" | ## ## ### ## ## ## ### ### | \n" +
" | ## ## ########## ######### ### ### | \n" +
" | ## ## ######## ####### ### ### | \n" +
" | ## ## # ### ### | \n" +
" + ------------------------------------------------------------ + \r\n";
assertEquals("with ascii text", expected, actual);
}
public void testCustomHandler() throws UnsupportedEncodingException {
ConsoleBox box = this.basicBox(25);
box.handler(new ConsoleBoxKeyHandler() {
public boolean shouldHandle(String key) {
return "a".equals(key) || "c".equals(key);
}
public String handleValue(String key, String value) {
if ("a".equals(key)) {
return "a_replace";
}
if ("c".equals(key)) {
return "c_replace";
}
return value;
}
});
box.line("a", "valueOne");
box.line("b", "valueTwo");
box.line("c", "valueThree");
box.line("d", "valueFour");
final String actual = this.getBox(box);
final String expected = "\n" +
" + -----------TEST---------- + \n" +
" | a : a_replace | \n" +
" | b : valueTwo | \n" +
" | c : c_replace | \n" +
" | d : valueFour | \n" +
" + ------------------------- + \r\n";
assertEquals("with a custom handler", expected, actual);
}
public void testEmpty() throws UnsupportedEncodingException {
ConsoleBox box = this.basicBox(5);
box.empty();
box.empty();
final String actual = this.getBox(box);
final String expected = "\n" +
" + -TEST + \n" +
" + + \n" +
" + + \n" +
" + ----- + \r\n";
assertEquals("with empty content", expected, actual);
}
}

18
im-entry/src/main/java/net/sopod/soim/entry/worker/Worker.java

@ -5,6 +5,8 @@ import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType; import com.lmax.disruptor.dsl.ProducerType;
import net.sopod.soim.common.util.ImClock; import net.sopod.soim.common.util.ImClock;
import net.sopod.soim.entry.util.FastThreadLocalThreadFactory; import net.sopod.soim.entry.util.FastThreadLocalThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.*; import java.util.concurrent.*;
@ -13,6 +15,8 @@ import java.util.concurrent.*;
*/ */
public class Worker implements EventHandler<TaskEvent>, EventFactory<TaskEvent> { public class Worker implements EventHandler<TaskEvent>, EventFactory<TaskEvent> {
private static final Logger logger = LoggerFactory.getLogger(Worker.class);
private Disruptor<TaskEvent> disruptor; private Disruptor<TaskEvent> disruptor;
private RingBuffer<TaskEvent> ringBuffer; private RingBuffer<TaskEvent> ringBuffer;
@ -54,11 +58,15 @@ public class Worker implements EventHandler<TaskEvent>, EventFactory<TaskEvent>
@Override @Override
public void onEvent(TaskEvent taskEvent, long sequence, boolean endOfBatch) throws Exception { public void onEvent(TaskEvent taskEvent, long sequence, boolean endOfBatch) throws Exception {
long start = ImClock.millis(); try {
taskEvent.getTask().run(); long start = ImClock.millis();
long time = ImClock.millis() - start; taskEvent.getTask().run();
if (time > 100) { long time = ImClock.millis() - start;
System.out.println("任务执行时间过长:" + time); if (time > 100) {
logger.info("任务执行时间过长: {}", time);
}
} catch (Exception e) {
logger.error("任务执行失败:", e);
} }
// 事件对象不会释放,将数据置空 // 事件对象不会释放,将数据置空
taskEvent.setTask(null); taskEvent.setTask(null);

6
im-service-api/im-router-api/pom.xml

@ -17,6 +17,12 @@
<groupId>net.sopod</groupId> <groupId>net.sopod</groupId>
<version>1.0.0</version> <version>1.0.0</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-cluster</artifactId>
<version>${dubbo.version}</version>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

26
im-service-api/im-router-api/src/main/java/net/sopod/soim/router/api/route/ConsistentHashRoute.java

@ -0,0 +1,26 @@
package net.sopod.soim.router.api.route;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance;
import java.util.List;
/**
* ConsistentHashRoute
* 一致性 hash 服务路由
*
*
* @author tmy
* @date 2022-04-29 14:41
*/
public class ConsistentHashRoute extends AbstractLoadBalance {
@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
Invoker<T> invoker = invokers.get(0);
return null;
}
}

6
im-service/im-router/src/main/resources/application.yml

@ -20,4 +20,8 @@ dubbo:
group: so-im group: so-im
protocol: protocol:
name: dubbo name: dubbo
port: 3031 port: 3032
consumer:
check: false
provider:
loadbalance: consistenthash
Loading…
Cancel
Save