SpringCloud的十倍性能優化與SmartBuf編碼

smartbuf-springcloud是一個基於smartbufspring-cloud序列化插件。html

SmartBuf介紹

smartbuf是一種新穎、高效、智能、易用的跨語言序列化框架,它既擁有不亞於protobuf的高性能,也擁有與json相仿的通用性、可擴展性、可調試性等。java

Java語言生態中,它經過如下插件支持多種RPC框架:git

如下爲smartbuf-springcloud插件的具體介紹。github

smartbuf-springcloud介紹

此插件內部封裝了smartbuf序列化框架的packet模式,經過自定義的SmartbufMessageConverterspring容器中暴露了一個名爲application/x-smartbufHTTP消息編碼解碼器。spring

這個新增的application/x-smartbuf編碼器在複雜對象的數據傳輸過程當中,能夠提供優於protobuf的高性能。json

application/x-smartbuf編碼

此插件在配置文件META-INFO/spring.factories中聲明瞭一個名爲SmartbufAutoConfiguration的自動註解配置,即自定義Auto-Configuration數組

spring-boot初始化時會主動掃描並註冊它,所以不須要你作額外的配置。更多資料請參考SpringBoot文檔spring-mvc

SmartbufAutoConfiguration會向spring容器中增長一個新的SmartbufMessageConverter對象,它是一個自定義的HttpMessageConverter,其MediaTypeapplication/x-smartbuf性能優化

spring-mvc啓動後會把這個新增的HttpMessageConverter加入messageConverters中,若是後續http請求的頭信息中包括accept: application/x-smartbufcontent-type: application/x-smartbuf,則spring-mvc會將該請求的OutputStream編碼或InputStream解碼委託給SmartbufMessageConverter處理。網絡

整個過程和application/json的實現原理相似,不一樣之處在於application/x-smartbuf底層採用了另一種序列化方案:SmartBuf

總結:你不須要作任何額外的配置,此插件會自動向spring-mvc中註冊一個名爲application/x-smartbuf的數據編碼解碼器,它對於正常的http請求沒有任何影響,只有頭信息中的acceptcontent-type匹配到它時纔會被激活。

實例演示

本章節經過一個簡單的實例,介紹如何將smartbuf-springcloud引入本身的工程中,以及如何在代碼中使用它。

增長Maven依賴

你能夠經過如下maven座標添加smartbuf-springcloud的依賴:

<dependency>
  <groupId>com.github.smartbuf</groupId>
  <artifactId>smartbuf-springcloud</artifactId>
  <version>1.0.0</version>
</dependency>

使用application/json

spring-cloud底層使用http與服務端的spring-mvc進行通訊。例如服務端的Controller可能相似這樣:

@RestController
public class DemoController {
    @PostMapping("/hello")
    public String sayHello(String name) { return "hello " + name; }
}

調用方能夠聲明這樣的FeignClient與服務端通訊:

@FeignClient(name = "demo")
public interface DemoClient {
    @PostMapping(value = "/hello", consumes = "application/json", produces = "application/json")
    String hello(@RequestParam("name") String name);
}

調用方經過DemoClient請求服務端的DemoController時,feign會根據接口中聲明的consumesproduces,向服務端發送相似於這樣的請求:

=== MimeHeaders ===
accept = application/json
content-type = application/json
user-agent = Java/1.8.0_191
connection = keep-alive

服務端的spring-mvc會根據頭信息中的acceptcontent-type,肯定使用application/json來執行input的解碼和output的編碼。

使用application/x-smartbuf

如前文所言,smartbuf-springcloud會自動向spring-mvc中註冊一個名爲application/x-smartbuf的編碼解碼器,所以在引入maven依賴以後,不須要作任何額外的配置。

你只須要將DemoClient修改成這樣,注意consumesproduces的變化:

@FeignClient(name = "demo")
public interface DemoClient {
    @PostMapping(value = "/hello", consumes = "application/x-smartbuf", produces = "application/x-smartbuf")
    String hello(@RequestParam("name") String name);
}

以後feign就會使用application/x-smartbuf與服務端spring-mvc進行通訊,此時頭信息就相似這樣:

=== MimeHeaders ===
accept = application/x-smartbuf
content-type = application/x-smartbuf
user-agent = Java/1.8.0_191
connection = keep-alive

更妙的是,你能夠同時建立兩個接口,讓application/jsonapplication/x-smartbuf共存:

@FeignClient(name = "demo")
public interface DemoClient {
    @PostMapping(value = "/hello", consumes = "application/json", produces = "application/json")
    String helloJSON(@RequestParam("name") String name);

    @PostMapping(value = "/hello", consumes = "application/x-smartbuf", produces = "application/x-smartbuf")
    String helloSmartbuf(@RequestParam("name") String name);
}

客戶端能夠經過helloJSON使用application/json編碼方式,經過helloSmartbuf使用application/x-smartbuf編碼方式,而服務端會根據請求方指定的編碼類型自動進行切換。

具體演示代碼在此工程的demo子模塊中,你能夠直接checkout到本地執行。

性能對比

smartbuf的優勢在於其分區序列化所帶來的高壓縮率,尤爲是面對複雜對象、數組時,它的空間利用率遠超其餘序列化方案。

對於RPC而言,序列化耗時每每是納秒級,而邏輯處理、數據傳輸每每是毫秒級的,所以如下測試將採用單線程測試相同接口、相同次數的調用下,jsonsmartbuf的數據傳輸量和總耗時的差異。

下面咱們經過三個不一樣類型的接口測試一下jsonsmartbuf的區別。

hello測試

hello即前文提到的helloJsonhelloSmartbuf接口,輸入輸出參數都是String,接口內部代碼邏輯很是簡單。單線程循環調用400,000次總耗時分別爲:

  • JSON: 169秒
  • SmartBuf: 170秒

網絡輸入(bytes_input)輸出(bytes_output)總量分別爲:

hello-bytes

因爲smartbuf編碼中須要額外幾個字節來描述完整的數據信息,所以在處理String這種簡單數據時,它的空間利用率並不如json

getUser測試

getUser接口的實現方式以下:

@RestController
public class DemoController {
    private UserModel user = xxx; // initialized at somewhere else

    @PostMapping("/getUser")
    public UserModel getUser(Integer userId) { return user; }
}

其中user是一個專門用於測試的、隨機分配的對象,其具體模型能夠查閱demo源碼中的UserModel類。

調用方的FeignClient定義以下:

@FeignClient(name = "demo")
public interface DemoClient {
    @PostMapping(value = "/getUser", consumes = "application/json", produces = "application/json")
    UserModel getUserJSON(@RequestParam("userId") Integer userId);

    @PostMapping(value = "/getUser", consumes = "application/x-smartbuf", produces = "application/x-smartbuf")
    UserModel getUserSmartbuf(@RequestParam("userId") Integer userId);
}

單線程循環調用300,000次的總耗時分別爲:

  • JSON: 162秒
  • SmartBuf: 149秒

網絡輸入(bytes_input)輸出(bytes_output)總量分別爲:

getUser-bytes

能夠看到請求參數userId數據類型單一,所以jsonsmartbuf所使用的網絡流量幾乎同樣。而返回結果UserModel是一個比較複雜的對象,所以json網絡資源消耗量是smartbuf的將近三倍

由於測試環境爲localhost,網絡傳輸耗時對接口的總耗時沒有太大影響。

queryPost測試

queryPost接口的實現方式以下:

@RestController
public class DemoController {
    private List<PostModel> posts = xxx; // initialized at somewhere else

    @PostMapping("/queryPost")
    public List<PostModel> queryPost(String keyword) { return posts; }
}

此接口返回值posts是一個預先分配的、用於測試的PostModel數組,此數組長度爲固定的100,其具體模型及初始化能夠查閱demo源碼。

客戶端、調用方的FeignClient定義以下:

@FeignClient(name = "demo")
public interface DemoClient {
    @PostMapping(value = "/queryPost", consumes = "application/json", produces = "application/json")
    List<PostModel> queryPostJSON(@RequestParam("keyword") String keyword);

    @PostMapping(value = "/queryPost", consumes = "application/x-smartbuf", produces = "application/x-smartbuf")
    List<PostModel> queryPostSmartbuf(@RequestParam("keyword") String keyword);
}

單線程循環調用100,000次的總耗時分別爲:

  • JSON: 195秒
  • SmartBuf: 155秒

網絡輸入(bytes_input)輸出(bytes_output)總量分別爲:

queryPost-bytes

能夠看到請求參數keyword數據類型單一,所以jsonsmartbuf所使用的網絡流量幾乎同樣。而返回結果List<PostModel>是一個複雜對象的大數組,所以json網絡資源消耗量是smartbuf的將近十倍

由於測試環境爲localhost,網絡傳輸耗時對接口的總耗時沒有太大影響。

總結

在輸入輸出數據格式都很是簡單的RPC接口調用中,此插件所提供的application/x-smartbuf編碼沒有任何性能優點。

當接口數據中存在複雜對象、數組、集合等較大數據時,使用application/x-smartbuf能夠大幅下降net輸入輸出的字節流大小,好比在上文queryPost測試中,使用application/x-smartbuf時網絡輸入輸出字節總量僅爲application/json十分之一

難能難得的是,實際應用中application/x-smartbufapplication/json便可以共存,也能夠無縫切換。

好比對於某些簡單接口,能夠直接採用簡單的application/json編碼,而對於數據量比較大的複雜接口,能夠採用高效率的application/x-smartbuf編碼進行性能優化。

好比開發測試時直接使用application/json編碼,上線時再切換爲application/x-smartbuf編碼。

本文同步發佈於GitHub、我的主頁等

相關文章
相關標籤/搜索