文章目录
1.基本使用
Spring Cloud OpenFeign 是声明式的服务调用组件,它整合了 Spring Cloud Ribbon 和Spring Cloud Hystrix,拥有负载均衡和服务容错的功能,并且提供了一种声明式的Web服务客户端定义方式。一开始这个组件不叫这个名字,一开始就叫 Feign,但是 Netflix 中的组件现在已经停止开源工作,OpenFeign 是 Spring Cloud 团队在 Netflix Feign 的基础上开发出来的声明式服务调用组件。
在Spring Cloud OpenFeign 的实现下,我们只需要创建一个接口并用注解的方式来配置它,即可完成对服务提供者的接口绑定,简化了使用Spring Cloud Ribbon时需要自行封装服务调用客户端的开发量。下面简单示例如何使用Spring Cloud OpenFeign 。
1. 创建注册中心
新建Spring Boot项目,添加如下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
在 application.properties 中添加配置
#当前服务的名字
spring.application.name=eureka
#端口号(Eureka后台管理端端口)
server.port=1111
#默认情况下,Eureka Server也是一个普通的微服务,所以当它还是注册中心时,就有两层身份:
#1.注册中心;2.普通服务。默认当前eureka server自动把自己注册到注册中心中,
# 通过eureka.client.register-with-eureka=false设置不注册
eureka.client.register-with-eureka=false
#表示是否从Eureka Server上获取注册信息
eureka.client.fetch-registry=false
启动类上添加注解
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
2. 创建服务提供者
新建Spring Boot项目,添加如下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
在 application.properties 中添加配置
#服务名
spring.application.name=provider
#服务端口号
server.port=1113
#注册中心url地址
eureka.client.service-url.defaultZone=http://localhost:1111/eureka
创建Controller类,提供/hello接口给其他服务调用
@RestController
public class HelloController {
@Value("${server.port}")
Integer port;
@GetMapping("/hello")
public String hello(){
return "hello world port:"+port;
}
}
3. 创建服务调用者
新建Spring Boot项目,添加如下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
在启动类上添加==注解@EnableFeignClients ==来启用 Feign 的客户端功能
@SpringBootApplication
@EnableFeignClients
public class OpenfeignApplication {
public static void main(String[] args) {
SpringApplication.run(OpenfeignApplication.class, args);
}
}
在 application.properties 中添加配置,使项目注册到 Eureka 上:
spring.application.name=openfeign
server.port=4000
eureka.client.service-url.defaultZone=http://localhost:1111/eureka
定义接口,使用@FeignClient注解,指定要调用的服务为provider
@FeignClient(value = "provider")
public interface HelloService {
/**
* 请求provider的“/hello接口”
* @return
*/
@GetMapping("/hello")
public String hello();
}
创建controller,使用上面的open feign接口
@RestController
public class HelloController {
@Autowired
HelloService helloService;
@GetMapping("/hello")
public String hello(){
return helloService.hello();
}
}
启动Open Feign项目,浏览器访问/hello接口
2. 参数传递
服务提供者provider提供以下接口:
@RestController
public class HelloController {
@Value("${server.port}")
Integer port;
@GetMapping("/hello")
public String hello(){
return "hello world port:"+port;
}
@GetMapping("/test_get")
public String hell02(String name){
System.out.println(new Date()+"-"+name);
return "Hello "+name;
}
/**
* 测试post,接收key/value形式的参数
*/
@PostMapping("/user1")
public User addUser(User user){
return user;
}
/**
* 测试post,接收json形式的参数
*/
@PostMapping("/user2")
public User addUser2(@RequestBody User user){
return user;
}
/**
* 测试put,接收key/value形式的参数
* @param user
*/
@PutMapping("/user1")
public void updateUser(User user){
System.out.println(user);
}
/**
* 测试put,接收json格式的参数
* @param user
*/
@PutMapping("/user2")
public void updateUser2(@RequestBody User user){
System.out.println(user);
}
/**
* 测试delete,接收key/value
* @param id
*/
@DeleteMapping("/user1")
public void deleteUser1(Integer id){
System.out.println(id);
}
/**
* 测试delete,在路径上直接写参数
* @param id
*/
@DeleteMapping("/user2/{id}")
public void deleteUser2(@PathVariable Integer id){
System.out.println(id);
}
@GetMapping("/user3")
public void getUserByName(@RequestHeader("name") String name) throws UnsupportedEncodingException {
System.out.println("get header property of name:"+ URLDecoder.decode(name,"UTF-8"));
}
}
使用OpenFeign,方法参数一定要绑定参数名(凡是 key/value 形式的参数,一定要标记参数的名称。),如果通过header来传递参数,中文需要转码。不同参数类型传递示例:
@FeignClient(value = "provider")
public interface HelloService {
/**
* 请求provider的“/hello接口”
* @return
*/
@GetMapping("/hello")
public String hello();
/**
* 请求带key/value的方法
* @param name
* @return
*/
@GetMapping("/test_get")
public String hello2(@RequestParam("name") String name);
/**
* 请求“/user2”接口,参数为json格式的
* @param user
* @return
*/
@PostMapping("/user2")
public User insertUser(@RequestBody User user);
/**
* 测试“/user2/{id}”
* 注意@PathVariable("id")
* @param id
*/
@DeleteMapping("/user2/{id}")
public void deleteUserById(@PathVariable("id") Integer id);
@PutMapping("/user1")
public void updateUser(@RequestParam("id") Integer id,@RequestParam("username") String username,@RequestParam("password") String password);
/**
* @RequestHeader的用法
* 测试注解在Header中传递参数
* @param name
*/
@GetMapping("/user3")
public void getUserByName(@RequestHeader("name") String name);
}
在HelloController中进行调用测试
@RestController
public class HelloController {
@Autowired
HelloService helloService;
@GetMapping("/hello")
public String hello(){
return helloService.hello();
}
/**
* 测试各类请求的远程调用
*/
@GetMapping("/test_param")
public void testParam() throws UnsupportedEncodingException {
User user = new User();
user.setId(1);
user.setUsername("zhang2");
user.setPassword("321");
User user1 = helloService.insertUser(user);
helloService.deleteUserById(2);
helloService.updateUser(2,"Ethan","321");
helloService.getUserByName(URLEncoder.encode("不会吧","UTF-8"));
System.out.println(user1);
}
}
浏览器请求/test_param查看调用结果:
3. 日志配置
OpenFeign 中,我们可以通过配置日志,来查看整个请求的调用过程。日志级别一共分为四种:
- NONE:不开启日志,默认就是这个级别
- BASIC:记录请求方法、URL、响应状态码、执行时间
- HEADERS:在 BASIC 的基础上,增加记录请求/响应的头信息
- FULL:在 HEADERS 基础上,再增加请求和响应的body正文及元数据等。
OpenFeign日志的四种级别,可以通过Bean来配置:
@SpringBootApplication
@EnableFeignClients
public class OpenfeignApplication {
public static void main(String[] args) {
SpringApplication.run(OpenfeignApplication.class, args);
}
@Bean
Logger.Level loggerLevel(){
return Logger.Level.FULL;
}
}
最后,需要在applicaiton.properties中开启日志级别:
logging.level.top.javahai.openfeign=debug
其中top.javahai.openfeign是包名,重启OpenFeign测试是否打印。
4. 数据压缩配置
OpenFeign请求的数据压缩配置
# 开启请求的数据压缩,开启GZIP配置
feign.compression.request.enabled=true
# 开启响应的数据压缩,开启GZIP配置
feign.compression.response.enabled=true
# 压缩的数据类型,默认值为text/html,application/xml,application/json
feign.compression.request.mime-types=text/html,application/xml,application/json
# 压缩的数据下限,2048 表示当要传输的数据大于 2048 时,才会进行数据压缩,默认值为2048
feign.compression.request.min-request-size=2048
5. 服务降级配置
在Open Feign中可以使用Hystrix实现服务容错和降级的功能。首先在application.properties中配置开启Hystrix
#开启Hystric
feign.hystrix.enabled=true
5.1 配置fallback属性
首先定义服务降级的方法,实现HelloService。
@Component
@RequestMapping("/error")
public class HelloServiceFallback implements HelloService {
@Override
public String hello() {
return "error";
}
@Override
public String hello2(String name) {
return "error2";
}
@Override
public User insertUser(User user) {
return new User();
}
@Override
public void deleteUserById(Integer id) {
System.out.println("error");
}
@Override
public void updateUser(Integer id, String username, String password) {
System.out.println("error");
}
@Override
public void getUserByName(String name) {
System.out.println("error");
}
}
在HelloService中配置服务降级类
@FeignClient(value = "provider",fallback = HelloServiceFallback.class)
public interface HelloService {
}
5.2 配置FallbackFactory属性
或者可以通过配置FallbackFactory来实现服务降级
@Component
public class HelloServiceFallbackFactory implements FallbackFactory<HelloService> {
@Override
public HelloService create(Throwable throwable) {
return new HelloService() {
@Override
public String hello() {
return "error";
}
@Override
public String hello2(String name) {
return "error2";
}
@Override
public User insertUser(User user) {
return null;
}
@Override
public void deleteUserById(Integer id) {
}
@Override
public void updateUser(Integer id, String username, String password) {
}
@Override
public void getUserByName(String name) {
}
};
}
}
然后HelloService 中进行配置:
@FeignClient(value = "provider",fallbackFactory = HelloServiceFallbackFactory.class)
public interface HelloService {
}
5.3 Hystrix全局配置
常用全局配置如下:
hystrix:
command:#用于控制HystrixCommand的行为
default:
execution:
isolation:
strategy:THREAD#控制HystrixCommand的隔离策略,THREAD->线程池隔离策略(默认),SEMAPHORE->信号量隔离策略
thread:
timeoutInMilliseconds:1000#配置HystrixCommand执行的超时时间,执行超过该时间会进行服务降级处理
interruptOnTimeout:true#配置HystrixCommand执行超时的时候是否要中断
interruptOnCancel:true#配置HystrixCommand执行被取消的时候是否要中断
timeout:
enabled:true#配置HystrixCommand的执行是否启用超时时间
semaphore:
maxConcurrentRequests:10#当使用信号量隔离策略时,用来控制并发量的大小,超过该并发量的请求会被拒绝
fallback:
enabled:true#用于控制是否启用服务降级
circuitBreaker:#用于控制HystrixCircuitBreaker的行为
enabled:true#用于控制断路器是否跟踪健康状况以及熔断请求
requestVolumeThreshold:20#超过该请求数的请求会被拒绝
forceOpen:false#强制打开断路器,拒绝所有请求
forceClosed:false#强制关闭断路器,接收所有请求
requestCache:
enabled:true#用于控制是否开启请求缓存
collapser:#用于控制HystrixCollapser的执行行为
default:
maxRequestsInBatch:100#控制一次合并请求合并的最大请求数
timerDelayinMilliseconds:10#控制多少毫秒内的请求会被合并成一个
requestCache:
enabled:true#控制合并请求是否开启缓存
threadpool:#用于控制HystrixCommand执行所在线程池的行为
default:
coreSize:10#线程池的核心线程数
maximumSize:10#线程池的最大线程数,超过该线程数的请求会被拒绝
maxQueueSize:-1#用于设置线程池的最大队列大小,-1采用SynchronousQueue,其他正数采用LinkedBlockingQueue
queueSizeRejectionThreshold:5#用于设置线程池队列的拒绝阀值,由于LinkedBlockingQueue不能动态改版大小,使用时需要用该参数来控制线程数
实例配置只需要将全局配置中的default换成与之对应的key即可。
hystrix:
command:
HystrixComandKey:#将default换成HystrixComrnandKey
execution:
isolation:
strategy:THREAD
collapser:
HystrixCollapserKey:#将default换成HystrixCollapserKey
maxRequestsInBatch:100
threadpool:
HystrixThreadPoolKey:#将default换成HystrixThreadPoolKey
coreSize:10
配置文件中相关key的说明
- HystrixComandKey对应@HystrixCommand中的commandKey属性;
- HystrixCollapserKey对应@HystrixCollapser注解中的collapserKey属性;
- HystrixThreadPoolKey对应@HystrixCommand中的threadPoolKey属性。
6. Ribbon配置
6.1 全局配置
Open Feign 整合了 Ribbon 实现负载均衡,在Open Feign 中可以直接使用 Ribbon 的配置。
#服务请求连接超时时间(毫秒)
ribbon.ConnectTimeout=1000
#服务请求处理超时时间(毫秒)
ribbon.ReadTimeout=3000
#对超时请求启用重试机制
ribbon.OkToRetryOnAllOperations=true
#切换重试实例的最大个数
ribbon.MaxAutoRetriesNextServer=1
# 切换实例后重试最大次数
ribbon.MaxAutoRetries=1
#修改负载均衡策略
ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
在重试策略的配置中,如果MaxAutoRetries设置为1的话,Feign客户端会先尝试访问首选实例一次,失败后才更换实例访问,而更换实例访问的次数由MaxAutoRetriesNextServer参数决定,如果MaxAutoRetriesNextServer设置为2,则会尝试更换两次实例进行重试。
需要注意的是,Ribbon的超时和Hystrix的超时是两个概念。我们需要设置Hystrix的超时时间大于Ribbon的超时时间,否则Hystrix命令超时时,该命令直接熔断,则重试机制的配置就失去意义了。
6.2 负载均衡策略配置
最后一点负载均衡策略配置的选择,Ribbon提供了如下几种负载均衡策略:
- com.netflix.loadbalancer.RandomRule:从提供服务的实例中以随机的方式;
- com.netflix.loadbalancer.RoundRobinRule:以线性轮询的方式,就是维护一个计数器,从提供服务的实例中按顺序选取,第一次选第一个,第二次选第二个,以此类推,到最后一个以后再从头来过;
- com.netflix.loadbalancer.RetryRule:在RoundRobinRule的基础上添加重试机制,即在指定的重试时间内,反复使用线性轮询策略来选择可用实例;
- com.netflix.loadbalancer.WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择;
- com.netflix.loadbalancer.BestAvailableRule:选择并发较小的实例;
- com.netflix.loadbalancer.AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例;
- com.netflix.loadbalancer.ZoneAwareLoadBalancer:采用双重过滤,同时过滤不是同一区域的实例和故障实例,选择并发较小的实例。
6.3 指定服务配置
我们可以采用< client >.ribbon.key=value的格式进行设置各个服务的配置,其中client就是在FeignClient的name属性的名称,例如指定对provider服务提供的接口单独配置:
#服务请求连接超时时间(毫秒)
provider.ribbon.ConnectTimeout=1000
在定义Feign客户端的时候,我们使用了@FeignClient注解,在初始化过程中Spring Cloud Feign会根据该注解的name属性或value属性指定的服务名,自动创建一个同名的Ribbon客户端,所以我们就可以使用@FeignClient注解的name或value属性值来设置对应的Ribbon参数了。
参考:
1.《Spring Cloud微服务实战》
2.Spring Cloud OpenFeign:基于 Ribbon 和 Hystrix 的声明式服务调用
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/44269.html