写点什么

Socket.D 基于消息的响应式应用层网络协议

  • 2023-12-22
    福建
  • 本文字数:3478 字

    阅读完需:约 11 分钟

首先根据 Socket.D 官网的副标题,Socket.D 的自我定义是:


基于事件和语义消息流的网络应用协议。
复制代码


官网定义的特点是:


  • 基于事件,每个消息都可事件路由

  • 所谓语义,通过元信息进行语义描述

  • 流关联性,有相关的消息会串成一个流

  • 语言无关,使用二进制输传数据(支持 tcp, ws, udp)。支持多语言、多平台

  • 断线重连,自动连接恢复

  • 多路复用,一个连接便可允许多个请求和响应消息同时运行

  • 双向通讯,单链接双向互听互发

  • 自动分片,数据超出 16Mb,会自动分片、自动重组(udp 除外)

  • 接口简单,是响应式但用的是监听与回调风格(经典易用)

Socket.D 是基于这些特性需求诞生的一种新型响应式网络协议。Socket.D 借鉴了很多其他协议发展过程中遇到的问题,然后总结归纳进自己的实践当中。

基于 Socket.D 的一些主要特性分别做一下介绍,并和 HTTP 之类的常见协议进行比较:

  • Destination (URL) 显示连接地址

  • Event 事件

  • Multiplexed, Binary Protocol 多路复用的二进制协议

  • Bidirectional Streaming 双向流

  • Socket Resumption 连接恢复

  • Message passing 消息传递模型

  • Transport independent 与传输层解耦的应用层协议


一、两层路由能力


  • path 路由能力


Socket.D 是基于显示连接地址的,可以实现像 http 或 websocket 一样的“频道”路由的效果。地址例:


//模拟聊天场景的用户地址sd:tcp://127.0.0.1:8602
//模拟聊天场景的管理员地址sd:tcp://127.0.0.1:8602/admin?u=admin&p=1234
复制代码


  • event 路由能力


Socket.D 每个消息都有事件描述,可以起到 path 或 topic 或 cmd 类似的路由效果。示例:


//模拟消息中间件的发布指令client.send("event.mq.publish", new StringEvent("{userId:1}").metaSet("topic","demo"));
//模拟消息中间件的订阅指令client.send("event.mq.subscribe", new StringEvent("").metaSet("topic","demo"));
复制代码


二、多路复用的二进制协议


现在 Multiplexing,Asynchronous,Non-blocking I/O 已经被说烂了,基本上就是标配。这些特性意味着什么?拿 HTTP 的发展史感受一下:


从 HTTP1.0 到 HTTP3.0 在传输性能上的进步


  • 在 HTTP1.0 时代,每个 HTTP request 都要新建一个网络连接。网络连接不能复用

  • HTTP1.1 时代,一个网络连接仍然在一个时候只能负责一个 request,但是整个 request/response 结束后连接可以得到复用。

  • 会有文章讲到 HTTP1.1 的核心是 pipeline 功能,是也不是。pipelining 支持一个 TCP 连接上按照顺序连续发送多个 HTTP 请求而不需要等待前一个请求的响应,但是它同时要求 HTTP response 也要按照请求的顺序逐个发送,这对服务器提出了很多要求,而且如果第一个响应很慢会拖累所有的后续响应(pipeling 的队头阻塞),所以事实上并没有得到多少运用。即使到今天大部分浏览器仍然是默认关闭 HTTP pipelining 功能的,所以说 HTTP1.1 的主要突破还只是连接复用。

  • HTTP2.0 是个飞跃,开始支持 multiplexing,一个 TCP 连接上可以同时承载多个 request/response,用这种方式替代 1.1 的 pipelining 提升 HTTP 的并行效果,也自然不存在什么队头阻塞了。每一个 request/response 的信息流,我们把它称作一个 HTTP stream。这个时候一个 HTTP client 对于一个 origin,只需要建立一个 TCP 就够了。(但是 multiplexing 带来了新的问题)

  • 现在 HTTP3.0 也差不多了。2.0 解决了 1.1pipelining 的队头阻塞问题,但是却无法解决 TCP 本身的队头阻塞。而因为 TCP/IP 在内核协议栈中,简直无法升级,于是 HTTP 选择了 QUIC 作为新的传输层协议。 QUIC 基于 UDP,在用户模式中实现了类似 TCP 的 connection oriented 的功能同时解决 TCP 的队头阻塞,自带 multiplexing 等等。


所以,HTTP/2 具有的优点,Socket.D 都有。另外,Socket.D 是一个二进制协议,也就是说在一个 Socket.D 连接上传输的消息体对数据格式没有任何要求,应用程序可以为所欲为的压缩数据量的大小。


这样的二进制协议通常来说能给性能带来极大的提升,但是产生的代价是,网络中间件也会因为无法解读消息体中的数据,丧失了在对具体应用流量进行监控,日志和路由的能力。所以 Socket.D 通过把每个消息体分成 sid, event, data 和 metaString 的方式,在保证高效传输的前提下,也提供了暴露元数据给网络中间件的能力(方便做语义处理),同时还能路由消息。


frame: {flag, message: {sid, event, entity: { metaString, data}}}
复制代码


对于每个 data,应用可以采用不同的序列化方法。metaString 则采用标准的 url queryString 的通用格式(所有网络中间件通用)。


  • data 一般作为应用本身需要传递的业务数据,采取自定义的高效序列化方式,且对网络基础设施不可见

  • metaString 采用标准的 url queryString 的通用格式。在分布式传输的过程中,这些中间件可以按需求对 metaString 进行读写,然后调整路由。


三、双向流


上面提到,HTTP 这几年在传输性能上进步了很多。但说到底在应用层仍然仅支持 client request/server response 的交互模型。


这里一些同学可能有疑问,比如:


那 HTTP/2 推出的 Server Push 是什么


HTTP2.0 推出了一个新的 Server Push 功能,但这个功能通常只是用来提前将一些静态资源返还给用户而已。举个例子:一个简单的网站有三个静态资源组成: index.html, index.css, index.js。 我们打开浏览器打开 index.html,就会发起一个 HTTP request 拿到 index.html。在不使用 server push 的情况下,我们要等浏览器解析出 index.css 和 index.js 之后才会再次向服务器发起请求。而运用 server push,服务器可以根据一些规则预知到浏览器也需要 index.css 和 index.js,并在客户端发送新的请求之前直接推送给该客户端。


所以,这个功能的使用场景非常有限,而且也不是一个真正双向的交互模式。


结论:仅仅使用 HTTP/2 协议,不在其基础之上再加一层其他协议的情况下是无法在应用层实现双向流的(比如,gRPC)。


回到交互模式,Socket.D 的几种模式:


  • Send

  • SendAndRequest -> Reply

  • SendAndSubscribe -> Stream Reply

  • Reply

  • ReplyEnd

  • Session(双向使用上面的发送与答复)


通常来说,越复杂的交互模式,为了保存交互状态,就需要占用更多的内存和计算资源。这也是为什么 Socket.D 会提供多种不同的 API。另外,当 Socket.D 的 client 和 server 建立了长连接之后,任何一方都可以是 Requester 或是 Responder。服务器也可以扮演 Requester 的角色,首先发起 Request。


四、异步消息传递


Socket.D 还有另外一个非常重要的概念使之完全区分于类 HTTP 协议,那就是异步消息传递。


不同于 HTTP 当中存在 Request,Response。Socket.D 在网络传输上只有 Frame 这一个消息格式。有一个相同点是,类 HTTP 协议通常拥有一个显式的 destination (URL),使用起来会非常有亲切感和简单。


如果 Requester 的 Socket.D 消息 R 首先通过了一个网络中间件(Broker),那么请求者(Requester)并不关心该消息的最终目的地在哪里,网络中间件可以全权负责路由模块的实现。该架构可以支持微服务,也可以支持 IOT 场景,等等。



这种架构很有意思,不仅能在微服务中加入 streaming 支持,还有如下特点:


1.客户端也可以暴露服务


由于 Socket.D 的双向流特性,和 Broker 建立连接的客户端即可以做 Requester 也可以做 Responder,比如图中的 Device 1 和 Device 2 虽然是移动终端或者 IOT 设备,但仍然可以向其他设备或数据中心的主机提供服务


2.自动服务注册/发现


Socket.D 在成功建立连接时。如果该 client 想要暴露一个服务,则在连接地址上给自己取个名字(就像加入一个社交群,让别人能 At 到你)。连接成功建立之后,Socket.D Broker 可以直接通过记录网络连接状况来达到服务注册和发现的效果。通过'@'参数取名示例:


sd:tcp://127.0.0.1:8602?@=demoapp
复制代码


3.基于消息 metaString 实现请求路由


前面说过,Socket.D 是一个二进制协议,但仍然可以在消息体中通过 metaString 来暴露信息给网络中间件。每个消息实体的 metaString 会带有‘@’参数, 告诉 Socket.D Broker 消息应该转发给谁,从而实现路由效果。示例:


client.send("/demo", new StringEntity("").at("demoapp"));
复制代码


与连接时给自己取名的 '@' 参数相乎应。取了名,就能被“别人” at 到!


4.暴露服务不需要 Ip 和 Port


整个架构当中,所有节点都只需要知道 Broker 的地址和服务端口即可。只要成功连接信息流就是双向的。Borker 可以直接通过建立的 Connection 寻址服务节点,所有的服务调用者都不需要知道服务暴露方的地址

而 Broker 又可以通过连接的 url 地址,进行签权控制。


5.天生的中心化管理


管理微服务集群往往需要有一个中心化的控制中心,在这个架构中 Socket.D Broker 就是自然而然的中心,知道整个集群中所有的情况。


文章转载自:带刺的坐椅

原文链接:https://www.cnblogs.com/noear/p/17917697.html

体验项目:http://www.jnpfsoft.com/?from=001

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
Socket.D 基于消息的响应式应用层网络协议_网络协议_不在线第一只蜗牛_InfoQ写作社区