├── .gitignore
├── LICENSE
├── README.md
├── articles
├── bitchat-overview
│ └── bitchat-overview.md
├── drawios
│ └── bitchat-overview.xml
└── resources
│ └── bitchat-overview
│ ├── client-connect.jpg
│ ├── client-reconnect.jpg
│ ├── cluster-arch.jpg
│ ├── list-user.jpg
│ ├── login.jpg
│ ├── received-p2p-msg.jpg
│ ├── register.jpg
│ ├── send-p2p-msg.jpg
│ ├── server-startup.jpg
│ └── stand-alone-arch.jpg
├── bitchat-client
├── pom.xml
└── src
│ └── main
│ └── java
│ └── io
│ └── bitchat
│ └── client
│ ├── Client.java
│ ├── ClientFactory.java
│ ├── ClientHandler.java
│ ├── ClientInitializer.java
│ ├── ClientMode.java
│ ├── GenericClient.java
│ ├── HealthyChecker.java
│ └── SimpleClientFactory.java
├── bitchat-core
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── io
│ │ │ └── bitchat
│ │ │ ├── core
│ │ │ ├── IdleStateChecker.java
│ │ │ ├── LoadBalancer.java
│ │ │ ├── ServerAttr.java
│ │ │ ├── executor
│ │ │ │ ├── AbstractExecutor.java
│ │ │ │ └── Executor.java
│ │ │ ├── id
│ │ │ │ ├── IdFactory.java
│ │ │ │ ├── MemoryIdFactory.java
│ │ │ │ └── SnowflakeIdFactory.java
│ │ │ └── init
│ │ │ │ ├── InitAble.java
│ │ │ │ ├── InitOrder.java
│ │ │ │ └── Initializer.java
│ │ │ ├── http
│ │ │ ├── ContentType.java
│ │ │ ├── HttpContext.java
│ │ │ ├── RenderType.java
│ │ │ ├── RequestMethod.java
│ │ │ ├── controller
│ │ │ │ ├── Controller.java
│ │ │ │ ├── ControllerProxy.java
│ │ │ │ ├── Mapping.java
│ │ │ │ ├── Param.java
│ │ │ │ └── ProxyInvocation.java
│ │ │ ├── converter
│ │ │ │ ├── AbstractConverter.java
│ │ │ │ ├── Converter.java
│ │ │ │ ├── PrimitiveConverter.java
│ │ │ │ └── PrimitiveTypeUtil.java
│ │ │ ├── exception
│ │ │ │ ├── InvocationException.java
│ │ │ │ └── ValidationException.java
│ │ │ ├── maker
│ │ │ │ ├── DefaultHtmlMaker.java
│ │ │ │ ├── HtmlMaker.java
│ │ │ │ ├── HtmlMakerEnum.java
│ │ │ │ └── HtmlMakerFactory.java
│ │ │ ├── router
│ │ │ │ ├── BadClientSilencer.java
│ │ │ │ ├── MethodlessRouter.java
│ │ │ │ ├── OrderlessRouter.java
│ │ │ │ ├── PathPattern.java
│ │ │ │ ├── RouteResult.java
│ │ │ │ └── Router.java
│ │ │ ├── util
│ │ │ │ ├── HtmlContentUtil.java
│ │ │ │ ├── HttpRenderUtil.java
│ │ │ │ └── HttpRequestUtil.java
│ │ │ └── view
│ │ │ │ ├── HtmlKeyHolder.java
│ │ │ │ ├── Page404.java
│ │ │ │ ├── Page500.java
│ │ │ │ ├── PageError.java
│ │ │ │ └── PageIndex.java
│ │ │ ├── lang
│ │ │ ├── BeanMapper.java
│ │ │ ├── config
│ │ │ │ ├── BaseConfig.java
│ │ │ │ ├── ConfigFactory.java
│ │ │ │ ├── SnowflakeConfig.java
│ │ │ │ └── ThreadPoolConfig.java
│ │ │ ├── constants
│ │ │ │ ├── ResultCode.java
│ │ │ │ └── ServiceName.java
│ │ │ └── util
│ │ │ │ └── GenericsUtil.java
│ │ │ ├── packet
│ │ │ ├── Command.java
│ │ │ ├── DefaultPacket.java
│ │ │ ├── Packet.java
│ │ │ ├── PacketType.java
│ │ │ ├── Payload.java
│ │ │ ├── PendingPackets.java
│ │ │ ├── Request.java
│ │ │ ├── codec
│ │ │ │ ├── PacketCodec.java
│ │ │ │ ├── PacketDecoder.java
│ │ │ │ └── PacketEncoder.java
│ │ │ ├── ctx
│ │ │ │ ├── CommandProcessorContext.java
│ │ │ │ └── RequestProcessorContext.java
│ │ │ ├── factory
│ │ │ │ ├── CommandFactory.java
│ │ │ │ ├── PacketFactory.java
│ │ │ │ ├── PayloadFactory.java
│ │ │ │ └── RequestFactory.java
│ │ │ ├── interceptor
│ │ │ │ ├── Interceptor.java
│ │ │ │ ├── InterceptorBuilder.java
│ │ │ │ ├── InterceptorHandler.java
│ │ │ │ └── InterceptorProvider.java
│ │ │ └── processor
│ │ │ │ ├── AbstractCommandProcessor.java
│ │ │ │ ├── AbstractRequestProcessor.java
│ │ │ │ ├── CommandProcessor.java
│ │ │ │ ├── Processor.java
│ │ │ │ └── RequestProcessor.java
│ │ │ ├── serialize
│ │ │ ├── DefaultSerializerChooser.java
│ │ │ ├── FastJsonSerializer.java
│ │ │ ├── HessianSerializer.java
│ │ │ ├── JdkSerializer.java
│ │ │ ├── KryoSerializer.java
│ │ │ ├── ProtoStuffSerializer.java
│ │ │ ├── SerializeAlgorithm.java
│ │ │ ├── Serializer.java
│ │ │ └── SerializerChooser.java
│ │ │ └── ws
│ │ │ ├── Frame.java
│ │ │ ├── FrameFactory.java
│ │ │ ├── PendingFrames.java
│ │ │ └── codec
│ │ │ └── FrameCodec.java
│ └── resources
│ │ ├── config
│ │ ├── base-config.properties
│ │ ├── snowflake-config.properties
│ │ └── thread-pool-config.properties
│ │ └── logback.xml
│ └── test
│ └── java
│ └── io
│ └── bitchat
│ ├── core
│ └── IdTest.java
│ ├── lang
│ └── config
│ │ └── ConfigTest.java
│ └── serialize
│ └── SerializeTest.java
├── bitchat-router
├── pom.xml
└── src
│ └── main
│ └── java
│ └── io
│ └── bitchat
│ └── router
│ ├── RouterServer.java
│ └── RouterServerAttr.java
├── bitchat-server
├── pom.xml
└── src
│ └── main
│ └── java
│ └── io
│ └── bitchat
│ └── server
│ ├── AbstractServer.java
│ ├── ClusterServer.java
│ ├── HeartBeatProcessor.java
│ ├── ProtocolDispatcher.java
│ ├── Server.java
│ ├── ServerAttrHolder.java
│ ├── ServerBootstrap.java
│ ├── ServerFactory.java
│ ├── ServerInitializer.java
│ ├── ServerMode.java
│ ├── ServerShell.java
│ ├── ServerSpeaker.java
│ ├── SessionIdKeeper.java
│ ├── SimpleServerFactory.java
│ ├── StandaloneServer.java
│ ├── channel
│ ├── ChannelHelper.java
│ ├── ChannelListener.java
│ ├── ChannelManager.java
│ ├── ChannelType.java
│ ├── ChannelWrapper.java
│ ├── DefaultChannelListener.java
│ └── DefaultChannelManager.java
│ ├── http
│ ├── ControllerContext.java
│ ├── HttpExecutor.java
│ └── HttpHandler.java
│ ├── packet
│ ├── PacketExecutor.java
│ └── PacketHandler.java
│ ├── rest
│ └── StatusController.java
│ ├── session
│ ├── AbstractSessionManager.java
│ ├── DefaultSession.java
│ ├── DefaultSessionManager.java
│ ├── Session.java
│ ├── SessionHelper.java
│ └── SessionManager.java
│ └── ws
│ ├── FrameExecutor.java
│ └── FrameHandler.java
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled source #
2 | ###################
3 | *.com
4 | *.class
5 | *.dll
6 | *.exe
7 | *.o
8 | *.so
9 |
10 | # Packages #
11 | ############
12 | # it's better to unpack these files and commit the raw source
13 | # git has its own built in compression methods
14 | *.7z
15 | *.dmg
16 | *.gz
17 | *.iso
18 | *.jar
19 | *.rar
20 | *.tar
21 | *.zip
22 |
23 | # Logs and databases #
24 | ######################
25 | *.log
26 |
27 | # OS generated files #
28 | ######################
29 | .DS_Store*
30 | ehthumbs.db
31 | Icon?
32 | Thumbs.db
33 |
34 | # Editor Files #
35 | ################
36 | *~
37 | *.swp
38 |
39 | # Gradle Files #
40 | ################
41 | .gradle
42 | .m2
43 |
44 | # Build output directies
45 | target/
46 | build/
47 |
48 | ### STS ###
49 | .apt_generated
50 | .classpath
51 | .factorypath
52 | .project
53 | .settings
54 | .springBeans
55 |
56 | # IntelliJ specific files/directories
57 | out
58 | .idea
59 | *.ipr
60 | *.iws
61 | *.iml
62 | atlassian-ide-plugin.xml
63 |
64 | # Eclipse specific files/directories
65 | .classpath
66 | .project
67 | .settings
68 | .metadata
69 |
70 | # NetBeans specific files/directories
71 | .nbattrs
72 | nbproject/private/
73 | build/
74 | nbbuild/
75 | dist/
76 | nbdist/
--------------------------------------------------------------------------------
/articles/drawios/bitchat-overview.xml:
--------------------------------------------------------------------------------
1 |
9 | * A client which can communicate with Server 10 | *
11 | * 12 | * @author houyi 13 | */ 14 | public interface Client { 15 | 16 | /** 17 | * connect server 18 | */ 19 | void connect(); 20 | 21 | /** 22 | * send request to server 23 | * 24 | * @param request the request packet 25 | * @return the response future 26 | */ 27 | CompletableFuture34 | * 2: a response packet 35 | * this kind of packet will be handled or not due to biz 36 | *
37 | * 3: a command packet
38 | * this kind of packet is a one way packet sent by server
39 | *
40 | * @param ctx the context
41 | * @param packet the response
42 | */
43 | @SuppressWarnings("unchecked")
44 | @Override
45 | public void channelRead0(ChannelHandlerContext ctx, Packet packet) {
46 | log.debug("ClientPacketDispatcher has received {}", packet);
47 | byte type = packet.getType();
48 | if (type == PacketType.PACKET_TYPE_REQUEST) {
49 | onRequest(ctx, packet);
50 | } else if (type == PacketType.PACKET_TYPE_RESPONSE) {
51 | onResponse(packet);
52 | } else {
53 | onCommand(ctx, packet);
54 | }
55 | }
56 |
57 | @Override
58 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
59 | ctx.close();
60 | log.error("ctx close,cause:", cause);
61 | }
62 |
63 | private void onRequest(ChannelHandlerContext ctx, Packet packet) {
64 | Payload payload = requestHandler.process(ctx, packet.getRequest());
65 | Packet response = PacketFactory.newResponsePacket(payload, packet.getId());
66 | writeResponse(ctx, response);
67 | }
68 |
69 | private void writeResponse(ChannelHandlerContext ctx, Packet response) {
70 | if (response != null) {
71 | ctx.channel().writeAndFlush(response);
72 | }
73 | }
74 |
75 | private void onResponse(Packet packet) {
76 | CompletableFuture
15 | * This ChannelHandler will check
16 | * whether the Channel is healthy or not
17 | *
20 | * It will keep a heart beat with
21 | * the Server by send a Ping
22 | * and the Server will return a Pong
23 | *
26 | * If the Channel is InActive it will
27 | * try to reconnect to Server
28 | *
24 | * A Client which connect to server directly
25 | *
36 | * A Client which connect to server by
37 | * request the {@link io.bitchat.core.ServerAttr}
38 | * from a ${@link io.bitchat.core.LoadBalancer}
39 | *
12 | * A ChannelHandler which will check
13 | * the idle state of each Channel
14 | *
17 | * This ChannelHandler can be added to
18 | * Client or Server Channel Pipeline
19 | *
22 | * But one thing should be confirmed is
23 | * that this ChannelHandler can not be
24 | * {@link io.netty.channel.ChannelHandler.Sharable}
25 | *
6 | * A load balancer can return a healthy server
7 | * and make sure the servers are load balanced
8 | *
11 | * A config which holds the address and port of a server
12 | *
5 | * A init Function
6 | * Any Class want to do some init work
7 | * just implements this interface will
8 | * be ok
9 | *
20 | * A InitAble executor
21 | *
22 | * The init method of this class
23 | * should be called before Server
24 | * startup to init all the InitAble
25 | * classes
26 | *
13 | * String
14 | * boolean
15 | * byte
16 | * short
17 | * int
18 | * long
19 | * float
20 | * double
21 | * char
22 | * Boolean
23 | * Byte
24 | * Short
25 | * Integer
26 | * Long
27 | * Float
28 | * Double
29 | * Character
30 | * BigInteger
31 | * BigDecimal
32 | *
33 | * @author yunfeng.cheng
34 | */
35 | public class PrimitiveTypeUtil {
36 |
37 | /**
38 | * 私有的构造函数防止用户进行实例化。
39 | */
40 | private PrimitiveTypeUtil() {
41 | }
42 |
43 | /**
44 | * 基本类型
45 | **/
46 | private static final Class>[] PRI_TYPE = {
47 | String.class,
48 | boolean.class,
49 | byte.class,
50 | short.class,
51 | int.class,
52 | long.class,
53 | float.class,
54 | double.class,
55 | char.class,
56 | Boolean.class,
57 | Byte.class,
58 | Short.class,
59 | Integer.class,
60 | Long.class,
61 | Float.class,
62 | Double.class,
63 | Character.class,
64 | BigInteger.class,
65 | BigDecimal.class
66 | };
67 |
68 | /**
69 | * 基本数组类型
70 | **/
71 | private static final Class>[] PRI_ARRAY_TYPE = {
72 | String[].class,
73 | boolean[].class,
74 | byte[].class,
75 | short[].class,
76 | int[].class,
77 | long[].class,
78 | float[].class,
79 | double[].class,
80 | char[].class,
81 | Boolean[].class,
82 | Byte[].class,
83 | Short[].class,
84 | Integer[].class,
85 | Long[].class,
86 | Float[].class,
87 | Double[].class,
88 | Character[].class,
89 | BigInteger[].class,
90 | BigDecimal[].class
91 | };
92 |
93 | /**
94 | * 基本类型默认值
95 | */
96 | private static final Map