要做一件事, 需要知道三个要素,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.cloudgroupId>
<artifactId>spring-cloud-starter-feignartifactId>
<dependency>
完整的 pom 文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.chry</groupId>
<artifactId>springcloud.helloworld.feign.client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud.helloworld.feign.client</name>
<description>Demo Feigh client application</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.RC1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
2. 创建启动类
需加上 @EnableFeignClients 注解以使用 Feign, 使用 @EnableDiscoveryClient 开启服务自动发现
3. 添加配置文件 application.yml, 使用端口 8902, 名字定义为 service-feign, 并注册到 eureka 服务中心
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8902
spring:
application:
name: service-feign
4. 定义 Feign:一个用 @FeignClient 注解的接口类
@FeignClient
用于通知 Feign 组件对该接口进行代理(不需要编写接口实现),使用者可直接通过 @Autowired
注入; 该接口通过 value 定义了需要调用的 SERVICE-HELLOWORLD 服务(通过服务中心自动发现机制会定位具体 URL); @RequestMapping 定义了 Feign 需要访问的 SERVICE-HELLOWORLD 服务的 URL(本例中为根“/”)
5. 定义一个 WebController
注入之前通过 @FeignClient 定义生成的 bean,
sayHello()映射到 http://localhost:8902/hello, 在这里,我修改了 Hello World 服务的映射,将根“/”, 修改成了“/hello”
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
依赖:
<!-- 使用Apache HttpClient替换Feign原生httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>${feign-httpclient}</version>
</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 接口抛出业务异常以供调用者处理。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于