最近线上存在一些cpu的锯齿情况,类似于推荐项目就一个核心接口,业务逻辑和计算链路很长;
在某些个人认为不是瓶颈的中间件使用比如redis的mget或者pipelineGet都偶尔超过10ms甚至30ms;
引发了我一些思考
- 对于中间件比如redis的使用,一定要在使用前熟悉它的api和是否有超时的概念,如果本身就有timeout的概念,用多线程包一层(防止hang住)真的是纯纯的多余
- 线程的使用不要套娃,套娃的结果,会导致很多匪夷所思的情况发生
- 大量使用线程的场景,一定要使用线程池;基本素养但是有的时候真的会忘记编程规范
- 线程数不是越多越好,上下文的切换开销也不小,对于耗时如果是不存在过长的情况,不一定非得用线程池
- 有很多的线程池使用场景,比如Future和Completablefuture包裹的线程,要对线程的超时做处理,否则会存在很多的
孤儿线程
,主流程判断线程都超时了,直接丢弃继续走后续的逻辑,但是线程里面的任务还在执行,这会损耗一部分的cpu开销,甚至浪费线程,虽然java不能立即终止线程,我们也尽可能对它做标记;
public static <T> void cancelFuture(Future<T> future) {
try {
if (future != null) {
future.cancel(true);
}
} catch (Exception e) {
// doNothing
}
}
public <T> T call(Callable<T> callable, long timeout) throws Exception {
ListenableFuture<T> f = this.service.submit(callable);
try {
T result = f.get(timeout, TimeUnit.MILLISECONDS);
return result;
} catch (TimeoutException | InterruptedException | ExecutionException e) {
cancelFuture(f);
throw e;
}
}
- 对于一些不是一类的任务,并且需要保障效率的时候,线程池别混用,别为了省事儿,用别人创建的线程池包自己的线程,qps高的时候纯纯的互相伤害
- 线程池的拒绝策略一定要用,某些时刻可以保命,可以看出个人的细节和严谨程度