Fork me on GitHub

RPC (Remote Procedur call ) 远程服务调用

RPC (Remote Procedur call ) 远程服务调用

什么是rpc?

举个小河上的桥例子.

rpc的作用?
  • 屏蔽远程调用和本地的区别,让我们感觉像本地调用
  • 隐藏底层网络通信的复杂性
联想网络通信本质是啥?

netty 网络通信的框架

一个完整的rpc会涉及到哪些过程?
  • 数据传输,保证其可靠性通常都是TCP来传输,常用的http协议就是在其基础之上

  • 网络数据肯定是二进制数据,那么就会涉及到序列化和反序列化. 这里也会产生数据通信之间的协议.

    • 数据格式的约定内容叫协议.协议包括请求头和请求体.

      请求头: 一般用于身份识别、协议标识、数据大小、请求类型、序列化类型

      消息体 : 请求业务参数信息和扩展信息

协议

http 协议和rpc 协议都属于应用层协议.

Rpc 数据请求通过网络,二进制格式传输,写入本地socket 通过网卡发到另外的设备上.

数据也不是一次把数据全部传递过去,中间可能需要拆包,合并请求等(合并的前提是同一个tcp连接上的数据)

这些取决于tcp的窗口大小,系统参数配置.

协议 也是给请求一个参考边界. 例如数据大小,结尾格式等.可以联想到redis 的resp协议使用#key#value 这样的分割方式.

为啥还要rpc自己的协议? http协议不能满足要求吗? 他们有什么样的区别?

http协议属于无状态协议,而且协议本身有很多无用的内容,比如换行符、回车符、请求头导致数据多.

无法实现请求跟响应关联,每次都需要重新建立连接,响应完成关闭连接.

rpc 是如果和实现请求和响应关联的呢?

dubbo 是消费者发出请求,Atomiclong 产生消息id,,之后底层IO是异步发送消息,dubbo发送请求后,需要阻塞等待消费者返回消息.消费者id存储在map结构里,服务提供者返回附带请求消息id,之后dubbo 通过消息id就能对应上请求和响应啦.

怎么完成自定义的协议呢?

  • 协议的边界

    给一个固定大小的长度表示数据长度,之后在根据数据的大小进行读取数据.

  • 数据的序列化方式

    如果不指定序列化方式的话,那么也解析不出来数据的格式啊?

得出结论,数据大小,序列化的方式这些 固定下来的数据空间,我们可以放到协议头上,具体的请求内容放置在协议体.

bit offset 0-15 16-47 48-63 64-71 72-79 80-87
0 魔术位 数据长度 消息id 请求和响应关联 协议版本 消息类型 序列化方式
协议体 请求接口 请求参数 可变长度

可扩展的协议?

RPC序列化 方式

protobuf —》protostuff 不支持null ,不支持单纯的Map List 对象集合需要放在对象里.

kryo、hessian

序列化考虑参考图:

序列化注意事项:

  • 对象要尽量简单,没有太多的依赖关系,属性不要太多,尽量高内聚;
  • 入参对象与返回值对象体积不要太大,更不要传太大的集合;
  • 尽量使用简单的、常用的、开发语言原生的对象,尤其是集合类;
  • 对象不要有复杂的继承关系,最好不要有父子类的情况。

零拷贝传输数据

通常是程序发起写–〉应用缓存区(cpu拷贝)–〉内核缓冲区– (依靠DMA) –〉网卡–〉其他设备
每一次都需要把数据写到用户空间缓冲区 之后到系统内核的缓冲区 ,这样每次都需要2次的写.
所谓的零拷贝 就是直接写入内核,之后从内核直接读取数据…就是把数据用户空间和内核空间都将数据写入到同一个地方. 虚拟内存的方式?
二种解决方式:

  • mmap+write
  • senfile
    还要深入了解下以后.
    netty的零copy-zero 是怎么做的呢?
    todo 后续要进行进一步了解
    netty是完全站在了用户空间上,也就是jvm上.偏向数据操作的优化
  • netty 提供了compositeByteBuf 类 ,可以将多个byteBuf 合并为逻辑上的byteBuf,避免byteBuf间copy.
  • byteBuf 支持slice 操作,可以分解为多个共享同一存储区域的byteBuf.
  • 通过wrap 操作,可以将 byte[] 数组,byteBuf,byteBuffer 等包装成netty 自己的 byteBuf对象
    netty 解决 Tcp 沾包,拆包 都是靠compositeByteBuf 类 wrap 和 slice 去解决的.
    Netty 可以采用Direct Buffers 堆外内存的方式解决,与虚拟内存一样的效果.
    还提供了FileRegion 中包装NIO 的 FileChannel.trasnsferTo() 方法实现了零拷贝,这和linux 中的sendfile 原理一样.

扩展阅读 c10k 问题
https://www.jianshu.com/p/ba7fa25d3590
https://blog.csdn.net/lsgqjh/article/details/86622532

  • selector 模式
    使用fd_set 结构告诉内核监听哪些文件句柄,之后轮询逐个排查状态,句柄是有上限的,逐个检查吞吐量低, 每次调用都重复初始化fd_set .
  • poll 模式
    主要解决了selector 模式2缺点,一个句柄上线的问题(链表方式存储)以及重复初始化的问题. (不同字段标注关注事件和发生事件)
  • epoll 模式
    event-callback 方式. 仅对发生变化的文件句柄感兴趣. 通过epoll_ctl 注册文件描述符fd , 一旦fd 就绪,内核就会采用callback激活fd ,epoll_wait 收到通知,通知应用程序.而且epoll一个文件描述符管理多个描述符,将用户进程的文件描述符事件存放在内核一个事件表里. 这样数据从内核一次到用户进程地址空间.epoll 依赖 linux 系统,需要开启epoll .


    本文欢迎转载,但是希望注明出处并给出原文链接。
    如果你有任何疑问,欢迎在下方评论区留言,我会尽快答复。
    如果你喜欢或者不喜欢这篇文章,欢迎你发邮件到 alonecong@126.com 告诉我你的想法,你的建议对我非常重要。



    本文作者: 不利索的阿瓜
    联系方式: alonecong@126.com
    版权声明: 除特别声明外,所有文章均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

本文欢迎转载,但是希望注明出处并给出原文链接。 如果你有任何疑问,欢迎在下方评论区留言,我会尽快答复。 如果你喜欢或者不喜欢这篇文章,欢迎你发邮件到 alonecong@126.com 告诉我你的想法,你的建议对我非常重要。

------ 本文结束感谢您的阅读! ------
0%