网络协议 21 - RPC 协议(中)- 基于 JSON 的 RESTful 接口协议
原文:网络协议 21 - RPC 协议(中)- 基于 JSON 的 RESTful 接口协议

    上一节我们了解了基于 XML 的 SOAP 协议,SOAP 的 S 是啥意思来着?是 Simple,但是好像一点儿都不简单啊!

传输协议问题

    对于 SOAP 来讲,比如我创建一个订单,用 POST,在 XML 里面写明动作是 CreateOrder;删除一个订单,还是用 POST,在 XML 里面写明了动作是 DeleteOrder。其实创建订单完全可以使用 POST 动作,然后在 XML 里面放一个订单的信息就可以了,而删除用 DELETE 动作,然后在 XML 里面放一个订单的 ID 就可以了。

    于是上面的那个 SOAP 就变成下面这个简单的模样。

POST /purchaseOrder HTTP/1.1
Host: www.cnblog.com
Content-Type: application/xml; charset=utf-8
Content-Length: nnn<?xml version="1.0"?><order><date>2018-07-01</date><className> 板栗焖鸡 </className><price>58</price></order>

    而且 XML 的格式也可以改成另外一种简单的文本化的对象表示格式 JSON。

    经常写 Web 应用的应该已经发现,这就是 RESTful 格式的 API 的样子。

协议约定问题

    然而 RESTful 可不仅仅是指 API,而是一种架构风格,全称 Representational State Transfer,表述性状态转移,来自一篇重要的论文《架构风格与基于网络的软件架构设计》(Architectural Styles and the Design of Network-based Software Architectures)。

    这篇文章从深层次,更加抽象地论证了一个互联网应用应该有的设计要点,而这些设计要点,成为后来我们能看到的所有高并发应用设计都必须要考虑的问题,再加上 REST API 比较简单直接,所以后来几乎成为互联网应用的标准接口。

    因此,和 SOAP 不一样,REST 不是一种严格规定的标准,它其实是一种设计风格。如果按这种风格进行设计,RESTful 接口和 SOAP 接口都能做到,只不过后面的架构是 REST 倡导的,而 SOAP 相对比较关注前面的接口。

    而且由于能够通过 WSDL 生成客户端的 Stub,因而 SOAP 常常被用于类似传统的 RPC 方式,也即调用远端和调用本地是一样的。

    然而本地调用和远程跨网络调用毕竟不一样,这里的不一样还不仅仅是因为有网络而导致的客户端和服务端的分离,从而带来的网络性能问题。更重要的问题是,客户端和服务端谁来维护状态。所谓的状态就是对某个数据当前处理到什么程度了。

    这里举几个例子,例如,我浏览到哪个目录了,我看到第几页了,我要买个东西,需要扣减一下库存,这些都是状态。本地调用其实没有人纠结这个问题,因为数据都在本地,谁处理都一样,而且一边处理了,另一边马上就能看到。

    当有了 RPC 之后,我们本来期望对上层透明,就像上一节说的“远在天边,尽在眼前”。于是使用 RPC 的时候,对于状态的问题也没有太多的考虑。

    就像 NFS 一样,客户端会告诉服务端,我要进入哪个目录,服务端必须要为某个客户端维护一个状态,就是当前这个客户端浏览到哪个目录了。例如,客户端输入 cd hello,服务端要在某个地方记住,上次浏览到 /root/liuchao 了,因而客户的这次输入,应该给它显示 /root/liuchao/hello 下面的文件列表。而如果有另一个客户端,同样输入 cd hello,服务端也在某个地方记住,上次浏览到 /var/lib,因而要给客户显示的是 /var/lib/hello。

    不光 NFS,如果浏览翻页,我们经常要实现函数 next(),在一个列表中取下一页,但是这就需要服务端记住,客户端 A 上次浏览到 20~30 页了,那它调用 next(),应该显示 30~40 页,而客户端 B 上次浏览到 100~110 页了,调用 next() 应该显示 110~120 页。

    上面的例子都是在 RPC 场景下,由服务端来维护状态,很多 SOAP 接口设计的时候,也常常按这种模式。这种模式原来没有问题,是因为客户端和服务端之间的比例没有失衡。因为一般不会同时有太多的客户端同时连上来,所以 NFS 还能把每个客户端的状态都记住。

    公司内部使用的 ERP 系统,如果使用 SOAP 的方式实现,并且服务端为每个登录的用户维护浏览到报表那一页的状态,由于一个公司内部的人也不会太多,把 ERP 放在一个强大的物理机上,也能记得过来。

    但是互联网场景下,客户端和服务端就彻底失衡了。你可以想象“双十一”,多少人同时来购物,作为服务端,它能记得过来吗?当然不可能,只好多个服务端同时提供服务,大家分担一下。但是这就存在一个问题,服务端怎么把自己记住的客户端状态告诉另一个服务端呢?或者说,你让我给你分担工作,你也要把工作的前因后果给我说清楚啊!

    那服务端索性就要想了,既然这么多客户端,那大家就分分工吧。服务端就只记录资源的状态,例如文件的状态,报表的状态,库存的状态,而客户端自己维护自己的状态。比如,你访问到哪个目录了啊,报表的哪一页了啊,等等。

    这样对于 API 也有影响,也就是说,当客户端维护了自己的状态,就不能这样调用服务端了。例如客户端说,我想访问当前目录下的 hello 路径。服务端说,我怎么知道你的当前路径。所以客户端要先看看自己当前路径是 /root/liuchao,然后告诉服务端说,我想访问 /root/liuchao/hello 路径。

    再比如,客户端说我想访问下一页,服务端说,我怎么知道你当前访问到哪一页了。所以客户端要先看看自己访问到了 100~110 页,然后告诉服务器说,我想访问 110~120 页。

    这就是服务端的无状态化。这样服务端就可以横向扩展了,一百个人一起服务,不用交接,每个人都能处理。

    所谓的无状态,其实是服务端维护资源的状态,客户端维护会话的状态。对于服务端来讲,只有资源的状态改变了,客户端才调用 POST、PUT、DELETE 方法来找我;如果资源的状态没变,只是客户端的状态变了,就不用告诉我了,对于我来说都是统一的 GET。

    虽然这只改进了 GET,但是已经带来了很大的进步。因为对于互联网应用,大多数是读多写少的。而且只要服务端的资源状态不变,就给了我们缓存的可能。例如可以将状态缓存到接入层,甚至缓存到 CDN 的边缘节点,这都是资源状态不变的好处。

    按照这种思路,对于 API 的设计,就慢慢变成了以资源为核心,而非以过程为核心。也就是说,客户端只要告诉服务端你想让资源状态最终变成什么样就可以了,而不用告诉我过程,不用告诉我动作。

    还是文件目录的例子。客户端应该访问哪个绝对路径,而非一个动作,我就要进入某个路径。再如,库存的调用,应该查看当前的库存数目,然后减去购买的数量,得到结果的库存数。这个时候应该设置为目标库存数(但是当前库存数要匹配),而非告知减去多少库存。

    这种 API 的设计需要实现幂等,因为网络不稳定,就会经常出错,因而需要重试,但是一旦重试,就会存在幂等的问题,也就是同一个调用,多次调用的结果应该一样,不能一次支付调用,因为调用三次变成了支付三次。不能进入 cd a,做了三次,就变成了 cd a/a/a。也不能扣减库存,调用了三次,就扣减三次库存。

    当然按照这种设计模式,无论 RESTful API 还是 SOAP API 都可以将架构实现成无状态的,面向资源的、幂等的、横向扩展的、可缓存的。

    但是 SOAP 的 XML 正文中,是可以放任何动作的。例如 XML 里面可以写 < ADD >,< MINUS > 等。这就方便使用 SOAP 的人,将大量的动作放在 API 里面。

    RESTful 没这么复杂,也没给客户提供这么多的可能性,正文里的 JSON 基本描述的就是资源的状态,没办法描述动作,而且能够出发的动作只有 CRUD,也即 POST、GET、PUT、DELETE,也就是对于状态的改变。

    所以,从接口角度,就让你死了这条心。当然也有很多技巧的方法,在使用 RESTful API 的情况下,依然提供基于动作的有状态请求,这属于反模式了。

服务发现问题

    对于 RESTful API 来讲,我们已经解决了传输协议的问题——基于 HTTP,协议约定问题——基于 JSON,最后要解决的是服务发现问题。

    有个著名的基于 RESTful API 的跨系统调用框架叫 Spring Cloud。在 Spring Cloud 中有一个组件叫 Eureka。传说,阿基米德在洗澡时发现浮力原理,高兴得来不及穿上裤子,跑到街上大喊:“Eureka(我找到了)!”所以 Eureka 是用来实现注册中心的,负责维护注册的服务列表。

    服务分服务提供方,它向 Eureka 做服务注册、续约和下线等操作,注册的主要数据包括服务名、机器 IP、端口号、域名等等。

    另外一方是服务消费方,向 Eureka 获取服务提供方的注册信息。为了实现负载均衡和容错,服务提供方可以注册多个。

    当消费方要调用服务的时候,会从注册中心读出多个服务来,那怎么调用呢?当然是 RESTful 方式了。

    Spring Cloud 提供一个 RestTemplate 工具,用于将请求对象转换为 JSON,并发起 Rest 调用,RestTemplate 的调用也是分 POST、PUT、GET、 DELETE 的,当结果返回的时候,根据返回的 JSON 解析成对象。

    通过这样封装,调用起来也很方便。

小结

  • SOAP 过于复杂,而且设计是面向动作的,因而往往因为架构问题导致并发量上不去;
  • RESTful 不仅仅是一个 API,而且是一种架构模式,主要面向资源,提供无状态服务,有利于横向扩展应对高并发。
posted on 2019-04-29 10:06 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/10788755.html

网络协议 21 - RPC 协议(中)- 基于 JSON 的 RESTful 接口协议相关推荐

  1. 网络协议——基于JSON的RESTful接口协议

    关于基于 XML 的 SOAP 协议其实使用起来并不简单.对于 SOAP 来讲,无论 XML 中调用的是什么函数,多是通过 HTTP 的 POST 方法发送的.但是咱们原来学 HTTP 的时候,我们知 ...

  2. 基于FPGA的SSI接口协议实现

    基于FPGA的SSI接口协议实现 SSI 是一种主机和从机点对点的通信接口,其中从机可以是具有 SSI 协议的各种传感器,例如磁致伸缩位移传感器.编码器等. SSI协议采用主机主动式读取方式,从机根据 ...

  3. python框架 mysql数据库_在Python的框架中为MySQL实现restful接口的教程

    最近在做游戏服务分层的时候,一直想把mysql的访问独立成一个单独的服务DBGate,原因如下: 请求收拢到DBGate,可以使DBGate变为无状态的,方便横向扩展 当请求量或者存储量变大时,mys ...

  4. 网络编程基础socket 重要中:TCP/UDP/七层协议

    计算机网络的发展及基础网络概念 问题:网络到底是什么?计算机之间是如何通信的? 早期 : 联机 以太网 : 局域网与交换机 广播 主机之间"一对所有"的通讯模式,网络对其中每一台主 ...

  5. RTSP/RTMP/GB28181协议视频监控平台搭建之国网B接口协议介绍

    我们知道TSINGSEE青犀视频全线产品对应了不同的视频协议,比如EasyNVR就是支持RTSP协议的视频平台,EasyDSS是支持RTMP协议的视频平台,EasyGBS是支持GB28181协议的视频 ...

  6. python 网页调试_使用Django 2.0构建Python Restful Web服务:七)在浏览器中浏览和调试restful接口...

    一.前言 在之前的文章中,我们创建好的web接口都通过Postman这个软件调用各种HTTP请求方法来进行调试和查看,这样返回的结果很容易被编程语言和应用程序所接收和识别,但是却并不便于我们直观的查看 ...

  7. Socket开发探秘--基于Json格式的数据协议收发

    前面发表过两篇随笔:<Socket开发探秘--基类及公共类的定义>和<Socket开发探秘--数据封包和拆包>,介绍了Socket方面的开发.本文继续探讨使用Json格式来作为 ...

  8. 基于FPGA的UART接口协议设计

    一.PC终端概述 PC终端,Personal Computer 智能终端,通俗的讲,就是利用电脑GUI界面控制我们的外部硬件电路. 因此设计到了PC与外部硬件电路的通信接口.对于台式电脑.个人笔记本, ...

  9. php中使用json做api,JSONAPI在PHP中的应用

    现在服务端程序员的主要工作已经不再是套模版,而是编写基于 JSON 的 API 接口.可惜大家编写接口的风格往往迥异,这就给系统集成带来了很多不必要的沟通成本,如果你有类似的困扰,那么不妨关注一下 J ...

  10. 软件定义网路核心原理与应用实践(二)SDN接口协议

    文章目录 OpenFlow协议 OpenFlow v1.0协议 OF流表 OF安全通道 OF协议消息 OF协议面临的问题 OF-CONFIG协议 设计需求 数据模型 其他SDN南向接口协议 XMPP ...

最新文章

  1. Windows下Libvirt Java API使用教程(三)- TLS认证访问和动态链接文件依赖
  2. leetcode算法题--最后一块石头的重量 II★
  3. vue 获取元素在浏览器的位置_JavaScript获取窗口位置和元素坐标(兼容版)
  4. 2021 音视频技术趋势不完全预测
  5. eclipse插件镜像改为国内镜像
  6. java 文件夹不存在的解决方案
  7. LeetCode 1899. 合并若干三元组以形成目标三元组
  8. 关于Myeclipse自带JDK与本机安装JDK的的区别
  9. Leetcode——C++突击面试
  10. B站手机缓存的视频在电脑上转换观看
  11. 电机驱动详解--从原理到智能车驱动(DRV8701)
  12. 探究腾讯云TCA和阿里acp的区别
  13. GitHub干货分享
  14. 《水浒传》读后(5) 其它人物
  15. NavigationView简单操作
  16. OPENSTACK-2-管理OSP内部通信-管理消息交换服务
  17. 李彦宏: 《硅谷商战》 节选
  18. dedecms mytag_js.php,一种奇特的DEDE隐藏后门办法_91Ri.org
  19. ubuntu下sqlite可视化工具
  20. win2000 java,把一个普通应用程序变为win2000(NT)中的服务-JSP教程,Java技巧及代码...

热门文章

  1. NoSql数据库Redis的在ubuntu下的部署使用
  2. Linux yum源码包安装和卸载
  3. Javascript设计模式(二)工厂模式
  4. Android开发之BUG专讲:入门篇(二)
  5. 读取xml文件分析 EntityName 时出错的解决方案
  6. ROS(9):机器人开源项目poppy-project
  7. jQuery 5 条件选择器
  8. [Android] 开发一款软件我学到了些什么?
  9. CF1047E Region Separation
  10. [USACO DEC13] 牛棒球