1.Feign
为服务应用中,ribbon和hystrix同时出现,feign整合两者,并提供了声明式消费者客户端.
feign是一种集成工具功能有:远程调用,ribbon-负载均衡和重试,hystrix-降级 熔断
1.1Feign远程调用
Fegin提供了声明式客户端,只需要定义一个接口,就可以通过接口做远程调用.具体调用代码通过动态代理添加.
// 调用商品服务的远程调用接口
// 通过注解配置3件事:调用哪个服务,调用什么路径,向这个路径提交什么参数
@FeignClient(name="item-service")
public interface ItemFeignClient {
@GetMapping("/{orderId}")
JsonResult<List<Item>> getItems(@PathVariable String orderId);
}
1.1.1添加声明式客户端
1.OpenFegin依赖
2.启动类添加@EnableFeignClient
3.定义声明式客户端接口 例如:ItemFeignClient UserFeignClient OrderFeignClient
4.添加一个测试用的控制器,使用三个声明式客户端接口调用远程服务.
1.2Feign集成Ribbon
负载均衡 重试
Feign默认启用了Ribbon的负载均衡和重试 0配置.
默认重试参数:MaxAutoRetries=0 MaxAutoRetriesNextServer=1 ReadTimeout=1000
重试参数配置
# 对所有服务都有效
ribbon:
MaxAutoRetries: 1
# 对 item-service 单独配置,对其他服务无效
item-service:
ribbon:
MaxAutoRetries: 0
1.3Feign集成Hystrix
默认不启用hystrix,Feign不推荐启用hystrix
1.3.1如果有特殊需求要启用hystrix,首先做基础配置
1.添加hystrix完整依赖
2.添加@EnableCircuitBreaker
3.yml配置feign.hystrix.enable=true
1.3.2降级
在声明式客户端接口的注解中,制定一个降级类(之前的是降级方法)
@FeignClient(name="item-service", fallback=降级类.class)
public interface ItemFeignClient {
....
}
降级类要作为声明式客户端接口的子类来定义.
1.3.3hystrix监控
用actuator暴露hystrix.stream监控端点
1.actuator依赖
2.暴露监控端点
m.e.w.e.i=hystrix.stream
3.http://localhost:3001/actuator/hystrix.stream
4.通过调用后台服务,产生监控数据.
1.4熔断
10秒内20次请求,50%失败执行了降级代码.
2.代码展示
用feign代替hystrix+ribbon
2.1新建fegin项目
2.2pom.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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.tedu</groupId>
<artifactId>sp09-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sp09-feign</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<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>
<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>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</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>
</project>
2.3application.yml
spring:
application:
name: feign
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
2.4主程序添加@EnableDiscoveryClient和@EnableFeignClients注解
package cn.tedu.sp09;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Sp09FeignApplication {
public static void main(String[] args) {
SpringApplication.run(Sp09FeignApplication.class, args);
}
}
2.5.1 ItemFeignService
package cn.tedu.sp09.service;
import java.util.List;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
@FeignClient("item-service")
public interface ItemFeignService {
@GetMapping("/{orderId}")
JsonResult<List<Item>> getItems(@PathVariable String orderId);
@PostMapping("/decreaseNumber")
JsonResult decreaseNumber(@RequestBody List<Item> items);
}
2.5.2 UserFeignService
package cn.tedu.sp09.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
@FeignClient("user-service")
public interface UserFeignService {
@GetMapping("/{userId}")
JsonResult<User> getUser(@PathVariable Integer userId);
// 拼接路径 /{userId}/score?score=新增积分
@GetMapping("/{userId}/score")
JsonResult addScore(@PathVariable Integer userId, @RequestParam Integer score);
}
2.5.3 UserFeignService
package cn.tedu.sp09.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.web.util.JsonResult;
@FeignClient("order-service")
public interface OrderFeignService {
@GetMapping("/{orderId}")
JsonResult<Order> getOrder(@PathVariable String orderId);
@GetMapping("/")
JsonResult addOrder();
}
2.5.4 FeignController
package cn.tedu.sp09.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.sp09.service.ItemFeignService;
import cn.tedu.sp09.service.OrderFeignService;
import cn.tedu.sp09.service.UserFeignService;
import cn.tedu.web.util.JsonResult;
@RestController
public class FeignController {
@Autowired
private ItemFeignService itemService;
@Autowired
private UserFeignService userService;
@Autowired
private OrderFeignService orderService;
@GetMapping("/item-service/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
return itemService.getItems(orderId);
}
@PostMapping("/item-service/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List<Item> items) {
return itemService.decreaseNumber(items);
}
/
@GetMapping("/user-service/{userId}")
public JsonResult<User> getUser(@PathVariable Integer userId) {
return userService.getUser(userId);
}
@GetMapping("/user-service/{userId}/score")
public JsonResult addScore(@PathVariable Integer userId, Integer score) {
return userService.addScore(userId, score);
}
/
@GetMapping("/order-service/{orderId}")
public JsonResult<Order> getOrder(@PathVariable String orderId) {
return orderService.getOrder(orderId);
}
@GetMapping("/order-service")
public JsonResult addOrder() {
return orderService.addOrder();
}
}
2.6调用流程
2.7启动服务并测试
http://localhost:3001/item-service/35
http://localhost:3001/user-service/7
http://localhost:3001/user-service/7/score?score=100
http://localhost:3001/order-service/123abc
http://localhost:3001/order-service/
源码: https://github.com/benwang6/spring-cloud-repo
3.Feign+Ribbon负载均衡和重试
无需额外配置,feign默认已启用了ribbon负载均衡和重试机制,可以通过配置对参数进行调整.
重试的默认配置参数
ConnectTimeout=1000
ReadTimeout=1000
MaxAutoRetries=0
MaxAutoRetriesNextServer=1
3.1application.yml配置ribbon超时和重试
spring:
application:
name: feign
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
ribbon:
ConnectTimeout: 1000
ReadTimeout: 1000
item-service:
ribbon:
MaxAutoRetries: 1
MaxAutoRetriesNextServer: 2
ConnectTimeout: 1000
ReadTimeout: 500
3.2启动服务,访问测试
http://localhost:3001/item-service/35
4.Feign+hystrix降级
feign默认没有启用hystrix,添加配置,启用hystrix
4.1application.yml配置
feign:
hystrix:
enabled: true
启用后访问服务器:
http://localhost:3001/item-service/35
默认一秒会快速失败,没有降级方法,会显示白半页.
可以添加配置暂时减少降级超时时间,以便后续对降级进行测试
......
feign:
hystrix:
enabled: true
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 500
4.feign+hystrix降级
4.1feign远程接口中指定降级类
远程调用失败后会执行降级代码
4.2指定降级类
4.2.1 ItemFeignService
...
@FeignClient(name="item-service", fallback = ItemFeignServiceFB.class)
public interface ItemFeignService {
...
4.2.2UserFeignService
...
@FeignClient(name="user-service", fallback = UserFeignServiceFB.class)
public interface UserFeignService {
...
4.2.3OrderFeignService
...
@FeignClient(name="order-service",fallback = OrderFeignServiceFB.class)
public interface OrderFeignService {
...
4.3降级类
4.3.1 ItemFeignServiceFB
package cn.tedu.sp09.service;
import java.util.List;
import org.springframework.stereotype.Component;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
@Component
public class ItemFeignServiceFB implements ItemFeignService {
@Override
public JsonResult<List<Item>> getItems(String orderId) {
return JsonResult.err("无法获取订单商品列表");
}
@Override
public JsonResult decreaseNumber(List<Item> items) {
return JsonResult.err("无法修改商品库存");
}
}
4.3.2UserFeignServiceFB
package cn.tedu.sp09.service;
import org.springframework.stereotype.Component;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
@Component
public class UserFeignServiceFB implements UserFeignService {
@Override
public JsonResult<User> getUser(Integer userId) {
return JsonResult.err("无法获取用户信息");
}
@Override
public JsonResult addScore(Integer userId, Integer score) {
return JsonResult.err("无法增加用户积分");
}
}
4.3.3 OrderFeignServiceFB
package cn.tedu.sp09.service;
import org.springframework.stereotype.Component;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.web.util.JsonResult;
@Component
public class OrderFeignServiceFB implements OrderFeignService {
@Override
public JsonResult<Order> getOrder(String orderId) {
return JsonResult.err("无法获取商品订单");
}
@Override
public JsonResult addOrder() {
return JsonResult.err("无法保存订单");
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。