Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

对于Spring Cloud Feign入门示例的一点思考

NotificationsYou must be signed in to change notification settings

birthToSpring/feign-demo

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

对于Spring Cloud Feign入门示例的一点思考

Spring Cloud Feign

Spring Cloud Feign是一套基于Netflix Feign实现的声明式服务调用客户端。它使得编写Web服务客户端变得更加简单。我们只需要通过创建接口并用注解来配置它既可完成对Web服务接口的绑定。它具备可插拔的注解支持,包括Feign注解、JAX-RS注解。它也支持可插拔的编码器和解码器。Spring Cloud Feign还扩展了对Spring MVC注解的支持,同时还整合了Ribbon和Eureka来提供均衡负载的HTTP客户端实现。

分布式应用早在十几年前就开始出现,各自的应用运行在各自的tomcat,jboss一类的容器中,他们之间的相互调用变成了一种远程调用,而实现远程调用的方式很多。按照协议划分,可以有RPC,Webservice,http。不同的框架也对他们有了各自的实现,如dubbo(x),motan就都是RPC框架,本文所要讲解的Feign便可以理解为一种http框架,用于分布式服务之间通过Http进行接口交互。说他是框架,有点过了,可以理解为一个http工具,只不过在spring cloud全家桶的体系中,它比httpclient,okhttp,retrofit这些http工具都要强大的多。

入门

先用一个简单的例子,看看如何在项目中使用Feign。示例项目使用maven多module构建,采用springcloud的Dalston.SR1版本

<dependencyManagement>     <dependencies>         <dependency>             <groupId>org.springframework.cloud</groupId>             <artifactId>spring-cloud-dependencies</artifactId>             <version>Dalston.SR1</version>             <type>pom</type>             <scope>import</scope>         </dependency>     </dependencies> </dependencyManagement>

服务提供方

在本例子中,使用两个应用模块,展示分布式应用中如何进行接口交互。restful-provider担任服务提供方,restful-consumer担任服务消费者。

  • restful-provider新建一个modulerestful-provider-app,模块中只需要写一个CalculateController.java即可

     @RestController @RequestMapping("/api") public class CalculateController {     @PostMapping("/add")     public Integer add(@RequestParam Integer a,@RequestParam Integer b){         return a+b;     }      @PostMapping("/subtract")     public Integer subtract(@RequestParam Integer a,@RequestParam Integer b){         return a-b;     } }
  • 配置文件application.yml:

      server:    port: 7070

一个服务端就写好了,提供两个计算服务的接口,可以通过http访问

服务消费方

  • 使用Feign编写消费方,在restful-consumer项目中,我们将接口的定义和消费者应用分成两个module,restful-consumer-api-definitionrestful-consumer-app

  • 在接口定义模块中,只有一个Feign接口:

      @FeignClient(value = "calculate",path = "/api")  public interface CalculateApi {        @PostMapping(path = "/add")      Integer add(@RequestParam("a") Integer a,@RequestParam("b") Integer b);        @PostMapping(path = "/subtract")      Integer subtract(@RequestParam("a") Integer a,@RequestParam("b") Integer b);    }

tip:@RequestParam中的参数值不能省略,否则会出现错误

  • restful-consumer-app依赖上面的restful-consumer-api-definition模块,并且启用Feign代理,自动生成一个远程调用。启动类配置:

     @EnableFeignClients(basePackages = {"sinosoftsh.consumer.api"}) @SpringBootApplication public class ConsumerApp {      public static void main(String []args){         SpringApplication.run(ConsumerApp.class,args);     }  }

使用@EnableFeignClients(basePackages = {"sinosoftsh.consumer.api"})扫描接口类所在的包,spring的容器中才会有代理实现类。

  • 不要忘记配置消费者的相关属性,在application.yml中

      server:    port: 7080    ribbon:    eureka:     enabled: false    calculate:    ribbon:      listOfServers: localhost:7070    logging:    level:      org.apache.http: trace

    在CalculateApi 接口的定义中,我们使用了一个calculate作为服务名称,必须要在配置文件中配置calculate所在的ip地址才行,由于本文只是作为一个示例,所以没有使用注册中心,在配置中禁用了eureka。最后一行的日志配置,可以发现其实Feign内部其实使用的是现成的http工具:httpclient,okhttp3,可以通过配置替换实现

  • 整体的项目结构如下:

![这里写图片描述](http://img.blog.csdn.net/20170803170857775?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzgxNTU0Ng==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)图一 第一种依赖关系结构
  • 再编写一个单元测试类,验证一下Feign是否被正确的配置了

      @RestController  public class ConsumerController {        @Autowired      CalculateApi calculateApi;        @RequestMapping("/test")      public String test() {          Integer result = calculateApi.add(1, 2);          System.out.println("the result is " + result);          return "success";      }    }

思考

回顾一下我们入门实例,服务提供方使用的是一个RestController暴露计算服务,服务消费方使用http工具(Feign)进行远程调用,这再清晰不过了,也是符合软件设计的,因为Feign接口的定义是存在于消费方,所以是真正的松耦合。但是习惯了使用rpc共享接口的设计,我们也可以将接口定义在服务提供方,这样做的好处是,服务可能被多个消费者使用,不需要每个消费者都定义一次Feign接口。

![这里写图片描述](http://img.blog.csdn.net/20170803171756343?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzgxNTU0Ng==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)图2 第二种依赖关系结构在`restful-provider`创建一个`restful-provider-api-definition`模块,将`CalculateApi.java`的定义迁移到服务提供方,相应的`restful-provider-app`也可以进行改造:
@RestController@RequestMapping("/api")public class CalculateController implements CalculateApi{//    @PostMapping("/add")    @Override    public Integer add(@RequestParam Integer a,@RequestParam Integer b){        return a+b;    }//    @PostMapping("/subtract")    @Override    public Integer subtract(@RequestParam Integer a,@RequestParam Integer b){        return a-b;    }}

因为接口的定义和服务提供方现在在一个限界上下文中,接口的定义同时也宣告了应该提供什么样的服务,所以直接继承CalculateApi。这里的理解比较绕,现在的设计中,CalculateApi在服务消费者和服务提供者中的定位是不一样的,服务消费者需要在启动类扫描CalculateApi所在的包,生成代理对象,远程调用;而在服务提供方则一定不能扫描CalculateApi所在的包,否则会污染容器中的CalculateApi实现类,要知道,CalculateController 之上有一个@RestController注解,意味着已经有一个本地代理实现了,我们也可以在服务提供方注入CalculateApi,便是进行的本地调用了,这符合我们的初衷:我自己的提供的服务,本地当然可以调用。在服务提供方的启动类上要额外注意@ComponentScan,@EnableFeignClients的扫描。

这样,当我们有多个消费者,只需要让他们配置Feign,并且引入服务提供方的接口定义,扫描,即可进行远程调用。有点类似于RPC的共享接口。

设计原则

restful设计以语言无关,松耦合的优势著称。在Spring Cloud Feign的相关文档中有这样的描述:

It is generally not advisable to share an interface between a server and a client. It introduces tight coupling, and also actually doesn’t work with Spring MVC in its current form (method parameter mapping is not inherited).

不建议使用上述改进后的共享接口的方式,并且警告我们,springmvc的注解在Feign接口中的定义和实现类中是不可继承的。关于这点,仁者见仁,智者见智。我们现在项目依旧是采用共享接口的方式,这样可以使得开发变得便捷,多个消费者不需要重复定义。

下面是关于耦合和共享接口的一些讨论:spring-cloud/spring-cloud-netflix#951spring-cloud/spring-cloud-netflix#659spring-cloud/spring-cloud-netflix#646https://jmnarloch.wordpress.com/2015/08/19/spring-cloud-designing-feign-client/

注意事项

  • 当接口定义中出现了实体类时,需要使用@RequestBody注解。多个实体类,则需要用一个大的vo对其进行包裹,要时刻记住,Feign接口最终是会转换成一次http请求。

  • 接口定义中的注解和实现类中的注解要分别写一次,不能继承。

  • Feign调用一般配合eureka等注册中心使用,并且在客户端可以支持Hystrix机制,本文为了讲解共享接口这一设计,所以重心放在了Feign上,实际开发中,这些spring cloud的其他组件通常配套使用。

  • 对http深入理解,在使用Feign时可以事半功倍。

About

对于Spring Cloud Feign入门示例的一点思考

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Java100.0%

[8]ページ先頭

©2009-2025 Movatter.jp