zoukankan      html  css  js  c++  java
  • Spring Cloud 入门教程(六): 用声明式REST客户端Feign调用远端HTTP服务

    首先简单解释一下什么是声明式实现?

    要做一件事, 需要知道三个要素,where, what, how。即在哪里( where)用什么办法(how)做什么(what)。什么时候做(when)我们纳入how的范畴。

    1)编程式实现: 每一个要素(where,what,how)都需要用具体代码实现来表示。传统的方式一般都是编程式实现,业务开发者需要关心每一处逻辑

    2)声明式实现: 只需要声明在哪里(where )做什么(what),而无需关心如何实现(how)。Spring的AOP就是一种声明式实现,比如网站检查是否登录,开发页面逻辑的时候,只需要通过AOP配置声明加载页面(where)需要做检查用户是否登录(what),而无需关心如何检查用户是否登录(how)。如何检查这个逻辑由AOP机制去实现, 而AOP的登录检查实现机制与正在开发页面的逻辑本身是无关的。

    在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端。Feign就是Spring Cloud提供的一种声明式REST客户端。可以通过Feign访问调用远端微服务提供的REST接口。现在我们就用Feign来调用SERVICE-HELLOWORLD暴露的REST接口,以获取到“Hello World”信息。在使用Feign时,Spring Cloud集成了Ribbon和Eureka来提供HTTP客户端的负载均衡。

    下面我们就采用Feign的方式来调用Hello World服务集群。

    1. 创建Maven工程,加入spring-cloud-starter-feign依赖

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-feign</artifactId>
            </dependency>

    完整的pom文件如下:

    复制代码
     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <project xmlns="http://maven.apache.org/POM/4.0.0" 
     3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     4         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     5     <modelVersion>4.0.0</modelVersion>
     6     <groupId>com.chry</groupId>
     7     <artifactId>springcloud.helloworld.feign.client</artifactId>
     8     <version>0.0.1-SNAPSHOT</version>
     9     <packaging>jar</packaging>
    10     <name>springcloud.helloworld.feign.client</name>
    11     <description>Demo Feigh client application</description>
    12 
    13     <parent>
    14         <groupId>org.springframework.boot</groupId>
    15         <artifactId>spring-boot-starter-parent</artifactId>
    16         <version>1.5.3.RELEASE</version>
    17         <relativePath/> <!-- lookup parent from repository -->
    18     </parent>
    19 
    20     <properties>
    21         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    22         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    23         <java.version>1.8</java.version>
    24     </properties>
    25 
    26     <dependencies>
    27         <dependency>
    28             <groupId>org.springframework.cloud</groupId>
    29             <artifactId>spring-cloud-starter-eureka</artifactId>
    30         </dependency>
    31         <dependency>
    32             <groupId>org.springframework.cloud</groupId>
    33             <artifactId>spring-cloud-starter-feign</artifactId>
    34         </dependency>
    35         <dependency>
    36             <groupId>org.springframework.boot</groupId>
    37             <artifactId>spring-boot-starter-web</artifactId>
    38         </dependency>
    39 
    40         <dependency>
    41             <groupId>org.springframework.boot</groupId>
    42             <artifactId>spring-boot-starter-test</artifactId>
    43             <scope>test</scope>
    44         </dependency>
    45     </dependencies>
    46 
    47     <dependencyManagement>
    48         <dependencies>
    49             <dependency>
    50                 <groupId>org.springframework.cloud</groupId>
    51                 <artifactId>spring-cloud-dependencies</artifactId>
    52                 <version>Dalston.RC1</version>
    53                 <type>pom</type>
    54                 <scope>import</scope>
    55             </dependency>
    56         </dependencies>
    57     </dependencyManagement>
    58 
    59     <build>
    60         <plugins>
    61             <plugin>
    62                 <groupId>org.springframework.boot</groupId>
    63                 <artifactId>spring-boot-maven-plugin</artifactId>
    64             </plugin>
    65         </plugins>
    66     </build>
    67 
    68     <repositories>
    69         <repository>
    70             <id>spring-milestones</id>
    71             <name>Spring Milestones</name>
    72             <url>https://repo.spring.io/milestone</url>
    73             <snapshots>
    74                 <enabled>false</enabled>
    75             </snapshots>
    76         </repository>
    77     </repositories>
    78 </project>
    复制代码

    2. 创建启动类,需呀加上@EnableFeignClients注解以使用Feign, 使用@EnableDiscoveryClient开启服务自动发现

    复制代码
     1 package springcloud.helloworld.feign.service;
     2 
     3 import org.springframework.boot.SpringApplication;
     4 import org.springframework.boot.autoconfigure.SpringBootApplication;
     5 import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
     6 import org.springframework.cloud.netflix.feign.EnableFeignClients;
     7 
     8 @SpringBootApplication
     9 @EnableDiscoveryClient
    10 @EnableFeignClients
    11 public class ServiceFeignApplication {
    12     public static void main(String[] args) {
    13         SpringApplication.run(ServiceFeignApplication.class, args);
    14     }
    15 }
    复制代码

    3. 添加配置文件application.yml, 使用端口8902, 名字定义为service-feign, 并注册到eureka服务中心

    复制代码
    1 eureka:
    2    client:
    3       serviceUrl:
    4         defaultZone: http://localhost:8761/eureka/
    5 server:
    6    port: 8902
    7 spring:
    8    application:
    9       name: service-feign
    复制代码

    4. 定义Feign:一个用@FeignClient注解的接口类,

    @FeignClient用于通知Feign组件对该接口进行代理(不需要编写接口实现),使用者可直接通过@Autowired注入; 该接口通过value定义了需要调用的SERVICE-HELLOWORLD服务(通过服务中心自动发现机制会定位具体URL); @RequestMapping定义了Feign需要访问的SERVICE-HELLOWORLD服务的URL(本例中为根“/”)

    复制代码
     1 package springcloud.helloworld.feign.service;
     2 
     3 import org.springframework.cloud.netflix.feign.FeignClient;
     4 import org.springframework.web.bind.annotation.RequestMapping;
     5 import org.springframework.web.bind.annotation.RequestMethod;
     6 
     7 @FeignClient(value = "SERVICE-HELLOWORLD")
     8 public interface HelloWorldService {
     9     @RequestMapping(value = "/",method = RequestMethod.GET)
    10     String sayHello();
    11 }
    复制代码

    Spring Cloud应用在启动时,Feign会扫描标有@FeignClient注解的接口,生成代理,并注册到Spring容器中。生成代理时Feign会为每个接口方法创建一个RequetTemplate对象,该对象封装了HTTP请求需要的全部信息,请求参数名、请求方法等信息都是在这个过程中确定的,Feign的模板化就体现在这里

    5. 定义一个WebController。

    注入之前通过@FeignClient定义生成的bean, 

    sayHello()映射到http://localhost:8902/hello, 在这里,我修改了Hello World服务的映射,将根“/”, 修改成了“/hello”。

    复制代码
     1 package springcloud.helloworld.feign.service;
     2 
     3 import org.springframework.beans.factory.annotation.Autowired;
     4 import org.springframework.web.bind.annotation.RequestMapping;
     5 import org.springframework.web.bind.annotation.RequestMethod;
     6 import org.springframework.web.bind.annotation.RestController;
     7 
     8 @RestController
     9 public class WebController {
    10     @Autowired HelloWorldService helloWorldFeignService;
    11     @RequestMapping(value = "/hello",method = RequestMethod.GET)
    12     public String sayHello(){
    13         return helloWorldFeignService.sayHello();
    14     }
    15 }
    复制代码

    6. 启动Feign应用, 访问http://localhost:8902/hello, 多次刷新,可以看到和前一章Ribbon里面的应用一样, 两个Hello World服务的输出交替出现。说明通过Feign访问服务, Spring Cloud已经缺省使用了Ribbon负载均衡。

                    

    6. 在Feign中使用Apache HTTP Client

    Feign在默认情况下使用的是JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址gwai会保持一个长连接,即利用HTTP的persistence connection 。我们可以用Apache的HTTP Client替换Feign原始的http client, 从而获取连接池、超时时间等与性能息息相关的控制能力。Spring Cloud从Brixtion.SR5版本开始支持这种替换,首先在项目中声明Apache HTTP Client和feign-httpclient依赖:

    复制代码
     1 <!-- 使用Apache HttpClient替换Feign原生httpclient -->
     2         <dependency>
     3             <groupId>org.apache.httpcomponents</groupId>
     4             <artifactId>httpclient</artifactId>
     5         </dependency>
     6         <dependency>
     7             <groupId>com.netflix.feign</groupId>
     8             <artifactId>feign-httpclient</artifactId>
     9             <version>${feign-httpclient}</version>
    10         </dependency>
    复制代码
    然后在application.properties中添加:
    feign.httpclient.enabled=true

     7. Feign的Encoder、Decoder和ErrorDecoder

    Feign将方法签名中方法参数对象序列化为请求参数放到HTTP请求中的过程,是由编码器(Encoder)完成的。同理,将HTTP响应数据反序列化为Java对象是由解码器(Decoder)完成的。默认情况下,Feign会将标有@RequestParam注解的参数转换成字符串添加到URL中,将没有注解的参数通过Jackson转换成json放到请求体中。注意,如果在@RequetMapping中的method将请求方式指定为POST,那么所有未标注解的参数将会被忽略,例如:

    @RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET)
    void update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName, DataObject obj);

    此时因为声明的是GET请求没有请求体,所以obj参数就会被忽略。

    在Spring Cloud环境下,Feign的Encoder只会用来编码没有添加注解的参数。如果你自定义了Encoder, 那么只有在编码obj参数时才会调用你的Encoder。对于Decoder, 默认会委托给SpringMVC中的MappingJackson2HttpMessageConverter类进行解码。只有当状态码不在200 ~ 300之间时ErrorDecoder才会被调用。ErrorDecoder的作用是可以根据HTTP响应信息返回一个异常,该异常可以在调用Feign接口的地方被捕获到。我们目前就通过ErrorDecoder来使Feign接口抛出业务异常以供调用者处理。

  • 相关阅读:
    Ros与Vrep平台搭建
    场景采集难点
    写给师弟师妹论文排版秘籍
    采集项目笔记2
    采集项目记录1
    NLP&Python笔记——nltk模块基础操作
    Hash算法(含python实现)
    Python学习笔记——Socket通信
    Python学习笔记——GIF倒放处理
    OSError: [WinError 126] 找不到指定的模块 —— 解决办法
  • 原文地址:https://www.cnblogs.com/7788IT/p/11324279.html
Copyright © 2011-2022 走看看