Spring Cloud OpenFeign集成Protocol Buffer

本文做者張天,著有《Spring Cloud 微服務架構進階》一書。html

背景

 在以前的文章中,咱們介紹過基於Spring Cloud微服務架構,其中,微服務實例之間的交互方式通常爲RESTful HTTP請求或RPC調用。Spring Cloud已經爲開發者提供了專門用於RESTful HTTP請求處理的OpenFeign組件,可是並無相關的RPC調用組件。今天,咱們就要定製OpenFeign的編解碼器,使用Google的Protocol Buffer編碼,讓它擁有RPC調用的數據傳輸和轉換效率高的優勢。java

 OpenFeign是一個聲明式RESTful HTTP請求客戶端,它使得編寫Web服務客戶端更加方便和快捷。它有較強的定製性,能夠根據本身的需求來對它的各個方面進行定製,好比說編解碼器,服務路由解析和負載均衡。git

 而Protocol Buffer 是Google的一種輕便高效的結構化數據存儲格式,能夠用於結構化數據串行化,或者說序列化。它很適合作數據存儲或 RPC 數據交換格式。可用於通信協議、數據存儲等領域的語言無關、平臺無關、可擴展的序列化結構數據格式。目前提供了 C++、Java、Python 三種語言的 API。github

 OpenFeign默認使用HttpUrlConnection進行網絡請求的發送; 相關實現代碼在DefaultFeignLoadBalancedConfigurationClient.Default。而其使用的編解碼器默認爲jackson2,默認配置爲HttpMessageConvertersAutoConfigurationspring

 Protocol Buffer的編解碼效率要遠高於jackson2,在微服務實例頻頻通訊的場景下,使用Protocol Buffer編解碼時會少佔用系統資源,而且效率較高。具體詳見這個對比對比各類序列化和反序列化框架的性能的文檔,github.com/eishay/jvm-… bash

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));
    }
}
複製代碼

 其中ProtobufHttpMessageConverterHttpMessageConverters的Protobuf的實現類,負責使用Protocol Buffer進行網絡請求和響應的編解碼。而SpringEncoderResponseEntityDecoder是OpenFeign中的編解碼器實現類。架構

 下面,咱們來看一下OpenFeign中發送網絡請求的接口定義。@FeignClient中配置了ProtoFeignConfiguration爲自定義配置類。app

@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插件自動生成的。須要注意的是,必須將@RequestMappingconsumesproduces屬性設置爲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 微服務架構進階

封面
《Spring Cloud 微服務架構進階》購買地址:item.jd.com/12453340.ht…
相關文章
相關標籤/搜索