• Feign 是一个声明式的 REST 客户端,它用了基于接口的注解方式,很方便实现客户端配置。
• Feign 最初由 Netflix 公司提供,但不支持SpringMVC注解,后由SpringCloud 对其封装,支持了SpringMVC注 解,让使用者更易于接受。
Feign依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
Feign调用接口:
package com.itheima.consumer.feign;
import com.itheima.consumer.FeignLogConfig;
import com.itheima.consumer.domain.Goods;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* feign声明式接口,发起远程调用
* String url = "http://FEIGN-PROVIDER/goods/findOne/"+id;
* Goods goods = restTemplate.getForObject(url, Goods.class);
*
* 1.定义接口
* 2.接口上添加注解,@FeignClient,设置属性为 服务提供者的 应用名称,日志级别配置类
* 3.编写调用接口,接口的声明规则和提供方接口保持一致
* 4.注入该接口,调用接口方法完成远程调用
*/
@FeignClient(value = "FEIGN-PROVIDER",configuration = FeignLogConfig.class)
public interface GoodsFeignClient {
@GetMapping("/goods/findOne/{id}")
public Goods findGoodsById(@PathVariable("id") int id);
}
package com.itheima.consumer.controller;
import com.itheima.consumer.domain.Goods;
import com.itheima.consumer.feign.GoodsFeignClient;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private GoodsFeignClient goodsFeignClient;
@GetMapping("/goods/{id}")
public Goods findGoodsById(@PathVariable("id") int id){
Goods goods = goodsFeignClient.findGoodsById(id);
return goods;
}
}
启动类 开启Feign功能
package com.itheima.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableDiscoveryClient // 激活DiscoveryClient
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients //开启Feign功能
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class,args);
}
}
服务调用方配置
• Feign 底层依赖于 Ribbon 实现负载均衡和远程调用。
• Ribbon默认1秒超时。
• 超时配置:
开启SpringBoot框架的debug日志
# 设置当前的日志级别 debug,feign只支持记录debug级别的日志
logging:
level:
root: debug
logging:
level:
com.itheima: debug
服务调用方配置
• Feign 只能记录 debug 级别的日志信息。
• 定义Feign日志级别Bean
服务调用方根包下定义日志级别
package com.itheima.consumer;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignLogConfig {
/**
* NONE:不记录
* BASIC:记录基本的请求行,响应状态码数据
* HEADERS:记录基本的请求行,响应状态码,记录响应头信息
* FULL:记录完整的请求 响应数据
* @return
*/
@Bean
public Logger.Level debug(){
return Logger.Level.FULL;
}
}
• 启用该Bean:
@FeignClient(configuration = XxxConfig.class)
package com.itheima.consumer.feign;
import com.itheima.consumer.FeignLogConfig;
import com.itheima.consumer.domain.Goods;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* feign声明式接口,发起远程调用
* String url = "http://FEIGN-PROVIDER/goods/findOne/"+id;
* Goods goods = restTemplate.getForObject(url, Goods.class);
*
* 1.定义接口
* 2.接口上添加注解,@FeignClient,设置属性为 服务提供者的 应用名称,日志级别配置类
* 3.编写调用接口,接口的声明规则和提供方接口保持一致
* 4.注入该接口,调用接口方法完成远程调用
*/
@FeignClient(value = "FEIGN-PROVIDER",configuration = FeignLogConfig.class)
public interface GoodsFeignClient {
@GetMapping("/goods/findOne/{id}")
public Goods findGoodsById(@PathVariable("id") int id);
}
• Hystix 是 Netflix 开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败(雪崩)。
Hystix 主要功能
• Hystix 降级:当服务发生异常或调用超时,返回默认数据
package com.itheima.provider.controller;
import com.itheima.provider.domain.Goods;
import com.itheima.provider.service.GoodsService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
/**
* Goods Controller 服务提供方
*/
@RestController
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsService goodsService;
@Value("${server.port}")
private int port;
/**
* 降级
* 1.出现异常
* 2.服务器调用超时
* @param id
* @return
*/
@GetMapping("/findOne/{id}")
//指定降级后调用的方法名称
@HystrixCommand(fallbackMethod = "findOne_fallback",commandProperties = {
//设置hystrix的超时时间,默认1秒
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public Goods findOne(@PathVariable("id") int id){
int i = 1/0;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Goods goods = goodsService.findOne(id);
goods.setTitle(goods.getTitle() + ":" + port);//将端口号,设置到了 商品标题上
return goods;
}
/**
* 定义降级方法:
* 返回值,参数和原方法一致
*/
public Goods findOne_fallback(int id){
Goods goods = new Goods();
goods.setTitle("降级了.....");
return goods;
}
}
package com.itheima.consumer.feign;
import com.itheima.consumer.domain.Goods;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "HYSTRIX-PROVIDER",fallback = GoodsFeignClientFallback.class)
public interface GoodsFeignClient {
@GetMapping("/goods/findOne/{id}")
public Goods findGoodsById(@PathVariable("id") int id);
}
package com.itheima.consumer.feign;
import com.itheima.consumer.domain.Goods;
import org.springframework.stereotype.Component;
/**
* feign的客户端降级处理类
* 1.定义类:实现feign客户端接口
* 2.使用@Component注解将该类的Bean加入到Spring中
*/
@Component
public class GoodsFeignClientFallback implements GoodsFeignClient {
@Override
public Goods findGoodsById(int id) {
Goods goods = new Goods();
goods.setTitle("又降级了...");
return goods;
}
}
# 开启feign对hystrix的支持
feign:
hystrix:
enabled: true
• Hystrix 熔断机制,用于监控微服务调用情况,当失 败的情况达到预定的阈值(5秒失败20次),会打开 断路器,拒绝所有请求,直到服务恢复正常为止。
• circuitBreaker.sleepWindowInMilliseconds:监控时间
• circuitBreaker.requestVolumeThreshold:失败次数
• circuitBreaker.errorThresholdPercentage:失败率
依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
配置文件:
server:
port: 80
spring:
application:
name: api-gateway-server
cloud:
# 网关配置
gateway:
# 路由配置:转发规则
routes: #集合
#id:唯一标识,默认是一个uuid
#uri:转发路径
#predicates:条件,用于请求网关路径的匹配规则
- id: gateway-provider
# uri: http://localhost:8001/
uri: lb://GATEWAY-PROVIDER
predicates:
- Path=/goods/**
filters:
- AddRequestParameter=username,zhangsan
- id: gateway-consumer
# uri: http://localhost:9000/
uri: lb://GATEWAY-CONSUMER
predicates:
- Path=/order/**
discovery:
locator:
enabled: true # 设置为true,请求路径前可以加微服务名称
lower-case-service-id: true # 允许小写
eureka:
client:
service-url:
defealtZone: http://localhost:8761/eureka
uri:
静态路由:http://locathost:8080/
动态路由:lb://服务名称
package com.itheima.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class MyFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("自定义全局过滤器执行了...");
return chain.filter(exchange);//放行
}
/**
* 过滤器排序
* @return 数值越小,越先执行
*/
@Override
public int getOrder() {
return 0;
}
}