此处省略了创建用户类User和统一返回前端的响应类Result,RestTemplate配置,UserService接口的创建,具体的可以参考项目源码
@RestController
@RequestMapping(“/user”)
public class UserHystrixController {
@Autowired
private UserService userService;
@GetMapping(“/testFallback/{id}”)
public Result testFallback(@PathVariable Long id) {
return userService.getUser(id);
}
@GetMapping(“/testException/{id}”)
public Result testException(@PathVariable Long id) {
return userService.getUserException(id);
}
@GetMapping(“/testCommand/{id}”)
public Result getUserCommand(@PathVariable Long id) {
return userService.getUserCommand(id);
}
@GetMapping(“/testCache/{id}”)
public Result testCache(@PathVariable Long id) {
userService.getUserCache(id);
userService.getUserCache(id);
userService.getUserCache(id);
return new Result(“操作成功”, 200);
}
@GetMapping(“/testRemoveCache/{id}”)
public Result testRemoveCache(@PathVariable Long id) {
userService.getUserCache(id);
userService.removeCache(id);
userService.getUserCache(id);
return new Result(“操作成功”, 200);
}
@GetMapping(“/testCollapser”)
public Result testCollapser() throws ExecutionException, InterruptedException {
Future future1 = userService.getUserFuture(1L);
Future future2 = userService.getUserFuture(2L);
future1.get();
future2.get();
ThreadUtil.safeSleep(200);
Future future3 = userService.getUserFuture(3L);
future3.get();
return new Result(“操作成功”, 200);
}
}
在UserHystrixController中添加用于测试服务降级的接口:
@GetMapping(“/testFallback/{id}”)
public Result testFallback(@PathVariable Long id) {
return userService.getUser(id);
}
在UserService中添加调用方法与服务降级方法,方法上需要添加@HystrixCommand注解:
@HystrixCommand(fallbackMethod = “fallbackMethod1”)
public Result getUser(Long id) {
return restTemplate.getForObject(userServiceUrl + “/user/{1}”, Result.class, id);
}
/**
-
声明的参数需要包含controller的声明参数
-
@param id
-
@return
*/
public Result fallbackMethod1(@PathVariable Long id) {
return new Result(“服务调用失败”, 500);
}
启动eureka-server、user-service、hystrix-service服务
调用接口进行测试:http://localhost:8401/user/testFallback/1
关闭user-service服务重新测试该接口,发现已经发生了服务降级:
@HystrixCommand中的常用参数
-
fallbackMethod:指定服务降级处理方法;
-
ignoreExceptions:忽略某些异常,不发生服务降级;
-
commandKey:命令名称,用于区分不同的命令;
-
groupKey:分组名称,Hystrix会根据不同的分组来统计命令的告警及仪表盘信息;
-
threadPoolKey:线程池名称,用于划分线程池。
设置命令、分组及线程池名称
在UserHystrixController中添加测试接口:
@GetMapping(“/testCommand/{id}”)
public Result getUserCommand(@PathVariable Long id) {
return userService.getUserCommand(id);
}
在UserService中添加方式实现功能:
@HystrixCommand(fallbackMethod = “fallbackMethod1”,
commandKey = “getUserCommand”,
groupKey = “getUserGroup”,
threadPoolKey = “getUserThreadPool”)
public Result getUserCommand(Long id) {
return restTemplate.getForObject(userServiceUrl + “/user/{1}”, Result.class, id);
}
使用ignoreExceptions忽略某些异常降级
在UserHystrixController中添加测试接口:
@GetMapping(“/testException/{id}”)
public Result testException(@PathVariable Long id) {
return userService.getUserException(id);
}
在UserService中添加实现方法,这里忽略了NullPointerException,当id为1时抛出IndexOutOfBoundsException,id为2时抛出NullPointerException:
@HystrixCommand(fallbackMethod = “fallbackMethod2”, ignoreExceptions = {NullPointerException.class})
public Result getUserException(Long id) {
if (id == 1) {
throw new IndexOutOfBoundsException();
} else if (id == 2) {
throw new NullPointerException();
}
return restTemplate.getForObject(userServiceUrl + “/user/{1}”, Result.class, id);
}
public Result fallbackMethod2(@PathVariable Long id, Throwable e) {
LOGGER.error(“id {},throwable class:{}”, id, e.getClass());
return new Result(“服务调用失败”, 500);
}
调用接口进行测试:http://localhost:8401/user/tesException/1
调用接口进行测试:http://localhost:8401/user/tesException/2
当系统并发量越来越大时,我们需要使用缓存来优化系统,达到减轻并发请求线程数,提供响应速度的效果。
相关注解
-
@CacheResult:开启缓存,默认所有参数作为缓存的key,cacheKeyMethod可以通过返回String类型的方法指定key;
-
@CacheKey:指定缓存的key,可以指定参数或指定参数中的属性值为缓存key,cacheKeyMethod还可以通过返回String类型的方法指定;
-
@CacheRemove:移除缓存,需要指定commandKey。
测试使用缓存
在UserHystrixController中添加使用缓存的测试接口,直接调用三次getUserCache方法:
@GetMapping(“/testCache/{id}”)
public Result testCache(@PathVariable Long id) {
userService.getUserCache(id);
userService.getUserCache(id);
userService.getUserCache(id);
return new Result(“操作成功”, 200);
}
在UserService中添加具有缓存功能的getUserCache方法:
@CacheResult(cacheKeyMethod = “getCacheKey”)
@HystrixCommand(fallbackMethod = “fallbackMethod1”, commandKey = “getUserCache”)
public Result getUserCache(Long id) {
LOGGER.info(“getUserCache id:{}”, id);
return restTemplate.getForObject(userServiceUrl + “/user/{1}”, Result.class, id);
}
/**
-
为缓存生成key的方法
-
@return
*/
public String getCacheKey(Long id) {
return String.valueOf(id);
}
调用接口测试http://localhost:8401/user/testCache/1,这个接口中调用了三次getUserCache方法,但是只打印了一次日志,说明有两次走的是缓存:
测试移除缓存
在UserHystrixController中添加移除缓存的测试接口,调用一次removeCache方法:
@GetMapping(“/testRemoveCache/{id}”)
public Result testRemoveCache(@PathVariable Long id) {
userService.getUserCache(id);
userService.removeCache(id);
userService.getUserCache(id);
return new Result(“操作成功”, 200);
}
在UserService中添加具有移除缓存功能的removeCache方法:
@HystrixCommand
@CacheRemove(commandKey = “getUserCache”, cacheKeyMethod = “getCacheKey”)
public Result removeCache(Long id) {
LOGGER.info(“removeCache id:{}”, id);
return restTemplate.postForObject(userServiceUrl + “/user/delete/{1}”, null, Result.class, id);
}
调用接口测试http://localhost:8401/user/testRemoveCache/1,可以发现有两次查询都走的是接口:
缓存使用过程中的问题
在缓存使用过程中,我们需要在每次使用缓存的请求前后对HystrixRequestContext进行初始化和关闭,否则会出现如下异常:
java.lang.IllegalStateException: Request caching is not available. Maybe you need to initialize the HystrixRequestContext?
at com.netflix.hystrix.HystrixRequestCache.get(HystrixRequestCache.java:104) ~[hystrix-core-1.5.18.jar:1.5.18]
at com.netflix.hystrix.AbstractCommand$7.call(AbstractCommand.java:478) ~[hystrix-core-1.5.18.jar:1.5.18]
at com.netflix.hystrix.AbstractCommand$7.call(AbstractCommand.java:454) ~[hystrix-core-1.5.18.jar:1.5.18]
这里我们通过使用过滤器,在每个请求前后初始化和关闭HystrixRequestContext来解决该问题:
@Component
@WebFilter(urlPatterns = “/*”, asyncSupported = true)
public class HystrixRequestContextFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
filterChain.doFilter(servletRequest, servletResponse);
} finally {
context.close();
}
}
}
微服务系统中的服务间通信,需要通过远程调用来实现,随着调用次数越来越多,占用线程资源也会越来越多。Hystrix中提供了@HystrixCollapser用于合并请求,从而达到减少通信消耗及线程数量的效果。
@HystrixCollapser的常用属性
-
batchMethod:用于设置请求合并的方法;
-
collapserProperties:请求合并属性,用于控制实例属性,有很多;
-
timerDelayInMilliseconds:collapserProperties中的属性,用于控制每隔多少时间合并一次请求;
功能演示
在UserHystrixController中添加testCollapser方法,这里我们先进行两次服务调用,再间隔200ms以后进行第三次服务调用:
@GetMapping(“/testCollapser”)
public Result testCollapser() throws ExecutionException, InterruptedException {
Future future1 = userService.getUserFuture(1L);
Future future2 = userService.getUserFuture(2L);
future1.get();
future2.get();
ThreadUtil.safeSleep(200);
Future future3 = userService.getUserFuture(3L);
future3.get();
return new Result(“操作成功”, 200);
}
使用@HystrixCollapser实现请求合并,所有对getUserFuture的的多次调用都会转化为对getUserByIds的单次调用:
@HystrixCollapser(batchMethod = “listUsersByIds”,collapserProperties = {
@HystrixProperty(name = “timerDelayInMilliseconds”,value = “100”)
})
public Future getUserFuture(Long id) {
return new AsyncResult() {
@Override
public User invoke() {
Result result = restTemplate.getForObject(userServiceUrl + “/user/{1}”, Result.class, id);
Map data = (Map) result.getData();
User user = BeanUtil.mapToBean(data, User.class, true);
LOGGER.info(“getUserById username:{}”,user.getUsername());
return user;
}
};
}
@HystrixCommand
public List listUsersByIds(List ids) {
LOGGER.info(“listUsersByIds:{}”,ids);
Result result = restTemplate.getForObject(userServiceUrl + “/user/listUsersByIds?ids={1}”, Result.class, CollUtil.join(ids, “,”));
return (List)result.getData();
}
注意:测试之前需要重启user-service服务,因为刚才测试请求缓存把数据删了一个,不然会报错
访问接口测试http://localhost:8401/user/testCollapser,由于我们设置了100毫秒进行一次请求合并,前两次被合并,最后一次自己单独合并了。
全局配置
hystrix:
command: #用于控制HystrixCommand的行为
default:
execution:
isolation:
strategy: THREAD #控制HystrixCommand的隔离策略,THREAD->线程池隔离策略(默认),SEMAPHORE->信号量隔离策略
thread:
timeoutInMilliseconds: 1000 #配置HystrixCommand执行的超时时间,执行超过该时间会进行服务降级处理
interruptOnTimeout: true #配置HystrixCommand执行超时的时候是否要中断
interruptOnCancel: true #配置HystrixCommand执行被取消的时候是否要中断
timeout:
enabled: true #配置HystrixCommand的执行是否启用超时时间
semaphore:
maxConcurrentRequests: 10 #当使用信号量隔离策略时,用来控制并发量的大小,超过该并发量的请求会被拒绝
fallback:
enabled: true #用于控制是否启用服务降级
circuitBreaker: #用于控制HystrixCircuitBreaker的行为
enabled: true #用于控制断路器是否跟踪健康状况以及熔断请求
requestVolumeThreshold: 20 #超过该请求数的请求会被拒绝
forceOpen: false #强制打开断路器,拒绝所有请求
forceClosed: false #强制关闭断路器,接收所有请求
requestCache:
enabled: true #用于控制是否开启请求缓存
collapser: #用于控制HystrixCollapser的执行行为
default:
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
拒绝
forceOpen: false #强制打开断路器,拒绝所有请求
forceClosed: false #强制关闭断路器,接收所有请求
requestCache:
enabled: true #用于控制是否开启请求缓存
collapser: #用于控制HystrixCollapser的执行行为
default:
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-RqcYvyWp-1715061179214)]
[外链图片转存中…(img-34jfBoAt-1715061179215)]
[外链图片转存中…(img-hfRPAlBy-1715061179215)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!