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

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

发布评论

分享到:

IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

Spring Cloud 入门教程(七): 熔断机制 -- 断路器详解
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。