阅读这篇文章之前,建议先阅读和这篇文章关联的内容。

[1]详细剖析分布式微服务架构下网络通信的底层实现原理(图解)

[2][年薪60W的技巧]工作了5年,你真的理解Netty以及为什么要用吗?(深度干货)

[3]深度解析Netty中的核心组件(图解+实例)

[4]BAT面试必问细节:关于Netty中的ByteBuf详解

[5]通过大量实战案例分解Netty中是如何解决拆包黏包问题的?

[6]基于Netty实现自定义消息通信协议(协议设计及解析应用实战)

[7]全网最详细最齐全的序列化技术及深度解析与应用实战

在前面的内容中,我们已经由浅入深的理解了Netty的基础知识和实现原理,相信大家已经对Netty有了一个较为全面的理解。那么接下来,我们通过一个手写RPC通信的实战案例来带大家了解Netty的实际应用。

为什么要选择RPC来作为实战呢?因为Netty本身就是解决通信问题,而在实际应用中,RPC协议框架是我们接触得最多的一种,所以这个实战能让大家了解到Netty实际应用之外,还能理解RPC的底层原理。

什么是RPC

RPC全称为(Remote Procedure Call),是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议,简单理解就是让开发者能够像调用本地服务一样调用远程服务。

既然是协议,那么它必然有协议的规范,如图6-1所示。

为了达到“让开发者能够像调用本地服务那样调用远程服务”的目的,RPC协议需像图6-1那样实现远程交互。

  • 客户端调用远程服务时,必须要通过本地动态代理模块来屏蔽网络通信的细节,所以动态代理模块需要负责将请求参数、方法等数据组装成数据包发送到目标服务器
  • 这个数据包在发送时,还需要遵循约定的消息协议以及序列化协议,最终转化为二进制数据流传输
  • 服务端收到数据包后,先按照约定的消息协议解码,得到请求信息。
  • 服务端再根据请求信息路由调用到目标服务,获得结果并返回给客户端。

图6-1

业内主流的RPC框架

凡是满足RPC协议的框架,我们成为RPC框架,在实际开发中,我们可以使用开源且相对成熟的RPC框架解决微服务架构下的远程通信问题,常见的rpc框架:

  1. Thrift:thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。
  2. Dubbo:Dubbo是一个分布式服务框架,以及SOA治理方案。其功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等。 Dubbo是阿里巴巴内部的SOA服务化治理方案的核心框架,Dubbo自2011年开源后,已被许多非阿里系公司使用。

手写RPC注意要点

基于上文中对于RPC协议的理解,如果我们自己去实现,需要考虑哪些技术呢? 其实基于图6-1的整个流程应该有一个大概的理解。

  • 通信协议,RPC框架对性能的要求非常高,所以通信协议应该是越简单越好,这样可以减少编解码带来的性能损耗,大部分主流的RPC框架会直接选择TCP、HTTP协议。
  • 序列化和反序列化,数据要进行网络传输,需要对数据进行序列化和反序列化,前面我们说过,所谓的序列化和反序列化是不把对象转化成二进制流以及将二进制流转化成对象的过程。在序列化框架选择上,我们一般会选择高效且通用的算法,比如FastJson、Protobuf、Hessian等。这些序列化技术都要比原生的序列化操作更加高效,压缩比也较高。
  • 动态代理, 客户端调用远程服务时,需要通过动态代理来屏蔽网络通信细节。而动态代理又是在运行过程中生成的,所以动态代理类的生成速度、字节码大小都会影响到RPC整体框架的性能和资源消耗。常见的动态代理技术: Javassist、Cglib、JDK的动态代理等。

基于Netty手写实现RPC

理解了RPC协议后,我们基于Netty来实现一个RPC通信框架。

代码详见附件 netty-rpc-example

图6-2 项目模块组成

需要引入的jar包:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.projectlombok</groupId>
  7. <artifactId>lombok</artifactId>
  8. </dependency>
  9. <dependency>
  10. <groupId>com.alibaba</groupId>
  11. <artifactId>fastjson</artifactId>
  12. <version>1.2.72</version>
  13. </dependency>
  14. <dependency>
  15. <groupId>io.netty</groupId>
  16. <artifactId>netty-all</artifactId>
  17. </dependency>

模块依赖关系:

  • provider依赖 netty-rpc-protocol和netty-rpc-api

  • cosumer依赖 netty-rpc-protocol和netty-rpc-api

netty-rpc-api模块

图6-3 netty-rpc-api模块组成

IUserService

  1. public interface IUserService {
  2. String saveUser(String name);
  3. }

netty-rpc-provider模块

图6-4 netty-rpc-provider模块组成

UserServiceImpl

  1. @Service
  2. @Slf4j
  3. public class UserServiceImpl implements IUserService {
  4. @Override
  5. public String saveUser(String name) {
  6. log.info("begin saveUser:"+name);
  7. return "Save User Success!";
  8. }
  9. }

NettyRpcProviderMain

注意,在当前步骤中,描述了case的部分,暂时先不用加,后续再加上

  1. @ComponentScan(basePackages = {"com.example.spring","com.example.service"}) //case1(后续再加上)
  2. @SpringBootApplication
  3. public class NettyRpcProviderMain {
  4. public static void main(String[] args) throws Exception {
  5. SpringApplication.run(NettyRpcProviderMain.class, args);
  6. new NettyServer("127.0.0.1",8080).startNettyServer(); //case2(后续再加上)
  7. }
  8. }

netty-rpc-protocol

开始写通信协议模块,这个模块主要做几个事情

  • 定义消息协议
  • 定义序列化反序列化方法
  • 建立netty通信

图6-5

定义消息协议

之前我们讲过自定义消息协议,我们在这里可以按照下面这个协议格式来定义好。

  1. /*
  2. +----------------------------------------------+
  3. | 魔数 2byte | 序列化算法 1byte | 请求类型 1byte |
  4. +----------------------------------------------+
  5. | 消息 ID 8byte | 数据长度 4byte |
  6. +----------------------------------------------+
  7. */

Header

  1. @AllArgsConstructor
  2. @Data
  3. public class Header implements Serializable {
  4. /*
  5. +----------------------------------------------+
  6. | 魔数 2byte | 序列化算法 1byte | 请求类型 1byte |
  7. +----------------------------------------------+
  8. | 消息 ID 8byte | 数据长度 4byte |
  9. +----------------------------------------------+
  10. */
  11. private short magic; //魔数-用来验证报文的身份(2个字节)
  12. private byte serialType; //序列化类型(1个字节)
  13. private byte reqType; //操作类型(1个字节)
  14. private long requestId; //请求id(8个字节)
  15. private int length; //数据长度(4个字节)
  16. }

RpcRequest

  1. @Data
  2. public class RpcRequest implements Serializable {
  3. private String className;
  4. private String methodName;
  5. private Object[] params;
  6. private Class<?>[] parameterTypes;
  7. }

RpcResponse

  1. @Data
  2. public class RpcResponse implements Serializable {
  3. private Object data;
  4. private String msg;
  5. }

RpcProtocol

  1. @Data
  2. public class RpcProtocol<T> implements Serializable {
  3. private Header header;
  4. private T content;
  5. }

定义相关常量

上述消息协议定义中,涉及到几个枚举相关的类,定义如下

ReqType

消息类型

  1. public enum ReqType {
  2. REQUEST((byte)1),
  3. RESPONSE((byte)2),
  4. HEARTBEAT((byte)3);
  5. private byte code;
  6. private ReqType(byte code) {
  7. this.code=code;
  8. }
  9. public byte code(){
  10. return this.code;
  11. }
  12. public static ReqType findByCode(int code) {
  13. for (ReqType msgType : ReqType.values()) {
  14. if (msgType.code() == code) {
  15. return msgType;
  16. }
  17. }
  18. return null;
  19. }
  20. }

SerialType

序列化类型

  1. public enum SerialType {
  2. JSON_SERIAL((byte)0),
  3. JAVA_SERIAL((byte)1);
  4. private byte code;
  5. SerialType(byte code) {
  6. this.code=code;
  7. }
  8. public byte code(){
  9. return this.code;
  10. }
  11. }

RpcConstant

  1. public class RpcConstant {
  2. //header部分的总字节数
  3. public final static int HEAD_TOTAL_LEN=16;
  4. //魔数
  5. public final static short MAGIC=0xca;
  6. }

定义序列化相关实现

这里演示两种,一种是JSON方式,另一种是Java原生的方式

ISerializer

  1. public interface ISerializer {
  2. <T> byte[] serialize(T obj);
  3. <T> T deserialize(byte[] data,Class<T> clazz);
  4. byte getType();
  5. }

JavaSerializer

  1. public class JavaSerializer implements ISerializer{
  2. @Override
  3. public <T> byte[] serialize(T obj) {
  4. ByteArrayOutputStream byteArrayOutputStream=
  5. new ByteArrayOutputStream();
  6. try {
  7. ObjectOutputStream outputStream=
  8. new ObjectOutputStream(byteArrayOutputStream);
  9. outputStream.writeObject(obj);
  10. return byteArrayOutputStream.toByteArray();
  11. } catch (IOException e) {
  12. e.printStackTrace();
  13. }
  14. return new byte[0];
  15. }
  16. @Override
  17. public <T> T deserialize(byte[] data, Class<T> clazz) {
  18. ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(data);
  19. try {
  20. ObjectInputStream objectInputStream=
  21. new ObjectInputStream(byteArrayInputStream);
  22. return (T) objectInputStream.readObject();
  23. } catch (IOException e) {
  24. e.printStackTrace();
  25. } catch (ClassNotFoundException e) {
  26. e.printStackTrace();
  27. }
  28. return null;
  29. }
  30. @Override
  31. public byte getType() {
  32. return SerialType.JAVA_SERIAL.code();
  33. }
  34. }

JsonSerializer

  1. public class JsonSerializer implements ISerializer{
  2. @Override
  3. public <T> byte[] serialize(T obj) {
  4. return JSON.toJSONString(obj).getBytes();
  5. }
  6. @Override
  7. public <T> T deserialize(byte[] data, Class<T> clazz) {
  8. return JSON.parseObject(new String(data),clazz);
  9. }
  10. @Override
  11. public byte getType() {
  12. return SerialType.JSON_SERIAL.code();
  13. }
  14. }

SerializerManager

实现对序列化机制的管理

  1. public class SerializerManager {
  2. private final static ConcurrentHashMap<Byte, ISerializer> serializers=new ConcurrentHashMap<Byte, ISerializer>();
  3. static {
  4. ISerializer jsonSerializer=new JsonSerializer();
  5. ISerializer javaSerializer=new JavaSerializer();
  6. serializers.put(jsonSerializer.getType(),jsonSerializer);
  7. serializers.put(javaSerializer.getType(),javaSerializer);
  8. }
  9. public static ISerializer getSerializer(byte key){
  10. ISerializer serializer=serializers.get(key);
  11. if(serializer==null){
  12. return new JavaSerializer();
  13. }
  14. return serializer;
  15. }
  16. }

定义编码和解码实现

由于自定义了消息协议,所以 需要自己实现编码和解码,代码如下

RpcDecoder

  1. @Slf4j
  2. public class RpcDecoder extends ByteToMessageDecoder {
  3. /*
  4. +----------------------------------------------+
  5. | 魔数 2byte | 序列化算法 1byte | 请求类型 1byte |
  6. +----------------------------------------------+
  7. | 消息 ID 8byte | 数据长度 4byte |
  8. +----------------------------------------------+
  9. */
  10. @Override
  11. protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
  12. log.info("==========begin RpcDecoder ==============");
  13. if(in.readableBytes()< RpcConstant.HEAD_TOTAL_LEN){
  14. //消息长度不够,不需要解析
  15. return;
  16. }
  17. in.markReaderIndex();//标记一个读取数据的索引,后续用来重置。
  18. short magic=in.readShort(); //读取magic
  19. if(magic!=RpcConstant.MAGIC){
  20. throw new IllegalArgumentException("Illegal request parameter 'magic',"+magic);
  21. }
  22. byte serialType=in.readByte(); //读取序列化算法类型
  23. byte reqType=in.readByte(); //请求类型
  24. long requestId=in.readLong(); //请求消息id
  25. int dataLength=in.readInt(); //请求数据长度
  26. //可读区域的字节数小于实际数据长度
  27. if(in.readableBytes()<dataLength){
  28. in.resetReaderIndex();
  29. return;
  30. }
  31. //读取消息内容
  32. byte[] content=new byte[dataLength];
  33. in.readBytes(content);
  34. //构建header头信息
  35. Header header=new Header(magic,serialType,reqType,requestId,dataLength);
  36. ISerializer serializer=SerializerManager.getSerializer(serialType);
  37. ReqType rt=ReqType.findByCode(reqType);
  38. switch(rt){
  39. case REQUEST:
  40. RpcRequest request=serializer.deserialize(content, RpcRequest.class);
  41. RpcProtocol<RpcRequest> reqProtocol=new RpcProtocol<>();
  42. reqProtocol.setHeader(header);
  43. reqProtocol.setContent(request);
  44. out.add(reqProtocol);
  45. break;
  46. case RESPONSE:
  47. RpcResponse response=serializer.deserialize(content,RpcResponse.class);
  48. RpcProtocol<RpcResponse> resProtocol=new RpcProtocol<>();
  49. resProtocol.setHeader(header);
  50. resProtocol.setContent(response);
  51. out.add(resProtocol);
  52. break;
  53. case HEARTBEAT:
  54. break;
  55. default:
  56. break;
  57. }
  58. }
  59. }

RpcEncoder

  1. @Slf4j
  2. public class RpcEncoder extends MessageToByteEncoder<RpcProtocol<Object>> {
  3. /*
  4. +----------------------------------------------+
  5. | 魔数 2byte | 序列化算法 1byte | 请求类型 1byte |
  6. +----------------------------------------------+
  7. | 消息 ID 8byte | 数据长度 4byte |
  8. +----------------------------------------------+
  9. */
  10. @Override
  11. protected void encode(ChannelHandlerContext ctx, RpcProtocol<Object> msg, ByteBuf out) throws Exception {
  12. log.info("=============begin RpcEncoder============");
  13. Header header=msg.getHeader();
  14. out.writeShort(header.getMagic()); //写入魔数
  15. out.writeByte(header.getSerialType()); //写入序列化类型
  16. out.writeByte(header.getReqType());//写入请求类型
  17. out.writeLong(header.getRequestId()); //写入请求id
  18. ISerializer serializer= SerializerManager.getSerializer(header.getSerialType());
  19. byte[] data=serializer.serialize(msg.getContent()); //序列化
  20. header.setLength(data.length);
  21. out.writeInt(data.length); //写入消息长度
  22. out.writeBytes(data);
  23. }
  24. }

NettyServer

实现NettyServer构建。

  1. @Slf4j
  2. public class NettyServer{
  3. private String serverAddress; //地址
  4. private int serverPort; //端口
  5. public NettyServer(String serverAddress, int serverPort) {
  6. this.serverAddress = serverAddress;
  7. this.serverPort = serverPort;
  8. }
  9. public void startNettyServer() throws Exception {
  10. log.info("begin start Netty Server");
  11. EventLoopGroup bossGroup=new NioEventLoopGroup();
  12. EventLoopGroup workGroup=new NioEventLoopGroup();
  13. try {
  14. ServerBootstrap bootstrap = new ServerBootstrap();
  15. bootstrap.group(bossGroup, workGroup)
  16. .channel(NioServerSocketChannel.class)
  17. .childHandler(new RpcServerInitializer());
  18. ChannelFuture channelFuture = bootstrap.bind(this.serverAddress, this.serverPort).sync();
  19. log.info("Server started Success on Port:{}", this.serverPort);
  20. channelFuture.channel().closeFuture().sync();
  21. }catch (Exception e){
  22. log.error("Rpc Server Exception",e);
  23. }finally {
  24. workGroup.shutdownGracefully();
  25. bossGroup.shutdownGracefully();
  26. }
  27. }
  28. }

RpcServerInitializer

  1. public class RpcServerInitializer extends ChannelInitializer<SocketChannel> {
  2. @Override
  3. protected void initChannel(SocketChannel ch) throws Exception {
  4. ch.pipeline()
  5. .addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,12,4,0,0))
  6. .addLast(new RpcDecoder())
  7. .addLast(new RpcEncoder())
  8. .addLast(new RpcServerHandler());
  9. }
  10. }

RpcServerHandler

  1. public class RpcServerHandler extends SimpleChannelInboundHandler<RpcProtocol<RpcRequest>> {
  2. @Override
  3. protected void channelRead0(ChannelHandlerContext ctx, RpcProtocol<RpcRequest> msg) throws Exception {
  4. RpcProtocol resProtocol=new RpcProtocol<>();
  5. Header header=msg.getHeader();
  6. header.setReqType(ReqType.RESPONSE.code());
  7. Object result=invoke(msg.getContent());
  8. resProtocol.setHeader(header);
  9. RpcResponse response=new RpcResponse();
  10. response.setData(result);
  11. response.setMsg("success");
  12. resProtocol.setContent(response);
  13. ctx.writeAndFlush(resProtocol);
  14. }
  15. private Object invoke(RpcRequest request){
  16. try {
  17. Class<?> clazz=Class.forName(request.getClassName());
  18. Object bean= SpringBeansManager.getBean(clazz); //获取实例对象(CASE)
  19. Method declaredMethod=clazz.getDeclaredMethod(request.getMethodName(),request.getParameterTypes());
  20. return declaredMethod.invoke(bean,request.getParams());
  21. } catch (ClassNotFoundException | NoSuchMethodException e) {
  22. e.printStackTrace();
  23. } catch (IllegalAccessException e) {
  24. e.printStackTrace();
  25. } catch (InvocationTargetException e) {
  26. e.printStackTrace();
  27. }
  28. return null;
  29. }
  30. @Override
  31. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  32. super.exceptionCaught(ctx, cause);
  33. }
  34. }

SpringBeansManager

  1. @Component
  2. public class SpringBeansManager implements ApplicationContextAware {
  3. private static ApplicationContext applicationContext;
  4. @Override
  5. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  6. SpringBeansManager.applicationContext=applicationContext;
  7. }
  8. public static <T> T getBean(Class<T> clazz){
  9. return applicationContext.getBean(clazz);
  10. }
  11. }

需要注意,这个类的构建好之后,需要在netty-rpc-provider模块的main方法中增加compone-scan进行扫描

  1. @ComponentScan(basePackages = {"com.example.spring","com.example.service"}) //修改这里
  2. @SpringBootApplication
  3. public class NettyRpcProviderMain {
  4. public static void main(String[] args) throws Exception {
  5. SpringApplication.run(NettyRpcProviderMain.class, args);
  6. new NettyServer("127.0.0.1",8080).startNettyServer(); // 修改这里
  7. }
  8. }

netty-rpc-consumer

接下来开始实现消费端

RpcClientProxy

  1. public class RpcClientProxy {
  2. public <T> T clientProxy(final Class<T> interfaceCls,final String host,final int port){
  3. return (T) Proxy.newProxyInstance
  4. (interfaceCls.getClassLoader(),
  5. new Class<?>[]{interfaceCls},
  6. new RpcInvokerProxy(host,port));
  7. }
  8. }

RpcInvokerProxy

  1. @Slf4j
  2. public class RpcInvokerProxy implements InvocationHandler {
  3. private String serviceAddress;
  4. private int servicePort;
  5. public RpcInvokerProxy(String serviceAddress, int servicePort) {
  6. this.serviceAddress = serviceAddress;
  7. this.servicePort = servicePort;
  8. }
  9. @Override
  10. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  11. log.info("begin invoke target server");
  12. //组装参数
  13. RpcProtocol<RpcRequest> protocol=new RpcProtocol<>();
  14. long requestId= RequestHolder.REQUEST_ID.incrementAndGet();
  15. Header header=new Header(RpcConstant.MAGIC, SerialType.JSON_SERIAL.code(), ReqType.REQUEST.code(),requestId,0);
  16. protocol.setHeader(header);
  17. RpcRequest request=new RpcRequest();
  18. request.setClassName(method.getDeclaringClass().getName());
  19. request.setMethodName(method.getName());
  20. request.setParameterTypes(method.getParameterTypes());
  21. request.setParams(args);
  22. protocol.setContent(request);
  23. //发送请求
  24. NettyClient nettyClient=new NettyClient(serviceAddress,servicePort);
  25. //构建异步数据处理
  26. RpcFuture<RpcResponse> future=new RpcFuture<>(new DefaultPromise<>(new DefaultEventLoop()));
  27. RequestHolder.REQUEST_MAP.put(requestId,future);
  28. nettyClient.sendRequest(protocol);
  29. return future.getPromise().get().getData();
  30. }
  31. }

定义客户端连接

在netty-rpc-protocol这个模块的protocol包路径下,创建NettyClient

  1. @Slf4j
  2. public class NettyClient {
  3. private final Bootstrap bootstrap;
  4. private final EventLoopGroup eventLoopGroup=new NioEventLoopGroup();
  5. private String serviceAddress;
  6. private int servicePort;
  7. public NettyClient(String serviceAddress,int servicePort){
  8. log.info("begin init NettyClient");
  9. bootstrap=new Bootstrap();
  10. bootstrap.group(eventLoopGroup)
  11. .channel(NioSocketChannel.class)
  12. .handler(new RpcClientInitializer());
  13. this.serviceAddress=serviceAddress;
  14. this.servicePort=servicePort;
  15. }
  16. public void sendRequest(RpcProtocol<RpcRequest> protocol) throws InterruptedException {
  17. ChannelFuture future=bootstrap.connect(this.serviceAddress,this.servicePort).sync();
  18. future.addListener(listener->{
  19. if(future.isSuccess()){
  20. log.info("connect rpc server {} success.",this.serviceAddress);
  21. }else{
  22. log.error("connect rpc server {} failed .",this.serviceAddress);
  23. future.cause().printStackTrace();
  24. eventLoopGroup.shutdownGracefully();
  25. }
  26. });
  27. log.info("begin transfer data");
  28. future.channel().writeAndFlush(protocol);
  29. }
  30. }

RpcClientInitializer

  1. @Slf4j
  2. public class RpcClientInitializer extends ChannelInitializer<SocketChannel> {
  3. @Override
  4. protected void initChannel(SocketChannel ch) throws Exception {
  5. log.info("begin initChannel");
  6. ch.pipeline()
  7. .addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,12,4,0,0))
  8. .addLast(new LoggingHandler())
  9. .addLast(new RpcEncoder())
  10. .addLast(new RpcDecoder())
  11. .addLast(new RpcClientHandler());
  12. }
  13. }

RpcClientHandler

需要注意,Netty的通信过程是基于入站出站分离的,所以在获取结果时,我们需要借助一个Future对象来完成。

  1. @Slf4j
  2. public class RpcClientHandler extends SimpleChannelInboundHandler<RpcProtocol<RpcResponse>> {
  3. @Override
  4. protected void channelRead0(ChannelHandlerContext ctx, RpcProtocol<RpcResponse> msg) throws Exception {
  5. log.info("receive rpc server result");
  6. long requestId=msg.getHeader().getRequestId();
  7. RpcFuture<RpcResponse> future=RequestHolder.REQUEST_MAP.remove(requestId);
  8. future.getPromise().setSuccess(msg.getContent()); //返回结果
  9. }
  10. }

Future的实现

在netty-rpc-protocol模块中添加rpcFuture实现

RpcFuture

  1. @Data
  2. public class RpcFuture<T> {
  3. //Promise是可写的 Future, Future自身并没有写操作相关的接口,
  4. // Netty通过 Promise对 Future进行扩展,用于设置IO操作的结果
  5. private Promise<T> promise;
  6. public RpcFuture(Promise<T> promise) {
  7. this.promise = promise;
  8. }
  9. }

RequestHolder

保存requestid和future的对应结果

  1. public class RequestHolder {
  2. public static final AtomicLong REQUEST_ID=new AtomicLong();
  3. public static final Map<Long,RpcFuture> REQUEST_MAP=new ConcurrentHashMap<>();
  4. }

需要源码的同学,请关注公众号[跟着Mic学架构],回复关键字[rpc],即可获得

版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Mic带你学架构

如果本篇文章对您有帮助,还请帮忙点个关注和赞,您的坚持是我不断创作的动力。欢迎关注「跟着Mic学架构」公众号公众号获取更多技术干货!

手把手教你基于Netty实现一个基础的RPC框架(通俗易懂)的更多相关文章

  1. 教你用 Netty 实现一个简单的 RPC!

    众所周知,dubbo 底层使用了 Netty 作为网络通讯框架,而 Netty 的高性能我们之前也分析过源码,对他也算还是比较了解了. 今天我们就自己用 Netty 实现一个简单的 RPC 框架. 1 ...

  2. 手把手教你用netty撸一个ZkClient

    原文地址: https://juejin.im/post/5dd296c0e51d4508182449a6 前言 有这个想法的缘由是前一阵子突发奇想, 想尝试能不能直接利用js连接到zookeeper ...

  3. netty系列之:来,手把手教你使用netty搭建一个DNS tcp服务器

    目录 简介 搭建netty服务器 DNS服务器的消息处理 DNS客户端消息请求 总结 简介 在前面的文章中,我们提到了使用netty构建tcp和udp的客户端向已经公布的DNS服务器进行域名请求服务. ...

  4. 手把手教你基于SqlSugar4编写一个可视化代码生成器(生成实体,以SqlServer为例,文末附源码)

    在开发过程中免不了创建实体类,字段少的表可以手动编写,但是字段多还用手动创建的话不免有些浪费时间,假如一张表有100多个字段,手写有些不现实. 这时我们会借助一些工具,如:动软代码生成器.各种ORM框 ...

  5. 手写一个类SpringBoot的HTTP框架:几十行代码基于Netty搭建一个 HTTP Server

    本文已经收录进 : https://github.com/Snailclimb/netty-practical-tutorial (Netty 从入门到实战:手写 HTTP Server+RPC 框架 ...

  6. 自己用 Netty 实现一个简单的 RPC

    目录: 需求 设计 实现 创建 maven 项目,导入 Netty 4.1.16. 项目目录结构 设计接口 提供者相关实现 消费者相关实现 测试结果 总结 源码地址:github 地址 前言 众所周知 ...

  7. Delphi - 手把手教你基于D7+Access常用管理系统架构的设计与实现 (更新中)

    前言 从事软件开发工作好多年了,学的越深入越觉得自己无知,所以还是要对知识保持敬畏之心,活到老,学到老! 健身和代码一样都不能少,身体是革命的本钱,特别是我们这种高危工种,所以小伙伴们运动起来!有没有 ...

  8. 基于Netty的一个WeoSocket通信服务器与客户端代码(非JS代码)

    基于Netty的一个WeoSocket通信服务器与客户端代码(非JS代码) 咳咳,在这里呢,小轩就不多说什么是WebSocket的,还有呢,小轩为什么不给出JS-Client代码?网上太多代码可以用了 ...

  9. 手把手教你从零写一个简单的 VUE--模板篇

    教程目录1.手把手教你从零写一个简单的 VUE2.手把手教你从零写一个简单的 VUE--模板篇 Hello,我又回来了,上一次的文章教会了大家如何书写一个简单 VUE,里面实现了VUE 的数据驱动视图 ...

随机推荐

  1. Python下载课件

    from urllib.request import urlretrieve # #下载网络文件到本地 import os os.chdir("C:/Users/RankFan/Deskto ...

  2. Hive语法及其进阶(二)

    1.使用JDBC连接Hive 1 import java.sql.Connection; 2 import java.sql.DriverManager; 3 import java.sql.Prep ...

  3. Spring源码之创建AOP代理之增强器的获取

    前言 在上一篇博文中我们说到了通过自定义配置完成了对AnnotationAwareAspectJAutoProxyCreator类型的自动注册,那么这个类究竟做了什么工作从而完成AOP的操作呢?首先我 ...

  4. Unity——FSM有限状态机

    FSM有限状态机 一.设计思路 1.共同的状态父类,提供可重写的进入,保持,退出该状态的生命周期方法: 2.状态机,管理所有状态(增删查改),状态机运行方法(Run): 3.在角色控制器中,实例化状态 ...

  5. Pandas 数据的一些基本操作

    一个很偶然的机会,主动出击挑战一个之前没有尝试过的新东西,在做的过程中需要处理一些csv文件的数据,以下是我总结的一些小方法,希望对和我一样的新手朋友们有所帮助,初次尝试,望路过的朋友有更好的方法可以 ...

  6. 重磅 | 阿里开源首个 Serverless 开发者平台 Serverless Devs

    Serverless 从概念提出到应用,已经走过了 8 个年头,开发者对 Serverless 的使用热情不断高涨.为帮助开发者实现一键体验多云产品,极速部署 Serverless 项目,10 月 2 ...

  7. Go语言核心36讲(Go语言进阶技术二)--学习笔记

    08 | container包中的那些容器 我们在上次讨论了数组和切片,当我们提到数组的时候,往往会想起链表.那么 Go 语言的链表是什么样的呢? Go 语言的链表实现在标准库的container/l ...

  8. 按键检测GPIO输入

    1. 项目 通过按键控制开关LED灯,按下按键灯亮,再按一下灯灭. 2. 代码 mian.c #include "stm32f10x.h" //相当于51单片机中的 #includ ...

  9. FastAPI 学习之路(三十七)元数据和文档 URL

    你可以在 FastAPI 应用中自定义几个元数据配置. 你可以设定: Title:在 OpenAPI 和自动 API 文档用户界面中作为 API 的标题/名称使用. Description:在 Ope ...

  10. 【二食堂】Alpha - Scrum Meeting 6

    Scrum Meeting 6 例会时间:4.16 11:40 - 12:10 进度情况 组员 昨日进度 今日任务 李健 1. 文本区域进度40%,UI需要进行调整issue 1. 继续文本区域的开发 ...