态化,缓存化;
业务领域之中多少都有很多数据是“不变”,或固定时间内“不定的”,而这些数据在业务请求中又是常用的,“热点的”,比如收银台配置信息,渠道网关的参数信息,或是领域模型中的数据结构信息。这些数据一般散落或有序散落在数据库表结构之下,大可以在应用从容器中启动之初就加载入JVM或Cache,避免每次请求都要去读取DB,可以很大程度地提高服务性能。
运营化,后台化;
也是由第一点引出,当配置信息发生变更的时候怎么办?一般的方式是提SQL修改表记录,再去清除在Cache中的过期配置信息,再让这个生效的配置信息热加载到缓存去。这么做的坏处有两点,一是需要保证SQL的正确性,这点很重要;二是重复劳动。一种一劳永逸的办法就是后台化,将需要配置的信息后台化,如此增删改查的操作都会变得省心不少,还有一个好处是产品找你的机会少。
高可靠性——提早准备好业务降级策略;
理想是美好的,而现实总是残酷的。相信在每个开发的事业生涯总是会遇到很多“不幸”,比如数据库读写挂,数据库线程阻塞服务大量超时,你依赖的服务因为各种原因返回超时,缓存雪崩/击穿了等,如果没有一点点防备,那么你的服务也自然是跟着挂了,更不幸得是你的正确服务也可能受到连带影响。在这种情况下,服务降级往往能起到立竿见影的效果。降级的方案或手段有很多种,比如异常处理try{}catch,非关键路径下的开关,FastFail,FastOver等。
高可靠性——压测/流控/限流;
一般工作日饭点,或双休日饭点,或秒杀,或大促节日等,服务的访问量相较平常都会激增几倍或几十倍,如果服务能应付这些特殊节点,那么大部分情况下应该都会安全的。即使是你提供给别人的服务,也需要防止服务被异常突发流量调用所导致的影响。流量控制是预防这种情况的好办法。举个例子:517期间,我们做了一个支付提交的限流策略。当支付后台的待处理请求已超过当前能处理的范围时,返回前台排队提示,并随机产生一个排队倒计时,以减少再次提交产生的流量高峰。
高可靠性——超时时间/重试机制的选择;
这点从部门老大的某次分享会上取得,感觉非常有用。再举个例子,每次选择一个第三方支付工具去支付时,支付渠道都会去对应的第三方申请一个类似预订单号的唯一标识。如果正常情况下,这个第三方服务的响应时间是1s,但某天这个服务响应时间大面积超时,远超过1s,很快Load就是飙高,请求线程池被打满,整条服务链路响应都变得很慢。这种情况很可能是没有设置超时时间或者上下游设置了不当的超时时间导致。对此可以从几个方面考虑,一是每个服务提供方应当根据自身情况提供给服务请求方一个合理的超时时间范围,二每个服务调用方应基于服务链路的请求数和请求模式评估自身服务的正确服务响应时间范围,三善用超时重试机制,过度的重试容易让下游服务更加“措手不及”,四异步化,异步相比同步减少了无效时间的等待,提高了线程的服务效率。
高可用性——服务读写分离;
我们的业务应用大部分都是对数据的加工处理,比如订单数据的维护,优惠信息的查询,优惠资格的扣减等,对这些数据的加工处理基本都需要保持数据的ACID性,而大部分我们都在这一点交给数据库来保证,所以我们的架构一般都是两层是,上面是应用服务,下层是数据库。而上层的应用是无状态的,可以通过横向扩展来实现,瓶颈就落在了下层的数据库。当然下层的横向也可以通过分库分表来达到,但分库分表之后也会存在一些问题,比如增加了技术成本,如何选择分片策略,分布式事务如何保持数据一致性,或者一些历史原因。不管怎么样,回到单库的瓶颈,点评单库的架构是一主多从(2-3从),多从解决了数据库的读的高可用,而写的高可用则只依赖于主库。从应用服务的角度出发,如何能降低主库的风险呢?读写分离,将非实时读流量彻底从主库迁移到从库上去。实际的操作方式有以下几种,一拆分服务,将服务按照读写拆分,单独部署,如此读服务的压力完全由从主库承担;二通过中间件或DAL层,在SQL指定特定的读从库标记,在自定义的DAL层做区别,已分流这部分读流量。前者的好处是不依赖中间件就能做,但是需要早做;后者也是目前工程中的做法。最后一点说的有些乱,总结一句就是,“应用角度应尽早做好读写分离;善待主库”