我们首先说了基于XML的SOAP协议,其中的S是指的是Simple,但是使用起来更不简单
对于SOAP,无论XML中调用的是什么函数,大多是走的是HTTP的POST方法发送的,但是HTTP不仅仅有POST,还有PUT DELETE,GET等方法,正好对应着增删改查
传输协议问题
对SOAP来说,比如创建一个订单,用POST,在XML中写明动作是CreateOrder,删除一个订单,
动作是deleteOrder
于是,上面你的SOAP就是成了下面的样子
POST /purchaseOrder HTTP/1.1
Host: www.geektime.com Content-Type: application/xml; charset=utf-8 Content-Length: nnn <?xml version=”1.0″?> <order> <date>2018-07-01</date> <className>趣谈网络协议</className> <Author>刘超</Author> <price>68</price> </order> |
XML的格式也可以变成另一种简单的文本化的对象表示格式JSON
POST /purchaseOrder HTTP/1.1
Host: www.geektime.com Content-Type: application/json; charset=utf-8 Content-Length: nnn { “order”: { “date”: “2018-07-01”, “className”: “趣谈网络协议”, “Author”: “刘超”, “price”: “68” } } |
对于RESTful来说,不仅仅是指API,而是一种架构风格,全程是Representational Sate Transfer
表述性状态转移,来源于一篇论文 架构风格和基于网络的软件架构设计(Architectural Styles and the Design of Network-based Software Architectures)
和SOAP不同,REST并不是一种严格规定的标准,而是一种设计风格,按照这种风格来设计,RESTful接口和SOAP接口都能做到,只不过把后面的架构是REST倡导的,SOAP相对比较关注前面的接口
而且由于能够通过WSDL生成客户端的Stub,因而SOAP可以被利用与传统的RPC的方式
但是网络调用和本地调用有着很大的区别,不仅仅是客户端和服务端的分离,而且,客户端和服务端,谁来维护状态呢?
比如,我们进行浏览目录,谁来记录我已经浏览到第几页了,本地调用可以不用纠结,但是有了网络调用,就需要考虑这个状态问题
是由客户端来维护,还是服务器端来维护?
比如说,我浏览到了哪个目录了,我看到了第几页了,买个东西,扣减库存,这都是状态,本地调用不需纠结,但是网络调用都需要考虑到
就好比NFS一样,客户端会告诉服务端,我要进入哪个目录,服务端需要维护一个状态,就是这个客户端浏览到哪个目录了,客户端输入了cd hello,服务端要记住,上次浏览到了/root了,这次要前往/root/hello,另外一个客户端,输入了cd hello,服务端也要记住,上次浏览到了/var/lib,这次要进入/var/lib/hello
不光是NFS,如果浏览翻页,我们要实现函数 next(),一个列表中取下一页,但是这就需要服务端记住,客户端A上次浏览到20~30页了,调用next,显示30~40页
上面说的是RPC场景,由服务端来维护状态,很多SOAP接口设计,尝尝按照这个模式,这种模式原本没有问题,通过不会有太多的客户端练上来,所以NFS还可以直接使用
公司内部使用的ERP系统,如果使用SOAP的方式实现,并且服务端为每个登录的用户维护状态,一个公司内部的人不会太多,把ERP放在一个强大的物理机也行
但是,在网络上,比如双十一,这么多人同时购物,记得过来吗?服务端同事提供服务,大家分摊一下,但是如何将状态进行转移呢?
这样,我们理应将服务端维护资源状态,客户端维护会话的状态,对于服务端来说,资源状态改了,客户端才会调用POST,PUT,DELETE.如果资源状态没变,客户端的状态变了,就是简单的GET
这样,就是交给客户端自己维护自己的状态,客户端说,要访问目录下的hello路径,客户端会找到自己之前访问的路径,然后告诉服务端完整的路径,同理,对于分页,也是客户端去维护状态,然后告诉客户端具体的访问页数
这就是服务端的无状态华,服务端可以横向的扩展了,一百个人一起服务,不用对接,每个人都能处理
这样,我甚至就可以将很多资源进行缓存,哪怕是CDN的边缘节点
按照这种思路,互联网发展为了以资源为核心,而非以过程为核心的互联网,服务端只想知道资源会变成什么样
就好比,我们去下一个单,我们需要查看当前的库存路径,然后减去购买的数量,得到结果的库存数,然后设置为目标库存数
这就需要API的幂等性,不能出现无限重试的问题,就是对于同一调用,多次调用的结果应该一样,不能多次调用就运行多次,
按照这种思路,无论是RESTful API还是SOAP API都可以将架构设计为无状态的,面向资源,幂等的,横向扩展,可缓存的
这样,RESTful中没有办法去描述动作,能够发出的动作只有CRUD,对于状态的改变,所以从接口角度,就死了这条心
服务发现问题
对于RESTful API来说,我们已经解决了传输协议的的问题,基于HTTP,协议约定问题,基于JSON,最后解决服务发现的问题
常见的跨系统的调用框架叫做Spring Cloud,其中有一个组件Euerka,负责实现注册中心,负责维护注册的服务列表
服务分服务提供方,向Euerka做服务注册,续约,下线的操作,注册的主要数据包括服务名,机器ip,端口号,域名
另一方是服务消费方,从Eureka获取服务提供方的注册信息为了负载均衡和容错,服务注册方可以注册多个
当消费方要调用服务的时候,会从注册中心读出多个服务出来,然后利用RESTful方式来调用
Spring Cloud提供了一个RestTemplate工具,将请求对象转换为JSON,并且发起REST调用,返回时候,根据返回的JSON解析成对应对象
本章小结
SOAP太过于复杂,而且设计是面向动作的,往往因为架构问题导致并发量上不去
RESTful不仅仅是一个API,而是一种架构模式,主要面向资源,提供无状态的服务,有利于横向扩展
课后思考
1.讨论RESTful模型的时候,举了一个库存的例子,为何这么设计
2.基于文本的RPC虽然解决了二进制的问题,但是本身具有问题的
1.这样,就是将状态进行了转移,将状态交给了存储,这样业务可以无状态华运行,因为无状态,所以可以负载均衡
高并发下,尽管接口支持了幂等性,库存资源修改时需要支持CAS操作
2.基于文本,利于阅读,但不适合大数据交换,二进制压缩率更高,更适合高的吞吐量