微服务架构中,系统往往拆分成多个服务单元,各单元的应用间通过服务注册与订阅方式相互依赖,由于各单元都在不同的进程中运行,依赖通过远程调用的方式执行,可能会因为网络原因导致调用失败,就需要服务容错保护。
下面创建一个hystrix客户端,点击这里创建一个Gradle工程,boot版本用1.5.x
,添加依赖如下:
dependencyManagement {
imports {
mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Edgware.SR4'
}
}
dependencies {
compile('org.springframework.boot:spring-boot-starter')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-actuator')
compile 'org.slf4j:slf4j-api:1.7.14'
compile('org.springframework.cloud:spring-cloud-starter-hystrix')
compile('org.springframework.cloud:spring-cloud-starter-eureka')
compile('org.springframework.cloud:spring-cloud-starter-ribbon')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
然后编写配置文件application.properties
spring.application.name=hystrix-base
server.port=8088
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/,http://peer1:1111/eureka/
注意,先启动上一章中的注册中心服务,单机或集群都可以。
打开断路器,2种方式
@EnableHystrix
or
@EnableCircuitBreaker
@EnableHystrix
包含@EnableCircuitBreaker
编写controller,模拟服务超时引发断路,如下:
@RestController
public class HystrixDemoController {
private final static Random random = new Random();
@GetMapping("hello")
@HystrixCommand(fallbackMethod = "error",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "100")
})
public String hello()throws Exception{
int value = random.nextInt(200);
System.out.println("hello() costs "+ value + " ms.");
Thread.sleep(value);
return "hello";
}
public String error(){
return "error";
}
上面的代码,定义一个hello
接口,使用@HystrixCommand
配置接口断路超时时间为100毫秒,即超过100毫秒即引发断路。
启动服务,可以在注册中心http://localhost:1111/看到注册的服务,然后输入http://localhost:8088/hello并不断刷新,可以看到超时时会输出error。
其实,我们可以使用future模拟实现hystrix下,简单理解hystrix,如:
Random random = new Random();
ExecutorService service = Executors.newFixedThreadPool(1);
Future<String> future = service.submit(()->{
int value = random.nextInt(200);
System.out.println("hello() costs "+ value + " ms.");
Thread.sleep(value);
return "hello";
});
try {
future.get(100, TimeUnit.MILLISECONDS);
} catch (Exception e) {
System.out.println("超时保护!");
}
先创建一个线程池,然后提交一个任务得到future,给get设置时间限制,超出则报异常。
若使用future模拟hystrix是优秀的,那另一种方式将是卓越的,就是rxjava,下面先了解下线程控制的几个方法:
//直接在当前线程运行
Schedulers.immediate()
//启动新线程并在新线程执行操作
Schedulers.newThread()
//内部实现一个无上限的线程池,可重用空闲线程
Schedulers.io()
接下来,我们用rxjava实现一下hystrix,如下:
Random random = new Random();
Single.just("Hello")
.subscribeOn(Schedulers.immediate())
.subscribe(new Observer<String>() {
@Override
public void onCompleted() {
System.out.println("执行完毕!");
}
@Override
public void onError(Throwable e) {
System.out.println("熔断保护!");
}
@Override
public void onNext(String s) {
int value = random.nextInt(200);
System.out.println("hello() costs "+ value + " ms.");
if (value > 100) {
throw new RuntimeException("Timeout!");
}
}
});
上面就是在当前线程中发射字符串hello
,若大于100毫秒则抛异常进入onError,否则进入onCompleted。
通过上面2种方式的模拟,相信大家对hystrix不再陌生,但是对它的使用还不太熟悉吧,除了注解的方式,我们还将介绍另一种方式--编程方式,如下:
@GetMapping("hello2")
public String hello2()throws Exception{
return new HelloCommand().execute();
}
private class HelloCommand extends com.netflix.hystrix.HystrixCommand<String> {
protected HelloCommand(){
super(HystrixCommandGroupKey.Factory.asKey("Hello"),100);//100毫秒
}
@Override
protected String run() throws Exception {
int value = random.nextInt(200);
System.out.println("hello2() costs "+ value + " ms.");
Thread.sleep(value);
return "hello2";
}
@Override
protected String getFallback() {
return HystrixDemoController.this.error();
}
}
这是通过继承HystrixCommand
实现自定义命令,可以更新灵活的实现自己的需求,但写法比较复杂。
学习交流,请加群:64691032