Spring Cloud Feign 的两种使用姿势 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
hansonwang99
V2EX    程序员

Spring Cloud Feign 的两种使用姿势

  •  
  •   hansonwang99
    hansonwang99 2018 年 9 月 29 日 2851 次点击
    这是一个创建于 2755 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Profile


    概述

    最近结合一些别人的开源项目来学习 Spring Cloud,其中关于服务消费这方面的一个很便利的工具 Feign 让我记忆颇深。虽然网上的 Demo 和例子不胜枚举,但大多比较分散,本文就来集中记录一下声明式客户端 Feign 的一些使用姿势。

    注: 本文首发于 博客 CodeSheep 程序羊,欢迎光临 小站

    下文就结合例子来记录这一过程,代码在文尾处。


    创建基于 Eureka 的服务注册中心

    三个步骤即可搞定:

    • 建工程

    创建一个名为 eureka_server的 SpringBoot 工程,并在 pom.xml 中添加好对应依赖

     <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> 
    • 改主类

    修改应用主类,添加 @EnableEurekaServer注解

    @EnableEurekaServer @SpringBootApplication public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } } 
    • 加配置

    配置 application.properties 文件如下所示:

    spring.application.name=eureka-server server.port=1111 eureka.instance.hostname=localhost #默认设置下,服务注册中心自己也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为 eureka.client.register-with-eureka=false eureka.client.fetch-registry=false 
    • 启动服务注册中心

    浏览器访问之:

    浏览器访问服务注册中心

    此时还没有任何服务注册上来。


    创建服务提供者

    • 建工程

    创建一个名为 service_provider的 SpringBoot 工程,并在 pom.xml 中添加好对应依赖:

     <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> 
    • 改主类

    添加 @EnableDiscoveryClient注解

    @EnableDiscoveryClient @SpringBootApplication public class ServiceProviderApplication { public static void main(String[] args) { SpringApplication.run(ServiceProviderApplication.class, args); } } 
    • 添加控制器 DateServiceController

    提供一个 Restful 接口而已,该接口的作用是获取服务器上的时间并返回

    @RestController public class DateServiceController { @RequestMapping( value = "/test", method = RequestMethod.GET ) public String test( @RequestParam String param ){ return "hello " + param; } } 
    • 配置 application.properties 文件
    spring.application.name=service_provider server.port=1112 eureka.client.serviceUrl.defaultZOne=http://localhost:1111/eureka/ 
    • 启动工程

    浏览器访问服务注册中心,我们发现服务提供者 service_provider已经注册到 eureka_server上:

    服务提供者已注册上来了

    同时浏览器访问:http://localhost:1112/test?param=www.codesheep.cn,可以测试服务提供 service_provider提供的接口工作正常

    测试发现服务提供者的接口工作正常

    接下来我们创建服务消费者,是 Feign 该登场的时候了!


    创建基于 Feign 的服务消费者

    • 创建一个名为 service_consumer的 SpringBoot 工程,并在 pom.xml 中添加好对应依赖
     <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> 
    • 修改应用主类

    主要是添加有关 Feign 客户端的一些注解而已

    @EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class ServiceConsumerApplication { public static void main(String[] args) { SpringApplication.run(ServiceConsumerApplication.class, args); } } 
    • 创建一个 Feign 客户端的接口:DateServiceFeignClientInterfac

    很明显其内部用 @FeignClient( value = "service-provider" ) 声明的方式指向了 服务提供者,而接口方法则实现了对 服务提供者接口的实际调用

    @FeignClient( value = "service-provider" ) public interface DateServiceFeignClientInterface { @GetMapping("/test") String consumer( @RequestParam("param") String param ); } 
    • 创建控制器:DateServiceFeignController

    注意,这是服务消费者提供的 Rest 接口

    @RestController @RequestMapping("/consumer") public class DateServiceFeignController { @Resource DateServiceFeignClientInterface dateServiceFeignClientInterface; @GetMapping("/date") public String getDate( @RequestParam String param ) { return dateServiceFeignClientInterface.consumer( param ); } } 
    • 配置 application.properties
    spring.application.name=service-consumer server.port=1113 eureka.client.serviceUrl.defaultZOne=http://localhost:1111/eureka/ 
    • 启动服务消费者

    我们先去服务注册中心上看看,发现 服务消费者也注册上来了:

    服务消费者已注册上来

    然后我们浏览器访问 服务消费者提供的 Rest 接口: http://localhost:1113/consumer/date?param=www.codesheep.cn

    数据成功取回

    这样我们就通过 服务消费者的 Feign 客户端 取到了服务提供者 给予的接口数据。

    上面这就是声明式客户端 Feign 的第一种使用姿势,也是常用的手法,常见于很多 Demo

    下面我们来实践一下关于 Feign 的继承与实现机制,发现其使用更加灵活( Feign 支持接口继承方式快速生成客户端,颇有点 RPC 的意思(关于 RPC 的实践可以参考我的文章:[《 RPC 框架实践之:Google gRPC 》]( http://www.codesheep.cn/2018/05/21/RPC 框架实践之:Google-gRPC/)、[《 RPC 框架实践之:Apache Thrift 》]( http://www.codesheep.cn/2018/05/14/RPC 框架实践之:Apache-Thrift/)) )


    抽象出一个公共的 API 服务

    • 创建一个普通 Maven 项目:service_provider_api

    • 创建一个公共接口:DateService

    public interface DateService { @GetMapping("/api/test") String consumer( @RequestParam("param") String param ); } 

    改造之前的 服务提供者 / 消费者项目

    • 在服务消费者 service_consumer项目中添加一个新的 Feign 的客户端接口
    @FeignClient( value = "service-provider" ) public interface DateServiceFeignClientInterface2 extends DateService { } 
    • 并且在 service_consumer项目中添加一个新的控制器 DateServiceFeignController2
    @RestController @RequestMapping("/consumer2") public class DateServiceFeignController2 { @Resource DateServiceFeignClientInterface2 dateServiceFeignClientInterface2; @GetMapping("/date") public String getDate( @RequestParam String param ) { return dateServiceFeignClientInterface2.consumer( param ); } } 
    • 在服务提供者 service_provider项目中来实现我们在公共 api 项目 service_provider_api中的 DateService接口,赋予实际逻辑
    @RestController public class DateServiceController2 implements DateService { @Override public String consumer( @RequestParam String param) { Date now = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("今天是"+"yyyy 年 MM 月 dd 日 E kk 点 mm 分" ); String nowTime = simpleDateFormat.format( now ); return "hello again " + param + ", " + nowTime; } } 
    • 依次将 eureka_serverservice_providerservice_consumer 三个项目分别启动

    浏览器访问:http://localhost:1113/consumer2/date?param=www.codesheep.cn

    成功实现服务调用

    使用 feign 的继承特性时,可以将服务接口的定义从服务消费者中剥离出去,形成独立的 api 项目从而可以很方便的实现接口定义和依赖的共享,不用再复制粘贴接口进行绑定,当然这种做法存在的问题就是可能会导致服务提供者和服务消费者间的耦合度增高,此时如果服务提供者修改了一个接口定义,服务消费者可能也得跟着变,进而带来一些坑。



    后 记

    由于能力有限,若有错误或者不当之处,还请大家批评指正,一起学习交流!

    本文实验代码在此



    5 条回复    2018-09-29 12:16:05 +08:00
    LucasLee92
        1
    LucasLee92  
       2018 年 9 月 29 日
    感谢楼主,回帖收藏,有空看
    twocold0451
        2
    twocold0451  
       2018 年 9 月 29 日
    V 站竟然有技术贴,收藏一下
    Resource
        3
    Resource      2018 年 9 月 29 日
    再次被 @
    ksupertu
        4
    ksupertu  
       2018 年 9 月 29 日 via iPhone
    @twocold0451 笑出了猪叫声
    hansonwang99
        5
    hansonwang99  
    OP
       2018 年 9 月 29 日
    V 站技术文章不挺多的吗?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1054 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 79ms UTC 23:14 PVG 07:14 LAX 16:14 JFK 19:14
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86