说明
java 从零开始手写 RPC (01) 基于 socket 实现
java 从零开始手写 RPC (02)-netty4 实现客户端和服务端
写完了客户端和服务端,那么如何实现客户端和服务端的调用呢?
下面就让我们一起来看一下。

接口定义
计算方法
package com.github.houbb.rpc.common.service;
import com.github.houbb.rpc.common.model.CalculateRequest;
import com.github.houbb.rpc.common.model.CalculateResponse;
/**
* 计算服务接口
*
* Created: 2018/8/24 下午4:47
* Project: fake
*
* @author houbinbin
* @since 0.0.1
*/
public interface Calculator {
/**
* 计算加法
* @param request 请求入参
* @return 返回结果
*/
CalculateResponse sum(final CalculateRequest request);
}
pojo
对应的参数对象:
package com.github.houbb.rpc.common.model;
import java.io.Serializable;
/**
* 请求入参
*
* Created: 2018/8/24 下午5:05
* Project: fake
*
* @author houbinbin
* @since 0.0.3
*/
public class CalculateRequest implements Serializable {
private static final long serialVersionUID = 6420751004355300996L;
/**
* 参数一
*/
private int one;
/**
* 参数二
*/
private int two;
public CalculateRequest() {
}
public CalculateRequest(int one, int two) {
this.one = one;
this.two = two;
}
//getter setter toString
}
package com.github.houbb.rpc.common.model;
import java.io.Serializable;
/**
* 请求入参
*
* Created: 2018/8/24 下午5:05
* Project: fake
*
* @author houbinbin
* @since 0.0.3
*/
public class CalculateResponse implements Serializable {
private static final long serialVersionUID = -1972014736222511341L;
/**
* 是否成功
*/
private boolean success;
/**
* 二者的和
*/
private int sum;
public CalculateResponse() {
}
public CalculateResponse(boolean success, int sum) {
this.success = success;
this.sum = sum;
}
//getter setter toString
}
客户端
核心部分
RpcClient 需要添加对应的 Handler,调整如下:
Bootstrap bootstrap = new Bootstrap();
ChannelFuture channelFuture = bootstrap.group(workerGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer(){
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline()
.addLast(new LoggingHandler(LogLevel.INFO))
.addLast(new CalculateRequestEncoder())
.addLast(new CalculateResponseDecoder())
.addLast(new RpcClientHandler());
}
})
.connect(RpcConstant.ADDRESS, port)
.syncUninterruptibly();
netty 中的 handler 泳道设计的非常优雅,让我们的代码可以非常灵活地进行拓展。
接下来我们看一下对应的实现。
RpcClientHandler
package com.github.houbb.rpc.client.handler;
import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;
import com.github.houbb.rpc.client.core.RpcClient;
import com.github.houbb.rpc.common.model.CalculateRequest;
import com.github.houbb.rpc.common.model.CalculateResponse;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* 客户端处理类
*
* Created: 2019/10/16 11:30 下午
* Project: rpc
*
* @author houbinbin
* @since 0.0.2
*/
public class RpcClientHandler extends SimpleChannelInboundHandler {
private static final Log log = LogFactory.getLog(RpcClient.class);
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
CalculateRequest request = new CalculateRequest(1, 2);
ctx.writeAndFlush(request);
log.info("[Client] request is :{}", request);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
CalculateResponse response = (CalculateResponse)msg;
log.info("[Client] response is :{}", response);
}
}
这里比较简单,channelActive 中我们直接发起调用,入参的对象为了简单,此处固定写死。
channelRead0 中监听服务端的相应结果,并做日志输出。
CalculateRequestEncoder
请求参数是一个对象,netty 是无法直接传输的,我们将其转换为基本对象:
package com.github.houbb.rpc.client.encoder;
import com.github.houbb.rpc.common.model.CalculateRequest;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* @author binbin.hou
* @since 0.0.3
*/
public class CalculateRequestEncoder extends MessageToByteEncoder {
@Override
protected void encode(ChannelHandlerContext ctx, CalculateRequest msg, ByteBuf out) throws Exception {
int one = msg.getOne();
int two = msg.getTwo();
out.writeInt(one);
out.writeInt(two);
}
}
CalculateResponseDecoder
针对服务端的响应,也是同理。
我们需要把基本的类型,封装转换为我们需要的对象。
package com.github.houbb.rpc.client.decoder;
import com.github.houbb.rpc.common.model.CalculateResponse;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
/**
* 响应参数解码
* @author binbin.hou
* @since 0.0.3
*/
public class CalculateResponseDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List