学习黄勇smart-rpc的笔记
黄勇的码云地址:https://gitee.com/huangyong/rpc
黄勇的oschina博客地址:https://my.oschina.net/huangyong/blog/361751
服务端要干的事情,要发布接口,要监听从客户端发过来的请求,要执行本地的接口方法,将返回值写入响应中。
1、把需要发布的接口初始化到内存中
/**
* 这个方法来自ApplicationContextAware接口
* 顾名思义,拥有程序上下文环境
* 实现了这个接口,spring context初始化的时候,ApplicationContext便会被注入进来
* 我们就可以在其中注入自己的实现
* @param ctx
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
// 扫描带有 RpcService 注解的类并初始化 handlerMap 对象
Map<String, Object> serviceBeanMap = ctx.getBeansWithAnnotation(RpcService.class);
if (MapUtils.isNotEmpty(serviceBeanMap)) {
for (Object serviceBean : serviceBeanMap.values()) {
// 获取bean上的注解
RpcService rpcService = serviceBean.getClass().getAnnotation(RpcService.class);
// 获取服务名
String serviceName = rpcService.value().getName();
// 获取版本号
String serviceVersion = rpcService.version();
if (StringUtil.isNotEmpty(serviceVersion)) {
serviceName += "-" + serviceVersion;
}
handlerMap.put(serviceName, serviceBean);
}
}
}
/**
* 这个方法来自InitializingBean 接口
* 实现了这个口,初始化之前会先调用afterPropertiesSet方法
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
//建立两个Group,接受网络上传过来的数据
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 创建并初始化 Netty 服务端 Bootstrap 对象
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup);
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel channel) throws Exception {
// 设置通道中对象的处理方式
ChannelPipeline pipeline = channel.pipeline();
// 对传过来的RpcRequest对象进行解码
pipeline.addLast(new RpcDecoder(RpcRequest.class)); // 解码 RPC 请求
// 将返回的RpcResponse对象编码
pipeline.addLast(new RpcEncoder(RpcResponse.class)); // 编码 RPC 响应
// RpcServerHandler 实现了SimpleChannelInboundHandler 接口
// 具体实现channelRead0 方法对输入流进行处理
// 对服务端而言,输入流就是客户端传过来的请求
// 对客户端而言,输入流就是服务端返回的响应
pipeline.addLast(new RpcServerHandler(handlerMap)); // 处理 RPC 请求
}
});
bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
// 获取 RPC 服务器的 IP 地址与端口号
String[] addressArray = StringUtil.split(serviceAddress, ":");
String ip = addressArray[0];
int port = Integer.parseInt(addressArray[1]);
// 启动 RPC 服务器
// 线程监听ip 和 port
ChannelFuture future = bootstrap.bind(ip, port).sync();
// 注册 RPC 服务地址
if (serviceRegistry != null) {
for (String interfaceName : handlerMap.keySet()) {
// 当前服务已经启动,把服务注册到注册中心
serviceRegistry.register(interfaceName, serviceAddress);
LOGGER.debug("register service: {} => {}", interfaceName, serviceAddress);
}
}
LOGGER.debug("server started on port {}", port);
// 关闭rpc 服务器,监听任然存在
future.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
3、具体处理请求并写入响应的方法
/**
* 处理通道的对象
*
* @param ctx
* @param request
* @throws Exception
*/
@Override
public void channelRead0(final ChannelHandlerContext ctx, RpcRequest request) throws Exception {
// 创建并初始化 RPC 响应对象
RpcResponse response = new RpcResponse();
response.setRequestId(request.getRequestId());
try {
// 处理Request 对象
Object result = handle(request);
// 处理结果放到response
response.setResult(result);
} catch (Exception e) {
LOGGER.error("handle result failure", e);
response.setException(e);
}
// 写入 RPC 响应对象并自动关闭连接
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
/**
* 处理rpc请求的方法,请求不过是一些数据,怎么样处理这些数据由自己决定
* @param request
* @return
* @throws Exception
*/
private Object handle(RpcRequest request) throws Exception {
// 获取服务对象
String serviceName = request.getInterfaceName();
String serviceVersion = request.getServiceVersion();
if (StringUtil.isNotEmpty(serviceVersion)) {
serviceName += "-" + serviceVersion;
}
// 接口实例直接从服务端内存中获取
// 保证服务端有此接口,并且已经注册到注册中心
Object serviceBean = handlerMap.get(serviceName);
if (serviceBean == null) {
throw new RuntimeException(String.format("can not find service bean by key: %s", serviceName));
}
// 获取反射调用所需的参数
Class<?> serviceClass = serviceBean.getClass();
String methodName = request.getMethodName();
Class<?>[] parameterTypes = request.getParameterTypes();
Object[] parameters = request.getParameters();
// 执行反射调用
// Method method = serviceClass.getMethod(methodName, parameterTypes);
// method.setAccessible(true);
// return method.invoke(serviceBean, parameters);
// 使用 CGLib 执行反射调用
FastClass serviceFastClass = FastClass.create(serviceClass);
FastMethod serviceFastMethod = serviceFastClass.getMethod(methodName, parameterTypes);
return serviceFastMethod.invoke(serviceBean, parameters);
}