1、Ribbon有什么问题?
前面的可以发现当我们通过RestTemplate调用其它服务的API时,所需要的参数须在请求的URL中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率低下,并且显得好傻,不高级不优雅。
2、什么是Feign
Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。而Feign则会完全代理HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。Feign整合了Ribbon和Hystrix(关于Hystrix我们后面再讲),feign内部也使用了ribbon做负载均衡,可以让我们不再需要显式地使用这两个组件。
我们这里想要实现的效果和之前学习Ribbon案例的效果一样:

为了不和前面的案例混淆代码,我们搭建新工程“支付服务”来演示Feign。那在这里支付服务和订单服务充当的角色一样,都是服务消费者,通过调用用户服务来获取数据。
那么支付服务需要做什么事情?思路如下:
搭建基础项目结构
注册到Eureka
集成Feign向用户服务发起调用
1、创建工程
创建工程 springcloud-pay-server-1040,用来集成Feign,那么目前项目结构如下:
springcloud-parent
springcloud-eureka-server-1010
springcloud-order-server-1030
springcloud-pay-server-1040 //支付服务用来集成Feign
springcloud-user-common
springcloud-user-server-1020
pom.xml
2、导入依赖
这里先不集成Feign,先其他基础包注册到Eureka服务端,pom.xml如下:
<?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">
<parent>
<artifactId>springcloud-parent</artifactId>
<groupId>cn.itsource</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-pay-server-1040</artifactId>
<dependencies>
<dependency>
<!--1、引入Eureka客户端依赖-->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--必须引入web基础依赖,相当于引入了tomcat-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.itsource</groupId>
<artifactId>springcloud-user-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
3、配置启动类
/**
* @description: 支付服务启动类
*/
@SpringBootApplication
@EnableEurekaClient
public class PayServerApplication1040 {
public static void main(String[] args) {
SpringApplication.run(PayServerApplication1040.class);
}
}
4、配置文件application.yml
eureka:
client:
serviceUrl: #指向Eureka服务端地址
defaultZone: http://admin:123456@peer1:1010/eureka/,http://admin:123456@peer2:1011/eureka/,http://admin:123456@peer3:1012/eureka/
instance:
prefer-ip-address: true #开启用IP注册
instance-id: pay-server:1040 #自定义实例ID
server:
port: 1040
spring:
application:
name: pay-server #服务名
OK了,此时运行Eureka服务端(我这里只运行1010这一台),然后运行支付服务,浏览器访问:http://localhost:1010,可以看到支付服务已经可以显示了,说明支付服务已经成功注册到Eureka服务端了,接下来就可以集成Feign了
1、加入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、开启Feign支持
启动类添加@EnableFeignClients标签:

3、编写Feign客户端接口
@FeignClient("user-server")
public interface UserFeignClient {
@GetMapping(value = "/user/{id}")
User getUserById(@PathVariable("id")Long id);
}
解释
UserFeignClient : 这个接口就是用来调用 user-server这个服务的Feign的客户端接口 @FeignClient("user-server") : user-server是服务的名字,我们要调用户服务,feign根据服务名能够找到 目标服务 ,根据方法上的 GetMapping的值可以找到目标服务的controller的方法
千万注意:
1.服务名一定不要写错 2.@GetMapping一定要和目标服务的controller的方法的GetMapping一样 3.方法的参数一定要和目标服务的controller的方法的参数一样 4.方法的返回值一定要和目标服务的controller的方法的返回值一样
建议
服务名直接去目标服务配置中拷贝
方法直接从目标服务controller中拷贝
4、Feign和openFeign区别
Feign:
Feign是Spring Cloud组件中的一个轻量级RESTful的HTTP服务客户端
Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务
Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务
注意:Feign本身不支持Spring MVC的注解,它有一套自己的注解
openFeign:
OpenFeign是Spring Cloud 在Feign的基础上支持了Spring MVC的注解,如@RequesMapping等等
OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务
5、编写Controller
通过注入UserFeignClient ,直接发起调用:
/**
* @description: 支付服务API
*/
@RestController
public class PayController {
@Autowired
private UserFeignClient userFeignClient;
//浏览器来调用此接口
@RequestMapping("/pay/{id}")
public User getUserById(@PathVariable("id")Long id){
//使用Feign调用用户服务获取User
return userFeignClient.getUserById(id);
}
}
6、测试
启动Eureka服务端,启动用户服务,启动支付服务,然后通过浏览器访问pay-server的controller,浏览器输入:http://localhost:1050/pay/11,效果如下:

要使用Feign,我们除了导入依赖之外,需要主配置类通过@EnableFeignClients(value="")注解开启Feign,触发程序扫描 classPath中所有被@FeignClient注解的修饰类。
同时我们需要为Feign编写客户端接口,接口上需要写上@FeignClient注解。
当程序启动,含有@FeignClient注解的所有接口,都会被扫描到然后交给Spring管理,使用JDK动态代理为其生成一个代理对象,本质上是该代理对象注册到Spring容器中。
当请求到来时,调用接口实际上就是在调用代理对象,此时,所有请求都会被转交给Feign框架,Feign会为每个方法生成一个RequestTemplate,并封装好url,请求参数等信息,以 HTTP 的形式发送出去,生成request请求,交给Http客户端(UrlConnection ,HttpClient,OkHttp)
Http客户端会交给LoadBalancerClient,使用Ribbon的负载均衡发起调用
1、负载均衡配置
Feign已经集成了Ribbon,所以它的负载均衡配置基于Ribbon配置即可,这里使用yml简单配置负载均衡策略如下:
user-server:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #随机算法
2、Feign的超时配置
如果在服务调用时出现了 “feign.RetryableException : Read timed out…”错误日志,说明Ribbon处理超时 ,我们可以配置Ribbon的超时时间:
ribbon:
ConnectTimeout: 3000 #链接超时
ReadTimeout: 6000 #读取超时
如果服务调用出现“com.netflix.hystrix.exception.HystrixRuntimeException:… timed - out and no fallback available” 错误日志,是因为Hystrix超时,默认Feign集成了Hystrix,但是高版本是关闭了Hystrix,我们可以配置Hystrix超时时间:
feign:
hystrix:
enabled: true #开启熔断支持
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000 #hystrix超时时间
1、配置Feign日志打印内容
有的时候我们需要看到Feign的调用过程中的参数及相应,我们可以对Feign的日志进行配置,Feign支持如下几种日志模式来决定日志记录内容多少:
NONE,不记录(DEFAULT)
BASIC,仅记录请求方法和URL以及响应状态代码和执行时间
HEADERS,记录基本信息以及请求和响应标头
FULL,记录请求和响应的标题,正文和元数据
创建Feign配置类
@Configuration
public class FeignConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL; //打印Feign的所有日志
}
}
2、配置日志打印级别
配置UserFeignClient的日志打印级别,上面的配置是打印Feign的哪些内容,下面这个是配置日志框架打印日志的级别,不修改可能打印不出来日志,DEBUG打印日志调试信息。
logging:
level:
cn.itsource.feign.client.UserFeignClient: debug
OK了,启动相关服务后,再次访问支付服务后,控制台打印如下:

详细调用日志全部打印出来了,开始阶段方便定位问题。
可以通过开启Feign的数据压缩传输来节省网络开销,但是压缩数据会增加CPU的开销,所以太小的数据没必要压缩,可以通过压缩大小阈值来控制,配置如下:
feign:
compression:
request:
enabled: true
min-request-size: 1024 #最小阈值,小于这个不压缩
mime-types: text/xml,application/xml,application/json #压缩哪些类型的数据
response:
enabled: true