Spring Cloud OpenFeign整合Protocol Buffer
在之前的文章中,我們介紹過基於Spring Cloud微服務架構,其中,微服務例項之間的互動方式一般為RESTful HTTP請求或RPC呼叫。Spring Cloud已經為開發者提供了專門用於RESTful HTTP請求處理的OpenFeign元件,但是並沒有相關的RPC呼叫元件。今天,我們就要定製OpenFeign的編解碼器,使用Google的Protocol Buffer編碼,讓它擁有RPC呼叫的資料傳輸和轉換效率高的優點。
OpenFeign是一個宣告式RESTful HTTP請求客戶端,它使得編寫Web服務客戶端更加方便和快捷。它有較強的定製性,可以根據自己的需求來對它的各個方面進行定製,比如說編解碼器,服務路由解析和負載均衡。
而Protocol Buffer 是Google的一種輕便高效的結構化資料儲存格式,可以用於結構化資料序列化,或者說序列化。它很適合做資料儲存或 RPC 資料交換格式。可用於通訊協議、資料儲存等領域的語言無關、平臺無關、可擴充套件的序列化結構資料格式。目前提供了 C++、Java、Python 三種語言的 API。
OpenFeign預設使用 HttpUrlConnection
進行網路請求的傳送; 相關實現程式碼在 DefaultFeignLoadBalancedConfiguration
的 Client.Default
。而其使用的編解碼器預設為jackson2,預設配置為 HttpMessageConvertersAutoConfiguration
。
Protocol Buffer的編解碼效率要遠高於jackson2,在微服務例項頻頻通訊的場景下,使用Protocol Buffer編解碼時會少佔用系統資源,並且效率較高。具體詳見這個對比對比各種序列化和反序列化框架的效能的文件, ofollow,noindex">github.com/eishay/jvm-…

客戶端整合Protocol Buffer
開發人員可以使用自定義配置類對OpenFeign進行定製,提供OpenFeign所需要的編解碼元件例項,從而替代預設的元件例項,達到定製化的目的。自定義的配置類如下所示。
@Configuration public class ProtoFeignConfiguration { @Autowired private ObjectFactory<HttpMessageConverters> messageConverterObjectFactory; @Bean public ProtobufHttpMessageConverter protobufHttpMessageConverter() { return new ProtobufHttpMessageConverter(); } @Bean public Encoder springEncoder() { return new SpringEncoder(this.messageConverterObjectFactory); } @Bean public Decoder springDecoder() { return new ResponseEntityDecoder(new SpringDecoder(this.messageConverterObjectFactory)); } } 複製程式碼
其中 ProtobufHttpMessageConverter
是 HttpMessageConverters
的Protobuf的實現類,負責使用Protocol Buffer進行網路請求和響應的編解碼。而 SpringEncoder
和 ResponseEntityDecoder
是OpenFeign中的編解碼器實現類。
下面,我們來看一下OpenFeign中傳送網路請求的介面定義。 @FeignClient
中配置了 ProtoFeignConfiguration
為自定義配置類。
@FeignClient(name = "user", configuration = ProtoFeignConfiguration.class) public interface UserClient { @RequestMapping(value = "/info", method = RequestMethod.GET, consumes = "application/x-protobuf", produces = "application/x-protobuf") UserDTO getUserInfo(@RequestParam("id") Long id); } 複製程式碼
其中, UserDTO
是使用Protocol Buffer的maven外掛自動生成的。需要注意的是,必須將 @RequestMapping
的 consumes
和 produces
屬性設定為 application/x-protobuf
,表示網路請求和響應的編碼格式必須是Protobuf,否則可能會接收到406的錯誤響應碼。
下面是proto檔案中的資料格式定義,其中java_package是表明生成檔案的目標資料夾。該檔案中定義了UserDTO資料格式,它包括ID,名稱和主頁URL三個屬性。
syntax = "proto3"; option java_multiple_files = true; option java_package = "com.remcarpediem.feignprotobuf.proto.dto"; package com.remcarpediem.feignprotobuf.proto.dto; message UserDTO { int32 id = 1; string name = 2; string url = 3; } 複製程式碼
在pom檔案中配置build屬性,使用Protocol Buffer的maven外掛可以自動根據proto檔案生成Java程式碼。每個配置項都在程式碼中有對應的解釋。
<build> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.5.0</version> <extensions>true</extensions> <configuration> <!--預設值--> <protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot> <!--預設值--> <!--<outputDirectory>${project.build.directory}/generated-sources/protobuf/java</outputDirectory>--> <outputDirectory>${project.build.sourceDirectory}</outputDirectory> <!--設定是否在生成java檔案之前清空outputDirectory的檔案,預設值為true,設定為false時也會覆蓋同名檔案--> <clearOutputDirectory>false</clearOutputDirectory> <!--預設值--> <temporaryProtoFileDirectory>${project.build.directory}/protoc-dependencies</temporaryProtoFileDirectory> <!--更多配置資訊可以檢視https://www.xolstice.org/protobuf-maven-plugin/compile-mojo.html--> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>test-compile</goal> </goals> <!--也可以設定成區域性變數,執行compile或test-compile時才執行--> <!--<configuration>--> <!--<protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>--> <!--<outputDirectory>${project.build.directory}/generated-sources/protobuf/java</outputDirectory>--> <!--<temporaryProtoFileDirectory>${project.build.directory}/protoc-dependencies</temporaryProtoFileDirectory>--> <!--</configuration>--> </execution> </executions> </plugin> </plugins> </build> 複製程式碼
然後執行Protocol Buffer的maven外掛可以自動生成相關的資料類。
服務端
然後是服務端對於Protocol Buffer的整合。我們也需要使用自定義配置類將 ProtobufHttpMessageConverter
設定為系統預設的編解碼器,如下述程式碼所示。
@Configuration public class Conf { @Bean ProtobufHttpMessageConverter protobufHttpMessageConverter() { return new ProtobufHttpMessageConverter(); } } 複製程式碼
然後定義Controller的關於user的info介面。返回UserDTO例項作為網路請求的返回值。 ProtobufHttpMessageConverter
會自動將其轉換為Protocol Buffer的資料格式進行傳輸。
@RestController public class UserController { private String host = "http://blog.com/user/"; @GetMapping("/info") public UserDTO getUserInfo(@RequestParam("id") Long id) { return UserDTO.newBuilder(). setId(id).setName("Tom"). setUrl(host + "Tom").build(); } } 複製程式碼
本文的原始碼地址: GitHub: github.com/ztelur/feig…
總結
欲瞭解更詳細的實現原理和細節,大家可以關注筆者出版的《Spring Cloud 微服務架構進階》,本書中對Spring Cloud Finchley.RELEASE版本的各個主要元件進行原理講解和實戰應用,裡邊也有關於OpenFeign的原理和實現的詳細解析。更多的介紹見 Spring Cloud 微服務架構進階 。
