rpc调用过程

RPC简介

RPC是远程过程调用(Remote Procedure Call)的缩写形式。

RPC调用流程图

客户端方法:服务调用方所调用的接口

客户端代理:将接口封装成代理对象,并序列化请求参数、反序列化响应结果,使用远程传输协议调用服务端,(例如:Socket、Netty、RMI、HTTP等)。

远程网络调用:采用远程传输协议进行通信实现数据的传输。

服务端代理:服务端收到远程请求后,将二进制的数据反序列化为请求对象,然后调用本地接口返回响应数据并序列化结果发送出去。

服务端方法:服务提供者具体的实现,也就是最终请求的方法。

RPC的简单实现

接下来通过一个简单的demo来分析RPC调用过程

源码git地址:https://github.com/xxiangzh/x-rpc

项目结构,consumer为服务调用者,即客户端;provider为服务提供者,即服务端。

接口调用过程

代码详情

用户接口(客户端和服务端相同,复制过去即可)

public interface UserService {

    String getUserNameById(Long userId);
}

RPC通信参数(客户端和服务端相同,复制过去即可)

@Data
@NoArgsConstructor
@AllArgsConstructor
public class RpcRequest implements Serializable {
    private static final long serialVersionUID = 1L;

    private String methodName;

    private Long parameter;

}

用户接口实现类(客户端通过远程RPC最终调用的是这个接口)

public class UserServiceImpl implements UserService {

    public String getUserNameById(Long userId) {
        return userId > 0 ? "向振华" : "无名";
    }
}

服务提供者RPC通信类,采用socket通信

public class RpcHandler {

    public void run() throws Exception {
        ServerSocket serverSocket = new ServerSocket(8081);
        while (true) {
            Socket socket = serverSocket.accept();
            // 将请求体反序列化
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            Object object = objectInputStream.readObject();
            // 服务调用
            Object result = null;
            if (object instanceof RpcRequest) {
                RpcRequest rpcRequest = (RpcRequest) object;
                if ("getUserNameById".equals(rpcRequest.getMethodName())) {
                    UserService userService = new UserServiceImpl();
                    result = userService.getUserNameById(rpcRequest.getParameter());
                } else {
                    throw new RuntimeException("method not found");
                }
            }
            // 结果返回
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(result);
        }
        // TODO socket.close(); serverSocket.close();
    }
}

然后启动服务提供者

public class ProviderTest {

    public static void main(String[] args) throws Exception {
        new RpcHandler().run();
    }
}

服务调用者,用户接口的实现,这里是通过RPC方式调用

public class RpcUserServiceImpl implements UserService {

    public String getUserNameById(Long userId) {
        try {
            Socket socket = new Socket("127.0.0.1", 8081);
            // 将请求体序列化并发给服务提供方
            RpcRequest rpcRequest = new RpcRequest("getUserNameById", userId);
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(rpcRequest);
            // 将响应体反序列化
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            Object response = objectInputStream.readObject();
            // 返回结果
            return (String) response;
        } catch (Exception e) {
            return null;
        }
        // TODO socket.close();
    }
}

服务调用者测试类

public class ConsumerTest {

    public static void main(String[] args) {
        UserService userService = new RpcUserServiceImpl();
        System.out.println(userService.getUserNameById(1L));
        System.out.println(userService.getUserNameById(-1L));
    }
}

启动服务端,然后再运行客户端测试类,即可得到结果。通过这个demo可以了解到RPC远程调用的大致流程,但是真正的RPC框架远不止这么简单。

真正的RPC会对接口代理、对请求信息统一封装通过反射实例化,通过注册中心维护服务器实例,通过负载均衡提供服务的灵活性和可用性,另外还有监控、拦截器、日志等等。

Dubbo

版本:2.7

Dubbo(读音[ˈdʌbəʊ])是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring框架无缝集成。

Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。

接下来看看Dubbo服务调用过程

Dubbo整体设计

图片来源于:https://dubbo.apache.org/zh/docs/v2.7/dev/design/ 

各层说明 

  • config 配置层:对外配置接口,以 ServiceConfigReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
  • proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy 为中心,扩展接口为 ProxyFactory
  • registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactoryRegistryRegistryService
  • cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 ClusterDirectoryRouterLoadBalance
  • monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactoryMonitorMonitorService
  • protocol 远程调用层:封装 RPC 调用,以 InvocationResult 为中心,扩展接口为 ProtocolInvokerExporter
  • exchange 信息交换层:封装请求响应模式,同步转异步,以 RequestResponse 为中心,扩展接口为 ExchangerExchangeChannelExchangeClientExchangeServer
  • transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 ChannelTransporterClientServerCodec
  • serialize 数据序列化层:可复用的一些工具,扩展接口为 SerializationObjectInputObjectOutputThreadPool

整个调用链如下图所示

Dubbo调用链

整个过程看起来负责多了,但是核心流程并没变,只是加了很多高可用、高性能的扩展。

客户端通过ProxyFactory代理工厂创建代理类,然后进入过滤器链路,然后构建Invoker对象,通过注册中心发现服务,通过负载均衡策略选择远程服务提供者,发起远程调用,然后通过远程传输协议将数据发送到服务端,服务端接收到请求后对数据进行解析、序列化,从线程池获取服务线程执行,再进入服务提供者的过滤器链路,执行本地方并返回结果。

Spring Cloud Feign

Feign是一个声明式的REST客户端,它的目的就是让REST调用更加简单。Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。

Feign远程调用的基本流程

图来源于:https://www.cnblogs.com/crazymakercircle/p/11965726.html

Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的 Request 请求。通过Feign以及JAVA的动态代理机制,使得Java 开发人员,可以不用通过HTTP框架去封装HTTP请求报文的方式,完成远程服务的HTTP调用。

Feign默认采用HttpURLConnection进行远程通信,新能比较差,可以改为OKHttp或者Netty等。

猜你喜欢

转载自blog.csdn.net/Anenan/article/details/114640198