客户端如何调用服务端呢?
我们本节就来一起实现一下。
消费者实现
启动类的调整
ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(workerGroup, bossGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline() .addLast(new DelimiterBasedFrameDecoder(DelimiterUtil.LENGTH, delimiterBuf)) .addLast(new MqConsumerHandler(invokeService)); } }) // 这个参数影响的是还没有被accept 取出的连接 .option(ChannelOption.SO_BACKLOG, 128) // 这个参数只是过一段时间内客户端没有响应,服务端会发送一个 ack 包,以判断客户端是否还活着。 .childOption(ChannelOption.SO_KEEPALIVE, true);
这里我们通过指定分隔符解决 netty 粘包问题。
MqConsumerHandler 处理类
MqConsumerHandler 的实现如下,添加对应的业务处理逻辑。
package com.github.houbb.mq.consumer.handler; /** * @author binbin.hou * @since 1.0.0 */ public class MqConsumerHandler extends SimpleChannelInboundHandler { private static final Log log = LogFactory.getLog(MqConsumerHandler.class); /** * 调用管理类 * @since 1.0.0 */ private final IInvokeService invokeService; public MqConsumerHandler(IInvokeService invokeService) { this.invokeService = invokeService; } @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf byteBuf = (ByteBuf) msg; byte[] bytes = new byte[byteBuf.readableBytes()]; byteBuf.readBytes(bytes); RpcMessageDto rpcMessageDto = null; try { rpcMessageDto = JSON.parseObject(bytes, RpcMessageDto.class); } catch (Exception exception) { log.error("RpcMessageDto json 格式转换异常 {}", new String(bytes)); return; } if (rpcMessageDto.isRequest()) { MqCommonResp commonResp = this.dispatch(rpcMessageDto, ctx); if(commonResp == null) { log.debug("当前消息为 null,忽略处理。"); return; } writeResponse(rpcMessageDto, commonResp, ctx); } else { final String traceId = rpcMessageDto.getTraceId(); // 丢弃掉 traceId 为空的信息 if(StringUtil.isBlank(traceId)) { log.debug("[Server Response] response traceId 为空,直接丢弃", JSON.toJSON(rpcMessageDto)); return; } // 添加消息 invokeService.addResponse(traceId, rpcMessageDto); } } }
rpc 消息体定义
为了统一标准,我们的 rpc 消息体 RpcMessageDto
定义如下:
package com.github.houbb.mq.common.rpc; /** * @author binbin.hou * @since 1.0.0 */ public class RpcMessageDto implements Serializable { /** * 请求时间 */ private long requestTime; /** * 请求标识 */ private String traceId; /** * 方法类型 */ private String methodType; /** * 是否为请求消息 */ private boolean isRequest; private String respCode; private String respMsg; private String json; //getter&setter }
消息分发
对于接收到的消息体 RpcMessageDto,分发逻辑如下:
/** * 消息的分发 * * @param rpcMessageDto 入参 * @param ctx 上下文 * @return 结果 */ private MqCommonResp dispatch(RpcMessageDto rpcMessageDto, ChannelHandlerContext ctx) { final String methodType = rpcMessageDto.getMethodType(); final String json = rpcMessageDto.getJson(); String channelId = ChannelUtil.getChannelId(ctx); log.debug("channelId: {} 接收到 method: {} 内容:{}", channelId, methodType, json); // 消息发送 if(MethodType.P_SEND_MESSAGE.equals(methodType)) { // 日志输出 log.info("收到服务端消息: {}", json); // 如果是 broker,应该进行处理化等操作。 MqCommonResp resp = new MqCommonResp(); resp.setRespCode(MqCommonRespCode.SUCCESS.getCode()); resp.setRespMessage(MqC