├── bitchat-core ├── src │ ├── main │ │ ├── resources │ │ │ ├── config │ │ │ │ ├── snowflake-config.properties │ │ │ │ ├── thread-pool-config.properties │ │ │ │ └── base-config.properties │ │ │ └── logback.xml │ │ └── java │ │ │ └── io │ │ │ └── bitchat │ │ │ ├── http │ │ │ ├── maker │ │ │ │ ├── HtmlMakerEnum.java │ │ │ │ ├── HtmlMaker.java │ │ │ │ ├── DefaultHtmlMaker.java │ │ │ │ └── HtmlMakerFactory.java │ │ │ ├── exception │ │ │ │ ├── InvocationException.java │ │ │ │ └── ValidationException.java │ │ │ ├── view │ │ │ │ ├── HtmlKeyHolder.java │ │ │ │ ├── PageIndex.java │ │ │ │ ├── Page404.java │ │ │ │ ├── Page500.java │ │ │ │ └── PageError.java │ │ │ ├── controller │ │ │ │ ├── Controller.java │ │ │ │ ├── ControllerProxy.java │ │ │ │ ├── Mapping.java │ │ │ │ └── Param.java │ │ │ ├── converter │ │ │ │ ├── Converter.java │ │ │ │ ├── AbstractConverter.java │ │ │ │ └── PrimitiveTypeUtil.java │ │ │ ├── ContentType.java │ │ │ ├── RenderType.java │ │ │ ├── util │ │ │ │ ├── HtmlContentUtil.java │ │ │ │ └── HttpRenderUtil.java │ │ │ ├── RequestMethod.java │ │ │ ├── HttpContext.java │ │ │ └── router │ │ │ │ ├── BadClientSilencer.java │ │ │ │ └── RouteResult.java │ │ │ ├── lang │ │ │ ├── constants │ │ │ │ ├── ServiceName.java │ │ │ │ └── ResultCode.java │ │ │ ├── config │ │ │ │ ├── SnowflakeConfig.java │ │ │ │ ├── ThreadPoolConfig.java │ │ │ │ ├── ConfigFactory.java │ │ │ │ └── BaseConfig.java │ │ │ ├── util │ │ │ │ └── GenericsUtil.java │ │ │ └── BeanMapper.java │ │ │ ├── core │ │ │ ├── id │ │ │ │ ├── IdFactory.java │ │ │ │ ├── MemoryIdFactory.java │ │ │ │ └── SnowflakeIdFactory.java │ │ │ ├── init │ │ │ │ ├── InitAble.java │ │ │ │ ├── InitOrder.java │ │ │ │ └── Initializer.java │ │ │ ├── LoadBalancer.java │ │ │ ├── executor │ │ │ │ ├── Executor.java │ │ │ │ └── AbstractExecutor.java │ │ │ ├── ServerAttr.java │ │ │ └── IdleStateChecker.java │ │ │ ├── packet │ │ │ ├── interceptor │ │ │ │ ├── InterceptorBuilder.java │ │ │ │ ├── Interceptor.java │ │ │ │ ├── InterceptorHandler.java │ │ │ │ └── InterceptorProvider.java │ │ │ ├── PacketType.java │ │ │ ├── processor │ │ │ │ ├── CommandProcessor.java │ │ │ │ ├── Processor.java │ │ │ │ ├── RequestProcessor.java │ │ │ │ ├── AbstractCommandProcessor.java │ │ │ │ └── AbstractRequestProcessor.java │ │ │ ├── Command.java │ │ │ ├── factory │ │ │ │ ├── CommandFactory.java │ │ │ │ ├── PayloadFactory.java │ │ │ │ ├── RequestFactory.java │ │ │ │ └── PacketFactory.java │ │ │ ├── DefaultPacket.java │ │ │ ├── PendingPackets.java │ │ │ ├── Request.java │ │ │ ├── Payload.java │ │ │ ├── Packet.java │ │ │ ├── codec │ │ │ │ ├── PacketDecoder.java │ │ │ │ └── PacketEncoder.java │ │ │ └── ctx │ │ │ │ ├── CommandProcessorContext.java │ │ │ │ └── RequestProcessorContext.java │ │ │ ├── serialize │ │ │ ├── SerializerChooser.java │ │ │ ├── Serializer.java │ │ │ ├── JdkSerializer.java │ │ │ ├── FastJsonSerializer.java │ │ │ ├── SerializeAlgorithm.java │ │ │ ├── DefaultSerializerChooser.java │ │ │ ├── HessianSerializer.java │ │ │ ├── ProtoStuffSerializer.java │ │ │ └── KryoSerializer.java │ │ │ └── ws │ │ │ ├── PendingFrames.java │ │ │ ├── FrameFactory.java │ │ │ ├── Frame.java │ │ │ └── codec │ │ │ └── FrameCodec.java │ └── test │ │ └── java │ │ └── io │ │ └── bitchat │ │ ├── lang │ │ └── config │ │ │ └── ConfigTest.java │ │ ├── serialize │ │ └── SerializeTest.java │ │ └── core │ │ └── IdTest.java └── pom.xml ├── articles ├── resources │ └── bitchat-overview │ │ ├── login.jpg │ │ ├── register.jpg │ │ ├── list-user.jpg │ │ ├── cluster-arch.jpg │ │ ├── send-p2p-msg.jpg │ │ ├── client-connect.jpg │ │ ├── client-reconnect.jpg │ │ ├── received-p2p-msg.jpg │ │ ├── server-startup.jpg │ │ └── stand-alone-arch.jpg └── drawios │ └── bitchat-overview.xml ├── bitchat-server ├── src │ └── main │ │ └── java │ │ └── io │ │ └── bitchat │ │ └── server │ │ ├── channel │ │ ├── ChannelListener.java │ │ ├── ChannelType.java │ │ ├── ChannelHelper.java │ │ ├── ChannelManager.java │ │ ├── ChannelWrapper.java │ │ ├── DefaultChannelListener.java │ │ └── DefaultChannelManager.java │ │ ├── ServerAttrHolder.java │ │ ├── ServerSpeaker.java │ │ ├── session │ │ ├── Session.java │ │ ├── DefaultSessionManager.java │ │ ├── SessionManager.java │ │ ├── SessionHelper.java │ │ ├── DefaultSession.java │ │ └── AbstractSessionManager.java │ │ ├── Server.java │ │ ├── StandaloneServer.java │ │ ├── SessionIdKeeper.java │ │ ├── HeartBeatProcessor.java │ │ ├── ServerInitializer.java │ │ ├── ServerMode.java │ │ ├── ClusterServer.java │ │ ├── packet │ │ └── PacketExecutor.java │ │ ├── rest │ │ └── StatusController.java │ │ ├── ServerFactory.java │ │ ├── ws │ │ ├── FrameExecutor.java │ │ └── FrameHandler.java │ │ ├── ServerShell.java │ │ ├── SimpleServerFactory.java │ │ ├── ServerBootstrap.java │ │ ├── http │ │ └── HttpExecutor.java │ │ ├── AbstractServer.java │ │ └── ProtocolDispatcher.java └── pom.xml ├── bitchat-client ├── src │ └── main │ │ └── java │ │ └── io │ │ └── bitchat │ │ └── client │ │ ├── ClientMode.java │ │ ├── Client.java │ │ ├── ClientFactory.java │ │ ├── ClientInitializer.java │ │ ├── SimpleClientFactory.java │ │ ├── HealthyChecker.java │ │ ├── ClientHandler.java │ │ └── GenericClient.java └── pom.xml ├── bitchat-router ├── src │ └── main │ │ └── java │ │ └── io │ │ └── bitchat │ │ └── router │ │ ├── RouterServer.java │ │ └── RouterServerAttr.java └── pom.xml └── .gitignore /bitchat-core/src/main/resources/config/snowflake-config.properties: -------------------------------------------------------------------------------- 1 | workerId=1 2 | dataCenterId=1 -------------------------------------------------------------------------------- /articles/resources/bitchat-overview/login.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/all4you/bitchat/HEAD/articles/resources/bitchat-overview/login.jpg -------------------------------------------------------------------------------- /bitchat-core/src/main/resources/config/thread-pool-config.properties: -------------------------------------------------------------------------------- 1 | corePoolSize=10 2 | maxPoolSize=20 3 | idealTime=5 4 | blockingQueueCap=100 -------------------------------------------------------------------------------- /articles/resources/bitchat-overview/register.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/all4you/bitchat/HEAD/articles/resources/bitchat-overview/register.jpg -------------------------------------------------------------------------------- /articles/resources/bitchat-overview/list-user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/all4you/bitchat/HEAD/articles/resources/bitchat-overview/list-user.jpg -------------------------------------------------------------------------------- /articles/resources/bitchat-overview/cluster-arch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/all4you/bitchat/HEAD/articles/resources/bitchat-overview/cluster-arch.jpg -------------------------------------------------------------------------------- /articles/resources/bitchat-overview/send-p2p-msg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/all4you/bitchat/HEAD/articles/resources/bitchat-overview/send-p2p-msg.jpg -------------------------------------------------------------------------------- /articles/resources/bitchat-overview/client-connect.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/all4you/bitchat/HEAD/articles/resources/bitchat-overview/client-connect.jpg -------------------------------------------------------------------------------- /articles/resources/bitchat-overview/client-reconnect.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/all4you/bitchat/HEAD/articles/resources/bitchat-overview/client-reconnect.jpg -------------------------------------------------------------------------------- /articles/resources/bitchat-overview/received-p2p-msg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/all4you/bitchat/HEAD/articles/resources/bitchat-overview/received-p2p-msg.jpg -------------------------------------------------------------------------------- /articles/resources/bitchat-overview/server-startup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/all4you/bitchat/HEAD/articles/resources/bitchat-overview/server-startup.jpg -------------------------------------------------------------------------------- /articles/resources/bitchat-overview/stand-alone-arch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/all4you/bitchat/HEAD/articles/resources/bitchat-overview/stand-alone-arch.jpg -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/http/maker/HtmlMakerEnum.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.http.maker; 2 | 3 | /** 4 | * @author houyi 5 | **/ 6 | public enum HtmlMakerEnum { 7 | /** 8 | * 字符串 9 | */ 10 | STRING 11 | } 12 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/lang/constants/ServiceName.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.lang.constants; 2 | 3 | /** 4 | * @author houyi 5 | */ 6 | public interface ServiceName { 7 | 8 | String HEART_BEAT = "heartBeat"; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/core/id/IdFactory.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.core.id; 2 | 3 | /** 4 | * @author houyi 5 | */ 6 | public interface IdFactory { 7 | 8 | /** 9 | * get next id 10 | * @return next id 11 | */ 12 | long nextId(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/http/exception/InvocationException.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.http.exception; 2 | 3 | public class InvocationException extends Exception { 4 | private static final long serialVersionUID = 1L; 5 | 6 | public InvocationException(String message, Throwable cause) { 7 | super(message, cause); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /bitchat-server/src/main/java/io/bitchat/server/channel/ChannelListener.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.server.channel; 2 | 3 | import io.netty.channel.Channel; 4 | 5 | /** 6 | * @author houyi 7 | */ 8 | public interface ChannelListener { 9 | 10 | void channelActive(Channel channel, ChannelType channelType); 11 | 12 | void channelInactive(Channel channel); 13 | } 14 | -------------------------------------------------------------------------------- /bitchat-client/src/main/java/io/bitchat/client/ClientMode.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.client; 2 | 3 | /** 4 | * @author houyi 5 | */ 6 | public interface ClientMode { 7 | 8 | /** 9 | * direct connect to server mode 10 | */ 11 | int DIRECT_CONNECT_SERVER = 1; 12 | 13 | /** 14 | * connect to router mode 15 | */ 16 | int CONNECT_ROUTER = 2; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/http/view/HtmlKeyHolder.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.http.view; 2 | 3 | /** 4 | * @author houyi 5 | **/ 6 | public interface HtmlKeyHolder { 7 | 8 | /** 9 | * 未转义 10 | */ 11 | String START_NO_ESCAPE = "#["; 12 | 13 | /** 14 | * 对[转义 15 | */ 16 | String START_ESCAPE = "#\\["; 17 | 18 | String END = "]"; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /bitchat-core/src/main/resources/config/base-config.properties: -------------------------------------------------------------------------------- 1 | basePackage=io.bitchat 2 | serverPort=8864 3 | 4 | # the description sent to front end when server internal error occurred 5 | serverInternalError=Server Internal Error 6 | 7 | # the readerIdleTime for IdleStateChecker TimeUnit:Seconds 8 | readerIdleTime=60 9 | 10 | # the pingInterval for HealthyChecker TimeUnit:Seconds 11 | pingInterval=20 -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/core/init/InitAble.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.core.init; 2 | 3 | /** 4 | *
5 | * A init Function 6 | * Any Class want to do some init work 7 | * just implements this interface will 8 | * be ok 9 | *
10 | * 11 | * @author houyi 12 | */ 13 | public interface InitAble { 14 | 15 | /** 16 | * do init work 17 | */ 18 | void init(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /bitchat-router/src/main/java/io/bitchat/router/RouterServer.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.router; 2 | 3 | /** 4 | *5 | * A router server node 6 | *
7 | * 8 | * @author houyi 9 | */ 10 | public interface RouterServer { 11 | 12 | /** 13 | * start the router server 14 | */ 15 | void start(); 16 | 17 | /** 18 | * stop the router server 19 | */ 20 | void stop(); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/core/LoadBalancer.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.core; 2 | 3 | 4 | /** 5 | *6 | * A load balancer can return a healthy server 7 | * and make sure the servers are load balanced 8 | *
9 | * 10 | * @author houyi 11 | */ 12 | public interface LoadBalancer { 13 | 14 | /** 15 | * get a healthy server 16 | * 17 | * @return a healthy server 18 | */ 19 | ServerAttr nextServer(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/packet/interceptor/InterceptorBuilder.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.packet.interceptor; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * User can build an active interceptor list 7 | * 8 | * @author houyi 9 | **/ 10 | public interface InterceptorBuilder { 11 | 12 | /** 13 | * build the interceptor 14 | * 15 | * @return the list of the interceptor 16 | */ 17 | List5 | * Choose a Serializer according to the serialize algorithm 6 | *
7 | * 8 | * @author houyi 9 | */ 10 | public interface SerializerChooser { 11 | 12 | /** 13 | * choose a Serializer 14 | * 15 | * @param serializeAlgorithm the serialize algorithm 16 | * @return the Serializer 17 | */ 18 | Serializer choose(byte serializeAlgorithm); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /bitchat-server/src/main/java/io/bitchat/server/ServerAttrHolder.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.server; 2 | 3 | import io.bitchat.core.ServerAttr; 4 | 5 | /** 6 | * @author houyi 7 | */ 8 | public class ServerAttrHolder { 9 | 10 | private static ServerAttr serverAttr; 11 | 12 | public static void put(ServerAttr serverAttr) { 13 | ServerAttrHolder.serverAttr = serverAttr; 14 | } 15 | 16 | public static ServerAttr get() { 17 | return ServerAttrHolder.serverAttr; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/http/controller/Controller.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.http.controller; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.TYPE) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface Controller { 11 | 12 | /** 13 | * 请求uri 14 | * 15 | * @return url 16 | */ 17 | String path() default ""; 18 | 19 | } -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/http/exception/ValidationException.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.http.exception; 2 | 3 | /** 4 | * ValidationException 5 | * 6 | * @author houyi 7 | */ 8 | public class ValidationException extends RuntimeException { 9 | private static final long serialVersionUID = 1L; 10 | 11 | public ValidationException(String s) { 12 | super(s); 13 | } 14 | 15 | public ValidationException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/http/converter/Converter.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.http.converter; 2 | 3 | /** 4 | * 类型转换器所需要实现的总接口。TypeConverter中有唯一的一个方法,实现类请特别注意方法所需要返回的值。 5 | * @author houyi 6 | * @date 2017-10-20 7 | */ 8 | public interface Converter { 9 | 10 | /** 11 | * 类型转换 12 | * @param source 需要被转换的值 13 | * @param toType 需要被转换成的类型 14 | * @param params 转值时需要提供的可选参数 15 | * @return 经转换过的类型,如果实现类没有能力进行所指定的类型转换,应返回null 16 | */ 17 | Object convert(Object source, Class> toType, Object... params); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/packet/PacketType.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.packet; 2 | 3 | /** 4 | *5 | * PacketType 6 | *
7 | * 8 | * @author houyi 9 | */ 10 | public interface PacketType { 11 | 12 | /** 13 | * packet type: request 14 | */ 15 | byte PACKET_TYPE_REQUEST = (byte) 1; 16 | 17 | /** 18 | * packet type: response 19 | */ 20 | byte PACKET_TYPE_RESPONSE = (byte) 2; 21 | 22 | /** 23 | * packet type: command 24 | */ 25 | byte PACKET_TYPE_COMMAND = (byte) 3; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /bitchat-router/pom.xml: -------------------------------------------------------------------------------- 1 | 2 |8 | * A command processor 9 | * is aim to process the command 10 | *
11 | * 12 | * @author houyi 13 | */ 14 | public interface CommandProcessor { 15 | 16 | /** 17 | * process the command 18 | * 19 | * @param ctx the ctx 20 | * @param command the command 21 | */ 22 | void process(ChannelHandlerContext ctx, Command command); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/packet/processor/Processor.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.packet.processor; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 10 | *11 | * specify the name of 12 | * each {@link RequestProcessor} 13 | * or {@link CommandProcessor} 14 | *
15 | * 16 | * 17 | * @author houyi 18 | */ 19 | @Target({ElementType.TYPE}) 20 | @Retention(RetentionPolicy.RUNTIME) 21 | public @interface Processor { 22 | 23 | String name(); 24 | 25 | } -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/packet/Command.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.packet; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.io.Serializable; 7 | import java.util.Map; 8 | 9 | /** 10 | *11 | * A command model 12 | *
13 | * 14 | * @author houyi 15 | */ 16 | @Data 17 | @NoArgsConstructor 18 | public class Command implements Serializable { 19 | 20 | /** 21 | * the name of the command 22 | */ 23 | private String commandName; 24 | 25 | /** 26 | * the command content 27 | */ 28 | private Map8 | * A Server Speaker will transmit command between two servers 9 | *
10 | * 11 | * @author houyi 12 | */ 13 | public interface ServerSpeaker { 14 | 15 | /** 16 | * transmit a command to another Server 17 | * 18 | * @param serverAttr the server attr 19 | * @param packet the request packet 20 | * @return the response packet 21 | */ 22 | Packet speak(ServerAttr serverAttr, Packet packet); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /bitchat-client/src/main/java/io/bitchat/client/Client.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.client; 2 | 3 | import io.bitchat.packet.Packet; 4 | 5 | import java.util.concurrent.CompletableFuture; 6 | 7 | /** 8 | *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 | CompletableFuture7 | * A server node 8 | *
9 | * 10 | * @author houyi 11 | */ 12 | public interface Server { 13 | 14 | /** 15 | * return the attribute of current server 16 | * 17 | * @return the attribute of the server 18 | */ 19 | ServerAttr attribute(); 20 | 21 | /** 22 | * start the server 23 | */ 24 | void start(); 25 | 26 | /** 27 | * stop the server 28 | */ 29 | void stop(); 30 | 31 | /** 32 | * register to a router 33 | */ 34 | void registerToRouter(); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/lang/util/GenericsUtil.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.lang.util; 2 | 3 | import cn.hutool.core.util.NetUtil; 4 | 5 | import java.util.LinkedHashSet; 6 | 7 | /** 8 | * @author houyi 9 | */ 10 | public class GenericsUtil { 11 | 12 | /** 13 | * get local ip V4 14 | * 15 | * @return ipV4 16 | */ 17 | public static String getLocalIpV4() { 18 | LinkedHashSet11 | * A Config which holds the address and port of a router server 12 | *
13 | * 14 | * @author houyi 15 | */ 16 | @Data 17 | @Builder 18 | @NoArgsConstructor 19 | @AllArgsConstructor 20 | public class RouterServerAttr { 21 | private String address; 22 | private Integer port; 23 | 24 | public boolean valid() { 25 | return StrUtil.isNotBlank(address) && port != null; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/packet/processor/RequestProcessor.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.packet.processor; 2 | 3 | import io.bitchat.packet.Payload; 4 | import io.bitchat.packet.Request; 5 | import io.netty.channel.ChannelHandlerContext; 6 | 7 | /** 8 | *9 | * A request processor 10 | * is aim to process the request 11 | * and return a payload 12 | *
13 | * 14 | * @author houyi 15 | */ 16 | public interface RequestProcessor { 17 | 18 | /** 19 | * process the request 20 | * 21 | * @param ctx the ctx 22 | * @param request the request 23 | * @return the response 24 | */ 25 | Payload process(ChannelHandlerContext ctx, Request request); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/lang/constants/ResultCode.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.lang.constants; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * @author houyi 7 | */ 8 | @Getter 9 | public enum ResultCode { 10 | 11 | SUCCESS(200, "SUCCESS"), 12 | INTERNAL_ERROR(301, "INTERNAL_ERROR"), 13 | RESOURCE_NOT_FOUND(404, "RESOURCE_NOT_FOUND"), 14 | BIZ_FAIL(3001, "BIZ_FAIL"), 15 | RECORD_ALREADY_EXISTS(4001, "RECORD_ALREADY_EXISTS"), 16 | PARAM_INVALID(4002, "PARAM_INVALID"), 17 | ; 18 | 19 | private int code; 20 | private String message; 21 | 22 | ResultCode(int code, String message) { 23 | this.code = code; 24 | this.message = message; 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/packet/DefaultPacket.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.packet; 2 | 3 | 4 | import io.bitchat.packet.factory.PacketFactory; 5 | import io.bitchat.serialize.SerializeAlgorithm; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | *10 | * the only implement of {@link Packet} 11 | * user can create a packet by {@link PacketFactory} 12 | *
13 | * 14 | * @author houyi 15 | */ 16 | @NoArgsConstructor 17 | public class DefaultPacket extends Packet { 18 | 19 | @Override 20 | public byte algorithm() { 21 | return SerializeAlgorithm.PROTO_STUFF.getType(); 22 | } 23 | 24 | @Override 25 | public boolean handleAsync() { 26 | return false; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/packet/factory/PayloadFactory.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.packet.factory; 2 | 3 | import io.bitchat.lang.constants.ResultCode; 4 | import io.bitchat.packet.Payload; 5 | 6 | /** 7 | * @author houyi 8 | */ 9 | public class PayloadFactory { 10 | 11 | public static Payload newErrorPayload(int code, String msg) { 12 | Payload payload = new Payload(); 13 | payload.setErrorMsg(code, msg); 14 | return payload; 15 | } 16 | 17 | public static Payload newSuccessPayload() { 18 | Payload payload = new Payload(); 19 | payload.setSuccessMsg(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage()); 20 | return payload; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /bitchat-server/src/main/java/io/bitchat/server/StandaloneServer.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.server; 2 | 3 | import io.bitchat.server.channel.ChannelListener; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | /** 7 | *8 | * A standalone server 9 | *
10 | * 11 | * @author houyi 12 | */ 13 | @Slf4j 14 | public class StandaloneServer extends AbstractServer { 15 | 16 | public StandaloneServer(Integer serverPort) { 17 | super(serverPort); 18 | } 19 | 20 | public StandaloneServer(Integer serverPort, ChannelListener channelListener) { 21 | super(serverPort, channelListener); 22 | } 23 | 24 | @Override 25 | public void registerToRouter() { 26 | // do nothing 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/ws/PendingFrames.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.ws; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.util.Map; 6 | import java.util.concurrent.CompletableFuture; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | /** 10 | * @author houyi 11 | */ 12 | @Slf4j 13 | public class PendingFrames { 14 | 15 | private static Map5 | * A session id keeper aim to get the session id of p2p or group chat 6 | *
7 | * 8 | * @author houyi 9 | */ 10 | public interface SessionIdKeeper { 11 | 12 | /** 13 | * return the session id of p2p chat 14 | * 15 | * @param userId one user id 16 | * @param partnerId another user id 17 | * @return the session id 18 | */ 19 | Long p2pSessionId(Long userId, Long partnerId); 20 | 21 | /** 22 | * return the session id of group chat 23 | * 24 | * @param groupId group id 25 | * @return the session id 26 | */ 27 | Long groupSessionId(Long groupId); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/http/RenderType.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.http; 2 | 3 | /** 4 | * 返回的响应类型 5 | * 6 | * @author houyi 7 | */ 8 | public enum RenderType { 9 | 10 | /** 11 | * JSON 12 | */ 13 | JSON("application/json;charset=UTF-8"), 14 | /** 15 | * XML 16 | */ 17 | XML("text/xml;charset=UTF-8"), 18 | /** 19 | * TEXT 20 | */ 21 | TEXT("text/plain;charset=UTF-8"), 22 | /** 23 | * HTML 24 | */ 25 | HTML("text/html;charset=UTF-8"); 26 | 27 | private String contentType; 28 | 29 | RenderType(String contentType) { 30 | this.contentType = contentType; 31 | } 32 | 33 | public String getContentType() { 34 | return contentType; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/serialize/Serializer.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.serialize; 2 | 3 | /** 4 | *5 | * A Serializer which can serialize or deserialize object 6 | *
7 | * 8 | * @author houyi 9 | */ 10 | public interface Serializer { 11 | 12 | /** 13 | * serialize the object 14 | * 15 | * @param object the object need to serialize 16 | * @return byte array 17 | */ 18 | byte[] serialize(Object object); 19 | 20 | /** 21 | * deserialize the byte array 22 | * 23 | * @param bytes the byte array need to deserialize 24 | * @param clazz the class type byte array will deserialize to 25 | * @return object 26 | */ 27 |10 | * A pre handle interceptor 11 | *
12 | * 13 | * @author houyi 14 | **/ 15 | public abstract class Interceptor { 16 | 17 | /** 18 | * pre handle method of the interceptor 19 | * 20 | * @param channel the channel 21 | * @param packet the packet 22 | * @return the response 23 | */ 24 | public Payload preHandle(Channel channel, Packet packet) { 25 | return PayloadFactory.newSuccessPayload(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /bitchat-server/src/main/java/io/bitchat/server/channel/ChannelType.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.server.channel; 2 | 3 | import lombok.Getter; 4 | 5 | import java.util.Arrays; 6 | 7 | /** 8 | * @author houyi 9 | */ 10 | @Getter 11 | public enum ChannelType { 12 | /** 13 | * Packet 14 | */ 15 | Packet(1), 16 | /** 17 | * WebSocket 18 | */ 19 | WebSocket(2); 20 | 21 | private int type; 22 | 23 | ChannelType(int type) { 24 | this.type = type; 25 | } 26 | 27 | public static ChannelType getChannelType(int type) { 28 | return Arrays.stream(values()) 29 | .filter(channelType -> channelType.getType() == type) 30 | .findFirst() 31 | .orElse(null); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/core/executor/Executor.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.core.executor; 2 | 3 | import io.netty.util.concurrent.Future; 4 | import io.netty.util.concurrent.Promise; 5 | 6 | /** 7 | * @author houyi 8 | */ 9 | public interface Executor11 | * A request model 12 | *
13 | * 14 | * @author houyi 15 | */ 16 | @Data 17 | @NoArgsConstructor 18 | public class Request implements Serializable { 19 | 20 | /** 21 | * the name of the service 22 | */ 23 | private String serviceName; 24 | 25 | /** 26 | * the name of the method 27 | * if null will use doProcess() 28 | * as default 29 | */ 30 | private String methodName; 31 | 32 | /** 33 | * the request params 34 | */ 35 | private Map11 | * A config which holds the address and port of a server 12 | *
13 | * 14 | * @author houyi 15 | */ 16 | @Data 17 | @Builder 18 | @NoArgsConstructor 19 | @AllArgsConstructor 20 | public class ServerAttr { 21 | private String address; 22 | private int port; 23 | 24 | public static ServerAttr getLocalServer(int serverPort) { 25 | return ServerAttr.builder() 26 | .address(GenericsUtil.getLocalIpV4()) 27 | .port(serverPort) 28 | .build(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /bitchat-server/src/main/java/io/bitchat/server/session/SessionManager.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.server.session; 2 | 3 | import io.bitchat.server.channel.ChannelType; 4 | import io.netty.channel.ChannelId; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author houyi 10 | */ 11 | public interface SessionManager { 12 | 13 | boolean exists(ChannelType channelType, long userId); 14 | 15 | Session newSession(); 16 | 17 | void bound(Session session, ChannelId channelId, long userId); 18 | 19 | void removeSession(ChannelId channelId); 20 | 21 | Session getSession(String sessionId); 22 | 23 | List10 | * A cluster server 11 | *
12 | * 13 | * @author houyi 14 | */ 15 | @Slf4j 16 | public class ClusterServer extends AbstractServer { 17 | 18 | private RouterServerAttr routerServerAttr; 19 | 20 | public ClusterServer(Integer serverPort, RouterServerAttr routerServerAttr) { 21 | this(serverPort, null, routerServerAttr); 22 | } 23 | 24 | public ClusterServer(Integer serverPort, ChannelListener channelListener, RouterServerAttr routerServerAttr) { 25 | super(serverPort, channelListener); 26 | Assert.notNull(routerServerAttr, "routerServerAttr can not be null"); 27 | this.routerServerAttr = routerServerAttr; 28 | } 29 | 30 | @Override 31 | public void registerToRouter() { 32 | // register to router 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/lang/config/ConfigFactory.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.lang.config; 2 | 3 | import org.aeonbits.owner.Config; 4 | 5 | import java.util.Map; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | /** 9 | *10 | * A Config Factory which can 11 | * provide singleton Config instance 12 | *
13 | * 14 | * @author houyi 15 | */ 16 | public class ConfigFactory { 17 | 18 | private static Map9 | * A serialize algorithm enum 10 | *
11 | * 12 | * @author houyi 13 | */ 14 | @Getter 15 | public enum SerializeAlgorithm { 16 | 17 | /** 18 | * JDK 19 | */ 20 | JDK((byte) 1), 21 | /** 22 | * fastJson 23 | */ 24 | FAST_JSON((byte) 2), 25 | /** 26 | * hessian 27 | */ 28 | HESSIAN((byte) 3), 29 | /** 30 | * kryo 31 | */ 32 | KRYO((byte) 4), 33 | /** 34 | * protoStuff 35 | */ 36 | PROTO_STUFF((byte) 5); 37 | 38 | private byte type; 39 | 40 | SerializeAlgorithm(byte type) { 41 | this.type = type; 42 | } 43 | 44 | /** 45 | * get the enum class 46 | * 47 | * @param type the type 48 | * @return the enum class 49 | */ 50 | public static SerializeAlgorithm getEnum(Byte type) { 51 | return type == null ? null : Arrays.stream(values()) 52 | .filter(t -> t.getType() == type) 53 | .findFirst() 54 | .orElse(null); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /.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/ -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/packet/processor/AbstractCommandProcessor.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.packet.processor; 2 | 3 | import io.bitchat.packet.Command; 4 | import io.bitchat.packet.ctx.RequestProcessorContext; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | import java.util.Map; 9 | 10 | /** 11 | *12 | * An abstract CommandProcessor 13 | * Every Class extends AbstractCommandProcessor should have a NoArgument Constructor 14 | * see {@link RequestProcessorContext#initRequestProcessor()} 15 | *
16 | * 17 | * @author houyi 18 | */ 19 | @Slf4j 20 | public abstract class AbstractCommandProcessor implements CommandProcessor { 21 | 22 | @Override 23 | public void process(ChannelHandlerContext ctx, Command command) { 24 | try { 25 | doProcess(ctx, command.getContent()); 26 | } catch (Exception e) { 27 | log.error("process occurred error, cause={}", e.getMessage(), e); 28 | } 29 | } 30 | 31 | /** 32 | * do process 33 | * 34 | * @param content the command content 35 | */ 36 | public abstract void doProcess(ChannelHandlerContext ctx, Map24 | * A Client which connect to server directly 25 | *
26 | * 27 | * @param serverAttr the serverAttr 28 | */ 29 | @Override 30 | public Client newClient(ServerAttr serverAttr) { 31 | return new GenericClient(serverAttr); 32 | } 33 | 34 | /** 35 | *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 | *
40 | * 41 | * @param loadBalancer the load balancer 42 | */ 43 | @Override 44 | public Client newBalancedClient(LoadBalancer loadBalancer) { 45 | return new GenericClient(loadBalancer); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /bitchat-server/src/main/java/io/bitchat/server/packet/PacketExecutor.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.server.packet; 2 | 3 | import cn.hutool.core.lang.Singleton; 4 | import io.bitchat.core.executor.AbstractExecutor; 5 | import io.bitchat.packet.Packet; 6 | import io.bitchat.packet.factory.PacketFactory; 7 | import io.bitchat.packet.Payload; 8 | import io.bitchat.packet.ctx.RequestProcessorContext; 9 | import io.netty.channel.ChannelHandlerContext; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | /** 13 | * @author houyi 14 | */ 15 | @Slf4j 16 | public class PacketExecutor extends AbstractExecutor7 | * A default Serializer Chooser implement 8 | *
9 | * 10 | * @author houyi 11 | */ 12 | public class DefaultSerializerChooser implements SerializerChooser { 13 | 14 | private DefaultSerializerChooser() { 15 | 16 | } 17 | 18 | public static SerializerChooser getInstance() { 19 | return Singleton.get(DefaultSerializerChooser.class); 20 | } 21 | 22 | @Override 23 | public Serializer choose(byte serializeAlgorithm) { 24 | SerializeAlgorithm algorithm = SerializeAlgorithm.getEnum(serializeAlgorithm); 25 | if (algorithm == null) { 26 | return null; 27 | } 28 | switch (algorithm) { 29 | case JDK: { 30 | return JdkSerializer.getInstance(); 31 | } 32 | case FAST_JSON: { 33 | return FastJsonSerializer.getInstance(); 34 | } 35 | case HESSIAN: { 36 | return HessianSerializer.getInstance(); 37 | } 38 | case KRYO: { 39 | return KryoSerializer.getInstance(); 40 | } 41 | case PROTO_STUFF: { 42 | return ProtoStuffSerializer.getInstance(); 43 | } 44 | default: { 45 | return null; 46 | } 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/packet/processor/AbstractRequestProcessor.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.packet.processor; 2 | 3 | import io.bitchat.lang.constants.ResultCode; 4 | import io.bitchat.packet.Payload; 5 | import io.bitchat.packet.Request; 6 | import io.bitchat.packet.ctx.RequestProcessorContext; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | import java.util.Map; 11 | 12 | /** 13 | *14 | * An abstract RequestProcessor 15 | * Every Class extends AbstractRequestProcessor should have a NoArgument Constructor 16 | * see {@link RequestProcessorContext#initRequestProcessor()} 17 | *
18 | * @author houyi 19 | */ 20 | @Slf4j 21 | public abstract class AbstractRequestProcessor implements RequestProcessor { 22 | 23 | @Override 24 | public Payload process(ChannelHandlerContext ctx, Request request) { 25 | Payload payload = new Payload(); 26 | try { 27 | return doProcess(ctx, request.getParams()); 28 | } catch (Exception e) { 29 | payload.setErrorMsg(ResultCode.INTERNAL_ERROR.getCode(), e.getMessage()); 30 | log.error("process occurred error, cause={}", e.getMessage(), e); 31 | } 32 | return payload; 33 | } 34 | 35 | /** 36 | * do process 37 | * 38 | * @param params the request params 39 | * @return the response 40 | */ 41 | public abstract Payload doProcess(ChannelHandlerContext ctx, Map").append("Error Occur,Cause:").append("
").append(StrUtil.CRLF) 27 | .append(StrUtil.TAB).append(StrUtil.TAB).append("").append(HtmlKeyHolder.START_NO_ESCAPE + "errorMessage" + HtmlKeyHolder.END).append("
").append(StrUtil.CRLF) 28 | .append(StrUtil.TAB).append("12 | * A ChannelHandler which will check 13 | * the idle state of each Channel 14 | *
15 | * 16 | *17 | * This ChannelHandler can be added to 18 | * Client or Server Channel Pipeline 19 | *
20 | * 21 | *22 | * But one thing should be confirmed is 23 | * that this ChannelHandler can not be 24 | * {@link io.netty.channel.ChannelHandler.Sharable} 25 | *
26 | * 27 | * @author houyi 28 | */ 29 | @Slf4j 30 | public class IdleStateChecker extends IdleStateHandler { 31 | 32 | private static final int DEFAULT_READER_IDLE_TIME = 15; 33 | 34 | private int readerTime; 35 | 36 | public IdleStateChecker(int readerIdleTime) { 37 | super(readerIdleTime == 0 ? DEFAULT_READER_IDLE_TIME : readerIdleTime, 0, 0, TimeUnit.SECONDS); 38 | readerTime = readerIdleTime == 0 ? DEFAULT_READER_IDLE_TIME : readerIdleTime; 39 | } 40 | 41 | @Override 42 | protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) { 43 | log.warn("[{}] Hasn't read data after {} seconds, will close the channel:{}", IdleStateChecker.class.getSimpleName(), readerTime, ctx.channel()); 44 | ctx.channel().close(); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /bitchat-server/src/main/java/io/bitchat/server/channel/DefaultChannelListener.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.server.channel; 2 | 3 | import cn.hutool.core.lang.Singleton; 4 | import io.bitchat.server.session.DefaultSessionManager; 5 | import io.bitchat.server.session.SessionHelper; 6 | import io.bitchat.server.session.SessionManager; 7 | import io.netty.channel.Channel; 8 | import io.netty.channel.ChannelId; 9 | import lombok.extern.slf4j.Slf4j; 10 | 11 | /** 12 | * @author houyi 13 | */ 14 | @Slf4j 15 | public class DefaultChannelListener implements ChannelListener { 16 | 17 | private ChannelManager channelManager; 18 | private SessionManager sessionManager; 19 | 20 | private DefaultChannelListener() { 21 | channelManager = DefaultChannelManager.getInstance(); 22 | sessionManager = DefaultSessionManager.getInstance(); 23 | } 24 | 25 | public static ChannelListener getInstance() { 26 | return Singleton.get(DefaultChannelListener.class); 27 | } 28 | 29 | @Override 30 | public void channelActive(Channel channel, ChannelType channelType) { 31 | channelManager.addChannel(channel, channelType); 32 | log.info("Add a new Channel={}, channelType={}", channel, channelType); 33 | } 34 | 35 | @Override 36 | public void channelInactive(Channel channel) { 37 | ChannelId channelId = channel.id(); 38 | channelManager.removeChannel(channelId); 39 | sessionManager.removeSession(channelId); 40 | SessionHelper.markOffline(channel); 41 | log.info("Remove an inactive Channel={}", channel); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /bitchat-server/src/main/java/io/bitchat/server/ServerFactory.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.server; 2 | 3 | import io.bitchat.router.RouterServerAttr; 4 | import io.bitchat.server.channel.ChannelListener; 5 | 6 | /** 7 | *8 | * A server factory 9 | *
10 | * 11 | * @author houyi 12 | */ 13 | public interface ServerFactory { 14 | 15 | /** 16 | * create a standalone server 17 | * 18 | * @param serverPort the server port 19 | * @return the server 20 | */ 21 | Server newServer(Integer serverPort); 22 | 23 | /** 24 | * create a standalone server 25 | * 26 | * @param serverPort the server port 27 | * @param channelListener the channelListener 28 | * @return the server 29 | */ 30 | Server newServer(Integer serverPort, ChannelListener channelListener); 31 | 32 | /** 33 | * create a cluster server 34 | * 35 | * @param serverPort the server port 36 | * @param routerServerAttr the router attr 37 | * @return the server 38 | */ 39 | Server newClusterServer(Integer serverPort, RouterServerAttr routerServerAttr); 40 | 41 | /** 42 | * create a cluster server 43 | * 44 | * @param serverPort the server port 45 | * @param channelListener the channelListener 46 | * @param routerServerAttr the router attr 47 | * @return the server 48 | */ 49 | Server newClusterServer(Integer serverPort, ChannelListener channelListener, RouterServerAttr routerServerAttr); 50 | 51 | /** 52 | * get current server 53 | * 54 | * @return current server 55 | */ 56 | Server currentServer(); 57 | 58 | } 59 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/packet/factory/PacketFactory.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.packet.factory; 2 | 3 | import io.bitchat.core.id.IdFactory; 4 | import io.bitchat.core.id.SnowflakeIdFactory; 5 | import io.bitchat.lang.constants.ServiceName; 6 | import io.bitchat.packet.*; 7 | 8 | /** 9 | * @author houyi 10 | */ 11 | public class PacketFactory { 12 | 13 | private static IdFactory idFactory = SnowflakeIdFactory.getInstance(); 14 | 15 | public static Packet newRequestPacket(Request request, long id) { 16 | Packet packet = new DefaultPacket(); 17 | packet.setId(id); 18 | packet.setType(PacketType.PACKET_TYPE_REQUEST); 19 | packet.setRequest(request); 20 | return packet; 21 | } 22 | 23 | public static Packet newResponsePacket(Payload payload, long id) { 24 | Packet packet = new DefaultPacket(); 25 | packet.setId(id); 26 | packet.setType(PacketType.PACKET_TYPE_RESPONSE); 27 | packet.setPayload(payload); 28 | return packet; 29 | } 30 | 31 | public static Packet newCmdPacket(Command command) { 32 | // command packet can not provide id 33 | Packet packet = new DefaultPacket(); 34 | packet.setType(PacketType.PACKET_TYPE_COMMAND); 35 | packet.setCommand(command); 36 | return packet; 37 | } 38 | 39 | public static Packet newPingPacket() { 40 | Request request = new Request(); 41 | request.setServiceName(ServiceName.HEART_BEAT); 42 | Packet packet = PacketFactory.newRequestPacket(request, idFactory.nextId()); 43 | packet.setHandleAsync(false); 44 | return packet; 45 | } 46 | 47 | 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /bitchat-server/src/main/java/io/bitchat/server/ws/FrameExecutor.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.server.ws; 2 | 3 | import cn.hutool.core.lang.Singleton; 4 | import cn.hutool.core.util.StrUtil; 5 | import com.alibaba.fastjson.JSON; 6 | import io.bitchat.core.executor.AbstractExecutor; 7 | import io.bitchat.lang.BeanMapper; 8 | import io.bitchat.packet.Payload; 9 | import io.bitchat.packet.ctx.RequestProcessorContext; 10 | import io.bitchat.packet.Request; 11 | import io.bitchat.ws.Frame; 12 | import io.bitchat.ws.FrameFactory; 13 | import io.netty.channel.ChannelHandlerContext; 14 | import lombok.extern.slf4j.Slf4j; 15 | 16 | import java.util.Map; 17 | 18 | /** 19 | * @author houyi 20 | */ 21 | @Slf4j 22 | public class FrameExecutor extends AbstractExecutor { 23 | 24 | private RequestProcessorContext processorContext; 25 | 26 | private FrameExecutor() { 27 | this.processorContext = RequestProcessorContext.getInstance(); 28 | } 29 | 30 | public static FrameExecutor getInstance() { 31 | return Singleton.get(FrameExecutor.class); 32 | } 33 | 34 | @SuppressWarnings("unchecked") 35 | @Override 36 | public Frame doExecute(Object... request) { 37 | ChannelHandlerContext ctx = (ChannelHandlerContext) request[0]; 38 | Frame frame = (Frame) request[1]; 39 | Request req = BeanMapper.map(frame, Request.class); 40 | String paramJson = frame.getParamJson(); 41 | if (StrUtil.isNotBlank(paramJson)) { 42 | // 将json转换成Map 43 | req.setParams(JSON.parseObject(paramJson, Map.class)); 44 | } 45 | Payload payload = processorContext.process(ctx, req); 46 | return FrameFactory.newResponseFrame(payload, frame.getId()); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /bitchat-server/src/main/java/io/bitchat/server/ServerShell.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.server; 2 | 3 | import com.beust.jcommander.JCommander; 4 | import com.beust.jcommander.Parameter; 5 | import io.bitchat.router.RouterServerAttr; 6 | 7 | /** 8 | *9 | * A Main class that can be startup by jar or shell 10 | *
11 | * 12 | * @author houyi 13 | */ 14 | public class ServerShell { 15 | 16 | public static void main(String[] args) { 17 | ServerStartupParameter param = new ServerStartupParameter(); 18 | JCommander.newBuilder() 19 | .addObject(param) 20 | .build() 21 | .parse(args); 22 | ServerMode serverMode = ServerMode.getEnum(param.mode); 23 | RouterServerAttr routerServerAttr = RouterServerAttr.builder() 24 | .address(param.routerAddress) 25 | .port(param.routerPort) 26 | .build(); 27 | Integer serverPort = param.serverPort; 28 | 29 | ServerBootstrap bootstrap = new ServerBootstrap(); 30 | bootstrap.serverMode(serverMode) 31 | .routerServerAttr(routerServerAttr) 32 | .start(serverPort); 33 | } 34 | 35 | 36 | private static class ServerStartupParameter { 37 | 38 | @Parameter(names = "-mode", description = "Server mode. 1 : standalone mode 2 : cluster mode. If null will use default mode: standalone") 39 | private Integer mode; 40 | 41 | @Parameter(names = "-serverPort", description = "Server port. If null will use default port: 8864") 42 | private Integer serverPort; 43 | 44 | @Parameter(names = "-routerAddress", description = "Router address.") 45 | private String routerAddress; 46 | 47 | @Parameter(names = "-routerPort", description = "Router port.") 48 | private Integer routerPort; 49 | 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /bitchat-server/src/main/java/io/bitchat/server/SimpleServerFactory.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.server; 2 | 3 | import cn.hutool.core.lang.Singleton; 4 | import io.bitchat.router.RouterServerAttr; 5 | import io.bitchat.server.channel.ChannelListener; 6 | 7 | /** 8 | * @author houyi 9 | */ 10 | public class SimpleServerFactory implements ServerFactory { 11 | 12 | private Server currentServer; 13 | 14 | private SimpleServerFactory() { 15 | 16 | } 17 | 18 | public static ServerFactory getInstance() { 19 | return Singleton.get(SimpleServerFactory.class); 20 | } 21 | 22 | @Override 23 | public Server newServer(Integer serverPort) { 24 | currentServer = new StandaloneServer(serverPort); 25 | ServerAttrHolder.put(currentServer.attribute()); 26 | return currentServer; 27 | } 28 | 29 | @Override 30 | public Server newServer(Integer serverPort, ChannelListener channelListener) { 31 | currentServer = new StandaloneServer(serverPort, channelListener); 32 | ServerAttrHolder.put(currentServer.attribute()); 33 | return currentServer; 34 | } 35 | 36 | @Override 37 | public Server newClusterServer(Integer serverPort, RouterServerAttr routerServerAttr) { 38 | currentServer = new ClusterServer(serverPort, routerServerAttr); 39 | ServerAttrHolder.put(currentServer.attribute()); 40 | return currentServer; 41 | } 42 | 43 | @Override 44 | public Server newClusterServer(Integer serverPort, ChannelListener channelListener, RouterServerAttr routerServerAttr) { 45 | currentServer = new ClusterServer(serverPort, channelListener, routerServerAttr); 46 | ServerAttrHolder.put(currentServer.attribute()); 47 | return currentServer; 48 | } 49 | 50 | @Override 51 | public Server currentServer() { 52 | return currentServer; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /bitchat-core/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 |10 | * A server bootstrap 11 | *
12 | * 13 | * @author houyi 14 | */ 15 | public class ServerBootstrap { 16 | 17 | private ServerMode serverMode = ServerMode.STAND_ALONE; 18 | 19 | private RouterServerAttr routerServerAttr; 20 | 21 | private ChannelListener channelListener; 22 | 23 | public ServerBootstrap serverMode(ServerMode serverMode) { 24 | this.serverMode = serverMode == null ? ServerMode.STAND_ALONE : serverMode; 25 | return this; 26 | } 27 | 28 | public ServerBootstrap routerServerAttr(RouterServerAttr routerServerAttr) { 29 | if (ServerMode.CLUSTER == serverMode) { 30 | Assert.notNull(routerServerAttr, "routerServerAttr can not be null"); 31 | Assert.isTrue(routerServerAttr.valid(), "routerServerAttr is invalid"); 32 | this.routerServerAttr = routerServerAttr; 33 | } 34 | return this; 35 | } 36 | 37 | public ServerBootstrap channelListener(Class extends ChannelListener> channelListener) { 38 | this.channelListener = Singleton.get(channelListener); 39 | return this; 40 | } 41 | 42 | public void start(Integer serverPort) { 43 | ServerFactory factory = SimpleServerFactory.getInstance(); 44 | Server server; 45 | if (ServerMode.STAND_ALONE == serverMode) { 46 | server = factory.newServer(serverPort, channelListener); 47 | } else { 48 | Assert.notNull(routerServerAttr, "routerServerAttr can not be null cause you are starting the server in cluster mode"); 49 | Assert.isTrue(routerServerAttr.valid(), "routerServerAttr is invalid"); 50 | server = factory.newClusterServer(serverPort, channelListener, routerServerAttr); 51 | } 52 | server.start(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /bitchat-client/src/main/java/io/bitchat/client/HealthyChecker.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.client; 2 | 3 | import cn.hutool.core.lang.Assert; 4 | import io.bitchat.packet.Packet; 5 | import io.bitchat.packet.factory.PacketFactory; 6 | import io.netty.channel.Channel; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.channel.ChannelInboundHandlerAdapter; 9 | import lombok.extern.slf4j.Slf4j; 10 | 11 | import java.util.concurrent.TimeUnit; 12 | 13 | /** 14 | *15 | * This ChannelHandler will check 16 | * whether the Channel is healthy or not 17 | *
18 | * 19 | *20 | * It will keep a heart beat with 21 | * the Server by send a Ping 22 | * and the Server will return a Pong 23 | *
24 | * 25 | *26 | * If the Channel is InActive it will 27 | * try to reconnect to Server 28 | *
29 | * 30 | * @author houyi 31 | */ 32 | @Slf4j 33 | public class HealthyChecker extends ChannelInboundHandlerAdapter { 34 | 35 | private static final int DEFAULT_PING_INTERVAL = 5; 36 | 37 | private Client client; 38 | 39 | private int pingInterval; 40 | 41 | public HealthyChecker(Client client, int pingInterval) { 42 | Assert.notNull(client, "client can not be null"); 43 | this.client = client; 44 | this.pingInterval = pingInterval <= 0 ? DEFAULT_PING_INTERVAL : pingInterval; 45 | } 46 | 47 | @Override 48 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 49 | schedulePing(ctx); 50 | ctx.fireChannelActive(); 51 | } 52 | 53 | private void schedulePing(ChannelHandlerContext ctx) { 54 | ctx.executor().schedule(() -> { 55 | Channel channel = ctx.channel(); 56 | if (channel.isActive()) { 57 | Packet pingPacket = PacketFactory.newPingPacket(); 58 | channel.writeAndFlush(pingPacket); 59 | log.debug("[{}] Send a Ping={}", HealthyChecker.class.getSimpleName(), pingPacket); 60 | schedulePing(ctx); 61 | } 62 | }, pingInterval, TimeUnit.SECONDS); 63 | } 64 | 65 | @Override 66 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 67 | ctx.executor().schedule(() -> { 68 | log.info("[{}] Try to reconnecting...", HealthyChecker.class.getSimpleName()); 69 | client.connect(); 70 | }, 5, TimeUnit.SECONDS); 71 | ctx.fireChannelInactive(); 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/serialize/HessianSerializer.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.serialize; 2 | 3 | import cn.hutool.core.lang.Assert; 4 | import cn.hutool.core.lang.Singleton; 5 | import com.caucho.hessian.io.Hessian2Input; 6 | import com.caucho.hessian.io.Hessian2Output; 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | import java.io.ByteArrayInputStream; 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | 13 | /** 14 | * @author houyi 15 | */ 16 | @Slf4j 17 | public class HessianSerializer implements Serializer { 18 | 19 | private HessianSerializer() { 20 | 21 | } 22 | 23 | public static Serializer getInstance() { 24 | return Singleton.get(HessianSerializer.class); 25 | } 26 | 27 | @Override 28 | public byte[] serialize(Object object) { 29 | Assert.notNull(object, "the serialize object can not be null"); 30 | byte[] bytes = null; 31 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 32 | Hessian2Output hessian2Output = new Hessian2Output(outputStream); 33 | try { 34 | hessian2Output.writeObject(object); 35 | hessian2Output.flush(); 36 | bytes = outputStream.toByteArray(); 37 | } catch (IOException e) { 38 | log.warn("HessianSerializer [serialize] error, cause:{}", e.getMessage(), e); 39 | } finally { 40 | try { 41 | outputStream.close(); 42 | } catch (IOException e) { 43 | e.printStackTrace(); 44 | } 45 | } 46 | return bytes; 47 | } 48 | 49 | @SuppressWarnings("unchecked") 50 | @Override 51 | public12 | * The structure of a Packet is like blow: 13 | * +----------+----------+----------------------------+ 14 | * | size | value | intro | 15 | * +----------+----------+----------------------------+ 16 | * | 1 bytes | 0xBF | magic number | 17 | * | 1 bytes | | the type 1:req 2:res 3:cmd| 18 | * | 4 bytes | | content length | 19 | * | ? bytes | | the content | 20 | * +----------+----------+----------------------------+ 21 | *
22 | * 23 | */ 24 | @Data 25 | public class Frame implements Serializable { 26 | 27 | /** 28 | * the magic number of frame 29 | * 0xBF means BitChat Frame 30 | */ 31 | public static byte FRAME_MAGIC = (byte) 0xBF; 32 | 33 | /** 34 | * magic number 35 | */ 36 | private byte magic = Frame.FRAME_MAGIC; 37 | 38 | /** 39 | * the unique id 40 | * JS中要创建一个long的数值比较麻烦 41 | * 所以改成String类型 42 | */ 43 | private String id; 44 | 45 | /** 46 | *47 | * specify the type of the packet 48 | * 1-request 49 | * 2-response 50 | * 3-command 51 | *
52 | */ 53 | private byte type; 54 | 55 | // Request 56 | /** 57 | * the name of the service 58 | */ 59 | private String serviceName; 60 | 61 | /** 62 | * the name of the method 63 | */ 64 | private String methodName; 65 | 66 | /** 67 | * the request params 68 | * fix me 69 | * 如果通过protobuf js的库无法直接解析成Map 70 | * 所以通过在js中将参数转成JSON字符串 71 | * 然后再通过FastJson将字符串转成Map 72 | */ 73 | private String paramJson; 74 | 75 | 76 | // Payload 77 | /** 78 | * the status of request 79 | */ 80 | private boolean success; 81 | 82 | /** 83 | * the code of request 84 | */ 85 | private int code; 86 | 87 | /** 88 | * the errorMsg of request 89 | */ 90 | private String msg; 91 | 92 | /** 93 | * the result of request 94 | */ 95 | private String resultJson; 96 | 97 | 98 | // Command 99 | /** 100 | * the name of the command 101 | */ 102 | private String commandName; 103 | 104 | /** 105 | * the command content 106 | */ 107 | private String contentJson; 108 | 109 | 110 | } 111 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/core/executor/AbstractExecutor.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.core.executor; 2 | 3 | import io.bitchat.lang.config.ConfigFactory; 4 | import io.bitchat.lang.config.ThreadPoolConfig; 5 | import io.netty.util.concurrent.DefaultThreadFactory; 6 | import io.netty.util.concurrent.Future; 7 | import io.netty.util.concurrent.Promise; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | import java.util.concurrent.ArrayBlockingQueue; 11 | import java.util.concurrent.ThreadPoolExecutor; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | /** 15 | * @author houyi 16 | */ 17 | @Slf4j 18 | public abstract class AbstractExecutor9 | * A base abstract packet 10 | * Each Detailed Packet should extends the {@link Packet} 11 | *
12 | * 13 | *14 | * The structure of a Packet is like blow: 15 | * +----------+----------+----------------------------+ 16 | * | size | value | intro | 17 | * +----------+----------+----------------------------+ 18 | * | 1 bytes | 0xBC | magic number | 19 | * | 1 bytes | | serialize algorithm | 20 | * | 1 bytes | | the type 1:req 2:res 3:cmd| 21 | * | 4 bytes | | content length | 22 | * | ? bytes | | the content | 23 | * +----------+----------+----------------------------+ 24 | *
25 | * 26 | * 27 | * @author houyi 28 | */ 29 | @Data 30 | public abstract class Packet implements Serializable { 31 | 32 | /** 33 | * the magic number of packet 34 | * 0xBC means BitChat 35 | */ 36 | public static byte PACKET_MAGIC = (byte) 0xBC; 37 | 38 | /** 39 | * magic number 40 | */ 41 | private byte magic = Packet.PACKET_MAGIC; 42 | 43 | /** 44 | * the unique id 45 | */ 46 | private long id; 47 | 48 | /** 49 | *50 | * specify the type of the packet 51 | * 1-request 52 | * 2-response 53 | * 3-command 54 | *
55 | */ 56 | private byte type; 57 | 58 | /** 59 | * the request model 60 | */ 61 | private Request request; 62 | 63 | /** 64 | * the response 65 | */ 66 | private Payload payload; 67 | 68 | /** 69 | * the command 70 | */ 71 | private Command command; 72 | 73 | /** 74 | *75 | * the serialize algorithm 76 | *
77 | */ 78 | public byte algorithm = algorithm(); 79 | 80 | /** 81 | * whether handler the packet 82 | * in async way 83 | */ 84 | private boolean handleAsync = handleAsync(); 85 | 86 | /** 87 | *88 | * specify the serialize algorithm 89 | *
90 | * 91 | *92 | * the packet will be encode and decode 93 | * by the serialize algorithm 94 | *
95 | * 96 | * @return the serialize algorithm 97 | */ 98 | public abstract byte algorithm(); 99 | 100 | /** 101 | *102 | * whether handle the packet 103 | * in async way 104 | *
105 | * 106 | * @return the byte value of whether 107 | * handle packet in async way 108 | */ 109 | public abstract boolean handleAsync(); 110 | 111 | } 112 | -------------------------------------------------------------------------------- /bitchat-core/src/main/java/io/bitchat/packet/codec/PacketDecoder.java: -------------------------------------------------------------------------------- 1 | package io.bitchat.packet.codec; 2 | 3 | import io.bitchat.packet.DefaultPacket; 4 | import io.bitchat.packet.Packet; 5 | import io.bitchat.serialize.DefaultSerializerChooser; 6 | import io.bitchat.serialize.Serializer; 7 | import io.bitchat.serialize.SerializerChooser; 8 | import io.netty.buffer.ByteBuf; 9 | import io.netty.channel.ChannelHandlerContext; 10 | import io.netty.handler.codec.DecoderException; 11 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 12 | 13 | /** 14 | *15 | * the header field contains: 16 | *
25 | * see {@link Packet} for detail
26 | *
27 | * @author houyi
28 | */
29 | public class PacketDecoder extends LengthFieldBasedFrameDecoder {
30 |
31 | private SerializerChooser chooser;
32 |
33 | public static final int MAX_FRAME_LENGTH = Integer.MAX_VALUE;
34 |
35 | public static final int LENGTH_FIELD_OFFSET = 3;
36 |
37 | public static final int LENGTH_FIELD_LENGTH = 4;
38 |
39 | public PacketDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, SerializerChooser chooser) {
40 | super(maxFrameLength, lengthFieldOffset, lengthFieldLength);
41 | this.chooser = chooser != null ? chooser : DefaultSerializerChooser.getInstance();
42 | }
43 |
44 | @Override
45 | protected Packet decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
46 | // get the whole tcp package
47 | // decoded by super class
48 | ByteBuf decodedIn = (ByteBuf) super.decode(ctx, in);
49 | if (decodedIn == null) {
50 | return null;
51 | }
52 |
53 | // check the header length
54 | int leastPacketLen = LENGTH_FIELD_OFFSET + LENGTH_FIELD_LENGTH;
55 | if (decodedIn.readableBytes() < leastPacketLen) {
56 | throw new DecoderException("packet header length less than leastPacketLen=" + leastPacketLen);
57 | }
58 |
59 | byte magic = decodedIn.readByte();
60 | byte algorithm = decodedIn.readByte();
61 | Serializer serializer = chooser.choose(algorithm);
62 | if (serializer == null) {
63 | throw new DecoderException("serialize algorithm is invalid");
64 | }
65 |
66 | byte type = decodedIn.readByte();
67 | int len = decodedIn.readInt();
68 | if (decodedIn.readableBytes() != len) {
69 | throw new DecoderException("packet content marked length is not equals to actual length");
70 | }
71 |
72 | // read content
73 | byte[] content = new byte[len];
74 | decodedIn.readBytes(content);
75 |
76 | return serializer.deserialize(content, DefaultPacket.class);
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/bitchat-core/src/main/java/io/bitchat/http/HttpContext.java:
--------------------------------------------------------------------------------
1 | package io.bitchat.http;
2 |
3 | import cn.hutool.core.collection.CollectionUtil;
4 | import io.netty.channel.ChannelHandlerContext;
5 | import io.netty.handler.codec.http.HttpRequest;
6 | import io.netty.handler.codec.http.HttpResponse;
7 | import io.netty.handler.codec.http.cookie.Cookie;
8 | import io.netty.util.concurrent.FastThreadLocal;
9 | import lombok.extern.slf4j.Slf4j;
10 |
11 | import java.util.HashSet;
12 | import java.util.Set;
13 |
14 | /**
15 | * @author houyi
16 | **/
17 | @Slf4j
18 | public class HttpContext {
19 |
20 | /**
21 | * 使用FastThreadLocal替代JDK自带的ThreadLocal以提升并发性能
22 | */
23 | private static final FastThreadLocal
34 | * 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
20 | * Server frame ChannelHandler
21 | *
35 | * 通过source.getClass() 获得源Class
36 | */
37 | public static
45 | * 通过source.getClass() 获得源Class
46 | */
47 | public static
55 | * 预先通过BeanMapper.getType() 静态获取并缓存Type类型,在此处传入
56 | */
57 | public static
64 | * 不建议使用mapper.mapAsList(Iterable
73 | * 预先通过BeanMapper.getType() 静态获取并缓存Type类型,在此处传入
74 | */
75 | public static
82 | * 通过source.getComponentType() 获得源Class
83 | */
84 | public static
91 | * 预先通过BeanMapper.getType() 静态获取并缓存Type类型,在此处传入
92 | */
93 | public static
19 | * A Generic Packet Encoder
20 | *
21 | * A Generic Frame Codec
22 | * D map(S source, Class void map(S source, D d) {
48 | mapper.map(source, d);
49 | }
50 |
51 |
52 | /**
53 | * 极致性能的复制出新类型对象.
54 | * D map(S source, Type sourceType, Type,Class List sourceList, Class sourceClass, Class List sourceList, Type sourceType, Type D[] mapArray(final D[] destination, final S[] source, final Class D[] mapArray(D[] destination, S[] source, Type sourceType, Type