对接互联网所面临的最大的问题,就是巨大的用户量所带来的请求量和数据量,会是原来的N倍,能不能撑得住,大家都心里没底。虽然已经用了Dubbo实现了服务化,但是没有熔断,限流,降级的服务治理策略,有可能一个请求慢,高峰期波及一大片,或者请求全部接进来,最后都撑不住而挂一片。
一旦到了互联网大促级别,Oracle数据库是肯定扛不住的,需要从Oracle迁移到DDB分布式数据库
从SOA到微服务化,拆分粒度非常的细

微服务解决的问题:

1:快速迭代
第一一统天下,第二被第一收购,其他死翘翘。
2:提交代码频繁大量冲突
3:小功能上线独立,不需要等待其他模块开总监大会。
4:高并发
5:横向扩展,主要业务进行扩容,次要业务保持
5:服务熔断降级:保证次要业务不影响核心业务

微服务化的需要措施

持续集成

1:拆分如何保证功能不变,不引入Bug——持续集成

静态缓存

2:静态资源要拆分出来,缓存到接入层或者CDN,将大部分流量拦截在离用户近的边缘节点或者接入层缓存——数据中心,CND,静态缓存。
https://mp.weixin.qq.com/s?__biz=MzI1NzYzODk4OQ==&mid=2247484791&idx=1&sn=4cb4fb04b481c3aee8a882934c8d925f&chksm=ea151255dd629b43c6383d912234ae0d53fad34e354fdf985a6461aa2774deaf313a234d64fb&scene=21#wechat_redirect

容器化

3:应用的状态要从业务逻辑中拆分出来,使业务无状态,可以基于容器进行横向扩展——容器,Docker和K8S管理

拆分与服务发现

4:核心业务和非核心业务要拆分,方便核心业务的扩展以及非核心业务的降级 —— 微服务拆分与服务发现

库的分离

5:数据库要读写分离,要分库分表,数据库要具有横向扩展能力,不能成为瓶颈。

缓存

6:要层层缓存,只有少量的流量到达数据库——缓存的设计
缓存设计有CDN动静资源隔离,Tomact本地缓存,但是本地缓存存放在JVM中,会面临FullGC问题,还有就是分布式缓存,在Tomact和数据库中间添加一层,常用的有Redis和memcached,

缓存架构设计

1:多层次:某一层挂了,还有一层可以撑着。
2:分场景:要明确要存储大的无格式的数据,还是要存储小的有格式的数据,还是要存储一定需要持久化的数据。
3:要分片:多个实例,达到负载均衡,防止单个实例成为瓶颈或者热点。分片机制可以使用Redis的Cluster方式,分片的算法往往是哈希取模或者一致性哈希。

使用

1:和数据库结构一样,原样缓存
2:列表排序分页的缓存
如果我们想获取点赞最多的评论,或者最新的评论,然后列出来,一页一页的翻下去。
3:计数缓存
将计数作为结果放在缓存里面,当数据有改变的时候,调用计数服务增加或者减少计数,而非通过异步数据库count来更新缓存。
4:重构维度缓存
需要为了查询方便,将数据重新以另一个维度存储一遍,从而不用每次查询的时候都重新聚合,如果还是放在数据库,比较难维护,放在缓存就好一些。
例如一个商品的所有的帖子和帖子的用户,以及一个用户发表过的所有的帖子就是属于两个维度。
在这种场景下,数据量相对比较大,因而单纯用内存缓存memcached或者redis难以支撑,往往会选择使用levelDB进行存储,如果levelDB的性能跟不上,可以考虑在levelDB之前,再来一层memcached。

问题

1:实时性与一致性问题
2:缓存的穿透问题:没有读到怎么办?
为什么会出现缓存读取不到的情况呢?
第一:可能读取的是冷数据,原来从来没有访问过,所以需要到数据库里面查询一下,然后放入缓存,再返回给客户。
第二:可能数据因为有了写入,被实时的从缓存中删除了,就如第一个问题中描述的那样,为了保证实时性,当数据库中的数据更新了之后,马上删除缓存中的数据,导致这个时候的读取读不到,需要到数据库里面查询后,放入缓存,再返回给客户。
第三:可能是缓存实效了,每个缓存数据都会有实效时间,过了一段时间没有被访问,就会失效,这个时候数据就访问不到了,需要访问数据库后,再放入缓存。
第四:数据被换出,由于缓存内存是有限的,当使用快满了的时候,就会使用类似LRU策略,将不经常使用的数据换出,所以也要访问数据库。
第五:后端确实也没有,应用访问缓存没有,于是查询数据库,结果数据库里面也没有,只好返回客户为空,但是尴尬的是,每次出现这种情况的时候,都会面临着一次数据库的访问,纯属浪费资源,常用的方法是,讲这个key对应的结果为空的事实也进行缓存,这样缓存可以命中,但是命中后告诉客户端没有,减少了数据库的压力。
无论哪种原因导致的读取缓存读不到的情况,该怎么办?是个策略问题。
一种是同步访问数据库后,放入缓存,再返回给客户,这样实时性最好,但是给数据库的压力也最大。
另一种方式就是异步的访问数据库,暂且返回客户一个fallback值,然后同时触发一个异步更新,这样下次就有了,这样数据库压力小很多,但是用户就访问不到实时的数据了。
3:
解决的刷新策略
1:实时策略
读先从cache读,没读到在从数据库读然后再放入cache。写入是先写数据库,再删缓存
2:异步策略
读不到时,直接返回一个fallback数据,往消息队列放入数据加载的事件,可以削峰。
更新的时候:如果先更新数据库再更新缓存,实时性较差;先更新缓存在更新数据库,这种缓存完全挡在数据库前面了。实时性好,但是需要持久化机制和主备策略。
3:定时策略

异步化消息队列

熔断限流降级
配置中心
日志
链路追踪
压测

拆分规范

1:拆分最多三层,两次调用

拆分是为了横向扩展,因而应该横向拆分,而非纵向拆分成一串的。如:应该讲商品和订单模块进行拆分,而非将下单的十多个步骤进行拆分,然后一个调用一个。

2:单向调用,严禁循环调用

如果循环调用,升级就很头疼

3:串行改并行,异步化

如果有的组合服务处理流程的确很长,需要调用多个外部服务,应该考虑如何通过消息队列,实现异步化和解耦。
例如下单之后,要刷新缓存,要通知仓库等,这些都不需要再下单成功的时候就要做完,而是可以发一个消息给消息队列,异步通知其他服务。

4:接口实现幂等

幂等一般需要设计一个幂等表来实现,幂等表中的主键或者唯一键可以是transaction id,或者business id,可以通过这个id的唯一性标识一个唯一的操作。

5:接口数据定义严禁内嵌,穿透

接口对象应该

6:规范化工程名

服务发现选型

Dubbo注册到zookeeper里面的是接口,而springcloud注册到Eureka或者consul里面的是实例,
在规模比较小的情况下没有分别,但是规模一旦大了,例如实例数目万级别,接口数据就算十万级别,对于zookeeper中的树规模比较大,而且zookeeper是强一致性的,当一个节点挂了的时候,节点之间的数据同步会影响线上使用,而springcloud就好很多,实例级别少一个量级,另外consul也非强一致的。
很多springcloud可以做的事情,kubernetes也有相应的机制,而且由于是容器平台,相对比较通用,可以支持多语言,对于业务无侵入,但是也正因为是容器平台,对于微服务的运行生命周期的维护比较全面,对于服务之间的调用和治理,比较弱
因而实践中使用的时候,往往是kubernetes和springcloud结合使用,kubernetes负责提供微服务的运行环境,服务之间的调用和治理,由springlcoud搞定。

运维

有了镜像容器,开发交给运维的是一个容器镜像,容器内部的运行环境,应该体现在Dockerfile文件中,这个文件应该是开发写的。
容器镜像有个特点,就是ssh到里面做的任何修改,重启都不见了,恢复到镜像原来的样子,也就杜绝了原来我们部署环境,这改改,那修修最后部署成功的坏毛病。

DevOps团队