> For the complete documentation index, see [llms.txt](https://nixum.gitbook.io/note/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://nixum.gitbook.io/note/rpc-yu-yi-bu-she-ji.md).

# RPC与异步设计

\[TOC]

## RPC

![](https://github.com/Nixum/Java-Note/raw/master/picture/RPC%E7%AE%80%E5%8D%95%E6%A1%86%E6%9E%B6.png)

一个RPC框架的基本实现，高性能网络传输、序列化和反序列化、服务注册和发现，如果是实现客户端级别的服务注册和发现，还可以在SDK中提供容错、负载均衡、熔断、降级等功能。

客户端发起RPC调用，实际上是调用该RPC方法的桩，它和服务端提供的RPC方法有相同的方法签名，或者说实现了相同的接口，只是这个桩在客户端承担的是请求转发的功能，向客户端屏蔽调用细节（比如向发现与注册中心查询要请求的服务方的url），使其像在使用本地方法一样；服务端在收到请求后，由其RPC框架解析出服务名和请求参数，调用在RPC框架中注册的该接口的真正实现者，最后将结果返回给客户端。

一个简单的RPC实现可以由三部分组成：规定远程的接口和其实现，服务端提供接口注册和IO连接，客户端IO连接和接口代理

1. 首先是定义要提供的远程接口和其实现类
2. 服务端使用线程池处理IO，实现多路复用，使用socket去循环accept()，每个请求建立一个线程

   线程里注册远程接口实例，使用InputStream接收客户端发送的参数，如接口的字节文件，判断是哪个接口，哪个方法，什么参数；接收后反射调用接口方法，将结果通过OutputStream发送回客户端

   客户端在发送参数可以做一个封装，加入id，服务端处理得到结果后也加入此id，返回回去，表示此次调用完成
3. 客户端使用接口，动态代理的方式调用方法，在动态代理的实现里使用IO连接服务端，将远程接口字节码、方法参数这些东西做一个封装发送给服务端，等待返回结果，IO接收是阻塞的

参考[【Java】java实现的远程调用例子 rpc原理](https://blog.csdn.net/u010900754/article/details/78081428)

[RPC原理及RPC实例分析](http://www.importnew.com/22003.html)

## 异步通信

优点：解耦，减少服务间的依赖，获得更大的吞吐量，削峰，把抖动的吞吐量变得均匀。

缺点：业务处理变得复杂，比如引入新的中间件，意味着要维护多一套东西，有时可能还得保证消息顺序，失败重传，幂等等处理，比较麻烦；异步也导致了debug的时候比较麻烦；

### 定时轮询

发送方请求接收方进行业务处理，接收方先直接返回，之后接收方在自己处理，最后将结果保存起来，发送方定时轮询接收方，获取处理结果。

### 回调

发送方请求接收方进行业务处理时，带上发送方结果回调的url，接收方接收到请求后先立刻返回，之后接收方在自己处理，当处理结果出来时，调用发送方带过来的回调url，将处理结果发送给发送方。

同理在于服务内部的异步回调，也是如此，只是把url换成了callback方法，比如Java中的Future类+Callable类。

### 发布订阅

主要靠消息队列实现，不过比较适合发送方不太care处理结果的，如果care处理结果，可以再通过一条队列将结果传递下去，执行后面的处理。

### 事件驱动 + 状态机

可以依靠消息队列，本质还是发布订阅那一套，只是将触发的条件换成事件，消费者根据不同的事件触发不同的逻辑，然后再通过状态机保证处理事件顺序。

比较常见的场景是电商业务中围绕订单服务的一系列业务处理，比如订单创建完成后，订单服务发出订单创建的事件，对应库存服务，收到该事件，就会进行锁库操作等

#### 事件驱动模型

实际上是使用了观察者模式和状态模式来实现的，比较直观的例子就是android的EventBus，chrome的V8引擎都有用到此模型，这里仅总结并进行简单介绍

![](https://github.com/Nixum/Java-Note/raw/master/picture/%E4%BA%8B%E4%BB%B6%E9%A9%B1%E5%8A%A8%E5%9F%BA%E6%9C%AC%E6%A1%86%E6%9E%B6.png)

这里的demo是项目中使用到的组件的一个简化，真实的组件要比这个复杂的多，这里只简单罗列出基本的原理和优缺点。

原理：

1. 事件驱动-状态机的异步模型，本质上是底层controller维护一个阻塞队列，将外部请求转化为事件，通过事件在内部传递。
2. controller接收到请求，从对象复用池中获取一个上下文context并init，然后将事件交由context处理。context内有一套状态的扭转的控制流程，在不同的状态接收事件对业务逻辑进行处理，最后将处理结果交由注册的回调函数异步或者同步返回。
3. 每一个状态在处理完当前逻辑操作后将发送事件给阻塞队列，并扭转为下一个状态，等待下一个事件的到来。
4. 由于controller是单线程的，各个状态在处理的时候要求速率尽可能的快，以至于不会阻塞主线程，因此在controller内部还维护了一个延迟队列，用来接收延迟事件，状态通常在进行业务处理前会起一个定时器，如果超时将发送延迟事件给到延迟队列，来避免当前操作过长导致阻塞主线程，定时器由下一个状态来取消。
5. 一般会为每个请求分配id，每个id对应一个上下文context，上下文一般使用id + Map来实现同一个请求下的上下文切换、保存和恢复，使用对象复用池来避免上下文对象频繁初始化
6. 这套模型一般应用在中间件的设计上，当然也可以抽成通用框架，在写的时候就会发现，其实变化最多的是状态流程那一块，所以完全可以把这块抽出来 + netty进行网络通信就能搭出一套web框架出来了

优点：

* 是一个单线程的模型，本身就是线程安全的。
* 理论上一套业务逻辑拆分成小逻辑，交由不同的状态操作，各个状态的操作时间要求尽可能的短，不然会阻塞主线程
* 各个状态在进行逻辑操作时，如果处理的时间过长，一般会使用线程池+回调+事件的方式处理
* 状态机模式对应业务逻辑流程有比较强的控制，各个状态对应不同的职责
* 对CPU的利用率比较高，吞吐量比较高，因为可以一次处理多种业务请求，每个业务请求都能进行拆分进行异步处理，速率比较快，因此性能会比常用的Spring全家桶好很多吧，至少在项目使用中的感受是这样

缺点：

* 处理请求时会初始化一个上下文context来处理，所有异步操作的结果会暂存在上下文中，对内存的占用会比较高，对上下文里的参数存储也需要一套规范
* 模型相对来讲还是比较复杂，运用多种设计模式，包含了一些回调，需要有一定的设计模式基础，容易劝退
* 对象复用池，对象回收，线程池的操作，一不小心会造成内存泄漏；还要注意不能阻塞主线程，因此需要配合定时器进行处理
* 异步处理导致debug会麻烦一些，需要完善的日志调用链来补充

## 参考


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://nixum.gitbook.io/note/rpc-yu-yi-bu-she-ji.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
