RPC调用是面向服务架构场景下进行服务间调用的常用组件,一个完整的RPC调用的流程如图1所示:

图1 RPC调用流程

为了方便RPC调用者和服务者的开发,开发者们开发了很多RPC框架。比较有名的RPC框架有Google的gRPC、Facebook的Thrift 和 阿里的 Dubbo 等。这些框架在具体实现上虽然各不相同,但其工作原理基本上是一致的。

一个设计良好的RPC框架的愿景是简化服务提供者和调用者的开发,将图1中 2~8 步骤的所有操作全部隐藏,让开发者可以像开发和调用本地方法一样开发和调用远程方法。为了隐藏这些实现细节,RPC框架需要关注如下几点:

  1. 方法调用:方法调用的过程如何转换为可以传输的消息内容;
  2. 服务发现:如何发布服务并告知调用者服务地址;
  3. 数据传输:服务调用者和提供者之间如何进行数据传输。

下面我们以Java语言为例,就以上几点分别做出分析。

1. 方法调用

1.1 “协议”设定

要完成一个完整的方法调用,需要在调用者和被调用者之间设计一套“协议”对被调接口、被调方法、参数类型、参数值、返回类型、返回值等进行定义,如下是一段比较合理的通信“协议”:

public class Request implements Serializable {
private long sid;
private String serviceName;
private String methodName;
private Class<?>[] paramTypes;
private Object[] params;
} public class Response implements Serializable {
private long sid;
private Class<?> resultType;
private Object result;
}

Request里面,需要指定具体的服务、方法名,以及调用方法时需要传入的参数类型和参数值;Response里面直接指定返回值及其类型(因为返回值最多只有一个)。特别地,在Request 和 Response里面分别有一个 sid 字段,是为了在异步处理时能正确地对应请求和返回的关系。

1.2 请求、返回与“协议”对象的转化

为了简化服务方和调用方的实现,RPC框架里面需要完成方法请求与request对象之间的转换 和 处理结果与reponse对象之间的转换。对于Java,可以采用JDK的动态代理来实现这个转换;如果采用Spring框架,还可以通过Spring的动态代理类 -- FactoryBean进行实现。下面一段代码是通过JDK的动态代理实现本地调用转化为消息的一个demo:

// 动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class RpcProxyInvoker implements InvocationHandler {
private Class<?> clazz; public RpcProxyInvoker(Class<?> clazz) {
this.clazz = clazz;
} public static Object getProxy(Class<?> clazz) {
RpcProxyInvoker invoker = new RpcProxyInvoker(clazz);
return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, invoker);
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class<?>[] paramTypes = new Class[] {};
for (int idx = 0; idx < args.length; ++idx) {
paramTypes[idx] = args[idx].getClass();
} Request req = new Request();
req.setServiceName(clazz.getClass().getName());
req.setMethodName(method.getName());
req.setParameterTypes(paramTypes);
req.setParameters(args); /// ... 后续操作:序列化、网络传输等
return null;
}
} // 暴露给调用方的接口
public interface UserService {
UserDTO get(int userId);
} // 调用方调用简单示例
public class Main {
public static void main(String[] args) {
UserService userService = (UserService) RpcProxyInvoker.getProxy(UserService.class);
userService.get(1);
}
}

为了方便传输,需要对生成的Request对象进行序列化。最简单的序列化方法是采用Java的ObjectInput/OutputStream实现,当然也可以通过JSON等格式进行对象的序列化。
序列化结束后,交给网络模块进行网络传输并等待返回结果进行后续处理。

2. 服务发现

当RPC框架应用于大型分布式系统时,不可能将服务的地址人为地告知到每个调用方,应该自动化地完成这个“告知”工作。这个工作的流程如图2所示:

图2 服务发现

注册中心可以采用redis或者zookeeper等方式进行实现。
从服务提供者的角度看:当提供者服务启动时,需要自动向注册中心注册服务;当提供者服务停止时,需要向注册中心注销服务;提供者需要定时向注册中心发送心跳,一段时间未收到来自提供者的心跳后,认为提供者已经停止服务,从注册中心上摘取掉对应的服务。从调用者的角度看:调用者启动时订阅注册中心的消息并从注册中心获取提供者的地址;当有提供者上线或者下线时,注册中心会告知到调用者;调用者下线时,取消订阅。

3. 数据传输

服务提供者和调用者之间进行数据传输,可以借助基于端口的协议和基于HTTP的协议进行,如使用TCP协议通信 或 使用HTTP协议通信。通信方式上,可以使用传统的BIO(Blocked I/O) 或 NIO(New I/O)方式进行,在此不再一一赘述。

虽然RPC框架的原理比较简单,但要实现一个功能齐全、适应性强、对代码无侵入的RPC框架并非易事,需要在网络传输、序列化、Stub和Skeleton功能等方面下功夫。当然,在了解原理的情况下,使用一个RPC框架,对业务代码的编写和优化也有一定的促进作用。

链接:https://www.jianshu.com/p/f67e9f6605d7

微服务理论之三:RPC框架原理的更多相关文章

  1. 面试都在问的「微服务」「RPC」「服务治理」「下一代微服务」一文带你彻底搞懂!

    ❝ 文章每周持续更新,各位的「三连」是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) ❞ 单体式应用程序 与微服务相对的另一个概念是传统的「单体式应用程 ...

  2. 微服务,开源 RPC 框架 - Spring Cloud

    Spring Cloud:国外 Pivotal 公司 2014 年对外开源的 RPC 框架,仅支持 Java 语言 Spring Cloud 利用 Spring Boot 特性整合了开源行业中优秀的组 ...

  3. 【微服务】之三:从零开始,轻松搞定SpringCloud微服务-配置中心

    在整个微服务体系中,除了注册中心具有非常重要的意义之外,还有一个注册中心.注册中心作为管理在整个项目群的配置文件及动态参数的重要载体服务.Spring Cloud体系的子项目中,Spring Clou ...

  4. 微服务理论之五:微服务架构 vs. SOA架构

    一.面向服务的架构SOA 面向服务的架构是一种软件体系结构,应用程序的不同组件通过网络上的通信协议向其他组件提供服务.通信可以是简单的数据传递,也可以是两个或多个服务彼此协调连接.这些独特的服务执行一 ...

  5. 微服务理论之二:面向微服务架构与传统架构、SOA对比,以及云化对比

    一.Monolith 网上对Microservice进行介绍的文章常常以Monolith作为开头,我也不会例外.原因是,知道了Monolith的不便之后才能更容易地理解Microservice架构模式 ...

  6. 【微服务理论】API + BFF

    对于微服务,常见的架构模型就是API网关+服务. API网关实现鉴权.负载均衡.中间件等公共入口逻辑. 服务实现具体的业务功能. 那么,API网关设计中又有什么坑呢? 1.0版本 直接将服务穿透到外网 ...

  7. 从壹开始微服务 [ DDD ] 之三 ║ 简单说说:领域、子域、限界上下文

    前言 哈喽大家好,DDD领域驱动设计系列又开始了,前天周二的那篇入门文章中,也收到了一定的效果(写小说的除外),同时我也是倍感鸭梨,怎么说呢,DDD领域驱动设计已经有十年历史了,甚至更久,但是包括我在 ...

  8. 微服务理论之六:ESB与SOA的关系

    一.SOA和ESB一直是没有明确概念的两个缩略词 SOA----面向服务架构,实际上强调的是软件的一种架构,一种支撑软件运行的相对稳定的结构,表面含义如此,其实SOA是一种通过服务整合来解决系统集成的 ...

  9. 微服务理论之四:SOA

    1.什么是SOA? SOA代表了面向服务的架构. SOA是一种使用松耦合的黑盒子服务构建业务应用的体系架构,这些服务可以通过编排连接在一起以实现特定的功能. 2.SOA特点 以下是服务的SOA的主要特 ...

随机推荐

  1. 我的Android进阶之旅------>Android中android:windowSoftInputMode的用法

    面试题:如何在显示某个Activity时立即弹出软键盘? 答案:在AndroidManifest.xml文件中设置<activity>标签的android:windowSoftInputM ...

  2. struts2 封装获取表单数据的方式

    一.属性封装 1.在action中设置成员变量,变量名与表单中的name属性值相同 2.生成变量的set方法 实例 获取用户输入的用户名和密码 jsp页面 java代码 二.模型驱动(常用) 1.ac ...

  3. leetcode第一刷_Best Time to Buy and Sell Stock II

    这道题尽管是上一道题的增强.可是反而简单了. 能够交易无数次,可是买卖必须成对的出现. 为了简单起见.我用abc三股股票来说明,且忽略掉相等的情况.三个数一共同拥有六种大小关系.注意他们之间的先后顺序 ...

  4. Power Designer体验之旅

    版权声明:本文为博主原创文章.未经博主允许不得转载. https://blog.csdn.net/wang13667539325/article/details/36025245 从某种程度上说.不论 ...

  5. 【logback】认识logback

    一. Reference:http://www.cnblogs.com/yongze103/archive/2012/05/05/2484753.html 1. Logback为取代log4j而生,l ...

  6. python cookbook 迭代器与生成器

    代理迭代 a = [1, 2, 3] for i in iter(a): print(i) for i in a.__iter__(): print(i) 这里的两个方法是一样的,调用iter()其实 ...

  7. Docker 搭建本地Registry

    Docker已经将Registry开源,Registry本身也是一个容器. 1. 修改配置/etc/docker/daemon.json,去掉docker默认的https的访问   里面的内容是一个j ...

  8. EntityFramework 学习 一 Persistence in Entity Framework

    实体框架的持久化 当用EntityFramework持久化一个对象时,有两种情形:连接的和断开的 1.连接场景:使用同一个context上下文从数据库中查询和持久化实体时,查询和持久化实体期间,con ...

  9. hdoj1001--Sum Problem

    Problem Description Hey, welcome to HDOJ(Hangzhou Dianzi University Online Judge).In this problem, y ...

  10. jquery实现td控制显示宽度

    目的为了实现td表格元素出现省略的情况,然后点击中间位置是td的宽度增加. 实现代码如下,采用css+jquery的实现方式: <!DOCTYPE html> <html lang= ...