Seata源码(十三)Seata消息处理框架,掌握了可以当大佬~!!


铿然架构 | 作者 / 铿然一叶 这是铿然架构的第 103 篇原创文章


相关阅读:

萌新快速成长之路
如何编写软件设计文档
JAVA编程思想(一)通过依赖注入增加扩展性
JAVA编程思想(二)如何面向接口编程
JAVA编程思想(三)去掉别扭的if,自注册策略模式优雅满足开闭原则
JAVA编程思想(四)Builder模式经典范式以及和工厂模式如何选?
Java编程思想(七)使用组合和继承的场景
JAVA基础(一)简单、透彻理解内部类和静态内部类
JAVA基础(二)内存优化-使用Java引用做缓存
JAVA基础(三)ClassLoader实现热加载
JAVA基础(四)枚举(enum)和常量定义,工厂类使用对比
JAVA基础(五)函数式接口-复用,解耦之利刃
Seata源码(一)初始化
Seata源码(二)事务基础对象
Seata源码(三)事务处理类结构和流程
Seata源码(四)全局锁GlobalLock
Seata源码(五)Seata数据库操作
Seata源码(六)Seata的undo日志操作
Seata源码(七)Seata事务故障处理
Seata源码(八)Seata事务生命周期hook
Seata源码(九)TCC核心类和处理逻辑
Seata源码(十)RM接收到请求后的调用过程
Seata源码(十一)TC接收到请求后的处理过程
Seata源码(十二)Session管理和持久化\


1. 概述

Seata消息框架底层基于Netty,核心能力包括消息发送,接收,派发,数据压缩/解压缩,序列化/反序列化,编/解码,消息合并,同步/异步,以及消息处理hook。

2. 类结构

整个消息框架的类结构如下:

描述
AbstractNettyRemoting 消息收发、派发抽象类
AbstractNettyRemotingClient 客户端消息发送抽象类
AbstractNettyRemotingServer 服务端消息发送抽象类
NettyRemotingServer 服务端实现类
TmNettyRemotingClient TM客户端
RmNettyRemotingClient RM客户端
NettyClientBootstrap netty客户端启动类
NettyServerBootstrap netty服务端启动类
ClientHandler netty消息客户端处理类
ServerHandler netty消息服务端处理类
ProtocolV1Encoder 消息编码类
ProtocolV1Decoder 消息解码类
Compressor 数据压缩/解压缩接口,可通过配置指定压缩算法,由对应子类实现压缩/解压缩
Serializer 数据序列化/反序列化接口,可通过配置指定序列化算法,由对应子类实现序列化/反序列化
RemotingProcessor 业务处理接口,接收消息后由AbstractNettyRemoting派发给消息对应的子类处理
RpcMessage RPC消息,客户端和服务端交互的消息格式定义
MergedWarpMessage 合并消息封装类
MergeResultMessage 合并消息结果,合并消息请求的处理结果
MergedSendRunnable 合并消息发送线程
MessageFuture 异步消息封装类,用于发送异步消息,发送操作是异步的,但可以同步等待返回结果
RpcHook 消息处理hook,可以在消息处理前后添加处理逻辑
StatusRpcHook seata自带状态统计hook

3.源码

3.1 RpcMessage

RpcMessage消息内容如下:

属性 描述
id id标识,id生成器生成
messageType 消息类型
codec 序列化算法
compressor 压缩算法
headMap 消息头信息,一个Map<String, String>
body 业务消息,会根据序列化算法和压缩算法进行处理

其中消息类型(messageType)如下:

消息类型 描述
MSGTYPE_RESQUEST_SYNC 同步请求消息,有应答
MSGTYPE_RESPONSE 应答消息
MSGTYPE_RESQUEST_ONEWAY oneway请求消息,没有应答
MSGTYPE_HEARTBEAT_REQUEST 心跳请求
MSGTYPE_HEARTBEAT_RESPONSE 心跳应答

3.2 消息编解码

3.2.1 编码

编码字段和顺序:

消息类型 描述 取值 字节长度
MAGIC_CODE_BYTES 魔法编码 {(byte) 0xda, (byte) 0xda} 2
VERSION 版本 1 1
full length 消息长度 计算得到 4
head length rpc消息头长度 计算得到 2
messageType 消息类型 实际消息类型 1
codec 序列化算法 从配置获取 1
compressor 压缩算法 从配置获取 1
id 消息id id生成器生成 4
head 消息头信息 实际长度
body 消息体信息 实际长度

代码:

ProtocolV1Encoder.java

java 复制代码
   public void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) {
        try {
            if (msg instanceof RpcMessage) {
                RpcMessage rpcMessage = (RpcMessage) msg;

                int fullLength = ProtocolConstants.V1_HEAD_LENGTH;
                int headLength = ProtocolConstants.V1_HEAD_LENGTH;

                byte messageType = rpcMessage.getMessageType();
                out.writeBytes(ProtocolConstants.MAGIC_CODE_BYTES);
                out.writeByte(ProtocolConstants.VERSION);
                // full Length(4B) and head length(2B) will fix in the end. 
                out.writerIndex(out.writerIndex() + 6);
                out.writeByte(messageType);
                out.writeByte(rpcMessage.getCodec());
                out.writeByte(rpcMessage.getCompressor());
                out.writeInt(rpcMessage.getId());

                // direct write head with zero-copy
                Map<String, String> headMap = rpcMessage.getHeadMap();
                if (headMap != null && !headMap.isEmpty()) {
                    int headMapBytesLength = HeadMapSerializer.getInstance().encode(headMap, out);
                    headLength += headMapBytesLength;
                    fullLength += headMapBytesLength;
                }

                byte[] bodyBytes = null;
                if (messageType != ProtocolConstants.MSGTYPE_HEARTBEAT_REQUEST
                        && messageType != ProtocolConstants.MSGTYPE_HEARTBEAT_RESPONSE) {
                    // heartbeat has no body
                    Serializer serializer = EnhancedServiceLoader.load(Serializer.class, SerializerType.getByCode(rpcMessage.getCodec()).name());
                    bodyBytes = serializer.serialize(rpcMessage.getBody());
                    Compressor compressor = CompressorFactory.getCompressor(rpcMessage.getCompressor());
                    bodyBytes = compressor.compress(bodyBytes);
                    fullLength += bodyBytes.length;
                }

                if (bodyBytes != null) {
                    out.writeBytes(bodyBytes);
                }

                // fix fullLength and headLength
                int writeIndex = out.writerIndex();
                // skip magic code(2B) + version(1B)
                out.writerIndex(writeIndex - fullLength + 3);
                out.writeInt(fullLength);
                out.writeShort(headLength);
                out.writerIndex(writeIndex);
            } else {
                throw new UnsupportedOperationException("Not support this class:" + msg.getClass());
            }
        } catch (Throwable e) {
            LOGGER.error("Encode request error!", e);
        }
    }

消息头编码,就是遍历map,取出key和value进行编码:

HeadMapSerializer.java

java 复制代码
    public int encode(Map<String, String> map, ByteBuf out) {
        if (map == null || map.isEmpty() || out == null) {
            return 0;
        }
        int start = out.writerIndex();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            if (key != null) {
                writeString(out, key);
                writeString(out, value);
            }
        }
        return out.writerIndex() - start;
    }

3.2.2 解码

解码就是编码的反向操作。

ProtocolV1Decoder.java

java 复制代码
   public Object decodeFrame(ByteBuf frame) {
        byte b0 = frame.readByte();
        byte b1 = frame.readByte();
        if (ProtocolConstants.MAGIC_CODE_BYTES[0] != b0
                || ProtocolConstants.MAGIC_CODE_BYTES[1] != b1) {
            throw new IllegalArgumentException("Unknown magic code: " + b0 + ", " + b1);
        }

        byte version = frame.readByte();
        // TODO  check version compatible here

        int fullLength = frame.readInt();
        short headLength = frame.readShort();
        byte messageType = frame.readByte();
        byte codecType = frame.readByte();
        byte compressorType = frame.readByte();
        int requestId = frame.readInt();

        RpcMessage rpcMessage = new RpcMessage();
        rpcMessage.setCodec(codecType);
        rpcMessage.setId(requestId);
        rpcMessage.setCompressor(compressorType);
        rpcMessage.setMessageType(messageType);

        // direct read head with zero-copy
        int headMapLength = headLength - ProtocolConstants.V1_HEAD_LENGTH;
        if (headMapLength > 0) {
            Map<String, String> map = HeadMapSerializer.getInstance().decode(frame, headMapLength);
            rpcMessage.getHeadMap().putAll(map);
        }

        // read body
        if (messageType == ProtocolConstants.MSGTYPE_HEARTBEAT_REQUEST) {
            rpcMessage.setBody(HeartbeatMessage.PING);
        } else if (messageType == ProtocolConstants.MSGTYPE_HEARTBEAT_RESPONSE) {
            rpcMessage.setBody(HeartbeatMessage.PONG);
        } else {
            int bodyLength = fullLength - headLength;
            if (bodyLength > 0) {
                byte[] bs = new byte[bodyLength];
                frame.readBytes(bs);
                Compressor compressor = CompressorFactory.getCompressor(compressorType);
                bs = compressor.decompress(bs);
                Serializer serializer = EnhancedServiceLoader.load(Serializer.class, SerializerType.getByCode(rpcMessage.getCodec()).name());
                rpcMessage.setBody(serializer.deserialize(bs));
            }
        }

        return rpcMessage;
    }

3.3 消息序列化

3.3.1 配置获取

ProtocolConstants.java

java 复制代码
    byte CONFIGURED_CODEC = SerializerType.getByName(ConfigurationFactory.getInstance()
            .getConfig(ConfigurationKeys.SERIALIZE_FOR_RPC, SerializerType.SEATA.name())).getCode();

3.3.2 配置参数传入

构造rpc消息时传入:

AbstractNettyRemoting.java

java 复制代码
    protected RpcMessage buildRequestMessage(Object msg, byte messageType) {
        RpcMessage rpcMessage = new RpcMessage();
        rpcMessage.setId(getNextMessageId());
        rpcMessage.setMessageType(messageType);
        rpcMessage.setCodec(ProtocolConstants.CONFIGURED_CODEC);
        rpcMessage.setCompressor(ProtocolConstants.CONFIGURED_COMPRESSOR);
        rpcMessage.setBody(msg);
        return rpcMessage;
    }

3.3.3 创建序列化子类

通过EnhancedServiceLoader.load方法获取:

ProtocolV1Decoder.java和ProtocolV1Encoder.java

java 复制代码
Serializer serializer = EnhancedServiceLoader.load(Serializer.class, SerializerType.getByCode(rpcMessage.getCodec()).name());

3.4 消息压缩

压缩算法通过配置transport.compressor获取,默认不需要压缩,压缩算法有gzip,zip,sevenz,bzip2,lz4,deflater。

3.4.1 配置获取

ProtocolConstants.java

java 复制代码
    byte CONFIGURED_COMPRESSOR = CompressorType.getByName(ConfigurationFactory.getInstance()
            .getConfig(ConfigurationKeys.COMPRESSOR_FOR_RPC, CompressorType.NONE.name())).getCode();

3.4.2 参数传入

构造rpc消息时传入,同序列化参数。

3.4.3 创建压缩算法子类

调用工厂方法获取:

ProtocolV1Decoder.java和ProtocolV1Encoder.java

java 复制代码
Compressor compressor = CompressorFactory.getCompressor(rpcMessage.getCompressor());

工厂方法,最终和序列化一样,调用EnhancedServiceLoader.load方法生成:

CompressorFactory.java

java 复制代码
    public static Compressor getCompressor(byte code) {
        CompressorType type = CompressorType.getByCode(code);
        return CollectionUtils.computeIfAbsent(COMPRESSOR_MAP, type,
            key -> EnhancedServiceLoader.load(Compressor.class, type.name()));
    }

3.5 消息接收处理

3.5.1 客户端消息处理编排

在NettyClientBootstrap中添加了编解码处理类和ClientHandler类:

NettyClientBootstrap.java

java 复制代码
        bootstrap.handler(
            new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) {
                    ChannelPipeline pipeline = ch.pipeline();
                    pipeline.addLast(
                        new IdleStateHandler(nettyClientConfig.getChannelMaxReadIdleSeconds(),
                            nettyClientConfig.getChannelMaxWriteIdleSeconds(),
                            nettyClientConfig.getChannelMaxAllIdleSeconds()))
                        .addLast(new ProtocolV1Decoder())
                        .addLast(new ProtocolV1Encoder());
                    if (channelHandlers != null) {
                        addChannelPipelineLast(ch, channelHandlers);
                    }
                }
            });

3.5.2 服务端消息处理编排

在NettyServerBootstrap中添加了编解码处理类和ServerHandler类:

NettyServerBootstrap.java

java 复制代码
        this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupWorker)
            .channel(NettyServerConfig.SERVER_CHANNEL_CLAZZ)
            .option(ChannelOption.SO_BACKLOG, nettyServerConfig.getSoBackLogSize())
            .option(ChannelOption.SO_REUSEADDR, true)
            .childOption(ChannelOption.SO_KEEPALIVE, true)
            .childOption(ChannelOption.TCP_NODELAY, true)
            .childOption(ChannelOption.SO_SNDBUF, nettyServerConfig.getServerSocketSendBufSize())
            .childOption(ChannelOption.SO_RCVBUF, nettyServerConfig.getServerSocketResvBufSize())
            .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK,
                new WriteBufferWaterMark(nettyServerConfig.getWriteBufferLowWaterMark(),
                    nettyServerConfig.getWriteBufferHighWaterMark()))
            .localAddress(new InetSocketAddress(listenPort))
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) {
                    ch.pipeline().addLast(new IdleStateHandler(nettyServerConfig.getChannelMaxReadIdleSeconds(), 0, 0))
                        .addLast(new ProtocolV1Decoder())
                        .addLast(new ProtocolV1Encoder());
                    if (channelHandlers != null) {
                        addChannelPipelineLast(ch, channelHandlers);
                    }

                }
            });

3.5.3 客户端读消息

读消息时已经完成了解码,反序列化,解压缩:

ClientHandler.java

java 复制代码
        public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
            if (!(msg instanceof RpcMessage)) {
                return;
            }
            // 这里开始派发消息
            processMessage(ctx, (RpcMessage) msg);
        }

3.5.4 服务端读消息

读消息时已经完成了解码,反序列化,解压缩:

ServerHandler.java

java 复制代码
        public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
            if (!(msg instanceof RpcMessage)) {
                return;
            }
            // 这里开始派发消息
            processMessage(ctx, (RpcMessage) msg);
        }

3.6 消息派发

消息派发相关类结构如下:

采用通用的register & dispatch结构。

核心逻辑是先注册消息类型和RemotingProcessor子类的关系到缓存中,接收到消息后根据消息类型查找到对应的RemotingProcessor子类,将消息派发给子类处理。

注册逻辑摘选:

RmNettyRemotingClient.java

java 复制代码
   private void registerProcessor() {
        // 1.registry rm client handle branch commit processor
        RmBranchCommitProcessor rmBranchCommitProcessor = new RmBranchCommitProcessor(getTransactionMessageHandler(), this);
        super.registerProcessor(MessageType.TYPE_BRANCH_COMMIT, rmBranchCommitProcessor, messageExecutor);
        // 2.registry rm client handle branch commit processor
        RmBranchRollbackProcessor rmBranchRollbackProcessor = new RmBranchRollbackProcessor(getTransactionMessageHandler(), this);
        super.registerProcessor(MessageType.TYPE_BRANCH_ROLLBACK, rmBranchRollbackProcessor, messageExecutor);
        // 3.registry rm handler undo log processor
        RmUndoLogProcessor rmUndoLogProcessor = new RmUndoLogProcessor(getTransactionMessageHandler());
        super.registerProcessor(MessageType.TYPE_RM_DELETE_UNDOLOG, rmUndoLogProcessor, messageExecutor);
        // 4.registry TC response processor
        ClientOnResponseProcessor onResponseProcessor =
            new ClientOnResponseProcessor(mergeMsgMap, super.getFutures(), getTransactionMessageHandler());
        super.registerProcessor(MessageType.TYPE_SEATA_MERGE_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_BRANCH_REGISTER_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_BRANCH_STATUS_REPORT_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_GLOBAL_LOCK_QUERY_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_REG_RM_RESULT, onResponseProcessor, null);
        // 5.registry heartbeat message processor
        ClientHeartbeatProcessor clientHeartbeatProcessor = new ClientHeartbeatProcessor();
        super.registerProcessor(MessageType.TYPE_HEARTBEAT_MSG, clientHeartbeatProcessor, null);
    }

派发逻辑:

AbstractNettyRemoting.java

java 复制代码
   protected void processMessage(ChannelHandlerContext ctx, RpcMessage rpcMessage) throws Exception {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(String.format("%s msgId:%s, body:%s", this, rpcMessage.getId(), rpcMessage.getBody()));
        }
        Object body = rpcMessage.getBody();
        if (body instanceof MessageTypeAware) {
            MessageTypeAware messageTypeAware = (MessageTypeAware) body;
            // 查找RemotingProcessor实现类并调用
            final Pair<RemotingProcessor, ExecutorService> pair = this.processorTable.get((int) messageTypeAware.getTypeCode());
            if (pair != null) {
                if (pair.getSecond() != null) {
                    try {
                        pair.getSecond().execute(() -> {
                            try {
                                pair.getFirst().process(ctx, rpcMessage);
                            } catch (Throwable th) {
                                LOGGER.error(FrameworkErrorCode.NetDispatch.getErrCode(), th.getMessage(), th);
                            } finally {
                                MDC.clear();
                            }
                        });
                    } catch (RejectedExecutionException e) {
                        LOGGER.error(FrameworkErrorCode.ThreadPoolFull.getErrCode(),
                            "thread pool is full, current max pool size is " + messageExecutor.getActiveCount());
                        if (allowDumpStack) {
                            String name = ManagementFactory.getRuntimeMXBean().getName();
                            String pid = name.split("@")[0];
                            int idx = new Random().nextInt(100);
                            try {
                                Runtime.getRuntime().exec("jstack " + pid + " >d:/" + idx + ".log");
                            } catch (IOException exx) {
                                LOGGER.error(exx.getMessage());
                            }
                            allowDumpStack = false;
                        }
                    }
                } else {
                    try {
                        pair.getFirst().process(ctx, rpcMessage);
                    } catch (Throwable th) {
                        LOGGER.error(FrameworkErrorCode.NetDispatch.getErrCode(), th.getMessage(), th);
                    }
                }
            } else {
                LOGGER.error("This message type [{}] has no processor.", messageTypeAware.getTypeCode());
            }
        } else {
            LOGGER.error("This rpcMessage body[{}] is not MessageTypeAware type.", body);
        }
    }

3.7 消息合并处理

消息合并的目的是减少交互次数,提高性能。

3.7.1 合并消息结构

请求消息存储在MergedWarpMessage中,消息结构如下:

属性 描述
msgIds List,消息标识列表
msgs List,消息列表,顺序和前面的消息标识列表对应
typeCode 消息类型编码,固定值MessageType.TYPE_SEATA_MERGE

应答消息存储在MergeResultMessage中,消息结构如下:

属性 描述
msgs AbstractResultMessage[],结果消息数组
typeCode 消息类型编码,固定值MessageType.TYPE_SEATA_MERGE_RESULT

3.7.2 合并消息处理

首先将消息放入basket(当客户端支持批量发送请求时才放入backet):

AbstractNettyRemotingClient.java

java 复制代码
    public Object sendSyncRequest(Object msg) throws TimeoutException {
        String serverAddress = loadBalance(getTransactionServiceGroup(), msg);
        int timeoutMillis = NettyClientConfig.getRpcRequestTimeout();
        RpcMessage rpcMessage = buildRequestMessage(msg, ProtocolConstants.MSGTYPE_RESQUEST_SYNC);

        // send batch message
        // put message into basketMap, @see MergedSendRunnable
        if (NettyClientConfig.isEnableClientBatchSendRequest()) {

            // send batch message is sync request, needs to create messageFuture and put it in futures.
            MessageFuture messageFuture = new MessageFuture();
            messageFuture.setRequestMessage(rpcMessage);
            messageFuture.setTimeout(timeoutMillis);
            futures.put(rpcMessage.getId(), messageFuture);

            // put message into basketMap
            BlockingQueue<RpcMessage> basket = CollectionUtils.computeIfAbsent(basketMap, serverAddress,
                key -> new LinkedBlockingQueue<>());
            if (!basket.offer(rpcMessage)) {
                LOGGER.error("put message into basketMap offer failed, serverAddress:{},rpcMessage:{}",
                        serverAddress, rpcMessage);
                return null;
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("offer message: {}", rpcMessage.getBody());
            }
            if (!isSending) {
                synchronized (mergeLock) {
                    mergeLock.notifyAll();
                }
            }

            try {
                // 异步获取消息应答
                return messageFuture.get(timeoutMillis, TimeUnit.MILLISECONDS);
            } catch (Exception exx) {
                LOGGER.error("wait response error:{},ip:{},request:{}",
                    exx.getMessage(), serverAddress, rpcMessage.getBody());
                if (exx instanceof TimeoutException) {
                    throw (TimeoutException) exx;
                } else {
                    throw new RuntimeException(exx);
                }
            }

        } else {
            Channel channel = clientChannelManager.acquireChannel(serverAddress);
            return super.sendSync(channel, rpcMessage, timeoutMillis);
        }

    }

消息合并后发送,通过线程异步处理:

MergedSendRunnable.java

java 复制代码
                basketMap.forEach((address, basket) -> {
                    if (basket.isEmpty()) {
                        return;
                    }

                    MergedWarpMessage mergeMessage = new MergedWarpMessage();
                    while (!basket.isEmpty()) {
                        RpcMessage msg = basket.poll();
                        mergeMessage.msgs.add((AbstractMessage) msg.getBody());
                        mergeMessage.msgIds.add(msg.getId());
                    }
                    if (mergeMessage.msgIds.size() > 1) {
                        printMergeMessageLog(mergeMessage);
                    }
                    Channel sendChannel = null;
                    try {
                        // send batch message is sync request, but there is no need to get the return value.
                        // Since the messageFuture has been created before the message is placed in basketMap,
                        // the return value will be obtained in ClientOnResponseProcessor.
                        sendChannel = clientChannelManager.acquireChannel(address);
                        AbstractNettyRemotingClient.this.sendAsyncRequest(sendChannel, mergeMessage);
                    } 

服务端接收到合并消息的处理,并将合并消息的处理结果也合并后返回:

ServerOnRequestProcessor.java的onRequestMessage方法内

java 复制代码
        if (message instanceof MergedWarpMessage) {
            AbstractResultMessage[] results = new AbstractResultMessage[((MergedWarpMessage) message).msgs.size()];
            for (int i = 0; i < results.length; i++) {
                final AbstractMessage subMessage = ((MergedWarpMessage) message).msgs.get(i);
                // 这里的onRequest是DefaultCoordinator类的方法
                results[i] = transactionMessageHandler.onRequest(subMessage, rpcContext);
            }
            MergeResultMessage resultMessage = new MergeResultMessage();
            resultMessage.setMsgs(results);
            remotingServer.sendAsyncResponse(rpcMessage, ctx.channel(), resultMessage);
        } 

客户端收到合并请求处理结果的处理:

ClientOnResponseProcessor.java

java 复制代码
    public void process(ChannelHandlerContext ctx, RpcMessage rpcMessage) throws Exception {
        if (rpcMessage.getBody() instanceof MergeResultMessage) {
            MergeResultMessage results = (MergeResultMessage) rpcMessage.getBody();
            MergedWarpMessage mergeMessage = (MergedWarpMessage) mergeMsgMap.remove(rpcMessage.getId());
            for (int i = 0; i < mergeMessage.msgs.size(); i++) {
                int msgId = mergeMessage.msgIds.get(i);
                MessageFuture future = futures.remove(msgId);
                if (future == null) {
                    if (LOGGER.isInfoEnabled()) {
                        LOGGER.info("msg: {} is not found in futures.", msgId);
                    }
                } else {
                    future.setResultMessage(results.getMsgs()[i]);
                }
            }
        } 

3.8 消息异步处理

消息异步处理目的是提高效率。

服务端接收到请求后处理结果是异步发送的,而客户端要同步等待这个异步结果,为了实现这个目的,通过MessageFuture类来封装消息进行处理,其思路是利用CompletableFuture的能力,处理如下:

1.在发送请求后调用其get方法在客户端同步等待。

AbstractNettyRemotingClient.java的sendSyncRequest(Object msg)方法内:

java 复制代码
            try {
                // 异步获取消息应答
                return messageFuture.get(timeoutMillis, TimeUnit.MILLISECONDS);
            } 

2.服务端处理后,异步发送结果。

ServerOnRequestProcessor.java的onRequestMessage(ChannelHandlerContext ctx, RpcMessage rpcMessage)方法,结果是异步返回的:

java 复制代码
            remotingServer.sendAsyncResponse(rpcMessage, ctx.channel(), resultMessage);

            remotingServer.sendAsyncResponse(rpcMessage, ctx.channel(), result);

3.客户端收到异步应答后,通过其complete方法设置应答消息并唤醒get方法。

java 复制代码
   public void process(ChannelHandlerContext ctx, RpcMessage rpcMessage) throws Exception {
        ......
                MessageFuture future = futures.remove(msgId);
                if (future == null) {
                    if (LOGGER.isInfoEnabled()) {
                        LOGGER.info("msg: {} is not found in futures.", msgId);
                    }
                } else {
                    // 唤醒第1步中的messageFuture.get(timeoutMillis, TimeUnit.MILLISECONDS)
                    future.setResultMessage(results.getMsgs()[i]);
                }
            }
        } else {
            MessageFuture messageFuture = futures.remove(rpcMessage.getId());
            if (messageFuture != null) {
              // 唤醒第1步中的messageFuture.get(timeoutMillis, TimeUnit.MILLISECONDS)方法
                messageFuture.setResultMessage(rpcMessage.getBody());
            }
            ......
    }

3.9 RpcHook

RpcHook提供了扩展能力,使得可以在消息发送前后做定制处理。

3.9.1 RpcHook子类加载

AbstractNettyRemoting.java

java 复制代码
protected final List<RpcHook> rpcHooks = EnhancedServiceLoader.loadAll(RpcHook.class);

3.9.2 RpcHook子类调用

AbstractNettyRemoting.java

同步消息调用点(before和after方法都调用):

java 复制代码
   protected Object sendSync(Channel channel, RpcMessage rpcMessage, long timeoutMillis) throws TimeoutException {
        .....
        doBeforeRpcHooks(remoteAddr, rpcMessage);
        ......
        try {
            Object result = messageFuture.get(timeoutMillis, TimeUnit.MILLISECONDS);
            doAfterRpcHooks(remoteAddr, rpcMessage, result);
            return result;
        }

异步消息调用点(只调用doBeforeRpcHooks方法):

java 复制代码
    protected void sendAsync(Channel channel, RpcMessage rpcMessage) {
        ......
        doBeforeRpcHooks(ChannelUtil.getAddressFromChannel(channel), rpcMessage);
        ......
    }

遍历调用:

java 复制代码
    protected void doBeforeRpcHooks(String remoteAddr, RpcMessage request) {
        for (RpcHook rpcHook: rpcHooks) {
            rpcHook.doBeforeRequest(remoteAddr, request);
        }
    }

    protected void doAfterRpcHooks(String remoteAddr, RpcMessage request, Object response) {
        for (RpcHook rpcHook: rpcHooks) {
            rpcHook.doAfterResponse(remoteAddr, request, response);
        }
    }

阅过留痕,点赞,收藏,关注三连!

相关推荐
救救孩子把10 分钟前
深入理解 Java 对象的内存布局
java
落落落sss13 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
万物皆字节18 分钟前
maven指定模块快速打包idea插件Quick Maven Package
java
夜雨翦春韭25 分钟前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法
我行我素,向往自由32 分钟前
速成java记录(上)
java·速成
一直学习永不止步38 分钟前
LeetCode题练习与总结:H 指数--274
java·数据结构·算法·leetcode·数组·排序·计数排序
邵泽明38 分钟前
面试知识储备-多线程
java·面试·职场和发展
程序员是干活的1 小时前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节
煸橙干儿~~1 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
2401_854391081 小时前
Spring Boot大学生就业招聘系统的开发与部署
java·spring boot·后端