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进行网络请求的发送; 相关实现代码在DefaultFeignLoadBalancedConfigurationClient.Default。而其使用的编解码器默认为jackson2,默认配置为HttpMessageConvertersAutoConfiguration

 Protocol Buffer的编解码效率要远高于jackson2,在微服务实例频频通信的场景下,使用Protocol Buffer编解码时会少占用系统资源,并且效率较高。具体详见这个对比对比各种序列化和反序列化框架的性能的文档,https://github.com/eishay/jvm-serializers/wiki。

jvm平台编解码效率示意图

客户端集成Protocol Buffer

 开发人员可以使用自定义配置类对OpenFeign进行定制,提供OpenFeign所需要的编解码组件实例,从而替代默认的组件实例,达到定制化的目的。自定义的配置类如下所示。

 1@Configuration
 2public class ProtoFeignConfiguration {
 3    @Autowired
 4    private ObjectFactory<HttpMessageConverters> messageConverterObjectFactory;
 5    @Bean
 6    public ProtobufHttpMessageConverter protobufHttpMessageConverter() {
 7        return new ProtobufHttpMessageConverter();
 8    }
 9
10    @Bean
11    public Encoder springEncoder() {
12        return new SpringEncoder(this.messageConverterObjectFactory);
13    }
14
15    @Bean
16    public Decoder springDecoder() {
17        return new ResponseEntityDecoder(new SpringDecoder(this.messageConverterObjectFactory));
18    }
19}

其中ProtobufHttpMessageConverterHttpMessageConverters的Protobuf的实现类,负责使用Protocol Buffer进行网络请求和响应的编解码。而SpringEncoderResponseEntityDecoder是OpenFeign中的编解码器实现类。

 下面,我们来看一下OpenFeign中发送网络请求的接口定义。@FeignClient中配置了ProtoFeignConfiguration为自定义配置类。

1@FeignClient(name = "user", configuration = ProtoFeignConfiguration.class)
2public interface UserClient {
3    @RequestMapping(value = "/info", method = RequestMethod.GET,
4            consumes = "application/x-protobuf", produces = "application/x-protobuf")
5    UserDTO getUserInfo(@RequestParam("id") Long id);
6}

 其中,UserDTO是使用Protocol Buffer的maven插件自动生成的。需要注意的是,必须将@RequestMappingconsumesproduces属性设置为application/x-protobuf,表示网络请求和响应的编码格式必须是Protobuf,否则可能会接收到406的错误响应码。

 下面是proto文件中的数据格式定义,其中java_package是表明生成文件的目标文件夹。该文件中定义了UserDTO数据格式,它包括ID,名称和主页URL三个属性。

 1syntax = "proto3";
 2
 3option java_multiple_files = true;
 4option java_package = "com.remcarpediem.feignprotobuf.proto.dto";
 5
 6package com.remcarpediem.feignprotobuf.proto.dto;
 7
 8message UserDTO {
 9    int32 id = 1;
10    string name = 2;
11    string url = 3;
12}

 在pom文件中配置build属性,使用Protocol Buffer的maven插件可以自动根据proto文件生成Java代码。每个配置项都在代码中有对应的解释。

 1<build>
 2        <plugins>
 3            <plugin>
 4                <groupId>org.xolstice.maven.plugins</groupId>
 5                <artifactId>protobuf-maven-plugin</artifactId>
 6                <version>0.5.0</version>
 7                <extensions>true</extensions>
 8                <configuration>
 9                    <!--默认值-->
10                    <protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
11                    <!--默认值-->
12                    <!--<outputDirectory>${project.build.directory}/generated-sources/protobuf/java</outputDirectory>-->
13                    <outputDirectory>${project.build.sourceDirectory}</outputDirectory>
14                    <!--设置是否在生成java文件之前清空outputDirectory的文件,默认值为true,设置为false时也会覆盖同名文件-->
15                    <clearOutputDirectory>false</clearOutputDirectory>
16                    <!--默认值-->
17                    <temporaryProtoFileDirectory>${project.build.directory}/protoc-dependencies</temporaryProtoFileDirectory>
18                    <!--更多配置信息可以查看https://www.xolstice.org/protobuf-maven-plugin/compile-mojo.html-->
19                </configuration>
20                <executions>
21                    <execution>
22                        <goals>
23                            <goal>compile</goal>
24                            <goal>test-compile</goal>
25                        </goals>
26                        <!--也可以设置成局部变量,执行compile或test-compile时才执行-->
27                        <!--<configuration>-->
28                        <!--<protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>-->
29                        <!--<outputDirectory>${project.build.directory}/generated-sources/protobuf/java</outputDirectory>-->
30                        <!--<temporaryProtoFileDirectory>${project.build.directory}/protoc-dependencies</temporaryProtoFileDirectory>-->
31                        <!--</configuration>-->
32                    </execution>
33                </executions>
34            </plugin>
35        </plugins>
36    </build>

 然后运行Protocol Buffer的maven插件可以自动生成相关的数据类。

服务端

 然后是服务端对于Protocol Buffer的集成。我们也需要使用自定义配置类将ProtobufHttpMessageConverter设置为系统默认的编解码器,如下述代码所示。

1@Configuration
2public class Conf {
3    @Bean
4    ProtobufHttpMessageConverter protobufHttpMessageConverter() {
5        return new ProtobufHttpMessageConverter();
6    }
7}

 然后定义Controller的关于user的info接口。返回UserDTO实例作为网络请求的返回值。ProtobufHttpMessageConverter会自动将其转换为Protocol Buffer的数据格式进行传输。

 1@RestController
 2public class UserController {
 3    private String host = "http://blog.com/user/";
 4    @GetMapping("/info")
 5    public UserDTO getUserInfo(@RequestParam("id") Long id) {
 6        return UserDTO.newBuilder().
 7                setId(id).setName("Tom").
 8                setUrl(host + "Tom").build();
 9    }
10}

 本文的源码地址: GitHub:https://github.com/ztelur/feign-protobuf

总结

 欲了解更详细的实现原理和细节,大家可以关注笔者出版的《Spring Cloud 微服务架构进阶》,本书中对Spring Cloud Finchley.RELEASE版本的各个主要组件进行原理讲解和实战应用,里边也有关于OpenFeign的原理和实现的详细解析。更多的介绍见Spring Cloud 微服务架构进阶

原文发布于微信公众号 - aoho求索(aohoBlog)

原文发表时间:2018-10-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Gaussic

使用IntelliJ IDEA开发SpringMVC网站(四)用户管理 顶

访问GitHub下载最新源码:https://github.com/gaussic/SpringMVCDemo

1872
来自专栏Jaycekon

Spring-boot:快速搭建微框架服务

前言: Spring Boot是为了简化Spring应用的创建、运行、调试、部署等而出现的,使用它可以做到专注于Spring应用的开发,而无需过多关注XML的配...

52612
来自专栏java工会

推荐几个自己写的Java后端相关的范例项目

2505
来自专栏耕耘实录

Linux(Centos7.4和RHEL7.4)环境下基于chrony的NTP服务器的构建

版权声明:本文为耕耘实录原创文章,各大自媒体平台同步更新。欢迎转载,转载请注明出处,谢谢

1081
来自专栏代码拾遗

SpringMVC 教程 - 异步请求

在Servlet容器中启动异步支持之后,controller的方法可以通过DeferredResult包装返回值来支持异步处理。例如:

2003
来自专栏微信公众号:Java团长

Java Web现代化开发:Spring Boot + Mybatis + Redis二级缓存

Spring-Boot因其提供了各种开箱即用的插件,使得它成为了当今最为主流的Java Web开发框架之一。Mybatis是一个十分轻量好用的ORM框架。Red...

2482
来自专栏程序猿DD

Spring Boot中使用LDAP来统一管理用户信息

很多时候,我们在构建系统的时候都会自己创建用户管理体系,这对于开发人员来说并不是什么难事,但是当我们需要维护多个不同系统并且相同用户跨系统使用的情况下,如果每个...

7306
来自专栏木木玲

Netty in action ——— 事件循环 和 线程模式

2453
来自专栏kevindroid

NDK学习笔记(1)——第一个jni程序

1214
来自专栏Java架构解析

微服务网关Zuul迁移到Spring Cloud Gateway

本文将会介绍将微服务网关由Zuul迁移到Spring Cloud Gateway。

4K0

扫码关注云+社区

领取腾讯云代金券