HTTP/1.1 协议的潜力
RFC对于HTTP/1.1的规范,主要以 RFC2616 为基础,在 RFC7231 , RFC5789 进行了补充说明。
HTTP/1.1 协议,主要有9大方法,其中主要考察的有:
- POST 写操作
- PUT 写操作
- PATCH 写操作
- DELETE 写操作
- HEAD 读操作
- GET 读操作
- TRACE 读操作(但可能会产生副作用)
另外的 CONNECT 方法主要处理与 HTTP Proxy 相关的事务 , OPTIONS 方法用于对协议内容的协商。
在2014年,RFC通过 RFC7230-7236 对RFC2616进了「Replace」。 不过RFC2616应用实在是过于广泛,即便是被宣布「Dead」,运行在全世界各地的协议实现,仍然是主流。
幂等性考量
幂等的概念是,对于相同的请求,被处理任意次,对数据的操作结果,等同于处理一次。
满足幂等性要求的Method,一般都是对于数据操作安全的。对于读操作,显然是满足幂等性要求的。
- PUT
- DELETE
- HEAD
- GET
上述四个方法,HTTP/1.1 要求协以幂等的方式进行实现。其中 HEAD 以及是 GET 是明显的读操作,而 PUT 和 DELETE 是写操作。
在实现层面,对于幂等性的实现,都要求由请求方提供唯一性标识,也经常称之为「流水号」。它的生成方法,可以参见之前写的这篇文章:标识与唯一标识概览 。
当然,标识也可以通过请求方提供的多个唯一性要素,类似以「联合主键」的方式,在服务端进行计算和判定一致性。
承载性考量
HTTP Header
一般来说,Header部分的数据,都用来传输元数据。Header部分的数据结构,都以 Key-Value 的形式出现。
对于复杂的Value,也就是Value中再嵌套数据结构,可以采用「;」对齐进一步 Key-Value 化。
常见的 Request Header有:
- User-Agent : Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36
- Refer : https://www.google.com/
User-Agent: 用于申明客户端的身份,其本意是用于声明「用户操作行为的代理者」。
Refer: 用于申明引用的地址,也就是本次请求源于上一次请求的地址。在分布式架构下,这个字段本质上是可以很好地用于声明一个请求链路的「上一跳地址」。
常见的 Response Header有:
- Status Code :200
- Server :SimpleHTTP/0.6 Python/2.7.10
- Date : Mon, 19 Dec 2016 14:16:20 GMT
- ETag : W/"5715dcfb-47e8"
- Expires : W/"5715dcfb-47e8"
- Content-Type : application/json; charset=UTF-8
Status Code :是最为熟悉的响应头了。诸如 401 , 403 , 404 , 500 , 503 等等响应码,RFC都对其进行了「业务化的定义」。
Server : 默认都是以应用服务器的提供者和版本作为响应值。很多人都会把 Server 的概念等同于 Machine ,这是不正确的。
Date :指的是这一响应结果,在``Server``的响应时间,Client 可以对这些数据进行加以利用。
ETag :则是对响应体进行的摘要Hash计算,设计的初衷是为了让 Client 降低比较多个响应结果的差异,提高性能。
Expires :同样是提醒客户端,这一响应结果的有效期是多少,客户端可以自己根据过期时间,对所辖的缓存进行处理,或者出发其他时间。
Content-Type : 方便 Client 对响应体进行反序列化的解析操作。
HTTP Body
HTTP Body 并不是想象中的那样,只有 GET 方法不能够支持请求体的传输。
7个方法的 Request Body 支持情况如下:
- POST 支持
- PUT 支持
- PATCH 支持
- DELETE 不支持
- HEAD 不支持
- GET 不支持
- TRACE 不支持
需要注意的是,DELETE 方法并不支持请求体的传输。
再看一下7个方法对于 Response Body 的支持情况:
- POST 支持
- PUT 支持
- PATCH 支持
- DELETE 支持
- HEAD 不支持
- GET 支持
- TRACE 支持
相比之下,Response Body的支持情况,只有 HEAD 方法不支持了。
整体而言,对于用户贡献内容的行为(User Generate Content),HTTP/1.1 协议明显有着不平等的倾向性。
也就是说,只有 POST , PUT , PATCH 三个方法支持用户侧「提供内容」。
现如今,任何的一个互联网平台,都以 UGC 为核心,以用户提供的 内容 作为企业产品的生产资料,HTTP/1.1是不是有些落后了呢?
另外,作为程序员,不了解 PUT 以及 PATCH 这两个幂等Method的正确方法,并且在前端生成「唯一性标识」,没有成熟框架的条件下,选择大面积使用 POST 方法,似乎也得到了一个看似合理的解释。
提交-补偿模式的处理分布式事务的协调方案
对于分布式事务,一共有三种典型的协调方案,分别是:
- 提交 (Commit) - 补偿 (Compensation), C-C模式
- 尝试 (Try) - 确认(Confirm) / 取消(Cancel), T-C/C模式
- 事件溯源 (Event Sourcing), ES模式
此处,对 C-C 进行一个极为简单的概述。
C-C 模式,也就是对数据进行正向操作的「反操作」,是在同步业务场景下,最简单的模式。
分布式事务的参与者的模型,是 1:n , 也就是分属于不同服务的 1 个消费者( Client )与 n 个提供者( Server ), 共同完成一次协调过程。
在消费者内部,需要内置一个「协调器」。 每次协调过程,则对于一个「协调处理单」的实体概念。
消费者,每次在进行「协调请求」前,除了需要准备好请求内容之外,还要准备好生成一个「协调处理单」的唯一性标识,记为 RequestId 。 消费者如果作为调用链中的一个中间环节,那么还需要附加携带 TransactionId 作为全局流水号,为一次完整的全链路跟踪提供线索。
提供者,每次在处理请求时,需要记录上游提供的 RequestId ,同时还需要对响应结果,生成一个 ResponseId ,并携带 RequestId 作为关联标识。
提交-补偿模式的REST-like实现示例
消费方请求示例
- C-C Commit Request cURL Syntax
curl -X PUT -H "Content-Type: application/json" -H "User-Agent: <ConsumerId>" \\
-H "Referer: <TransactionId>" -d <RequestBody> "https://<domain>/<path>/<RequestId>[?v=<Version>]"
- C-C Commit Request cURL Example
curl -X PUT -H "Content-Type: application/json" -H "User-Agent: REST-like-Consumer" \\
-H "Referer: 0987654" -d '{ "title":"This is an request example." }' "https://api.yanjiong.wang/article/new/67890"
- C-C Commit Request HTTP/1.1 Example
PUT /article/new/67890 HTTP/1.1
Host: yanjiong.wang
Content-Type: application/json
User-Agent: REST-like-Consumer
Referer: 0987654
{
"title":"This is an request example."
}
提供方请求示例
- C-C Commit Response HTTP/1.1 Syntax
HTTP/1.1 <Status Code>
Server: <Provider>/<Version>
Content-type: application/json
[Date: <Date>]
[Last-Modified: <Date>]
[ETag: <Content Hash>]
[X-CC-RequestId: <RequestId>]
- C-C Commit Response HTTP/1.1 Example
HTTP/1.1 200 OK
Server: REST-like-Provider/2.1
Date: Wed, 21 Dec 2016 15:03:33 GMT
Last-Modified: Tue, 20 Dec 2016 08:37:25 GMT
ETag: W/"ebf-1591b60c988"
X-CC-RequestId: 67890
{
"title":"This is an response example."
}