IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 【专栏】RPC系列(实战)-负重前行的“动态代理” -> 正文阅读

[网络协议]【专栏】RPC系列(实战)-负重前行的“动态代理”

关注公众号:离心计划,一起离开地球表面?

【RPC系列合集】

【专栏】RPC系列(理论)-夜的第一章

【专栏】RPC系列(理论)-协议与序列化

【专栏】RPC系列(理论)-动态代理

【专栏】RPC系列(实战)-摸清RPC骨架

【专栏】RPC系列(实战)-优雅的序列化

【专栏】RPC系列(番外)-IO模型与线程模型

【专栏】RPC系列(番外)-“土气”的IO实现

【专栏】RPC系列(实战)-Hello Netty

【专栏】RPC系列(实战)-低配版NameServer

| 前言

? ? 上一小节我们实现了一个低配版的NameServer,那么NameServer对于服务端来说需要完成服务注册,那么对于客户端也就是调用方来说,NameServer也需要提供服务发现的功能,也就是上一小节实现代码中的seekService方法。让我们回到RPC的初衷,客户端像调用本地方法一样调用远程服务,在【专栏】RPC系列(理论)-动态代理中我们已经知道了,替我们负重前行的是代理,所以包括寻找服务、封装调用参数、调用远程服务等操作都是在代理中完成的,这一节我们就回到客户端调用开始剖析动态代理部分的实现。

? ? 这一节的代码在:https://github.com/JAYqq/my-sparrow-rpc/tree/v1.4

| 动态代理

? ? 首先我们新建一个代理工厂类ProxyFactory,两个方法分别表示创建代理类和设置Rpc的传输类,也就是我们之前实现过的NettyTransport

public interface ProxyFactory {
    <T> T createProxy(Class<T> clazz, ServiceMetaInfo metaInfo);

    void setRpcTransport(RpcTransport rpcTransport);
}

? ? 然后我们实现一个CglibProxyFactory,这里我们使用Cglib代理技术,同样基于JDK代理实现。还需要将该实现类注册到SPI中?com.sparrow.rpc.core.client.ProxyFactory

public class CglibProxyFactory implements ProxyFactory {
    RpcTransport rpcTransport;

    @Override
    public <T> T createProxy(Class<T> clazz, ServiceMetaInfo metaInfo) {
        CglibRpcProxy proxy = new CglibRpcProxy(clazz, metaInfo, rpcTransport);
        return (T) proxy.getProxy();
    }

    public void setRpcTransport(RpcTransport rpcTransport) {
        this.rpcTransport = rpcTransport;
    }
}

? ? CglibRpcProxy正是我们创建代理、发送Rpc请求的重要实现类,它的构造函数接受三个参数,分别是我们代理的目标Class、远程服务元信息(告诉服务端我调的是哪个服务)以及具体Transport类。

public class CglibRpcProxy implements MethodInterceptor

? ? Cglib代理需要实现MethodInterceptor,它创建代理类的方法getProxy是这样的,这样每次调用这个代理类中的方法就会被拦截到MethodInterceptor的intercept方法,重点也就是实现intercept方法,这个和【专栏】RPC系列(理论)-动态代理?中提到的JDK代理invoke是一样的作用

public Object getProxy() {
    //设置需要创建子类的类
    enhancer.setSuperclass(clazz);
    enhancer.setCallback(this);
    return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) {
    RpcRequest rpcRequest = new RpcRequest();
    //放入调用服务信息
    rpcRequest.setNameSpace(metaInfo.getNameSpace());
    rpcRequest.setServiceName(metaInfo.getServiceName());
    rpcRequest.setMethodName(method.getName());
    //指定用jdk序列化方式
    rpcRequest.setParameters(SerializeSupport.serialize(objects, SerializerType.TYPE_OBJECT_ARRAY.getType()));
    return callRemoteService(rpcRequest);
}

private Object callRemoteService(RpcRequest request) {
    RpcCommand rpcCommand = new RpcCommand();
    RpcHeader header = new RpcHeader();
    header.setType(CommandTypes.RPC_REQUEST.getType());
    header.setVersion("v1.0");
    header.setTraceId(UUID.randomUUID().toString());
    rpcCommand.setHeader(header);
    rpcCommand.setData(SerializeSupport.serialize(request));
    try {
        //todo 这个get与RpcResponseHandler的complete可讲
        RpcResponse rpcResponse = rpcTransport.send(rpcCommand).get();
        if (RspCode.SUCCESS.getCode() != rpcResponse.getCode()) {
            throw new RuntimeException(rpcResponse.getErrorMsg());
        }
        return rpcResponse.getData();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

? ? intercept的逻辑就是组装Request并通过RpcTransport将请求发送出去,大家可以回顾一下send方法,就是Netty相关的部分。这边我们将调用远程方法的参数指定使用了JDK序列化方式,这也是出于教程目的,至此我们的代码结构是这样

? ? 然后对于客户端来说,创建代理类这个过程应该是透明的,客户端需要做的就是直接调用代理类的某个方法。除此之外,我们的Transport也需要根据服务的地址创建出来,这部分逻辑我们提取到NettyRpcAccessor中去

@Override
public <T> T getRemoteService(ServiceMetaInfo metaInfo, Class<T> clazz) throws InterruptedException, TimeoutException {
    //查询服务地址
    NameService nameService = SpiSupport.load(NameService.class);
    String serviceSign = metaInfo.getServiceSign();
    URI uri = nameService.seekService(serviceSign);
    //与服务创建Transport
    TransportClient transportClient = SpiSupport.load(TransportClient.class);
    InetSocketAddress address = new InetSocketAddress(uri.getHost(), uri.getPort());
    RpcTransport transport = transportClient.createTransport(address, 3000);
    //创建代理类
    ProxyFactory proxyFactory = SpiSupport.load(ProxyFactory.class);
    proxyFactory.setRpcTransport(transport);
    return proxyFactory.createProxy(clazz, metaInfo);
}

? ? 这个方法将NameService、Netty以及动态代理三者联系到了一起,充分理解这个方法的过程很重要。

|?请求测试

? ? 至此,我们完成了Sparrow-Rpc所有的环节,现在我们编写测试类来测试一下我们一步步完成的RPC服务。我们创建一个UserService服务,并实现一个getUserByName方法,在client远程调用这个方法,完整的代码在sparrow-rpc-client和sparrow-rpc-server两个模块下,启动com.sparrow.rpc.server.Server和com.sparrow.rpc.client.Client?就可以看到效果了,通过控制台的信息可以看到通过传入的“Mason”服务端返回了具体的用户信息给我们,另外还有健康检测的打印信息来服务保活。

| 总结

? ? 这一节我们实现了动态代理部分的逻辑,大家应该体会到了动态代理在RPC中的应用是怎样的,它最主要的作用在于代理远程服务,封装了组装调用远程服务的请求并获取结果这部分的网络交互逻辑,对于客户端只需要调用代理类中的目标方法就行了,这也是我们在专栏开头说到,RPC像调用本地方法一样调用远程服务的“魔法”所在。

? ? 另外,这也是实战篇的最后一篇文章,整个Sparrow-Rpc已经可以跑起来了,虽然离一个工业级的RPC有很大差距,但是我们用拙劣的方法摸清了RPC中最重要的功能,并动手实现了代码细节,相信大家对RPC已经有了更加深入的认识了,这里我建议大家可以再去看看开源的RPC框架的设计,知道Sparrow-Rpc和它们还有哪些差距才有优化的方向。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 21:14:47  更:2022-10-08 21:16:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 21:31:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码