1。使用线程池执行异步任务为了减少阻塞时间,加快响应速度,将不需要返回结果的操作变成异步任务,使用线程池来执行,这是提高性能的一个手段。您可能会感到惊讶,这不是吗?首先,我们把异步任务分为两种:必须执行成功的和执行失败放弃的,显然大多数时候都是第一种。那么当你把一个任务扔到线程池的时候,你知道它是否完成了吗?如果服务器宕机、升级或重启,那些尚未完成或仍在排队的任务将丢失。结果是用户在促销活动中抢到的优惠券并没有发送给用户。更严重的后果是,订单在送往仓库系统的途中消失了。我推荐的方式是把任务交给消息中间件,让其分发给消息消费者执行(消费者可能就是发送者自己)。消息中间件可以要求消费者在完成任务后通知中间件,否则会重新分发消息,直到收到任务已经完成的通知。如果中间件没有这个功能,应用程序可以在完成任务后要求消费者发回一个“任务完成”的消息,但是应用程序不能同步等待这个消息,否则异步会退化为同步。更重要的是,消息中间件具有持久化功能,即使宕机,消息也不会丢失,长时间无法升级重启。消息中间件的缺点是很难定制失败的处理——你可能想定制重试间隔和重试次数等细节。从另一个角度来说,解决任务丢失的问题,不一定非得用消息中间件。可以在应用代码中将任务和完成状态保存到数据库中,用线程池执行,完成后更新状态。这不是很像作业调度(例如Quartz)吗?是的。不过,有些任务确实可有可无,比如“刷新一个不重要的缓存”这个任务,就扔到线程池里就行了。2、日志采用同步方式。我们知道性能瓶颈通常是I/O,尤其是数据库的I/O。所以我们使用了缓存,速度提高了——不一定。使用缓存将I/O变成内存计算,消除绝对瓶颈,速度提升一个数量级。在这个数量级上,一些原本不重要的因素开始产生影响。日志是一种I/O。顺序写入磁盘虽然很快,但还是比内存计算慢。更糟糕的是,当一个线程写日志时,另一个线程必须等它写完了才能继续写,否则日志会乱七八糟,当日志量大的时候,会断网。所以当你的系统性能达到一定程度后,你需要优化日志的性能(曾经出现过QPS提升3倍的情况),常用的两种方式:logless异步方式、loglesstoday、check明天为虫子哭泣。所以主要靠异步方式。logback可以配置(网上搜索)在RollingFileAppender上设置一个AsyncAppender,其实就是加了一个缓冲队列,变成了批量写入磁盘。缺点是看不到***日志(还没有写入磁盘)。queueSize默认为256,即使设置为16也有明显的性能提升,也缓解了无法及时看到***登录的问题。Log4j2的异步模式有更深入的优化,是否选择取决于测试数据。3.没有超时设置。如果网络忘记设置超时,系统可能随时挂掉。每次网络操作,记得设置超时时间,超过这个时间就放弃。访问分布式缓存也是如此。如果你不设置超时,你可以等到时间结束。互联网是如此不确定。4.没有统计缓存攻击率。攻击率低的缓存总比没有缓存差。LRU算法虽然效果很好,但也不一定没有例外。经常过时的数据和大量数据可能是一种负担。统计缓存攻击率的实现方式可以是在内存中统计,定时写入数据库或文件;也可以是在日志中记录攻击状态,以后统计。更优雅的实现也是可能的。当你的系统进入精耕细作时代,这个问题就不可避免地会浮出水面。5.没有缓存响应时间统计。缓存一定要快吗?我真的见过一个不快的。分布式缓存需要经过网络,网络抖一次,缓存抖三遍;还要看运维,运维抖一次,缓存抖三遍。必须观察这件事的微妙之处。小心,设置超时,并记住响应时间。一个简单的方法就是在响应时间过长的时候记录一行,一般情况下不记录。这样既能保留记录,又不会产生太多的日志。6.单缓存模式分布式缓存是由缓存中间件组成的集群,一致性好,缓存会很快同步到所有副本。但毕竟要经过网络,延迟是亚毫秒级的,负载聚合到中间件,可能会因为网络拥塞或者机器负载过高而造成性能波动。为了进一步提高某些服务的性能和稳定性,可能会辅以本地缓存。缓存必须具有过期策略。如果使用GuavaCache,可以选择expireAfterAccess(不常用)、expireAfterWrite或refreshAfterWrite策略:expire表示先丢弃旧数据,再从数据库加载新数据。这段时间数据库压力大,适合一般情况。refresh是保持旧数据,直到新数据异步加载完成,可以一直阻断数据库的压力,适用于高压情况。每个应用实例的本地缓存都是独立的,旧数据的失效取决于过期策略。作为一种改进,您可以使用消息队列。一个实例广播一条消息说某个数据无效,其他实例一个接一个地检查自己。这接近于实时同步。缓存更新方法也会影响性能。@左耳酒子的缓存更新例程已经很好的介绍了三个例程。下面我来比较一下:CacheAsidePattern:应用在数据库和缓存之间循环,以数据库为准。适用于一般情况,因此最常用。Read/WriteThroughPattern:应用只对缓存进行读写,缓存同步回写到数据库(同步是指应用等待回写完成)。理论性能略高。WriteBehindCachingPattern:应用只对缓存进行读写,缓存异步写回数据库(应用不等待回写完成,缓存宕机会丢失数据)。理论性能最好。如果有WriteAheadLogging特性,可以避免数据丢失,但性能会略有下降。7、分布式缓存加锁有些系统在进入精耕细作时代后,希望避免一种情况——当缓存失效时,很多应用实例同时访问数据库,增加了负载,浪费了资源。于是就有了锁定缓存的解决方案。简单版是在每个实例内部加一把锁,只允许一个线程从数据库中取一条数据。复杂的版本是在分布式缓存中设置锁。一条数据只允许一个实例从数据库中取出,然后放入缓存中供其他实例使用。复杂版的思路不错,但是注意加锁需要设置超时时间(记住我上面说的),不然如果持有锁的实例有问题,都会延迟。即使设置了超时,所有实例都可能等待超时,浪费时间。所以这个方案不一定好,以测试数据为准。8.工作太忙很多公司经常加班。其实效率低,不耐用。他们只能复制已有的经验,靠不断的更换来维持。带来的后果是:需求混乱、BUG巨大、创新乏力。做好技术,需要有条不紊,遇变稳定,输出持久。疲惫的模型无法做好大型服务器的开发,也很难做好各个领域的开发。
