「造個輪子」——設計 HTTP 請求全局上下文

前言

本次 Cicada 已經更新到了 v1.0.3java

主要是解決了兩個 issue,#9(Boss線程數好像設置有誤 ) #8(怎麼返回純字符串內容不要JSON格式?)git

因此本次的主要更新爲:github

  • Cicada 採用合理的線程分配來處理接入請求線程以及 IO 線程。
  • 支持多種響應方式(之前只有 json,如今支持 text)。
  • 爲了知足上者引入了 context
  • 優雅停機。

其中我以爲最核心也最有用的就是這個 Context,併爲此重構了大部分代碼。json

多種響應方式

在起初 Cicada 默認只能響應 json,這一點確實不夠靈活。加上後續也打算支持模板解析,因此不如直接在 API 中加入可以讓用戶自行選擇不一樣的響應方式。cookie

所以調整後的 API 以下。app

想要輸出 text/plain 時。ide

@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 中也加入了一個上下文的參數:post

/**
 * abstract execute method
 * @param context current context
 * @param param request params
 * @throws Exception throw exception
 */
void execute(CicadaContext context ,Param param) throws Exception;

下面就來看看這個 Context 是如何完成的。url

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() 來講:

其實就是設置了對應的響應方式、以及把響應內容寫入了 CicadaResponsehttpContent 中。

業務處理完後調用 responseContent() 進行響應:

responseContent(ctx,CicadaContext.getResponse().getHttpContent());

其實就是在上下文中拿到的響應方式及響應內容返回給客戶端。

卸載上下文

最後有點很是重要,那就是 卸載上下文

若是這裏不作處理,以後隨着請求的增多,ThreadLocal 裏存放的數據也愈來愈多,最終確定會致使內存溢出。

因此 CicadaContext.removeContext() 就是爲了及時刪除當前上下文。

優雅停機

最後還新增了一個停機的方法。

其實也就是利用 Hook 函數實現的。

因爲目前 Cicada 開的線程,佔用的資源都不是特別多,因此只是關閉了 Netty 所使用的線程。

若是後續新增了自身的線程等資源,那也能夠所有放到這裏來進行釋放。

總結

Cicada 已經更新了 4 個版本,雛形都有了。

後續會重點實現模板解析和註解請求路由完成,把 MVC 中的 view 完成就差很少了。

尚未了解的朋友能夠點擊下面連接進入主頁瞭解下。

https://github.com/TogetherOS/cicada

相關文章
相關標籤/搜索