Feign
微服务应用中,ribbon 和 hystrix 总是同时出现,feign 整合了两者,并提供了声明式消费者客户端
总的来说就是集成工具,集成了
- 远程调用
- ribbon
- hystrix
声明式的客户端
只需要声明一个抽象的接口,就可以通过接口方法调用远程服务
// 调用商品服务的声明式客户端接口
// 需要配置三条:调用哪个服务,调用这个服务的哪个路径,向这个路径提交什么参数
@FeignClient(name="item-service")//服务id
public interface ItemFeignClient {
@GetMapping("/{orderId}")//请求路径
JsonResult> getItems(@PathVariable String orderId);//传递参数
}
feign 利用了我们熟悉的 spring mvc 注解来对接口方法进行设置,方便我们使用
实现
创建feign项目 -->
添加依赖actuator/eureka client/feign/spring web -->
application.yml添加
spring: application: name: feign
server: port: 3001
eureka: client: service-url: defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
-->
主程序添加@EnableFeignClients
-->
按业务添加声明式客户端:
@FeignClient("item-service")
public interface ItemFeignService {
@GetMapping("/{orderId}")
JsonResult> getItems(@PathVariable String orderId);
@PostMapping("/decreaseNumber")
JsonResult decreaseNumber(@RequestBody List- items);
}
@FeignClient("user-service")
public interface UserFeignService {
@GetMapping("/{userId}")
JsonResult getUser(@PathVariable Integer userId);
// 拼接路径 /{userId}/score?score=新增积分
@GetMapping("/{userId}/score")
JsonResult addScore(@PathVariable Integer userId, @RequestParam Integer score);
}
注意:由于@GetMapping等注解是由feign去解析,而不是springmvc解析,所以可能会因为环境原因无法拿到请求所传的参数,需要加上@RequestParam
注解修饰参数,另外如果请求参数名与方法参数名不同,@RequestParam
不能省略,并且要指定请求参数名:@RequestParam("score") Integer s
-->
添加controller类,注入声明式客户端,将请求一一接受
@FeignClient("order-service")
public interface OrderFeignService {
@GetMapping("/{orderId}")
JsonResult getOrder(@PathVariable String orderId);
@GetMapping("/")
JsonResult addOrder();
}
@RestController
public class FeignController {
@Autowired
private ItemFeignService itemService;
@Autowired
private UserFeignService userService;
@Autowired
private OrderFeignService orderService;
@GetMapping("/item-service/{orderId}")
public JsonResult> getItems(@PathVariable String orderId) {
return itemService.getItems(orderId);
}
@PostMapping("/item-service/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List- items) {
return itemService.decreaseNumber(items);
}
@GetMapping("/user-service/{userId}")
public JsonResult
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 getOrder(@PathVariable String orderId) {
return orderService.getOrder(orderId);
}
@GetMapping("/order-service")
public JsonResult addOrder() {
return orderService.addOrder();
}
}
feign整合ribbon
- 无需额外配置,feign 默认已启用了 ribbon 负载均衡和重试机制。可以通过配置对参数进行调整
重试的默认配置参数:
ConnectTimeout=1000
ReadTimeout=1000
MaxAutoRetries=0
MaxAutoRetriesNextServer=1
虽然一般无需额外配置,但若是需要配置的话,可如下分别配置全局和局部:
application.yml 配置 ribbon 超时和重试
ribbon.xxx
全局配置item-service.ribbon.xxx
对特定服务实例的配置
Feign 集成 Hystrix,添加降级代码
feign 默认没有启用 hystrix,添加配置,启用 hystrix
基础配置
- 添加完整hystrix依赖
- feign.hystrix.enabled=true
- 启动类上加@EnableCircuitBreaker注解
添加降级代码
- @FeignClient(name="服务id",
fallback=降级类.class
) - 降级类需要实现声明式客户端接口,实现它的抽象方法
- 添加 @Component
例如下列代码:
@Component
public class ItemFeignServiceFB implements ItemFeignService {
@Override
public JsonResult> getItems(String orderId) {
return JsonResult.err("无法获取订单商品列表");
}
@Override
public JsonResult decreaseNumber(List- items) {
return JsonResult.err("无法修改商品库存");
}
}
暴露监控端点
- 添加 actuator 依赖
- 暴露 hystrix.stream 监控端点
m.e.w.e.i=hystrix.stream
management:
endpoints:
web:
exposure:
include: hystrix.stream
在order订单中添加feign调用item/user
1.添加依赖
右键点击项目编辑起步依赖,添加以下依赖:
- actuator
- feign
- hystrix
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.2.1.RELEASE
cn.tedu
sp04-orderservice
0.0.1-SNAPSHOT
sp04-orderservice
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
cn.tedu
sp01-commons
0.0.1-SNAPSHOT
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.boot
spring-boot-maven-plugin
org.springframework.cloud
spring-cloud-dependencies
Hoxton.RELEASE
pom
import
2.基本配置
application.yml配置:
ribbon 重试和 hystrix 超时这里没有设置,采用了默认值
spring:
application:
name: order-service
server:
port: 8201
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
feign:
hystrix:
enabled: true
management:
endpoints:
web:
exposure:
include: hystrix.stream
启动类配置注解:
@EnableCircuitBreaker//hystrix
@EnableFeignClients//feign
@SpringBootApplication
public class OrderserviceApplication {
public static void main(String[] args) {
SpringApplication.run(Sp04OrderserviceApplication.class, args);
}
3.定义声明式接口
定义item/user声明式接口供order调用
@FeignClient(name="",fallback=)/@GetMapping/参数列表(@PathVariable/@RequestBody)
@FeignClient(name="item-service", fallback = ItemFeignServiceFB.class)
public interface ItemFeignService {
@GetMapping("/{orderId}")
JsonResult> getItems(@PathVariable String orderId);
@PostMapping("/decreaseNumber")
JsonResult decreaseNumber(@RequestBody List- items);
}
@FeignClient(name="user-service", fallback = UserFeignServiceFB.class)
public interface UserFeignService {
@GetMapping("/{userId}")
JsonResult getUser(@PathVariable Integer userId);
@GetMapping("/{userId}/score")
JsonResult addScore(@PathVariable Integer userId, @RequestParam Integer score);
}
4.定义降级方法
仅作模拟,模拟使用缓存数据
@Component/实现重写方法
@Component
public class ItemFeignServiceFB implements ItemFeignService {
@Override
public JsonResult> getItems(String orderId) {
if(Math.random()<0.5) {
return JsonResult.ok().data(
Arrays.asList(new Item[] {
new Item(1,"缓存aaa",2),
new Item(2,"缓存bbb",1),
new Item(3,"缓存ccc",3),
new Item(4,"缓存ddd",1),
new Item(5,"缓存eee",5)
})
);
}
return JsonResult.err("无法获取订单商品列表");
}
@Override
public JsonResult decreaseNumber(List- items) {
return JsonResult.err("无法修改商品库存");
}
}
@Component
public class UserFeignServiceFB implements UserFeignService {
@Override
public JsonResult getUser(Integer userId) {
if(Math.random()<0.4) {
return JsonResult.ok(new User(userId, "缓存name"+userId, "缓存pwd"+userId));
}
return JsonResult.err("无法获取用户信息");
}
@Override
public JsonResult addScore(Integer userId, Integer score) {
return JsonResult.err("无法增加用户积分");
}
}
5.orderServiceImpl完成远程调用
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Autowired
private ItemClient itemClient;
@Autowired
private UserClient userClient;
@Override
public Order getOrder(String orderId) {
//调用user-service获取用户信息 user
JsonResult user = userClient.getUser(7);
//调用item-service获取商品信息 items
JsonResult> items = itemClient.getItems(orderId);
Order order = new Order();
order.setId(orderId);
order.setUser(user.getData());
order.setItems(items.getData());
return order;
}
@Override
public void addOrder(Order order) {
//调用item-service减少商品库存
itemClient.decreaseNumber(order.getItems());
//调用user-service增加用户积分
userClient.addScore(order.getUser().getId(), 100);
log.info("保存订单:"+order);
}
}