当前位置:首页 > Web开发 > 正文

responseB 丢给 ThreadB

2024-03-31 Web开发

标签:

Netty RPC 实现
观点

RPC,即 Remote Procedure Call(长途过程挪用),挪用长途计算机上的处事,就像挪用本地处事一样。RPC 可以很好的解耦系统,如 WebService 就是一种基于 Http 协议的 RPC。这个 RPC 整体框架

如下:

技术图片


关键技术

处事颁布与订阅:处事端使用 Zookeeper 注册处事地点,客户端从 Zookeeper 获取可用的处事地点。

通信:使用 Netty 作为通信框架。

Spring:使用 Spring 配置处事,加载 Bean,扫描注解。

动态代办代理:客户端使用代办代理模式透明化处事挪用。

动静编解码:使用 Protostuff 序列化和反序列化动静。

核心流程

处事消费方(client)挪用以本地挪用方法挪用处事;

client stub 接收到挪用后卖力将要领、参数等组装成能够进行网络传输的动静体;

client stub 找随处事地点,并将动静发送随处事端;

server stub 收到动静后进行解码;

server stub 按照解码功效挪用本地的处事;

本地处事执行并将功效返回给 server stub;

server stub 将返回功效打包成动静并发送至消费方;

client stub 接收到动静,并进行解码;

处事消费方得到最终功效。

RPC 的方针就是要 2~8 这些法式都封装起来,让用户对这些细节透明。JAVA 一般使用动态代办代理方法实现长途挪用。

技术图片


动静编解码

息数据布局(接口名称+要领名+参数类型和参数值+超不时间+ requestID)
客户真个请求动静布局一般需要包孕以下内容:

接口名称:在我们的例子里接口名是“HelloWorldService”,如果不传,处事端就不知道挪用哪个接口了;

要领名:一个接口内可能有很多要领,如果不传要领名处事端也就不知道挪用哪个要领;

参数类型和参数值:参数类型有很多,好比有 bool、int、long、double、string、map、list,甚至如 struct(class);以及相应的参数值;

超不时间:

requestID,标识独一请求 id,不才面一节会详细描述 requestID 的用处。

处事端返回的动静 : 一般包孕以下内容。返回值+状态 code+requestID

序列化

目前互联网公司广泛使用 Protobuf、Thrift、Avro 等成熟的序列化解决方案来搭建 RPC 框架,这些都是久经考验的解决方案。

通讯过程

核心问题(线程暂停、动静乱序)

如果使用 netty 的话,一般会用 channel.writeAndFlush()要领来发送动静二进制串,这个要领挪用后对付整个长途挪用(从发出请求到接收到功效)来说是一个异步的,即对付当前线程来说,将请求发送出来后,线程就可以往后执行了,至于处事真个功效,是处事端措置惩罚惩罚完成后,再以动静的形式发送给客户真个。于是这里呈现以下两个问题:

怎么让当前线程“暂停”,等功效回来后,再向后执行?

如果有多个线程同时进行长途要领挪用,这时成立在 client server 之间的 socket 连接上会有很多双方发送的动静通报,前后挨次也可能是随机的,server 措置惩罚惩罚完功效后,将功效动静发送给 client,client 收到很多动静,怎么知道哪个动静功效是原先哪个线程挪用的?如下图所示,线程 A 和线程 B 同时向 client socket 发送请求 requestA 和 requestB,socket 先后将 requestB 和 requestA 发送至 server,而 server 可能将 responseB 先返回,尽管 requestB 请求达到时间更晚。我们需要一种机制保证 responseA 丢给****ThreadA,responseB 丢给 ThreadB。

技术图片


通讯流程

requestID 生成-AtomicLong

client 线程每次通过 socket 挪用一次长途接口前,生成一个独一的 ID,即 requestID(requestID 必须保证在一个 Socket 连接里面是独一的),一般每每使用 AtomicLong从 0 开始累计数字生成独一 ID;存放回调东西 callback 到全局 ConcurrentHashMap

将 处 理 结 果 的 回 调 对 象 callback , 存 放 到 全 局 ConcurrentHashMap 里 面put(requestID, callback);synchronized 获取回调东西 callback 的锁并自旋 wait

当线程挪用 channel.writeAndFlush()发送动静后,紧接着执行 callback 的 get()要领试图获取长途返回的功效。在 get()内部,则使用 synchronized 获取回调东西 callback 的锁,再先检测是否已经获取到功效,如果没有,然后挪用 callback 的 wait()要领,释放callback 上的锁,让当前线程处于期待状态。监听动静的线程收到动静,找到 callback 上的锁并唤醒

温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/web/32610.html