smartbuf-springcloud
是一個基於smartbuf
的spring-cloud
序列化插件。html
smartbuf
是一種新穎、高效、智能、易用的跨語言序列化框架,它既擁有不亞於protobuf
的高性能,也擁有與json
相仿的通用性、可擴展性、可調試性等。java
在Java
語言生態中,它經過如下插件支持多種RPC
框架:git
smartbuf-dubbo
: 爲dubbo
提供了stream
模式的序列化擴展插件smartbuf-springcloud
: 爲spring-cloud
提供了packet
模式的序列化擴展插件如下爲smartbuf-springcloud
插件的具體介紹。github
smartbuf-springcloud
介紹此插件內部封裝了smartbuf
序列化框架的packet
模式,經過自定義的SmartbufMessageConverter
向spring
容器中暴露了一個名爲application/x-smartbuf
的HTTP
消息編碼解碼器。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
,其MediaType
爲application/x-smartbuf
。性能優化
spring-mvc
啓動後會把這個新增的HttpMessageConverter
加入messageConverters
中,若是後續http
請求的頭信息中包括accept: application/x-smartbuf
或content-type: application/x-smartbuf
,則spring-mvc
會將該請求的OutputStream
編碼或InputStream
解碼委託給SmartbufMessageConverter
處理。網絡
整個過程和application/json
的實現原理相似,不一樣之處在於application/x-smartbuf
底層採用了另一種序列化方案:SmartBuf
總結:你不須要作任何額外的配置,此插件會自動向spring-mvc
中註冊一個名爲application/x-smartbuf
的數據編碼解碼器,它對於正常的http
請求沒有任何影響,只有頭信息中的accept
或content-type
匹配到它時纔會被激活。
本章節經過一個簡單的實例,介紹如何將smartbuf-springcloud
引入本身的工程中,以及如何在代碼中使用它。
你能夠經過如下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
會根據接口中聲明的consumes
和produces
,向服務端發送相似於這樣的請求:
=== MimeHeaders ===
accept = application/json
content-type = application/json
user-agent = Java/1.8.0_191
connection = keep-alive
服務端的spring-mvc
會根據頭信息中的accept
和content-type
,肯定使用application/json
來執行input
的解碼和output
的編碼。
application/x-smartbuf
如前文所言,smartbuf-springcloud
會自動向spring-mvc
中註冊一個名爲application/x-smartbuf
的編碼解碼器,所以在引入maven
依賴以後,不須要作任何額外的配置。
你只須要將DemoClient
修改成這樣,注意consumes
和produces
的變化:
@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/json
與application/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而言,序列化耗時每每是納秒級,而邏輯處理、數據傳輸每每是毫秒級的,所以如下測試將採用單線程測試相同接口、相同次數的調用下,json
與smartbuf
的數據傳輸量和總耗時的差異。
下面咱們經過三個不一樣類型的接口測試一下json
與smartbuf
的區別。
hello
測試hello
即前文提到的helloJson
和helloSmartbuf
接口,輸入輸出參數都是String
,接口內部代碼邏輯很是簡單。單線程循環調用400,000
次總耗時分別爲:
JSON
: 169秒 SmartBuf
: 170秒 網絡輸入(bytes_input
)輸出(bytes_output
)總量分別爲:
因爲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
)總量分別爲:
能夠看到請求參數userId
數據類型單一,所以json
和smartbuf
所使用的網絡流量幾乎同樣。而返回結果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
)總量分別爲:
能夠看到請求參數keyword
數據類型單一,所以json
和smartbuf
所使用的網絡流量幾乎同樣。而返回結果List<PostModel>
是一個複雜對象的大數組,所以json
網絡資源消耗量是smartbuf
的將近十倍。
由於測試環境爲localhost
,網絡傳輸耗時對接口的總耗時沒有太大影響。
在輸入輸出數據格式都很是簡單的RPC
接口調用中,此插件所提供的application/x-smartbuf
編碼沒有任何性能優點。
當接口數據中存在複雜對象、數組、集合等較大數據時,使用application/x-smartbuf
能夠大幅下降net
輸入輸出的字節流大小,好比在上文queryPost
測試中,使用application/x-smartbuf
時網絡輸入輸出字節總量僅爲application/json
的十分之一。
難能難得的是,實際應用中application/x-smartbuf
與application/json
便可以共存,也能夠無縫切換。
好比對於某些簡單接口,能夠直接採用簡單的application/json
編碼,而對於數據量比較大的複雜接口,能夠採用高效率的application/x-smartbuf
編碼進行性能優化。
好比開發測試時直接使用application/json
編碼,上線時再切換爲application/x-smartbuf
編碼。
本文同步發佈於GitHub、我的主頁等