smart-rpc客户端

学习黄勇smart-rpc的笔记

黄勇的码云地址:https://gitee.com/huangyong/rpc

黄勇的oschina博客地址:https://my.oschina.net/huangyong/blog/361751


rpc是客户端把请求对象序列化成二进制通过网络传给服务端,服务端拿到这些二进制后反序列化成本地对象,执行本地方法调用,把生成的结果再用同样的方式传给客户端。如何获取服务端的地址,如何让对象在网络上传输,这两个过程中我们可以做很多自己的操作,比如服务注册,负载均衡,nio等。


1、客户端使用动态代理创建代理对象,smart-rpc使用jdk的动态代理

    /**
     * 动态代理对接口进行实现
     * 代理的好处,不管哪个方法都要进行这样的操作
     *
     * @param interfaceClass
     * @param serviceVersion
     * @param <T>
     * @return
     */
    public <T> T create(final Class<?> interfaceClass, final String serviceVersion) {
        // 创建动态代理对象
        return (T) Proxy.newProxyInstance(
                interfaceClass.getClassLoader(),
                new Class<?>[]{interfaceClass},
                new InvocationHandler() {
                    // invoke方法方法调用时执行
                    // 真正发送请求,接受响应的方法都在这里
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 创建 RPC 请求对象并设置请求属性
                        RpcRequest request = new RpcRequest();
                        // uuid做requestId
                        request.setRequestId(UUID.randomUUID().toString());
                        // 接口类型
                        request.setInterfaceName(method.getDeclaringClass().getName());
                        // 接口版本
                        request.setServiceVersion(serviceVersion);
                        // 方法名称
                        request.setMethodName(method.getName());
                        // 方法参数
                        request.setParameterTypes(method.getParameterTypes());
                        request.setParameters(args);
                        // 获取 RPC 服务地址
                        if (serviceDiscovery != null) {
                            String serviceName = interfaceClass.getName();
                            if (StringUtil.isNotEmpty(serviceVersion)) {
                                serviceName += "-" + serviceVersion;
                            }
                            //  从注册中心找到服务端的地址(ip+ port)
                            serviceAddress = serviceDiscovery.discover(serviceName);
                            LOGGER.debug("discover service: {} => {}", serviceName, serviceAddress);
                        }
                        if (StringUtil.isEmpty(serviceAddress)) {
                            throw new RuntimeException("server address is empty");
                        }
                        // 从 RPC 服务地址中解析主机名与端口号
                        String[] array = StringUtil.split(serviceAddress, ":");
                        String host = array[0];
                        int port = Integer.parseInt(array[1]);
                        // 创建 RPC 客户端对象并发送 RPC 请求
                        RpcClient client = new RpcClient(host, port);
                        long time = System.currentTimeMillis();
                        // 通过网络远程传输请求数据
                        // 接受返回的响应
                        RpcResponse response = client.send(request);
                        LOGGER.debug("time: {}ms", System.currentTimeMillis() - time);
                        if (response == null) {
                            throw new RuntimeException("response is null");
                        }
                        if (response.hasException()) {
                            throw response.getException();
                        } else {
                            return response.getResult();
                        }
                    }
                }
        );
    }


2、客户端将请求对象写入网络中

/**
     * 向远程服务器发送请求的过程
     * 实际上就是把我们自己封装的请求对象写进输出流中
     * @param request
     * @return
     * @throws Exception
     */
    public RpcResponse send(RpcRequest request) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            // 创建并初始化 Netty 客户端 Bootstrap 对象
            //  使用netty 传输数据
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group);
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel channel) throws Exception {
                    ChannelPipeline pipeline = channel.pipeline();
                    // 设置对传输的数据的处理
                    // 对RpcRequest对象要编码处理
                    pipeline.addLast(new RpcEncoder(RpcRequest.class)); // 编码 RPC 请求
                    // 对RpcResponse对象要解码处理
                    pipeline.addLast(new RpcDecoder(RpcResponse.class)); // 解码 RPC 响应
                    // 对通道返回数据的处理,保存到Response对象
                    pipeline.addLast(RpcClient.this); // 处理 RPC 响应
                }
            });
            bootstrap.option(ChannelOption.TCP_NODELAY, true);
            // 连接 RPC 服务器
            // 知道服务器地址,真正建立连接
            ChannelFuture future = bootstrap.connect(host, port).sync();
            // 写入 RPC 请求数据并关闭连接
            // 建立通道
            Channel channel = future.channel();
            // rpc请求对象在这被写入了通道中
            channel.writeAndFlush(request).sync();
            // 关闭通道
            channel.closeFuture().sync();
            // 返回 RPC 响应对象
            System.out.println("响应要被返回了");
            // 响应被保存到了Response对象中
            return response;
        } finally {
            group.shutdownGracefully();
        }
    }



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值