浅谈这些年做过的千万级系统重构项目
Posted 浅谈架构
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈这些年做过的千万级系统重构项目相关的知识,希望对你有一定的参考价值。
重构,需要做哪些事情。
正好最近有朋友公司php技术栈转Java,问我一些关于系统重构的问题。我总结下来如下三大点:
一、 做什么
首先要明确,要解决的问题是什么?做完重构能带来什么收益?做完是否能出现立竿见影的效果,投入产出比怎么样等等。
1、 第一要考虑组内成员的学习成本,虽然语言切换不是什么问题(个人亲身经历),但是至少得有个3个月左右缓冲期。2、要明确知道遇到的瓶颈是什么?要解决的核心问题是什么?3、模块划分,先确定哪些部分要重构成Java,先从一个点出发,解决核心问题。完善Java技术栈(注册中心,配置中心,分库分表中间件,监控,报警,全链路追踪)并且稳定运行之后,再迁移其它部分。
二、怎么做?
考虑清楚做什么之后,再去分析需要重构的模块,分析每一条SQL语句,分库分表怎么拆分,表结构是否需要修改。
分表shardkey是什么,当有非shardkey如何查询(建冗余表,还是汇总到ES+Hbase查询等)
分库分表如何做,是分库、分表,还是既分库又分表,分多少库,多少表。
考虑分库主要考虑是否有写入瓶颈,和方便以后水平扩展等问题。考虑分表主要是查询量比较大,个别表的量比较大。单表记录数量建议控制在1000W以下。
表结构修改了,是否需要双写。双写数据迁移方案怎么做。数据如何补仓,对比脚本编写等等。
灰度方案怎么做,怎么保证平滑迁移。
如果时间紧迫,可以先完成业务拆分,分库分表等核心部分。像监控、报警、全链路追踪这些辅助工具,可以等灰度上线之后再完善。
三、需要什么?
要初步估算预期目标
例如需要投入多少人力开发(一部分支持重构,一部分人还需要支持现有业务的正常运转),
我们要支撑下单多少TPS,重构后,需要多少docker服务资源,mysql数据库资源,Redis机器资源等。
重构,最重要的是什么?答案: 都重要,环环相扣,每一个环节都不能少。
如果非要我给出一个。
那么是:“灰度方案”
历史经验告诉我,重构就没有不出问题的。那么核心问题就是怎么去把风险降到最低程度。那就是“灰度方案”解决的问题。
下面我重点说说灰度方案。
假如你只是重构一个接口,那很简单,加个开关就行。影响也不会太大。
但是如果重构的整个订单系统,而且表结构也修改了,直接上线能行吗?出现问题后果不敢想象。
我们常用的方案就是按流量去灰度。
阶段 | 流量比例 | 说明 |
---|---|---|
阶段一 | 白名单 | (一般就添加内部人员,只有这部分人访问到新系统,有问题也只影响到我添加的这部分人) |
阶段二 | 万分之一 | 有问题只影响万分之一的人,比如我一分钟下单10W, 那有问题一分钟也就影响10个人。这个阶段基本上能发现一些问题。 |
阶段三 | 1% | 流量逐步放大。 |
阶段四 | 10% | 一般偶现问题在这个阶段会凸显出来,例如并发问题。 |
--- | ---- | ---- |
阶段N | 100% | 流量100%切换到新系统之后,老系统退出历史舞台。 |
以上分多少个阶段,可以由本公司的业务场景,请求量来决定。目的就是降低风险,让风险可控。
那么,按流量灰度怎么做呢?
关注我的人知道,我之前也写过文章,浅谈基于openresty(nginx+lua)开发轻量级,按流量控制的灰度模块,那这个什么时候会用呢。
这要看你重构系统的范围。假如你只是重构订单系统,对其它的组没有啥影响。那在网关层加一个灰度算法就能解决。
很简单,弄一个整型随机数。随机数 / 基数10000 < X 计算,当为true 走新系统,否则走老系统。例如 10%流量 , X = 10即可。
但是如果你重构的系统范围比较大,整个公司底层架构都要变,影响多个组重构,那可能就需要在nginx层开发lua灰度模块。
总结以前余老大说过这样一句话:"重构这件事,做一次就够了",我却折腾了三次。但是不得不说,重构之后系统性能提升,解决没有遇到的问题是很愉快的。也许,这就是架构之美吧。
在这里,也感谢曾经一起奋斗的兄弟们。
最后,如果你有PHP转Java语言,或者重构方面有什么问题的朋友,可以私信我交流。
欢迎关注 “浅谈架构” 公众号,不定期分享原创文章。一起交流学习,进步,共勉!
微服务注册中心如何承载大型系统的千万级访问?
一、问题起源
Spring Cloud微服务架构体系中,Eureka是一个至关重要的组件,它扮演着微服务注册中心的角色,所有的服务注册与服务发现,都是依赖Eureka的。不少初学Spring Cloud的朋友在落地公司生产环境部署时,经常会问:Eureka Server到底要部署几台机器?我们的系统那么多服务,到底会对Eureka Server产生多大的访问压力?Eureka Server能不能抗住一个大型系统的访问压力?如果你也有这些疑问,别着急!咱们这就一起去看看,Eureka作为微服务注册中心的核心原理下面这些问题,大家先看看,有个大概印象。带着这些问题,来看后面的内容,效果更佳!Eureka注册中心使用什么样的方式来储存各个服务注册时发送过来的机器地址和端口号?各个服务找Eureka Server拉取注册表的时候,是什么样的频率?各个服务是如何拉取注册表的?一个有几百个服务,部署了上千台机器的大型分布式系统,会对Eureka Server造成多大的访问压力?Eureka Server从技术层面是如何抗住日千万级访问量的?先给大家说一个基本的知识点,各个服务内的Eureka Client组件,默认情况下,每隔30秒会发送一个请求到Eureka Server,来拉取最近有变化的服务信息举个例子:库存服务原本部署在1台机器上,现在扩容了,部署到了3台机器,并且均注册到了Eureka Server上。然后订单服务的Eureka Client会每隔30秒去找Eureka Server拉取最近注册表的变化,看看其他服务的地址有没有变化。除此之外,Eureka还有一个心跳机制,各个Eureka Client每隔30秒会发送一次心跳到Eureka Server,通知人家说,哥们,我这个服务实例还活着!如果某个Eureka Client很长时间没有发送心跳给Eureka Server,那么就说明这个服务实例已经挂了。光看上面的文字,大家可能没什么印象。老规矩!咱们还是来一张图,一起来直观的感受一下这个过程。
二、Eureka Server设计精妙的注册表存储结构
现在咱们假设手头有一套大型的分布式系统,一共100个服务,每个服务部署在20台机器上,机器是4核8G的标准配置。也就是说,相当于你一共部署了100 20 = 2000个服务实例,有2000台机器。每台机器上的服务实例内部都有一个Eureka Client组件,它会每隔30秒请求一次Eureka Server,拉取变化的注册表。此外,每个服务实例上的Eureka Client都会每隔30秒发送一次心跳请求给Eureka Server。那么大家算算,Eureka Server作为一个微服务注册中心,每秒钟要被请求多少次?一天要被请求多少次?按标准的算法,每个服务实例每分钟请求2次拉取注册表,每分钟请求2次发送心跳这样一个服务实例每分钟会请求4次,2000个服务实例每分钟请求8000次换算到每秒,则是8000 / 60 = 133次左右,我们就大概估算为Eureka Server每秒会被请求150次那一天的话,就是8000 60 * 24 = 1152万,也就是每天千万级访问量好!经过这么一个测算,大家是否发现这里的奥秘了?首先,对于微服务注册中心这种组件,在一开始设计它的拉取频率以及心跳发送频率时,就已经考虑到了一个大型系统的各个服务请求时的压力,每秒会承载多大的请求量。所以各服务实例每隔30秒发起请求拉取变化的注册表,以及每隔30秒发送心跳给Eureka Server,其实这个时间安排是有其用意的。按照我们的测算,一个上百个服务,几千台机器的系统,按照这样的频率请求Eureka Server,日请求量在千万级,每秒的访问量在150次左右。即使算上其他一些额外操作,我们姑且就算每秒钟请求Eureka Server在200次~300次吧。所以通过设置一个适当的拉取注册表以及发送心跳的频率,可以保证大规模系统里对Eureka Server的请求压力不会太大。现在关键的问题来了,Eureka Server是如何保证轻松抗住这每秒数百次请求,每天千万级请求的呢?要搞清楚这个,首先得清楚Eureka Server到底是用什么来存储注册表的?三个字,看源码!接下来咱们就一起进入Eureka源码里一探究竟:
如上图所示,图中这个名字叫做registry的CocurrentHashMap,就是注册表的核心结构。看完之后忍不住先赞叹一下,精妙的设计!从代码中可以看到,Eureka Server的注册表直接基于纯内存,即在内存里维护了一个数据结构。各个服务的注册、服务下线、服务故障,全部会在内存里维护和更新这个注册表。各个服务每隔30秒拉取注册表的时候,Eureka Server就是直接提供内存里存储的有变化的注册表数据给他们就可以了。同样,每隔30秒发起心跳时,也是在这个纯内存的Map数据结构里更新心跳时间。一句话概括:维护注册表、拉取注册表、更新心跳时间,全部发生在内存里!这是Eureka Server非常核心的一个点。搞清楚了这个,咱们再来分析一下registry这个东西的数据结构,大家千万别被它复杂的外表唬住了,沉下心来,一层层的分析!首先,这个ConcurrentHashMap的key就是服务名称,比如“inventory-service”,就是一个服务名称。value则代表了一个服务的多个服务实例。举例:比如“inventory-service”是可以有3个服务实例的,每个服务实例部署在一台机器上。再来看看作为value的这个Map:Map<String, Lease<InstanceInfo>>这个Map的key就是服务实例的idvalue是一个叫做Lease的类,它的泛型是一个叫做InstanceInfo的东东,你可能会问,这俩又是什么鬼?首先说下InstanceInfo,其实啊,我们见名知义,这个InstanceInfo就代表了服务实例的具体信息,比如机器的ip地址、hostname以及端口号。而这个Lease,里面则会维护每个服务最近一次发送心跳的时间三、Eureka Server端优秀的多级缓存机制假设Eureka Server部署在4核8G的普通机器上,那么基于内存来承载各个服务的请求,每秒钟最多可以处理多少请求呢?根据之前的测试,单台4核8G的机器,处理纯内存操作,哪怕加上一些网络的开销,每秒处理几百请求也是轻松加愉快的。而且Eureka Server为了避免同时读写内存数据结构造成的并发冲突问题,还采用了多级缓存机制来进一步提升服务请求的响应速度。在拉取注册表的时候:首先从ReadOnlyCacheMap里查缓存的注册表。若没有,就找ReadWriteCacheMap里缓存的注册表。如果还没有,就从内存中获取实际的注册表数据。在注册表发生变更的时候:会在内存中更新变更的注册表数据,同时过期掉ReadWriteCacheMap。此过程不会影响ReadOnlyCacheMap提供人家查询注册表。一段时间内(默认30秒),各服务拉取注册表会直接读ReadOnlyCacheMap30秒过后,Eureka Server的后台线程发现ReadWriteCacheMap已经清空了,也会清空ReadOnlyCacheMap中的缓存下次有服务拉取注册表,又会从内存中获取最新的数据了,同时填充各个缓存。多级缓存机制的优点是什么?尽可能保证了内存注册表数据不会出现频繁的读写冲突问题。并且进一步保证对Eureka Server的大量请求,都是快速从纯内存走,性能极高。为方便大家更好的理解,同样来一张图,大家跟着图再来回顾一下这整个过程:
四、总结
通过上面的分析可以看到,Eureka通过设置适当的请求频率(拉取注册表30秒间隔,发送心跳30秒间隔),可以保证一个大规模的系统每秒请求Eureka Server的次数在几百次。同时通过纯内存的注册表,保证了所有的请求都可以在内存处理,确保了极高的性能另外,多级缓存机制,确保了不会针对内存数据结构发生频繁的读写并发冲突操作,进一步提升性能。上述就是Spring Cloud架构中,Eureka作为微服务注册中心可以承载大规模系统每天千万级访问量的原理。
以上是关于浅谈这些年做过的千万级系统重构项目的主要内容,如果未能解决你的问题,请参考以下文章