目录
一、OpenFeign
(一)测试OpenFeign
新建子项目cloud-consumer-feign-order80
1、pom文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fx.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
2、application.yml
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
3、主启动类
@SpringBootApplication
@EnableFeignClients
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class, args);
}
}
4、业务逻辑
新建PaymentFeignService接口:
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
/**
* 根据id获取
* @param id
* @return
*/
@GetMapping(value = "/payment/get/{id}")
CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
}
新建控制层:
@Resource
private PaymentFeignService paymentFeignService;
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
return paymentFeignService.getPaymentById(id);
}
(二)OpenFeign超时控制
在application.yml配置中添加如下配置,设置超时时间
# 设置feign客户端超时时间(OpenFeign默认集成ribbon)
ribbon:
# 建立连接所用时间,适用于网络状态正常时,连接所用时间
ConnectTimeout: 5000
# 建立连接后从服务器读取到可用资源所用时间
ReadTimeout: 5000
(三)OpenFeign日志打印
1、日志级别
NONE:默认,不显示任何日志
BASIC:仅记录请求方法、URL、响应状态码及执行时间
HEADERS:除了BASIC之外,还有请求和响应的头信息
FULL:除了HEADERS之外,还有请求和响应的正文和元数据
2、配置日志Bean
@Configuration
public class FeignConfig {
/**
* 显示全部日志
* @return
*/
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
3、配置application.yml
logging:
level:
# feign日志监控哪个接口
com.fx.springcloud.service.PaymentFeignService: debug
二、Hystrix
(一)重要概念
1、服务降级
fallback:服务器忙,请稍候再试,不让客户端等待并立即返回一个友好的提示。系统不可用时,一个兜底的解决方法。
发生服务降级的情况:
程序运行异常
超时
服务熔断出发服务降级
线程池/信号量打满导致
2、服务熔断
break:类比保险丝达到最大服务访问后,直接拒绝访问,然后调用服务降级的方法并返回友好提示
服务的降级 -> 进而熔断 -> 恢复调用链路
3、服务限流
flowlimit:在高并发等操作时,限制同一时间的访问量,让访问进行排队有序进行。
(二)构建支付微服务(测试Hystrix)
1、测试平台搭建
注:一个简单的测试用例,不做考虑,可忽略
1)构建名为cloud-provider-hystrix-payment8001的项目
2)pom文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fx.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- eureka client-->
<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-netflix-hystrix</artifactId>
</dependency>
</dependencies>
3)application.yml
server: port: 8001 spring: application: name: cloud-provider-hystrix-payment eureka: client: # 将自身注册到EurekaServer register-with-eureka: true # true,表示从EurekaServer抓取已有的注册信息 fetch-registry: true service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
4)业务类(用于测试,省略接口直接实现类)
@Service
public class PaymentService {
/**
* 执行成功的方法
* @return
*/
public String payment_Ok(Integer id) {
return "线程池:" + Thread.currentThread().getName() + " payment_Ok, id: " + id;
}
/**
* 模拟延时
* @param id
* @return
*/
public String payment_TimeOut(Integer id) {
// 暂停几秒
int time = 3;
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:" + Thread.currentThread().getName() + " payment_TimeOut, id: " + id;
}
}
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id) {
String result = paymentService.payment_Ok(id);
log.info("------------result :" + result);
return result;
}
@GetMapping(value = "/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result = paymentService.payment_TimeOut(id);
log.info("------------result :" + result);
return result;
}
}
2、测试支付模块
新建项目:
cloud-consumer-feign-hystrix-order80
1)pom文件
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.fx.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency> <!-- eureka client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- OpenFeign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
2)application.yml
server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
3) 业务类
service接口:
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {
@GetMapping(value = "/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping(value = "/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
3、服务降级
1)支付侧:8001端口的服务
@HystrixCommand(fallbackMethod = "payment_TimeOutHandler", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000") }) public String payment_TimeOut(Integer id)
在service的需要降级服务的方法上添加注解,表示如果该方法运行时间超过3秒或发生异常时,调用兜底方法“payment_TimeOutHandler”。
2)订单侧:80端口的服务
在application.yml中添加
# 启动后会将@FeignClient标记的那个service接口下所有的方法进行了hystrix包装,这些方法会应用默认超时时间为1s feign: hystrix: enabled: true # 自定义超时时间为3秒 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 3000 # 设置feign客户端超时时间(OpenFeign默认集成ribbon) ribbon: # 建立连接所用时间,适用于网络状态正常时,连接所用时间 ConnectTimeout: 5000 # 建立连接后从服务器读取到可用资源所用时间 ReadTimeout: 5000
启动类添加
@EnableHystrix
控制类修改:
@GetMapping(value = "/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
return "消费者80端口调用支付系统超时(1500ms),该支付系统繁忙请稍后再试";
}
3)全局服务降级DefaultProperties
在控制类配置全局兜底注解:
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") public class OrderHystrixController
4)通配服务降级,针对Feign接口
继承Feign接口并写降级处理的逻辑:
@Component
public class PaymentFallbackService implements PaymentHystrixService {
@Override
public String paymentInfo_OK(Integer id) {
return "PaymentFallbackService fall back paymentInfo_OK";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
return "PaymentFallbackService fall back paymentInfo_TimeOut";
}
}
Feign类修改注解:
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = PaymentFallbackService.class) public interface PaymentHystrixService
4、服务熔断
修改8001端口服务(支付模块),用以测试使用。
1)修改业务代码
// -------------服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"), // 是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), // 请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), // 经过一段时间后,下一个单个请求被允许通过(这是HALF-OPEN状态)。如果请求失败,则断路器将OPEN在睡眠窗口持续时间内返回到该状态。如果请求成功,则断路器切换到,CLOSED
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60") // 失败率达到多少后熔断
})
public String paymentCircuitBreaker(@PathVariable("Id") Integer id) {
if (id < 0) {
throw new RuntimeException("id 不能是负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "\t" + "调用成功,流水号:" + serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("Id") Integer id) {
return "id 不能为负数,请稍后再试 o(╥﹏╥)o id: " + id;
}
5、web监控页面
1)新建项目cloud-consumer-hystrix-dashboard9001
2)pom文件
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombook</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> </dependencies>
3)application.yml
server: port: 9001
4)主方法
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class, args);
}
}
注:待监控的服务都要引入依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
5)监控测试,监控8001
修改8001的启动类:添加如下内容
/**
* 此配置是为了服务监控,与服务本身没关系,SpringCloud升级后
* ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
* 只能自己在项目里配置servlet
* @return
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
注:
@EnableCircuitBreaker // 必须启动
测试监控:
输入http://localhost:9001/hystrixc,打开监控页面
在监控地址栏填入待监控服务地址:http://localhost:8001/hystrix.stream