从0到1微服务架构序列

从2012年微服务概念第一次被提出,到2015年风靡开来,已经走过了5年光阴。每个追求高可用架构的公司都在根据自己的业务尝试,摸索,改进从而形成了一套适合自己的微服务架构。国外也有非常多优秀的微服务架构方面的博客。其中比较深刻阐述微服务方法论的博客应该是martinfowler上面的一篇微服务文章,本文是自己在阅读和结合自己工作经验总结的一篇比较通用的为服务架构。

首先上图

架构图

无论微服务部署在私有云,还是公有云上都可以应用这个架构。下面举一个网上书城的例子详细阐述上图架构。

CDN

网上书城的一些静态资源,比如图片,音频,视频,图书样章,css文件,js文件等变化不大,但是又非常占下载带宽的资源都可以放在CDN上。有三方面方面好处:

  • 首先对于自己服务器来说

大大减轻了web服务器的处理压力。举一个简单的例子,加载网上书城首页的时候,假设首页有50张广告,图书等图片,每张图片100k,那么50张图片就是5M,100个用户同时访问首页的话流量就是500M,这些对自己web服务器压力还是非常大的。

  • 其次对于终端用户体验来说 通过CDN可以屏蔽地区带来的网络延迟,大大减少用户加载页面。提高终端用户的用户体验。
  • 减轻DDOS攻击对自己网站的影响 如果有了CDN,网站在经受DDOS攻击的时候,那些比较大的静态资源文件因为都在CDN上。因此DDOS的大部分压力会被CDN屏蔽,会减轻我们的WEB服务器的部分压力。

代理服务器

因为我们的所有的WEB服务器和应用服务器以及数据库都是不允许外网直接访问的。因此我们必须要有一个代理服务器做一层隔离。代理服务器有三个作用。

  • 隔离终端和web服务器的直接交互。使得终端用户直接看到的IP其实是代理服务器的ip和端口,并非web服务器的ip和端口。
  • 起到负载均衡的作用,把终端用户的流量均分到多态web服务器上。
  • 方便域名的绑定,在配置cdn的时候只用把域名和代理服务器ip绑定即可。这样防止在系统切换版本的时候web服务器ip更换带来的麻烦。

Q&A

Q: 常用的代理服务器有哪些?
A: 开源的代理服务器可以选择nginx或者apache,其中ngnix新版本自身也支持cluster容错率更高。

WEB服务器

为了展开下文,这里先举一个案例,这个案例会贯穿整个下文。小明设计了一套简单的网上书城。而且小明严格按照MVC的三层架构把书城做了出来。然后小明把整个书城打成一个war包,选了一台服务器,部署了上去。书城1.0正式上线。但是突然有一天,产品经理找到小明,说小明做的页面太丑了,要对前端的css样式作调整。然后小明乖乖的重新布局页面,然后在重新打包书城成一个war包,然后重新部署程序。这次产品经理很满意。又过了一段时间,老板找到了小明,说我们要举行一次非常大的促销当时候有非常多的PV量,想办法让书城扛住压力。小明想了想,决定吧服务器内存,cpu都开到原来的2倍,然后重新部署书城。

经过上面例子我们会发现,两个问题:第一个是,小明只是为了修改view层的东西,没有改什么业务逻辑层的东西,就不得不把整个MVC重新打包。这个是非常重量级;还有一个是,为了解决服务器访问压力,小明的思维一直停留在单点服务器的扩充上。这样不仅浪费资源,而且单台节点总会又上线。

我们已经知道了上面的缺点,那么我们就用微服务的视角去拆分上面系统。

把View层单独拆分成一个war包。业务逻辑层单独拆分成一个另外一个war包。view层和业务逻辑层用REST进行交互


他的好处如下

  • 前后端解耦

经过这次拆分,我们我们的view层,单独打包成一个war包,然后单独找一个服务器部署。这样如果需要修改view层的展示样式,那么只用重启view层的war包就可以了,业务逻辑层的war包可以正常运行不受干扰。而且这个时候,我们就会有更多的view层的框架选择,比如我们可以使用nodejs(比如[koa框架]), 或者python(比如django框架)、搭建一个非阻塞的view层架构。

  • 方便水平扩展 如果遇到促销,我们可以部署多个view层的war包到多态低配置的服务器上,这样一台挂掉,另外几台照样可以工作。

    Q&A

    Q:部署多个view层的程序如何做到 session共享?
    A: 这个也是在前端为服务拆分中必须遇到的问题。之前只有一台web服务器,session是单点的。现在水平扩展以后,多态web服务器可以采用如下几种解决方法

  • 不改web程序的情况下。在代理服务器上做手脚,把代理服务器的转发规则改成黏连方案。这样一个用户经过代理服务器的分发以后会一直映射到同一台web服务器。这样好处很明显,可以不用使用内存数据做session的全局缓存,也不用改现有的web程序。但是缺点也很明显,就是如果一台web服务器挂了,问题就来了。

  • 全局session缓存方案。这种方案网站有很多成熟的案例。这里就不一一列举了。这种方案的优点是web服务器真正做到了高可用,高水平扩展性,一台web服务器挂了,只要还有其他web服务器节点存在就还可以提供服务。但是缺点就是要进行一定规模的代码改进,和增加人力去维护session的缓存中间件。
  • 无状态session方案。 这种方案意思是,服务器不在记录session。用户每次登陆以后,服务器会返回一个token给客户端缓存。此后客户端每次请求,会把token带入request的header中,服务器每次处理请求的时候会去数据库(包含内存数据库)中校验token,同时取出对应的权限和用户profile信息。这样优点就是服务器不用缓存session。同时达到了上面的高可用高可扩展性的要求。缺点就是客户端的token一旦被劫持,可能产生未知后果。

应用层

经过小明拆分,已经把web层单独拆分开来了。但是随着书城的经营,业务量逐渐增大,业务层这个war包运行也越来越慢。于是小明准备把业务层按照产品,订单等方面拆成产品微服务和订单微服务,每个微服务打包成一个war包,单独部署。

但是问题来了。之前一个书城就一个war包,部署过程也很简单,开一台服务器,搭建tomcat然后部署war包,一气呵成。但是现在微服务越来越多,部署过程也越来越繁琐。但是团队成员还是那么几个人。怎么办?于是小明通过查找资料,发现了springboot这种技术,不仅开发方便,而且springboot打包的文件再也不用程序员维护tomcat了,因为springboot把tomcat内嵌了。只要把springboot程序打包成一个jra包,然后运行jar包,就会自动开一个web服务器。大大解放了生产力。同时小明也引入了Jenkins等CI/CD工具,把编译打包,部署过程都让jenkins做,自己做的只是在网页大红点一个jenkins页面的按钮,一个微服务就自动发布好了。

APP路由

随着微服务的拆分,现在小明已经有 web微服务,产品微服务,订单微服务。但是现在小明遇到一个很头疼的问题就是,web微服务找 产品微服务要数据的时候,需要知道产品微服务的IP地址,但是每次版本发布或者在水平扩展产品为服务(通俗点就是增加产品为服务的部署机器个数),产品微服务的ip地址都会改变。每次ip地址增加或者改变,小明都要修改web微服务的配置文件,然后重启web微服务生效。小明在想这个过程可不可以自动化?

APP路由来了,app路由做的事情就是主动发现新增的微服务,然后用一个路由表记录微服务的ip

微服务名字 微服务IP地址
产品微服务 192.168.1.2:8080, 192.168.1.3:8080
订单微服务 192.168.1.3:8080, 192.168.1.4:8080


这样web层只要查找这个路由表就知道对应的微服务的IP地址。

其实目前这种APP路由框架有很多,自己也可以基于zookeeper开发一个类似的服务发现和服务注册的框架,也可以用开源的框架比如:Netflix eureka, dubbo

消息中间件

随着书城业务进一步扩大,订单量逐渐攀升。之前的同步处理已经远远满足不了业务的增长,经常有客户点击下单的时候前台的滚动条一直转啊转,就是不见后台响应。于是小明想把下单过程做成异步,也就是用户界面点击下单,web微服务会把请求封装成一个message然后发送给消息中间件,然后快速给界面返回一个下单成功的状态码,这样防止用户一直阻塞在下单页面。然后订单微服务不断从消息中间消费message。作进一步处理。

于是消息中间件呼之欲出。目前主流的消息中间件太多了,Apache ActiveMQ ,JBoss 社区所研发的 HornetQ,Joram, Coridan的MantaRay, The OpenJMS Group的OpenJMS 等等。。。

缓存中间件

经过一段时间的推广,小明主导的网上书城已经经历了一次又一次的促销检验,已经逐渐走向健壮。但是随着业务的增长,小明发现程序层面的难题都已经解决了,但是数据库的压力却逐渐增大,书城的数据库经常CPU和内存爆表。经过几轮排查发现是因为书库中有10%的热门商品会被频繁访问到。而这10%的热数据几乎占了整个数据库性能的90%。因此小明决定引入缓存中间件,把这些热数据缓存在内存中。降低数据库压力,同时提高界面响应速度。 于是缓存中间件引入了。其实如果说消息中间件种类繁多的话,那么缓存中间件就没法穷举了。因为缓存中间件天生就是为业务而生的,不同业务场景会用道不同的缓存中间件,比如如果觉得自己程序比较小,那么自己完全可以用一个CurrentHashMap 自己封装一个缓存,根本不需要在学习和引入什么redis,memorycache什么的。当然了这里也列举几个流行的缓存中间件:redis, memorycache, mongo,ehcache等等。。。

数据库

数据库压力虽然在引入缓存中间件以后有所下降,但是现在网上书城已经逐渐做大,程序也逐渐简装,品类也逐渐增多,订单量已经非常大了。数据库的瓶颈已经成为主要威胁。虽然有有缓存中间件(帮助缓存了10%热数据),但是我们不可能把所有数据都放到缓存里面,否则要数据库干吗。经过小明进一步细致排查,终于发现数据库中的库存表,价格表,用户表频繁读写,这30%的表的读写占了整个书库80%的性能,于是小明决定拆库,把库存表,价格表,产品表拆分开来单独一个数据库。对这个数据库做进一步的读写分离优化。

经过上面的脱胎换骨,小明主导的网上书城项目已经从原来的单个war包,人肉打包,人肉部署。逐渐改进拆分成了多个为服务,而且引入了APP路由来帮助各个微服务自动注册和服务发现。接着又引入了消息中间件来解决同步阻塞界面的问题。后面为了降低RDS的压力,提高系统响应速度引入了缓存中间件。接着又对根据热表进行拆库,和对数据库的读写分离。同时又引入了CI/CD打包发布部署工具。可以说现在的系统已经变成了可水平扩展,高可用的系统了。

到了这里关于微服务架构方法论的知识可以告一段落了,微服务架构还有很多,这里知识列举了其中一个。后面的篇章我会深入每个细节从代码角度详细讲解每个组件的开发细节。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
慷慨打赏