Netty是基于事件驱动的,异步的NIO网络应用框架,可以用来开发高性能,高可靠性的网络IO程序。市面上很多RPC框架底层IO通信都是用Netty实现的,这个项目的主要目的是通过实践加强对Netty应用的掌握,另外也可以通过这个项目来加深对RPC的理解。
RPC是什么
RPC是远程过程调用(Remote Procedure Call)的缩写形式。是跨应用之间调用的一个解决方案,通过在客户端和服务端之间建立TCP链接,发送请求序列化反序列化交换数据,让调用者像使用本地API一样简单。经常会有人纠结为什么不用HTTP,其实HTTP也是实现RPC的一种方式。
实现过程
协议
在说到服务端和客户端实现之前,先定义好相关的RPC协议。
SwiftMessage,pipeline中流转的消息POJO:
public class SwiftMessage {
private int length;
private byte[] content;
public SwiftMessage(int length, byte[] content) {
this.length = length;
this.content = content;
}
// 省略setter、getter
}
复制代码
RpcRequest,RpcResponse,handler中处理的请求响应对象:
public class RpcRequest implements Serializable {
private static final long serialVersionUID = 2104861261275175620L;
private String id;
private String className;
private String methodName;
private Object[] parameters;
private Class<?>[] parameterTypes;
}
复制代码
public class RpcResponse implements Serializable {
private static final long serialVersionUID = -1921327887856337850L;
private String requestId;
private int code;
private String errorMsg;
private Object data;
}
复制代码
ByteBuf与SwiftMessage转换的编码器和解码器:
public class SwiftMessageEncoder extends MessageToByteEncoder<SwiftMessage> {
@Override
protected void encode(ChannelHandlerContext ctx, SwiftMessage msg, ByteBuf out) throws Exception {
out.writeInt(msg.getLength());
out.writeBytes(msg.getContent());
}
}
复制代码
public class SwiftMessageDecoder extends ReplayingDecoder<Void> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
int length = in.readInt();
byte[] content = new byte[length];
in.readBytes(content);
out.add(new SwiftMessage(length, content));
}
}
复制代码
服务端
其实RPC Server简单些,只需要做下面几件事就可以:
- 启动服务,把自己注册到RPC服务中心(让客户端可以找到能建立连接)
@Component
public class SwiftServerRunner implements ApplicationRunner, ApplicationContextAware {
private void start(int port, String serverName) {
if (isRunning.get()) {
LOGGER.info("SwiftRPC服务已经在运行中");
return;
}
final NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
final NioEventLoopGroup workerGroup = new NioEventLoopGroup(16);
try {
SwiftServerHandler handler = new SwiftServerHandler(rpcServiceMap);
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new SwiftMessageDecoder());
pipeline.addLast(new SwiftMessageEncoder());
pipeline.addLast(handler);
}
});
ChannelFuture channelFuture = bootstrap.bind(port).sync();
isRunning.set(true);
String rpcServer = "127.0.0.1:" + port;
registerRpcServer(serverName, rpcServer);
LOGGER.info("SwiftRPC服务已经启动,端口:{}", port);
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
isRunning.set(false);
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
throw new RuntimeException("启动SwiftRPC服务失败", e);
}
}
private void registerRpcServer(String serverName, String rpcServer) throws Exception {
// 简单的将rpc server列表保存到redis中
redisTemplate.opsForHash().put("rpc-server", serverName, rpcServer);
}
}
复制代码
- 监听客户端的连接,接收请求数据,反序列化成RpcRequest
@Override
protected void channelRead0(ChannelHandlerContext ctx, SwiftMessage msg) throws Exception {
byte[] content = msg.getContent();
RpcRequest request = Kryos.deserialize(content, RpcRequest.class);
}
复制代码
- 利用RpcRequest中的参数通过反射执行具体的业务实现方法,再把结果写回到与客户端的连接中
@ChannelHandler.Sharable
public class SwiftServerHandler extends SimpleChannelInboundHandler<SwiftMessage> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, SwiftMessage msg) throws Exception {
byte[] content = msg.getContent();
RpcRequest request = Kryos.deserialize(content, RpcRequest.class);
RpcResponse response = new RpcResponse();
response.setRequestId(request.getId());
try {
Object result = handler(request);
response.setData(result);
} catch (Exception e) {
response.setCode(-1);
response.setErrorMsg(e.toString());
}
byte[] resContent = Kryos.serialize(response);
SwiftMessage message = new SwiftMessage(resContent.length, resContent);
ctx.writeAndFlush(message);
}
private Object handler(RpcRequest request) throws Exception {
String className = request.getClassName();
Object rpcService = rpcServiceMap.get(className);
if (rpcService == null) {
throw new RuntimeException("未找到RPC服务类:" + className);
}
// 通过反射调用业务层
String methodName = request.getMethodName();
Object[] parameters = request.getParameters();
Class<?>[] parameterTypes = request.getParameterTypes();
Class<?> clazz = rpcService.getClass();
Method method = clazz.getMethod(methodName, parameterTypes);
return method.invoke(rpcService, parameters);
}
}
复制代码
客户端
客户端稍微复杂些,因为客户端需要接收外部的调用请求(比如一个rest接口发来的请求),根据不同的请求选择不同的RPC服务端发送请求,最后还需要等待返回的结果。
- 注册RPC接口。因为要想通过注入的方式调用service的方法,就需要将service注册成Spring的bean
@Autowired
private UserService userService;
复制代码
但是UserService具体的实现类在服务端,所以需要自定义一个ComponentScan,扫描所有的RPC接口,注册成Spring的bean,并设置对应的FactoryBean来完成实例化工作,可以参考Mybatis的执行过程。在FactoryBean中用动态代理来生成每个接口的代理类执行各个方法。所以最终的RPC调用是在这个代理类的invoke方法中发起的。
public class SwiftRpcServiceRegister implements ImportBeanDefinitionRegistrar {
private static final Logger LOGGER = LoggerFactory.getLogger(SwiftRpcServiceRegister.class);
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(SwiftRpcServiceScan.class.getName());
if (annotationAttributes != null) {
String[] packages = (String[]) annotationAttributes.get("packages");
SwiftRpcClassPathBeanDefinitionScanner scanner = new SwiftRpcClassPathBeanDefinitionScanner(registry);
scanner.addIncludeFilter(new AnnotationTypeFilter(SwiftRpcService.class));
if (packages == null || packages.length == 0) {
LOGGER.warn("扫描RPC目录为空");
return;
}
Set<BeanDefinitionHolder> definitionHolders = scanner.doScan(packages);
for (BeanDefinitionHolder holder : definitionHolders) {
GenericBeanDefinition beanDefinition = (GenericBeanDefinition) holder.getBeanDefinition();
String className = beanDefinition.getBeanClassName();
if (className == null) {
continue;
}
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(className);
beanDefinition.setBeanClass(SwiftRpcFactoryBean.class);
beanDefinition.getPropertyValues().add("rpcInterface", className);
beanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
static class SwiftRpcClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
SwiftRpcClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
super(registry, false);
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return metadata.isInterface() && metadata.isIndependent();
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
return super.doScan(basePackages);
}
}
}
复制代码
通过rpcInterface的代理来执行请求的调用。
public class SwiftRpcFactory<T> implements InvocationHandler {
private Class<T> rpcInterface;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String className = rpcInterface.getName();
String methodName = method.getName();
SwiftRpcService rpcService = rpcInterface.getAnnotation(SwiftRpcService.class);
String rpcServer = rpcService.server();
RpcRequest request = new RpcRequest();
request.setId(UUID.randomUUID().toString());
request.setClassName(rpcInterface.getName());
request.setMethodName(methodName);
request.setParameters(args);
request.setParameterTypes(method.getParameterTypes());
SwiftClientRunner swiftClient = ApplicationContextHolder.getApplicationContext().getBean(SwiftClientRunner.class);
return swiftClient.sendRequest(request, rpcServer);
}
}
复制代码
- 选择对应RPC Server的连接,发送请求,并等待结果返回
public Object sendRequest(RpcRequest request, String rpcServer) throws InterruptedException, IOException {
Channel channel = getChannel(rpcServer);
if (channel != null && channel.isActive()) {
// 发送请求后阻塞等待返回结果
SynchronousQueue<Object> queue = swiftClientHandler.sendRequest(request, channel);
return queue.poll(60, TimeUnit.SECONDS);
}
return null;
}
复制代码
@Component
@ChannelHandler.Sharable
public class SwiftClientHandler extends SimpleChannelInboundHandler<SwiftMessage> {
private final ConcurrentHashMap<String, SynchronousQueue<Object>> requests = new ConcurrentHashMap<>();
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, SwiftMessage swiftMessage) throws Exception {
byte[] content = swiftMessage.getContent();
RpcResponse response = Kryos.deserialize(content, RpcResponse.class);
String requestId = response.getRequestId();
SynchronousQueue<Object> queue = requests.get(requestId);
queue.put(response.getData());
}
SynchronousQueue<Object> sendRequest(RpcRequest request, Channel channel) throws IOException {
SynchronousQueue<Object> queue = new SynchronousQueue<>();
requests.put(request.getId(), queue);
byte[] content = Kryos.serialize(request);
SwiftMessage message = new SwiftMessage(content.length, content);
channel.writeAndFlush(message);
return queue;
}
}
复制代码
总结
这里只是实现了一个很简单的RPC调用,很多地方都是简单处理了,比如用redis的hash存储代替服务注册中心。但是可以借助这个项目对RPC的工作流程有一个全面的了解,在学习和使用其他RPC框架也很有帮助。下面是对服务端和客户端工作流程的总结: 服务端:启动Netty服务,注册服务信息到注册中心,接收请求反序列化成RpcRequest,通过反射执行本地业务代码,将结果写回到Socket。 客户端:扫描RpcInterface列表,注册到Spring容器中心,启动服务,读取注册中心的RPC Server地址,建立连接,通过代理发起远程调用,等待结果返回。
项目地址:github.com/Phantom0103…
参考&感谢:
github.com/mybatis/spr…
juejin.cn/post/684490…