「造個輪子」——設計 HTTP 請求全域性上下文
前言
本次 Cicada 已經更新到了 v1.0.3
。
所以本次的主要更新為:
- Cicada 採用合理的執行緒分配來處理接入請求執行緒以及 IO 執行緒。
- 支援多種響應方式(以前只有 json,現在支援 text)。
- 為了滿足上者引入了
context
。 - 優雅停機。
其中我覺得最核心也最有用的就是這個 Context
,併為此重構了大部分程式碼。
多種響應方式
在起初 Cicada
預設只能響應 json
,這一點確實不夠靈活。加上後續也打算支援模板解析,所以不如直接在 API 中加入可讓使用者自行選擇不同的響應方式。
因此調整後的 API 如下。
想要輸出
text/plain
時。
@CicadaAction("textAction") public class TextAction implements WorkAction { @Override public void execute(CicadaContext context, Param param) throws Exception { String url = context.request().getUrl(); String method = context.request().getMethod(); context.text("hello world url=" + url + " method=" + method); } }
而響應輸出
application/json
時只需要把需要響應的物件寫入到json()
方法中.
因此原有的業務 action 中也加入了一個上下文的引數:
/**
* abstract execute method
* @param context current context
* @param param request params
* @throws Exception throw exception
*/
void execute(CicadaContext context ,Param param) throws Exception;
下面就來看看這個 Context
Cicada Context
先看看有了這個上下文之後可以做什麼。
比如有些場景下我們需要拿到本次請求中的頭資訊,這時就可以通過這個 Context
物件直接獲取。
當然不止是頭資訊:
- 獲取請求頭。
- 設定響應頭。
- 設定
cookie
。 - 獲取請求
URL
。 - 獲取請求的
method
(get/post)等。
其實通過這些特點可以看出這些資訊其實都和一次 請求、響應
密切相關,並且各個請求之間的資訊應互不影響。
這樣的特性是不是非常熟悉,沒錯那就是 ThreadLocal
,它可以將每個執行緒的資訊儲存起來互不影響。
ThreadLocal 的原理本次不做過多分析,只談它在 Cicada 中的應用。
CicadaContext.class
先來看看 CicadaContext
這個類的主要成員變數以及方法。
成員變數是兩個介面 CicadaRequest、CicadaResponse
,名稱就能看出肯定是存放請求和響應資料的。
HttpDispatcher.class
想要存放本次請求的上下文自然是在真正請求分發的地方 HttpDispatcher
。
這裡改的較大的就是兩個紅框處,第一部分是做上下文初始化及賦值。
第二部分自然就是解除安裝上下文。
先看初始化。
CicadaRequest cicadaRequest = CicadaHttpRequest.init(defaultHttpRequest) ;
首先是將 request 初始化:
CicadaHttpRequest
自然是實現了 CicadaRequest
介面:
這裡只儲存了請求的 URL、method 等資訊,後續要加的請求頭也存放在此處即可。
Response
也是同理的。
這兩個具體的實現類都私有化了建構函式,防止外部破壞了整體性。
接著將當前請求的上下文儲存到了 CicadaContext
中。
CicadaContext.setContext(new CicadaContext(cicadaRequest,cicadaResponse));
而這個函式本質使用的則是 ThreadLocal
來存放 CicadaContext
。
public static void setContext(CicadaContext context){
ThreadLocalHolder.setCicadaContext(context) ;
}
private static final ThreadLocal<CicadaContext> CICADA_CONTEXT= new ThreadLocal() ;
/**
* set cicada context
* @param context current context
*/
public static void setCicadaContext(CicadaContext context){
CICADA_CONTEXT.set(context) ;
}
處理業務及響應
接著就是處理業務,呼叫不同的 API 做不同響應。
拿 context.text()
來說:
其實就是設定了對應的響應方式、以及把響應內容寫入了 CicadaResponse
的 httpContent
中。
業務處理完後呼叫 responseContent()
進行響應:
responseContent(ctx,CicadaContext.getResponse().getHttpContent());
其實就是在上下文中拿到的響應方式及響應內容返回給客戶端。
解除安裝上下文
最後有點非常重要,那就是 解除安裝上下文。
如果這裡不做處理,之後隨著請求的增多,ThreadLocal
裡存放的資料也越來越多,最終肯定會導致記憶體溢位。
所以 CicadaContext.removeContext()
就是為了及時刪除當前上下文。
優雅停機
最後還新增了一個停機的方法。
其實也就是利用 Hook
函式實現的。
由於目前 Cicada
開的執行緒,佔用的資源都不是特別多,所以只是關閉了 Netty 所使用的執行緒。
如果後續新增了自身的執行緒等資源,那也可以全部放到這裡來進行釋放。
總結
Cicada
已經更新了 4 個版本,雛形都有了。
後續會重點實現模板解析和註解請求路由完成,把 MVC
中的 view
完成就差不多了。
還沒有了解的朋友可以點選下面連結進入主頁瞭解下?。