springboot+Hystrix服务器容错保护
点关注不迷路,欢迎再来!
精简博客内容,尽量已专业术语来分享。
努力做到对每一位认可自己的读者负责。
帮助别人的同时更是丰富自己的良机。
Hystrix实现了断路器,线程隔离等一系列服务保护功能。它也是基于Netfix的开源框架Hystrix实现的,该框架的目标在于通过控制那些访问远程系统,服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备服务降级,服务熔断,线程和信号隔离,请求缓存,请求合并以及服务监控等强大功能。
一.先创建一个Eureka-Server服务注册中心
回顾上节知识:springboot集成Eureka注册中心(四)
二.创建一个EurekaClient客户端
1.pom.xml配置
<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-server</artifactId>
</dependency>
<!-- 声明调用 -->
<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文件
eureka:
client:
serviceUrl: #注册中心的注册地址
defaultZone: http://127.0.0.1:8081/eureka/
server:
port: 8082 #服务端口号
spring:
application:
name: service-client #服务名称--调用的时候根据名称来调用该服务的方法
3.配置启动类引入@EnableFeignClients
package com.sun.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* 客户端启动类
* @author ex_sunqi
*
*/
@EnableCircuitBreaker
@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
4.编写控制器ClientController .java
package com.sun.eureka.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.sun.eureka.constant.URLConstant;
import com.sun.eureka.service.IClientSerive;
/**
* @author ex_sunqi
*
*/
@RestController
@RequestMapping(URLConstant.CLIENT)
public class ClientController {
@Autowired
private IClientSerive clientService;
@RequestMapping(value=URLConstant.INDEX , method=RequestMethod.GET)
public String indexHellp() {
return clientService.getTxt();
}
}
5.定义接口及实现类
package com.sun.eureka.service;
/**
* @author ex_sunqi
*
*/
public interface IClientSerive {
public String getTxt();
}
package com.sun.eureka.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.sun.eureka.constant.Response;
import com.sun.eureka.constant.URLConstant;
/**
*
* @author ex_sunqi
*
*/
@FeignClient("SERVICE-CUSTOM")
public interface ICustomSeriveFeign {
@RequestMapping(value =URLConstant.CUSTOM+URLConstant.INDEX, method = RequestMethod.GET)
public String getString();
}
注意:这里引入@HystrixCommand 用于指定回调方法
package com.sun.eureka.impl;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.sun.eureka.service.IClientSerive;
import com.sun.eureka.service.ICustomSeriveFeign;
@Service("clientService")
public class ClientServiceImpl implements IClientSerive{
@Autowired
private ICustomSeriveFeign clientSeriveFeign;
private Logger logger = LogManager.getLogger(ClientServiceImpl.class.getName());
//指定回调
@HystrixCommand(fallbackMethod="getTxtback")
public String getTxt() {
long start=System.currentTimeMillis();
String result=clientSeriveFeign.getString();
long end=System.currentTimeMillis();
logger.info("===========Spend time :"+ (end - start));
return result.toString();
}
//Spend time 大于2000,会返回error
public String getTxtback() {
return "error";
}
}
三.创建EurekaCustom消费者
1.pom.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 http://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.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.sun</groupId>
<artifactId>EurekaCustom</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>EurekaCustom</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<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-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</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>
</dependency>
<!--排除logback默认日志 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<!--引入log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</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.配置application.yml文件
eureka:
client:
serviceUrl: #注册中心的注册地址
defaultZone: http://127.0.0.1:8081/eureka/
server:
port: 8083 #服务端口号
spring:
application:
name: service-custom #服务名称--调用的时候根据名称来调用该服务的方法
3.配置启动类引入@EnableCircuitBreaker
package com.sun.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableEurekaClient
@EnableFeignClients
@EnableCircuitBreaker
@SpringBootApplication
public class EurekaCustomApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaCustomApplication.class, args);
}
}
4.编写控制器CustomController .java
package com.sun.eureka.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.sun.eureka.constant.URLConstant;
import com.sun.eureka.service.ICustomService;
/**
* 阻塞模式
* @author ex_sunqi
*
*/
@RestController
@RequestMapping(URLConstant.CUSTOM )
public class CustomController {
@Autowired
private ICustomService customService;
@RequestMapping(value=URLConstant.INDEX , method=RequestMethod.GET)
public String toString() {
try {
return customService.getString();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "请求异常";
}
}
}
5.定义接口及实现类
package com.sun.eureka.service;
public interface ICustomService {
public String getString() throws Exception;
}
在使用discoveryClient.getLocalServiceInstance()时,发现该方法已经过时。源码提示使用org.springframework.cloud.client.serviceregistry.Registration, 该类可以根据服务名,获取注册了该服务名的所有实例
package com.sun.eureka.impl;
import java.util.List;
import java.util.Random;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.stereotype.Service;
import com.sun.eureka.service.ICustomService;
@Service("customService")
public class CustomServiceImpl implements ICustomService {
private Logger logger = LogManager.getLogger(CustomServiceImpl.class.getName());
@Autowired
private Registration registration; // 服务注册
@Autowired
private DiscoveryClient client; // 服务发现客户端
@Override
public String getString() throws Exception{
//ServiceInstance instance = client.getLocalServiceInstance();此方法已失效
ServiceInstance instance = serviceInstance();
//Hystrix默认超时时间为2000毫秒,所有采用0~3000的随机数以让处理过程有一定概率发生超时来触发断路器。
int sleepTime = new Random().nextInt(3000);
logger.info("=========线程随机休眠时间"+sleepTime);
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
//instance.getServiceId() 获取服务
String result = "/testBalance, host:port=" + instance.getUri() + ", " + "service_id:" + instance.getServiceId();
logger.info(result);
return "From service-client , " + result;
}
public ServiceInstance serviceInstance() {
List<ServiceInstance> list = client.getInstances(registration.getServiceId());
if (list != null && list.size() > 0) {
for (ServiceInstance itm : list) {
if (itm.getPort() == 8083)
return itm;
}
}
return null;
}
}
四.访问Eureka注册中心
发现EurekaClient和EurekaCustom都已注册成功
见证奇迹的时刻,访问http://127.0.0.1:8082/client/index,看看会出现什么情况。
2019-07-10 20:17:01.837 INFO 23320 — [nio-8083-exec-8] com.sun.eureka.impl.CustomServiceImpl : =========线程随机休眠时间1072
2019-07-10 20:17:02.910 INFO 23320 — [nio-8083-exec-8] com.sun.eureka.impl.CustomServiceImpl : /getString, host:port=http://192.168.0.102:8083, service_id:SERVICE-CUSTOM
2019-07-10 20:17:02.923 INFO 23320 — [nio-8083-exec-7] com.sun.eureka.impl.CustomServiceImpl : /getString, host:port=http://192.168.0.102:8083, service_id:SERVICE-CUSTOM
-------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------
2019-07-10 20:18:59.304 INFO 23320 — [nio-8083-exec-9] com.sun.eureka.impl.CustomServiceImpl : =========线程随机休眠时间2116
2019-07-10 20:19:00.305 INFO 23320 — [io-8083-exec-10] com.sun.eureka.impl.CustomServiceImpl : =========线程随机休眠时间2385
2019-07-10 20:19:01.421 INFO 23320 — [nio-8083-exec-9] com.sun.eureka.impl.CustomServiceImpl : /getString, host:port=http://192.168.0.102:8083, service_id:SERVICE-CUSTOM
2019-07-10 20:19:02.691 INFO 23320 — [io-8083-exec-10] com.sun.eureka.impl.CustomServiceImpl : /getString, host:port=http://192.168.0.102:8083, service_id:SERVICE-CUSTOM
-------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------
出现以上两种不同结果,可能有些小伙伴会疑惑,但当你仔细观察打印日志的线程睡眠时间时,你会发现当睡眠时间大于2000毫秒时,会返回error。通过结果论证,表示我们的Hystrix的回调方法执行成功,因为Hystrix默认超时时间为2000毫秒,所有采用0~3000的随机数以让处理过程有一定概率发生超时来触发断路器。相信到了这里你应该明白Hystrix的作用了吧。