feat(master):Hystrix 请求缓存内容

Hystrix
master
土豆兄弟 3 weeks ago
parent 70b0eb3cbe
commit a29e4e7b11

@ -494,13 +494,7 @@
- 建议大家主键一定是自增的别用UUID之类的因为主键自增那么起码你的聚簇索引不会频繁的分裂主键值都是有序的就会自然的新增一个页而已但是如果你用的是UUID
么也会导致聚簇索引频繁的页分裂
### 109-111: 案例实战千万级用户场景下的运营系统SQL调优

@ -24,7 +24,7 @@
### 使用编程方式实现服务的容错、降级
- [NacosClientHystrixCommand.java] - [NacosClientHystrixObservableCommand.java]
### 编程方式开启 Hystrix 请求缓存

@ -35,10 +35,20 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- 工具方法 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.51</version>
</dependency>
</dependencies>
<!--

@ -2,6 +2,7 @@ package org.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;
@ -10,6 +11,7 @@ import org.springframework.cloud.context.config.annotation.RefreshScope;
@EnableDiscoveryClient
@RefreshScope // 刷新配置
@EnableCircuitBreaker // 启动 Hystrix
@ServletComponentScan // 扫描过滤器
public class HystrixApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class, args);

@ -1,15 +1,24 @@
package org.example.controller;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.example.hystrix.NacosClientHystrixCommand;
import org.example.hystrix.NacosClientHystrixObservableCommand;
import org.example.hystrix.UseHystrixCommandAnnotation;
import org.example.service.NacosClientService4HystrixDemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import rx.Observable;
import rx.Observer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Future;
/**
* <h1>Hystrix Controller</h1>
@ -21,8 +30,11 @@ public class HystrixController {
private final UseHystrixCommandAnnotation hystrixCommandAnnotation;
public HystrixController(UseHystrixCommandAnnotation useHystrixCommandAnnotation) {
private final NacosClientService4HystrixDemo nacosClientService4HystrixDemo;
public HystrixController(UseHystrixCommandAnnotation useHystrixCommandAnnotation, NacosClientService4HystrixDemo nacosClientService4HystrixDemo) {
this.hystrixCommandAnnotation = useHystrixCommandAnnotation;
this.nacosClientService4HystrixDemo = nacosClientService4HystrixDemo;
}
/**
@ -35,4 +47,86 @@ public class HystrixController {
serviceId, Thread.currentThread().getName());
return hystrixCommandAnnotation.getNacosClientInfo(serviceId);
}
@GetMapping("/simple-hystrix-command")
public List<ServiceInstance> getServiceInstanceByServiceId(
@RequestParam String serviceId) throws Exception {
// 第一种方式 - 耗时不长的场景使用
List<ServiceInstance> serviceInstances01 = new NacosClientHystrixCommand(
nacosClientService4HystrixDemo, serviceId
).execute(); // 同步阻塞 -> (创建一个新的线程来进行执行这个策略)
log.info("use execute to get service instances: [{}], [{}]",
JSON.toJSONString(serviceInstances01), Thread.currentThread().getName());
// 第二种方式 - 最常见的方式
List<ServiceInstance> serviceInstances02;
Future<List<ServiceInstance>> future = new NacosClientHystrixCommand(
nacosClientService4HystrixDemo, serviceId
).queue(); // 异步非阻塞, 但是已经调用
// 这里可以做一些别的事, 需要的时候再去拿结果
serviceInstances02 = future.get();
log.info("use queue to get service instances: [{}], [{}]",
JSON.toJSONString(serviceInstances02), Thread.currentThread().getName());
// 第三种方式
Observable<List<ServiceInstance>> observable = new NacosClientHystrixCommand(
nacosClientService4HystrixDemo, serviceId
).observe(); // 热响应调用, 直到下面 toBlocking().single() 的时候才开始调用, 但是已经启用了线程
List<ServiceInstance> serviceInstances03 = observable.toBlocking().single();
log.info("use observe to get service instances: [{}], [{}]",
JSON.toJSONString(serviceInstances03), Thread.currentThread().getName());
// 第四种方式
Observable<List<ServiceInstance>> toObservable = new NacosClientHystrixCommand(
nacosClientService4HystrixDemo, serviceId
).toObservable(); // 异步冷响应调用, 未调用, 也未启动线程
List<ServiceInstance> serviceInstances04 = toObservable.toBlocking().single();
log.info("use toObservable to get service instances: [{}], [{}]",
JSON.toJSONString(serviceInstances04), Thread.currentThread().getName());
// execute = queue + get
return serviceInstances01; // 随便返回一个结果, 3,4 不需要返回结果
}
@GetMapping("/hystrix-observable-command")
public List<ServiceInstance> getServiceInstancesByServiceIdObservable(
@RequestParam String serviceId) {
List<String> serviceIds = Arrays.asList(serviceId, serviceId, serviceId);
List<List<ServiceInstance>> result = new ArrayList<>(serviceIds.size());
NacosClientHystrixObservableCommand observableCommand =
new NacosClientHystrixObservableCommand(nacosClientService4HystrixDemo, serviceIds);
// 异步执行命令
Observable<List<ServiceInstance>> observe = observableCommand.observe();
// 注册获取结果
observe.subscribe(
new Observer<List<ServiceInstance>>() {
// 执行 onNext 之后再去执行 onCompleted
@Override
public void onCompleted() {
log.info("all tasks is complete: [{}], [{}]",
serviceId, Thread.currentThread().getName());
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onNext(List<ServiceInstance> instances) {
result.add(instances);
}
}
);
log.info("observable command result is : [{}], [{}]",
JSON.toJSONString(result), Thread.currentThread().getName());
return result.get(0); // 取一个返回
}
}

@ -0,0 +1,90 @@
package org.example.filter;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategyDefault;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* <h1> Hystrix </h1>
* */
@Slf4j
@Component
@WebFilter(
filterName = "HystrixRequestContextServletFilter",
urlPatterns = "/*", // 所有的 URL 都经过该请求
asyncSupported = true // 异步的请求调用也是支持的
)
public class HystrixRequestContextServletFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 初始化 Hystrix 请求上下文
// 在不同的 context 中缓存是不共享的
// 这个初始化是必须的
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
// 配置
hystrixConcurrencyStrategyConfig();
// 请求正常通过
chain.doFilter(request, response);
} finally {
// 关闭 Hystrix 请求上下文
context.shutdown();
}
}
/**
* <h2> Hystrix </h2>
* */
public void hystrixConcurrencyStrategyConfig() {
try {
HystrixConcurrencyStrategy target =
HystrixConcurrencyStrategyDefault.getInstance();
HystrixConcurrencyStrategy strategy =
HystrixPlugins.getInstance().getConcurrencyStrategy();
if (strategy instanceof HystrixConcurrencyStrategyDefault) {
// 如果已经就是我们想要配置的
return;
}
// 将原来其他的配置保存下来
HystrixCommandExecutionHook commandExecutionHook =
HystrixPlugins.getInstance().getCommandExecutionHook();
HystrixEventNotifier eventNotifier =
HystrixPlugins.getInstance().getEventNotifier();
HystrixMetricsPublisher metricsPublisher =
HystrixPlugins.getInstance().getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy =
HystrixPlugins.getInstance().getPropertiesStrategy();
// 先重置, 再把我们自定义的配置与原来的配置写回去
HystrixPlugins.reset();
HystrixPlugins.getInstance().registerConcurrencyStrategy(target);
HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
log.info("config hystrix concurrency strategy success");
} catch (Exception ex) {
log.error("Failed to register Hystrix Concurrency Strategy: [{}]",
ex.getMessage(), ex);
}
}
}

@ -0,0 +1,83 @@
package org.example.hystrix;
import com.netflix.hystrix.*;
import lombok.extern.slf4j.Slf4j;
import org.example.service.NacosClientService4HystrixDemo;
import org.springframework.cloud.client.ServiceInstance;
import java.util.Collections;
import java.util.List;
import static com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy.THREAD;
/**
* <h1> NacosClientService </h1>
* Hystrix :
* 1. 线
* 2. : + , (线, , 线, , )
* */
@Slf4j
public class NacosClientHystrixCommand extends HystrixCommand<List<ServiceInstance>> {
/** 需要保护的服务 */
private final NacosClientService4HystrixDemo nacosClientService;
/** 方法需要传递的参数 */
private final String serviceId;
public NacosClientHystrixCommand(NacosClientService4HystrixDemo nacosClientService, String serviceId) {
// 线程隔离
super(
Setter.withGroupKey(
HystrixCommandGroupKey.Factory.asKey("NacosClientService4HystrixDemo"))
.andCommandKey(HystrixCommandKey.Factory.asKey("NacosClientHystrixCommand"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("NacosClientPool"))
// 线程池 key 配置
.andCommandPropertiesDefaults(
HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(THREAD) // 线程池隔离策略
.withFallbackEnabled(true) // 开启降级
.withCircuitBreakerEnabled(true) // 开启熔断器
)
);
// 可以配置信号量隔离策略
// Setter semaphore =
// Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("NacosClientService4HystrixDemo"))
// .andCommandKey(HystrixCommandKey.Factory.asKey("NacosClientHystrixCommand"))
// .andCommandPropertiesDefaults(
// HystrixCommandProperties.Setter()
// .withCircuitBreakerRequestVolumeThreshold(10) // 至少有10个请求开始, 熔断器才开始熔断计算
// .withCircuitBreakerSleepWindowInMilliseconds(5000) // 熔断器的请求 5s 后会进入一个半打开的状态, 放开部分请求会进行一个重试
// .withCircuitBreakerErrorThresholdPercentage(50) // 当错误率到 50% 就开启熔断保护
// .withExecutionIsolationStrategy(SEMAPHORE) // 指定使用信号量隔离
// //..... 可以参考上面的线程隔离策略的配置
// );
this.nacosClientService = nacosClientService;
this.serviceId = serviceId;
}
/**
* <h2> run </h2>
* */
@Override
protected List<ServiceInstance> run() throws Exception {
// 保护逻辑
log.info("NacosClientService4HystrixDemo In Hystrix Command to Get Service Instance: [{}], [{}]",
this.serviceId, Thread.currentThread().getName());
return this.nacosClientService.getNacosClientInfo(this.serviceId);
}
/**
* <h2></h2>
* */
@Override
protected List<ServiceInstance> getFallback() {
log.warn("NacosClientService4HystrixDemo run error: [{}], [{}]",
this.serviceId, Thread.currentThread().getName());
return Collections.emptyList();
}
}

@ -0,0 +1,101 @@
package org.example.hystrix;
import com.alibaba.fastjson.JSON;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixObservableCommand;
import lombok.extern.slf4j.Slf4j;
import org.example.service.NacosClientService4HystrixDemo;
import org.springframework.cloud.client.ServiceInstance;
import rx.Observable;
import rx.Subscriber;
import java.util.Collections;
import java.util.List;
/**
* <h1>HystrixCommand, </h1>
* */
@Slf4j
public class NacosClientHystrixObservableCommand extends HystrixObservableCommand<List<ServiceInstance>> {
/** 要保护的服务 */
private final NacosClientService4HystrixDemo nacosClientService4HystrixDemo;
/** 方法需要传递的参数 */
private final List<String> serviceIds;
public NacosClientHystrixObservableCommand(NacosClientService4HystrixDemo nacosClientService4HystrixDemo,
List<String> serviceIds) {
super(
HystrixObservableCommand.Setter
.withGroupKey(HystrixCommandGroupKey
.Factory.asKey("NacosClientService4HystrixDemo"))
.andCommandKey(HystrixCommandKey
.Factory.asKey("NacosClientHystrixObservableCommand"))
.andCommandPropertiesDefaults(
HystrixCommandProperties.Setter()
.withFallbackEnabled(true) // 开启降级
.withCircuitBreakerEnabled(true) // 开启熔断器
)
);
this.nacosClientService4HystrixDemo = nacosClientService4HystrixDemo;
this.serviceIds = serviceIds;
}
/**
* <h2></h2>
* */
@Override
protected Observable<List<ServiceInstance>> construct() {
// Observable 有三个关键的事件方法, 分别是 onNext、onCompleted、onError
return Observable.unsafeCreate(subscriber -> {
try {
if (!subscriber.isUnsubscribed()) {
log.info("subscriber command task: [{}], [{}]",
JSON.toJSONString(serviceIds),
Thread.currentThread().getName());
serviceIds.forEach(
s -> subscriber
.onNext(nacosClientService4HystrixDemo.getNacosClientInfo(s))
);
subscriber.onCompleted();
log.info("command task completed: [{}], [{}]",
JSON.toJSONString(serviceIds),
Thread.currentThread().getName());
}
} catch (Exception ex) {
subscriber.onError(ex);
}
});
}
/**
* <h2></h2>
* */
@Override
protected Observable<List<ServiceInstance>> resumeWithFallback() {
return Observable.unsafeCreate(subscriber -> {
try {
if (!subscriber.isUnsubscribed()) {
log.info("(fallback) subscriber command task: [{}], [{}]",
JSON.toJSONString(serviceIds),
Thread.currentThread().getName());
subscriber.onNext(Collections.emptyList());
subscriber.onCompleted();
log.info("(fallback) command task completed: [{}], [{}]",
JSON.toJSONString(serviceIds),
Thread.currentThread().getName());
}
} catch (Exception ex) {
subscriber.onError(ex);
}
});
}
}

@ -22,12 +22,12 @@ public class NacosClientService4HystrixDemo {
* */
public List<ServiceInstance> getNacosClientInfo(String serviceId) {
// 测试 UseHystrixCommandAnnotation 的超时
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
}
// 1. 测试 UseHystrixCommandAnnotation 的超时
// try {
// Thread.sleep(2000);
// } catch (InterruptedException ex) {
//
// }
// 测试 NacosClientHystrixCommand 熔断
// throw new RuntimeException("has some error");

Loading…
Cancel
Save