微服務框架學習二:Http調用

1. HTTP接口的意義前端

二進制接口使用的是java/hessian序列化協議,不能很好的與其餘語言通訊,雖然hessian也是一種跨語言的通用協議,但不少語言沒有很好的實現該協議的產品。
因此爲了可以與其餘語言進行服務通訊,咱們實現了http + json的協議實現,利用json原生的跨語言的特性。java

 

2. 原理簡圖json

描述:經過Netty暴露http服務端口,接收到http請求,經過HttpDecoder將其解析爲HttpRequest,經過JSONDecoder提取service請求信息,生成Request請求對象,從而adapt到binary協議實現,交由request processor處理,返回Response結果,最終經過JSONEncoder編碼爲JSON格式數據,再經過HttpEncoder生成HttpResponse返回給Http接口調用方。數組

 

3. 協議格式安全

假定:有服務暴露接口ExampleService,服務名稱:http://service.huifu.com/ExampleService/exampleService_1.0.0,定義了服務方法public String sayHello(String message),則HTTP訪問協議描述以下:框架

請求協議:eclipse

Name Value Description
URL http://{machine-ip}:{http-port}/rpc.json HTTP服務的訪問地址,注意http端口號爲pegasus端口號+1000,即:pegasus配置爲8888,http端口就是9888
Method POST 訪問方式,推薦使用POST方式
Encoding UTF-8 HTTP請求編碼
Params _service

http://service.huifu.com/ExampleService/exampleService_1.0.0maven

訪問的服務名稱
_method

sayHello測試

訪問的服務接口方法名稱
_param {"message" : "kitty"}

傳遞到接口方法的參數值,使用json格式,形如:{"param1" : "value1", "param2" : {"f1" : 1, "f2" : "vvv"}},其中param1,param2爲方法的參數名稱,param1爲string類型,param2爲javabean類型,f1和f2爲該javabean的屬性名。ui

如:調用方法爲sayHello(String message),則對應json爲{"message":"kitty"}。若是調用方法爲:sayHello(RequestDTO userDTO),RequestDTO中有一個屬性爲name,則對應的json爲{"userDTO":{"name":"kitty"}}。

響應協議:

Name Value Description
格式 {"type" : "xxx", "result" : "xxx", "error" : "xxx"} HTTP服務接口的響應內容爲JSON格式
type "service" | "service-exception" | "exception"

"service":業務處理正常返回
"service-exception":業務處理拋出異常
"exception":框架處理拋出異常 

result type = "service"時,服務方法的返回值,爲JSON格式

若方法返回值爲簡單類型:
"result" : "hello world"
若爲複雜類型:
"result" : "{"f1" : "v1", "f2" : "v2"}" 

error

type = "service-exception" | "exception"時,返回的錯誤信息,爲JSON格式

形如{"type" : "com.huifu.xx.BizException", "message" : "order_no is not supported"}
 
 
4. 注意事項
  1. 關於服務方法參數名
    默認狀況下,pegasus經過asm讀取服務實現類的方法簽名信息,獲取方法的參數名,但因重構或其餘緣由,該方法的參數名有可能被修改,從而致使原有的http接口使用方沒法正常調用;
    因此推薦在有可能提供http訪問的服務方法上經過@Param註解定義參數名,這樣就能夠防止參數名被修改的狀況,如void sayHello(@Param("message") String message),此時該方法就能夠安全地重構爲void sayHello(@Param("message") String msg),而不影響原有調用方;
    注意:在使用的過程當中發現,經過javassist獲取參數名,若是使用javac編譯器編譯的class文件(maven默認使用),可能會出現沒法讀取參數名或讀取錯誤; 咱們遇到的幾個問題case改用eclipse的ecj編譯器後均可以fix,但咱們沒法去測試覆蓋全部的代碼case,所以若方法涉及到http調用時,請使用@Param的方式顯式提供參數名; 
    btw: maven使用ecj做爲編譯器的詳細配置以下 (1.0.2中已經使用asm替換javassist,該問題已FIXED) 

  2. 關於服務傳輸對象(DTO)的定義
    因爲使用json格式傳遞方法參數值,就牽扯到json ==> javabean的轉換過程,而這個過程要求能明確地知道全部java對象及其屬性的類型,因此在建立服務協議DTO對象時,若是這個對象牽扯到http方式的調用,那麼就須要明肯定義其類型,好比集合類型或數組,必須有明確的元素類型信息,因此Map, List, Set, Object[]都是沒法轉換的,必須是Map<String, String>, List<Order>, Set<Integer>, Order[]等有明確的元素類型聲明的集合類和數組,即混合類型的集合http方式不支持;
    因爲json對象沒法表示對象引用,因此在定義DTO時,應避免引用的方式,好比parent下定義了list<child>,child中又定義一個parent引用剛纔的parent對象,這種方式不容許。
    因爲json對象的key必定是string類型,因此對於DTO中的Map,只能是Map<String, xx>, Map<Integer, xx>, Map<BigDecimal, xx>, Map<Boolean, xx>,Map<Date, xx>這樣的Map類型,而Map<JavaBean, xx>這種類型則沒法經過http接口調用,由於json沒法表示這樣的key。json轉map:{"k1", "v1"} => map {"k1", "v1"}。
    對於有Map<JavaBean, JavaBean>這種類型的dto,若是又須要經過http方式訪問,那麼請重構該map類型爲List方式,好比List<MapBean>, MapBean中有JavaBean的key和JavaBean類型的value屬性,即經過List<Entry>的方式實現Map,此時能夠經過json表示,即[{"key1" : {xxx}, "value1" : {xxx}}, {"key2" : {xxx}, "value2" : {xxx}}]。
  3. 關於客戶端調用能夠按照正常的http方式訪問服務接口,因爲目前各語言尚未本身封裝的客戶端,因此集羣方面只能經過前端架設軟負載的方式實現,後期考慮各語言封裝本身的客戶端,內部維護到各服務提供者的http長鏈接,並按期地經過服務註冊中心的http接口更新服務提供者列表信息,搭配按期心跳探測移除不可用的鏈接,以便最終摒棄軟負載。經過http方式訪問服務接口時,須要在客戶端設置http請求的超時時間,以便在超過設定的超時時間後,客戶端會當即拋出超時異常,並中斷此次請求,不然會一直等待服務端返回,一旦服務端業務邏輯處理僵死,將沒法返回http響應,並一直維持該http鏈接,形成資源沒法釋放;
相關文章
相關標籤/搜索