smart-rpc服务端

学习黄勇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);
            }
        }
    }



2、启动线程监听服务端地址,接受从客户端传过来的数据,并把服务端地址和已发布的接口注册到注册中心

/**
     * 这个方法来自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);
    }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值