首页 > 技术文章 > 什么是RPC,用咱自己的语言通俗的讲讲。

changemax 2019-10-14 10:46 原文

风和丽日的下午,晴转多云的一天,一个正在读小学一年级的小朋友一脸天真烂漫的看着我,

突然发声问到:大哥哥,你知道什么是RPC吗?

我一本正经的回答道:不知道。

突然他就开始嘲讽我了:你特么作为一名资深专家,你竟然不知道RPC,就是Remote Procedure Call的一个缩写啊,远程过程调用!!!猪一样,在服务间通讯的一种方式!!。

我滴妈,这这这,,瞬间就脸红了。

然后他又补充道:那你知道啥是本地过程调用吗????

我支支吾吾的回答道:不不不不不知道啊。

他盯着我说道:我本来就在家,然后你给我一块钱,然后用这一块钱,让你给我去隔壁超市买了一包辣条。这就是本地调用了你的买辣条功能。这就是叫做本地过程调用。

那你又知道什么是远程过程调用吗?

我惊着瞪大了眼睛问道:你知道?

就是你不在家,在县城出差,然后我打个电话给你,吩咐你给我买几包辣条啊。这不就是我远程调用了你的买辣条功能吗。这不就是远程过程调用吗!!你啥时候出差?我给你发个RPC。或者说,现在咱就本地过程调用一下?

 

我羞愧不如,这小学一年级是吃了激素吗,大脑发育超标了吗。。。

 

 

好了,咱也不扯淡了,深入聊聊RPC吧。

说道RPC,我是缘由分布式接触到的,也正是因为分布式架构模型的诞生,也出现了远程过程调用这个萌发。

1.在单体服务中,我们会在一个服务项目上做很多很多的业务,比如用户接口:UserSerivce,以及我们需要对此接口进行实现:UserServiceImpl,那么我们需要执行一个编辑设备的操作,那么我们就会调用UserService的查询用户信息接口:getUserInfo。接后然后有了当前操作员的信息,然后就可以记录下当前编辑设备的操作员的信息。这也就是本地过程调用,在同一个项目中,且编译的时候,都是在同一个内存服务中,这种通过方法栈和参数栈就可以基本实现了。

2.但是呢,由于各种市场,线上需要,后台服务不得不要提升系统的高性能和高可用,高可靠等方案,我们不得不需要将整体系统进行重新的架构优化调整,那么就是分布式应用架构方案了。对于分布式服务划分规则,在现在的业内,讨论没有一个统一的说法,就是说:有人认为按业务逻辑划分服务;有人认为按领域模型进行划分服务,等等等,层次不一。这里笔者习惯用业务逻辑划分。比如上面说到的编辑设备操作,我们需要记录操作员的一些基本信息,用于日志查看,那么需要调用用户接口,先获取用户信息,再执行其他操作,这个时候,我们可能会将用户这个模块,划分为usercenterService中去,然后编辑设备操作的主体应用为machineService,这个时候,我machineSerivce需要执行编辑设备的操作的时候,需要调用getUserInfo这个接口。但是这个时候,已经实现服务分离了,在machineService中都找不到这个接口了,咋办呢?像那种前后端交互接口形式调用吗?machineService模拟客户端请求usercenterService吗?但是似乎有点繁琐,是否可用直接像spring调用bean接口的方法呢?

usercenterService.getUserInfo(userId);

就好像吧usercenterService作为一个bean对象,注入到spring容器中去呢?就好似spring ioc一样,通过代理模式一样,通过模拟客户端请求实现RPC远程过程调用的。

 

那么rpc到底解决了什么问题呢?

1.解决分布式系统中,各个服务之间调用逻辑功能的问题。让实际上是两个服务,但是在业务方法调用的时候,就好像在一个单体应用中调用的单体服务一样。

那么我们贴上一个实际项目中springcloud项目的一个客户端消费案例代码:(采用的是feign客户端工具实现远程调用)

package cn.changemax.erp.service;


import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;

import javax.validation.constraints.NotBlank;
import java.util.*;

/**
 * 书写备注:
 * 1、不支持PostMapping和GetMapping组合注解,只能使用RequestMapping在注解后通过method指定请求类型
 * 2、RequestParam注解中一定要书写value值,Feign不支持不书写value默认与后者相同
 * 3、对于复杂参数尤其是复杂对象,要用RequestBody注解,但是注意RequestBody在一个接口中只能出现一次
 * 4、新增接口时,同时要在 SpringCloudProducerFallbackFactory 类中做服务降级处理
 *
 * 服务之间书写备注:
 * 0、对于某些需求是直接从数据库中取数据而不经过服务生产方任何处理操作的接口,统一放在DirectDaoController中
 * 1、在服务生产方中提供给服务消费方的接口也要按照书写备注一、二、三格式
 * 2、服务之间的接口使用ServiceApiresult传递参数
 *
 * 问题排查备注:
 * 0、检查输出日志中的fallback error reason :中的问题,若是无法解决按序排查问题
 * 1、检查两边接口参数返回值类型是否匹配
 * 2、检查书写备注一、二、三
 * 3、检查服务生产方是否处理过久触发了熔断
 */
@Service
@FeignClient(name = "usercenter", fallbackFactory = SpringCloudProducerFallbackFactory.class)
public interface UserCenterServiceClient {

    @RequestMapping(value = "/api/uc/authority/getServiceUserAuthorityOrgTreeVOList", method = RequestMethod.GET)
    ServiceApiResult<List<OrgTreeVO>> getServiceUserAuthorityOrgTreeVOList(
            @RequestParam("appAbbreviation") String appAbbreviation,
            @RequestParam("userId") @NotBlank String userId,
            @RequestBody @RequestParam("jcRankdiagramDTO") String jcRankdiagramDTOString
    );
}

 

那么服务端生产是如何的呢?

其实也类似restful风格,也是一种在服务端暴露一个接口,供外部调用。但是RPC和Restful有啥区别呢?可以这么说,这两者并不能直接比较,两者并不是同一维度概念,RPC维度更广。

而Restful只是一个接口风格。RPC是面向过程的,Restful是面向资源的,所以说restful是一种很受欢迎的接口风格。RPC都是一种框架了。

推荐阅读