您的位置:首页 > 理论基础 > 计算机网络

Spring Cloud 入门教程(六): 用声明式REST客户端Feign调用远端HTTP服务

2017-08-01 14:42 1926 查看
首先简单解释一下什么是声明式实现?

要做一件事, 需要知道三个要素,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>


pom.xml
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接口抛出业务异常以供调用者处理。

以上6~7完全摘自 http://blog.csdn.net/neosmith/article/details/52449921
上一篇:Spring Cloud 入门教程(五): Ribbon实现客户端的负载均衡

下一篇:Spring Cloud 入门教程(七): 断路器

参考资料: http://blog.csdn.net/neosmith/article/details/52449921 http://blog.csdn.net/forezp/article/details/69808079 http://projects.spring.io/spring-cloud/spring-cloud.html#spring-cloud-feign
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: