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. 对单向调用的响应结果: 释放信号量
posted @ 2022-02-22 23:33  s686编程传  阅读(226)  评论(0编辑  收藏  举报