• RocketMQ(六) 服务端请求与响应的处理 源码分析


    RocketMQ(六) - 服务端请求与响应的处理 源码分析

    上一篇 《RocketMQ(五) - RemotingServer服务端 请求方法 源码分析》讲述了 RocketMQNetty服务端作为 请求发送方 的 三个 调用方法。

    本篇主要分析 服务端客户端发来的 请求响应 的处理方法。

    RocketMQNetty服务端 在启动方法中,向 NioSocketChannelpipeline中 添加了 很多 handler处理器, 而其中 的 NettyServerHandler 专门是用来处理 客户端发来的 请求与响应的。

        @ChannelHandler.Sharable
        class NettyServerHandler extends SimpleChannelInboundHandler<RemotingCommand> {
    		
            /**
            *  处理客户端发来消息的方法
            *  ctx  ctx对象
            *  msg  远程对象 (请求对象 / 响应对象)  该对象在之前的Encodehandler中已经被解析好了  
            */
            @Override
            protected void channelRead0(ChannelHandlerContext ctx, RemotingCommand msg) throws Exception {
                
                //  核心处理消息接受方法  
                // 这里会调用到父类 NettyRemotingAbstract的 processMessageReceived方法
                processMessageReceived(ctx, msg);
            }
        }
    

    1. 消息处理

    下面直接来看 NettyRemotingAbstractprocessMessageReceived方法

        public void processMessageReceived(ChannelHandlerContext ctx, RemotingCommand msg) throws Exception {
            final RemotingCommand cmd = msg;
            if (cmd != null) {
                
                // 获取 消息类型
                switch (cmd.getType()) {
              
                    case REQUEST_COMMAND:   // 客户端发起的请求 走这里
                        processRequestCommand(ctx, cmd);
                        break;
                    case RESPONSE_COMMAND: //  客户端响应给服务器的数据  走这里
                        processResponseCommand(ctx, cmd);
                        break;
                    default:
                        break;
                        
                }
            }
        }
    

    上述代码 很清晰, 根据 消息的类型 来选择 对应的 处理方法:

    1. REQUEST_COMMAND (客户端的请求)processRequestCommand(ctx, cmd);
    2. RESPONSE_COMMAND(客户端的响应)processResponseCommand(ctx, cmd);

    接下来 分别分析 这两个方法。

    2. 请求的处理

        // 处理客户端发来的请求
        public void processRequestCommand(final ChannelHandlerContext ctx, final RemotingCommand cmd) {
    
            // 根据 业务代码  从处理器映射表中  获取合适的 处理器和线程池资源
            final Pair<NettyRequestProcessor, ExecutorService> matched = this.processorTable.get(cmd.getCode());
    
            // 若没有找到 code对应的pair, 则使用 默认的defaultRequestProcessor  
            final Pair<NettyRequestProcessor, ExecutorService> pair = null == matched ? this.defaultRequestProcessor : matched;
    
            // 获取请求Id
            final int opaque = cmd.getOpaque();
    
            if (pair != null) {
    
                // 封装一个 runnable   核心逻辑入口...  业务线程..
                Runnable run = new Runnable() {
                    @Override
                    public void run() {
                        try {
                            //  RPC Hook before 逻辑...
         doBeforeRpcHooks(RemotingHelper.parseChannelRemoteAddr(ctx.channel()), cmd);
    
                            //  callback  封装响应客户端的逻辑.
                            final RemotingResponseCallback callback = new RemotingResponseCallback() {
                                @Override
                                public void callback(RemotingCommand response) {
                                    // 执行RPC HOOK 的 after 逻辑...
                                    doAfterRpcHooks(RemotingHelper.parseChannelRemoteAddr(ctx.channel()), cmd, response);
    
                                    // 条件成立: 说明 客户端发来的请求 是需要响应的
                                    if (!cmd.isOnewayRPC()) {
                                        if (response != null) {
                                            // 设置 请求id   (客户端根据 该opaque的值 ,在responseFutrueTable 中找到 responseFuture 并完成结果交互)
                                            response.setOpaque(opaque);
                                            // 设置 当前请求为响应类型
                                            response.markResponseType();
                                            try {
                                                // 将数据 交给 Netty IO 线程, 完成数据写和刷
                                                ctx.writeAndFlush(response);
                                            } catch (Throwable e) {
                                                log.error("process request over, but response failed", e);
                                                log.error(cmd.toString());
                                                log.error(response.toString());
                                            }
                                        } else {
                                        }
                                    }
                                }
                            };
    
                            // nameSrv 使用的是 DefaultRequestProcessor 是 AsyncNettyRequestProcessor的子类
                            // 因此 该条件会成立
                            if (pair.getObject1() instanceof AsyncNettyRequestProcessor) {
    
                                // 获取 处理器
                                AsyncNettyRequestProcessor processor = (AsyncNettyRequestProcessor)pair.getObject1();
                                // 参数1: ctx
                                // 参数2: cmd   客户端请求对象 remotingCommand
                                // 参数3: callback  封装响应客户端的回调逻辑.
                                processor.asyncProcessRequest(ctx, cmd, callback);
                            } else {
                                NettyRequestProcessor processor = pair.getObject1();
                                RemotingCommand response = processor.processRequest(ctx, cmd);
                                callback.callback(response);
                            }
                        } catch (Throwable e) {
                            log.error("process request exception", e);
                            log.error(cmd.toString());
    
                            if (!cmd.isOnewayRPC()) {
                                final RemotingCommand response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_ERROR,
                                    RemotingHelper.exceptionSimpleDesc(e));
                                response.setOpaque(opaque);
                                ctx.writeAndFlush(response);
                            }
                        }
                    }
                };
    
                // 拒绝策略处理
                if (pair.getObject1().rejectRequest()) {
                    final RemotingCommand response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_BUSY,
                        "[REJECTREQUEST]system busy, start flow control for a while");
                    response.setOpaque(opaque);
                    ctx.writeAndFlush(response);
                    return;
                }
    
                // 正常走这里 ...
    
                try {
                    // 将 (runnable 、ch 、 请求cmd)  封装成 RequestTask 对象
                    final RequestTask requestTask = new RequestTask(run, ctx.channel(), cmd);
    
                    // 注意: 这里 IO线程 会将任务提交到 业务线程中了
                    // pair.getObject2() =>  获取处理器对应的线程池
                    // submit(requestTask) => 将requestTask提交到线程池中, task执行时会执行 runnable的run()方法
                    pair.getObject2().submit(requestTask);
                    
                } catch (RejectedExecutionException e) {
            		//... 省略
                }
            } else {
                String error = " request type " + cmd.getCode() + " not supported";
                final RemotingCommand response =
                    RemotingCommand.createResponseCommand(RemotingSysResponseCode.REQUEST_CODE_NOT_SUPPORTED, error);
                response.setOpaque(opaque);
                ctx.writeAndFlush(response);
                log.error(RemotingHelper.parseChannelRemoteAddr(ctx.channel()) + error);
            }
        }
    

    上述代码虽然很长,但是逻辑非常清晰,这里做个小总结:

    1. 根据 requestCodeprocessorTable 中 获取 Pair , 若找不到则使用 默认的 Pair:

    ​ (processor: DefaultReqeustProcessor, executor: RemotingExecutor)

    1. 封装 核心业务任务 Runnable

    2. 封装 请求结果回调 对象 RemotingResponseCallback

    3. 核心方法: 调用 processor(DefaultReqeustProcessor) 的 asyncProcessRequest 方法

    4. 核心业务任务Runnable 提交给 RemotingExecutor 线程池执行

    其中具体的 DefaultReqeustProcessor 是如何 处理请求的,会在下一篇文章中详细分析。

    3. 响应的处理

    当服务端 向 客户端发送请求后, 客户端会返回响应消息, 则通过该方法来处理器。

        public void processResponseCommand(ChannelHandlerContext ctx, RemotingCommand cmd) {
            final int opaque = cmd.getOpaque();
            final ResponseFuture responseFuture = responseTable.get(opaque);
            if (responseFuture != null) {
                // 设置客户端 cmd
                responseFuture.setResponseCommand(cmd);
                // 将 responseFuture 从映射表 中 移除
                responseTable.remove(opaque);
    
    			
                if (responseFuture.getInvokeCallback() != null) {
                    //异步设置future结果
                    // 回调 对象处理结果
                    executeInvokeCallback(responseFuture);
                } else {
    
                    // 同步设置future 结果, 这一步会调用 countDownLatch.countDown(); 将 同步调用的业务线程唤醒
                    responseFuture.putResponse(cmd);
                    // 释放信号量
                    responseFuture.release();
                }
            } else {
                log.warn("receive response, but not matched any request, " + RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
                log.warn(cmd.toString());
            }
        }
    

    该代码 没什么好说的, 主要结合 上一篇《RocketMQ(五) - RemotingServer服务端 请求方法 源码分析》的 调用方法来分析。 其中的功能主要如下:

    1. responseTable 中 获取并移除对应的 responseFuture对象。
    2. 对同步调用的响应处理:
      1. 设置响应结果
      2. countDownLatch.countDown() 唤醒同步调用方法中阻塞的线程
    3. 对异步调用的响应处理:
      1. 设置 响应结果
      2. 释放信号量
    4. 对单向调用的响应结果: 释放信号量
  • 相关阅读:
    问:Linux下Chrome标题栏中文乱码
    通过printf设置Linux终端输出的颜色和显示方式
    连HTTPS都有漏洞,这么不安全的互联网我们还要继续用吗?
    为什么TCP连接不可靠
    TCP发送接口的返回值
    /proc/uptime详解
    Linux Kernel Version Numbering
    Understanding Linux /proc/cpuinfo
    Android Tips: Best Practices for Using Alpha
    如何让LinearLayout也有类似Button的点击效果?
  • 原文地址:https://www.cnblogs.com/s686zhou/p/15925522.html
Copyright © 2020-2023  润新知