最近有點時間,想學習下 http 的規范,理論和實踐結合學習是最有效果的學習方法,如是結合 Apache httpcomponets 的源碼,嘗試理解http的設計思想,但是整個學習下來,在代碼設計規划上也學習到不少東西,這要得益於httpcore,httpclient優秀的源碼.
http協議所描述的可以用一句話概括:點對點的消息交換(一端向另一端發起請求(request),接收端處理請求並返回消息(response)). 不管是http請求還是http響應,我們都把它當做http消息(message)。
(picture 1)
Apache HttpCore 中完全按照rfc文檔定義對象接口關系。HttpRequest 和 HttpResponse 都擴展繼承自 HttpMessage接口。picture 1 中,接口 HttpEntityClosingRequest 擴展了 HttpReqeust 接口,那HttpEntityClosingRequest 是在rfc文檔中對應怎樣的描述?
7 Entity
Request and Response messages MAY transfer an entity if not otherwise restricted by the request method or
response status code. An entity consists of entity-header fields and an entity-body, although some responses will only
include the entity-headers.
In this section, both sender and recipient refer to either the client or the server, depending on who sends and who
receives the entity.請求和返回消息都可以附帶一個消息實體傳輸,如果請求方法和返回碼都沒有限制的話。消息實體可以包括實體頭和實體內容,盡管有些返回消息只包括實體頭。
我在最開始看文檔時,始終對entiy的概念模模糊糊,理解不透,通過google一些文章和反復看文檔才有些眉目。其實我們可以這么理解entiy:對於消息請求時,例於post請求時,提交的表單內容即可看成entity消息實體;對於消息返回時,我們接收到<html>...</html>標記文檔即可看做entity實體。所以說一個消息(不管是請求還是響應消息),里面除了消息頭(header),就是消息實體(entity),當然目前為止,你可以這樣理解,后面我們會指出這里有些問題。相關參考
(picture 2)
消息實體在Apache HttpCore中相關接口定義如圖picture 2,可以看到有各種版本的entiy實現,不同的實現區別僅在對entiy的表現存在形式不同而也:
ByteArrayEntiy : byte數組實現
FileEntiy : 本地文件實現
InputStreamEntiy : io流文件實現
HttpEntityWrapper : 對別的entity進行包裝,添加新功能,利用decorator模式
BufferedHttpEntity : 繼承擴展HttpEntityWrapper,實現可緩沖的entity
這些entity接照其內容存在地方形式分為如下類別:
- streamed: The content is received from a stream, or generated on the fly. In particular, this category includes entities being received from a
connection
.Streamed
entities are generally notrepeatable
.- self-contained: The content is in memory or obtained by means that are independent from a connection or other entity. Self-contained entities are generally
repeatable
.- wrapping: The content is obtained from another entity.
- streamed : BaseHttpEntity,InputStreamEntity
- self-contained : FileEntity,BufferedHttpEntity
- wrapping : HttpEntityWrapper
這里比較有意思的是BufferedHttpEntity,它既是wrapping類別,也屬於self-contained類別。正是因為它是wrapping類別的,所以它會包裝其它的entity,如果被包裝的entity本身是self-contained的,則直接調用它的方法,如果被包裝的entity是streamed類型的,則會把它緩存起來,形成self-contained形式。
----------------------------------------------------------------------
Request Method(請求方法)
- Options
- Get
- Head
- Post
- Put
- Delete
- Trace
- Connect
這些請求方法中,我們最熟悉是Get和Post這兩種方法,其它的基本上沒有用到。不過由於最近開放平台的興起,開發人員慢慢的要接觸和熟悉起這些方法來。其實從這些方法名的語義上,便可直觀的了解這些方法所要表達的行為方式。Get:得到資源數據,Delete刪除資源數據......。關於其中更高層的思想境界,值得一看的是Roy Fielding的論文。
這里最容易讓人不理解的是Put和Post的區別到底是什么。它們都是通過指定的Uri來提交帶有entity的消息請求(request),並要求服務端(server)接收並處理這些請求。但是Post方法指的是廣義的處理方式,何為廣義上的處理呢,引用rfc文檔:
· Annotation of existing resources;
· Posting a message to a bulletin board, newsgroup, mailing list, or similar group of articles;
· Providing a block of data, such as the result of submitting a form, to a data-handling process;
· Extending a database through an append operation.
引用內容大概是說Post方法是對存在的資源進行注解;提交生成一個內部通告,新聞組,郵件列表;提交一些表單數據進行處理;擴展數據庫。
而Put方法具有更狹義的語義:提交一個附帶entity的請求消息(request)到指定的Uri,在該Uri上保存entity,如果該Uri之前保存過entity,則最新的entity覆蓋之。
所以說Post的操作語義范圍較Put更廣,Post指所有的處理資源方式,而Put則是注重Uri和Entity的對應關系。
舉個形象的例子:
POST : /book/add 這里是添加一本書,每Post一次則新增一本書
PUT : /book/add/1 這里添加ID為1的一本書,如果服務端發現此id=1的書已存在,則覆蓋之,相當於edit操作
-----------------------------------------------------------------
消息頭(Header)
消息頭可以看成是消息的元數據描述對象,它是對請求消息(request),響應消息(response),消息實體(entity)的元描述,比如請求消息頭Accept就是指定請求客戶端可以理解哪些媒體類型,Content-Length頭指定entity的大小長度。
(picture 3)
------------------------------------------------------------
下面把Apache HttpCore設計得比較好的地方拿出來分析下:
HttpParams
在Http各組件運行中,都要用到各種運行時的參數
(picture 4)
針對Connection(網絡連接資源,如Socket)進行“池”管理。
(picture 5)
主要是對URI中相同的Host建立的Socket連接實現reuseable化,提高效率和優化性能,滿足Http1.1中的Connection頭描述(值為keep-alive時不關閉每個當前連接,以供下次請求使用)
AOP架構模式,攔截處理request和response消息的各種頭,內容體處理
------------------------------------------------------------------------------
總結
這篇文章只是根據rfc規范和Apache HttpCore中的架構設計相互參考學習后的一些筆記,圖中列出的UML類圖也只是從抽象層面理解http的設計。后續會繼續從這些接口實現細節上,抽出一部分值得分享,記錄的內容寫下來。由於這篇文章只是學習筆記,如果您看起來比較不知所然,建議詳細學習文中的給出的學習鏈接。