TCC设计理解-高并发高可用(4)

参考:并发异步hytrix和dubbo服务治理适配博文

http://j360.me/2017/04/14/%E5%B9%B6%E5%8F%91%E5%BC%82%E6%AD%A5hytrix%E5%92%8Cdubbo%E6%9C%8D%E5%8A%A1%E6%B2%BB%E7%90%86%E9%80%82%E9%85%8D/

TCC事务成功和失败的判断依据通常使用异常来进行,异常在并发、异步、Hytrix框架中都有各自的异常捕获方式,如何将异常反馈到上层TCC框架是集成的头等大事。

另一方面,RPC框架通常并不推荐使用业务异常传输,在使用Result的情况下,在成功和失败都会正确地返回Result,这种情况下需要根据Result中的异常Code判断来抛出异常,让TCC框架捕获异常依据。

TCC高并发高可用

RPC框架使用Result通信机制,可以捕获的ResultCode方式可以是,该判断在Leader端执行:

  1. 在RPC框架Filter中可以捕获,但是无法进入到TCC框架
  2. 在业务代码层面捕获,定制程度高,可以根据并发、异步、Hytrix框架各自定制
  3. 在AOP层面捕获,AOP在执行真正的业务代码后,统一来处理Result,抛出异常,交由上层AOP处理抛出异常,如果包装了并发异步Hytrix框架,则继续往上抛出,Hytrix必须拦截处理确保不进行熔断,将业务交给Leader业务代码处理

不建议在并发异步Hytrix框架中做统一处理,但是需要在上述框架中做出相应的设置便于更上层代码能够拿到相应的数据,比如异常捕获、熔断

并发异步

并发异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Future<Integer> ageFuture = pool.submit(new Callable<Integer>() {
public Integer call() {
return userService.getUserAge(uid);
}
});
Future<Integer> snsFuture = pool.submit(new Callable<Integer>() {
public Integer call() {
return snsService.getFollowCount(uid);
}
});
int a = 0,c = 0;
try {
a = ageFuture.get(6,TimeUnit.SECONDS);
c = snsFuture.get(6,TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}

异步异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int a = 0;
try {
//互斥任务
CompletableFuture<Integer> af = ageFuture.thenCombineAsync(CompletableFuture.supplyAsync(() -> {
System.out.println("af:"+Thread.currentThread().getName());
return userService.getUserAge(uid);},pool3),(m,n) -> {
System.out.println("af2:"+Thread.currentThread().getName());
return m+n;},pool2);
a = af.get(6,TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println((end - start)/1000+" s 互斥任务:" + a );

Hytrix在Dubbo层

DubboFitler在执行请求时,在服务有返回的情况下都能够得到底层Dubbo的Result结果,不会存在业务异常问题。如果存在底层异常,可能会触发熔断,Hytrix作为高并发和高可用服务治理,放在这里是比较推荐的做法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public DubboHystrixCommand(Invoker<?> invoker,Invocation invocation){
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(invoker.getInterface().getName()))
.andCommandKey(HystrixCommandKey.Factory.asKey(String.format("%s_%d", invocation.getMethodName(),
invocation.getArguments() == null ? 0 : invocation.getArguments().length)))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withCircuitBreakerRequestVolumeThreshold(20)//10秒钟内至少19此请求失败,熔断器才发挥起作用
.withCircuitBreakerSleepWindowInMilliseconds(30000)//熔断器中断请求30秒后会进入半打开状态,放部分流量过去重试
.withCircuitBreakerErrorThresholdPercentage(50)//错误率达到50开启熔断保护
.withExecutionTimeoutEnabled(false))//使用dubbo的超时,禁用这里的超时
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(getThreadPoolCoreSize(invoker.getUrl()))));//线程池为30
this.invoker=invoker;
this.invocation=invocation;
}

Hytrix在AOP、业务代码中

  1. 如果在AOP末端或者业务代码中,在最后处理的阶段,不对返回Result做出任何处理,也就是不改变原本的执行逻辑,无需进行控制

  2. 如果在AOP顶端最优先处理阶段,将会收到下端抛出的异常,可能会存在熔断,不建议在这里进行

  3. 如果使用RpcException返回,因为在这个层面拿到的结果已经不是底层的Result而是业务的异常,所以非业务Result形式,不推荐在这里使用Hytrix