持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
我们基于netty构建一个RPC的服务端,进而了解一个RPC运行的基本原理。
定义rpc服务代理注解
用于标识需要作为远程调用的接口,应用于类上。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface RpcService { }
定义handler处理器
这里只列出接收数据关键处理代码,rpcServices为所有需要rpc调用的实例类对象
private Map<String, Object> rpcServices;
public RpcServerInboundHandler(Map<String, Object> rpcServices) {
this.rpcServices = rpcServices;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
RpcRequest rpcRequest = (RpcRequest) msg;
RpcResponse response = new RpcResponse();
response.setRequestType(rpcRequest.getRequestType());
try {
if(0 == rpcRequest.getRequestType()){
log.info("接收到客户端请求, 请求接口 -> {}, 请求方法 -> {}", rpcRequest.getClassName(), rpcRequest.getMethodName());
response.setRequestId(rpcRequest.getRequestId());
this.handleRequest(rpcRequest, response);
}else{
log.info("服务器收到心跳");
}
} catch (Exception e) {
e.printStackTrace();
response.setSuccess(false);
response.setErrorMessage(e.getMessage());
}
log.info("服务器响应 -> {}", JSON.toJSONString(response));
ctx.channel().writeAndFlush(response);
}
private void handleRequest(RpcRequest rpcRequest, RpcResponse response) throws Exception {
Object bean = rpcServices.get(rpcRequest.getClassName());
if (bean == null) {
throw new RuntimeException("未找到对应的服务: " + rpcRequest.getClassName());
}
Method method = bean.getClass().getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes());
method.setAccessible(true);
response.setReturnType(method.getReturnType());
response.setResult(method.invoke(bean, rpcRequest.getParameters()));
}
定义编码解码器
解码器
public class JsonDecoder extends LengthFieldBasedFrameDecoder {
public JsonDecoder() {
super(Integer.MAX_VALUE, 0, 4, 0, 4);
}
@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
ByteBuf msg = (ByteBuf) super.decode(ctx, in);
byte[] bytes = new byte[msg.readableBytes()];
msg.readBytes(bytes);
RpcRequest rpcRequest = JSON.parseObject(bytes, RpcRequest.class);
// 类型转换
if(null != rpcRequest.getParameters() && rpcRequest.getParameters().length > 0){
for(int n =0; n < rpcRequest.getParameters().length; n++){
if(null == rpcRequest.getParameters()[n]){
continue;
}
rpcRequest.getParameters()[n] = ((JSONObject) rpcRequest.getParameters()[n]).toJavaObject(rpcRequest.getParameterTypes()[n]);
}
}
return rpcRequest;
}
}
编码器
public class JsonEncoder extends MessageToByteEncoder<RpcResponse> {
@Override
protected void encode(ChannelHandlerContext ctx, RpcResponse rpcResponse, ByteBuf out) {
byte[] bytes = JSON.toJSONBytes(rpcResponse);
// 将消息体的长度写入消息头部
out.writeInt(bytes.length);
// 写入消息体
out.writeBytes(bytes);
}
}
定义消息结构体
RpcRequest
/** * 0 正常消息,1 心跳检查 */
private Integer requestType;
/** * 请求ID 用来标识本次请求以匹配RPC服务器的响应 */
private String requestId;
/** * 调用的类(接口)权限定名称 */
private String className;
/** * 调用的方法名 */
private String methodName;
/** * 方法参类型列表 */
private Class<?>[] parameterTypes;
/** * 方法参数 */
private Object[] parameters;
RpcResponse
/** * 0 正常消息,1 心跳检查 */
private Integer requestType;
/** * 响应对应的请求ID */
private String requestId;
/** * 调用是否成功的标识 */
private boolean success = true;
/** * 调用错误信息 */
private String errorMessage;
/** * 返回类型 */
private Class<?> returnType;
/** * 调用结果 */
private Object result;
启动rpc服务
启动netty服务,监听指定端口
@Component
@Slf4j
public class RpcServiceConfig {
@Value("${rpc.server.port}")
private int port;
@Bean
public CustomRpcServer serverStart(){
CustomRpcServer customRpcServer = new CustomRpcServer();
customRpcServer.setPort(port);
log.info("port={}", port);
return customRpcServer;
}
}
获取spring上下文容器中带有rpcservice注解的对象
@Slf4j
public class CustomRpcServer implements ApplicationContextAware, InitializingBean {
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
private Map<String, Object> rpcServices = new HashMap<>(5);
private int port;
public void setPort(int port) {
this.port = port;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, Object> services = applicationContext.getBeansWithAnnotation(RpcService.class);
for (Map.Entry<String, Object> entry : services.entrySet()) {
Object bean = entry.getValue();
Class<?>[] interfaces = bean.getClass().getInterfaces();
for (Class<?> inter : interfaces) {
rpcServices.put(inter.getName(), bean);
}
}
log.info("加载RPC服务数量:{}", rpcServices.size());
}
@Override
public void afterPropertiesSet() {
threadPoolTaskExecutor.submit(() -> {
// 监听连接的 parent channel 的线程组
EventLoopGroup boss = new NioEventLoopGroup(1);
// 负责客户端连接读写的 child channel 线程组
EventLoopGroup worker = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
// 设置reactor 线程
bootstrap.group(boss, worker)
// 装配流水线
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// 超时机制
pipeline.addLast(new IdleStateHandler(0, 0, 30));
pipeline.addLast(new JsonDecoder());
pipeline.addLast(new JsonEncoder());
// pipeline管理channel中的Handler
// 在channel队列中添加一个handler来处理业务
pipeline.addLast(new RpcServerInboundHandler(rpcServices));
}})
// 设置nio类型的channel
.channel(NioServerSocketChannel.class);
// 开始绑定server
// 通过调用sync同步方法阻塞直到绑定成功
ChannelFuture future = bootstrap.bind(port).sync();
log.info("RPC 服务器启动, 监听端口:" + port);
// 监听通道关闭事件
// 应用程序会一直等待,直到channel关闭
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
boss.shutdownGracefully();
worker.shutdownGracefully();
}
});
}
}