Spring Cloud实战微服务及架构概述
文章目录
自从Martin Fowler在2014年写了一篇名为Microservices的文章后,微服务就在服务端架构中开始流行起来,至今依然很火热。
微服务架构模式是目前最流行的架构模式之一。在各互联网公司中,都有着丰富的实践案例。比如国外的有Amazon、eBay、Netflix等。国内的有阿里巴巴、腾讯、百度等。
近年来,同花顺也有诸多项目基于微服务架构实现,积累了大量的微服务架构实践案例。
2015年Dubbo
2017年Spring Cloud
2019年Spring Cloud In K8s
那么什么是微服务架构呢?
微服务架构的本质就是将一个原本独立的系统拆分成多个小型的服务,这些小型的服务都在各自独立的进程中运行,服务之间通过远程通信进行调用。
我会以互联网大金融综合实训平台为例来讲解一下微服务架构的演变过程。
有一天,产品跑过来说,我们需要做一个面向金融相关专业学生的模拟实践训练系统。这个系统的需求是这样子的,我们需要有老师和学生两个角色。老师能够管理学生,举行交易比赛和查看交易记录。学生呢,可以相应的在上面进行模拟交易。
好,我们来梳理一下需求:
看上去很简单嘛(SO EASY !),需求不多,开发同学们,(啥也不说,撸起袖子来),拿起键盘就是干。
搭框架:
Spring, Spring MVC, MyBatis。
Controller, Service, Dao。
一顿操作猛如虎,由于第一阶段的需求不多,系统很快就开发完成了,经过测试可以部署上线。✌️ 💓
系统上线后,也满足了实践教学的需要。同学们可以在系统里真实的体验股票交易,老师可以方便的进行查看同学们的交易记录,提出合理的指导和给出相应的得分。大家都很开心,心里美滋滋。(好开心,好有成就感,满满的自豪。我们真是棒棒哒! 💥)
其他课程的老师看到了,嗯,这个系统不错,我们也要。于是,吧啦吧啦,产品同学经过一波分析和整理。我们需要上线以下功能:
交易中心,模拟公司,银行,基金,期货。
功能一多,产品设计变得复杂,期间伴随着一大波需求变更。
OK。开发同学们一接到需求,就噼里啪啦的往里面加功能。很快,功能又开发完成了。这个时候的系统变成了下图这样子。
虽然,代码开发完成,功能也如期提测。但是,这个时候问题也开始逐渐的暴露出来了。( 好心塞哟 😢 😭)
测试人员开始抱怨了…
发布怎么这么慢呀。
怎么这多BUG呀。
这个功能怎么又坏了啊。
为什么会出现这些问题呢?
由于单个系统的功能越来越多,代码也越来越多,引用的第三方包也越来越多,导致系统在初始化的时候需要做更多的事情,那么启动时间也自然变得越来越慢。
功能变得复杂,业务之间存在着关联关系。但是,模块和模块之间却没有清晰的隔离,在完成需要跟其他模块进行交互的功能的时候,随意的加接口,改接口,直接查对方的数据,这将导致代码的耦合性变得越来越高,导致修改变得越来越困难。
anyway,不管怎么样,在测试和开发同学的共同努力下,BUG也被一一解决了。测试通过,系统终于上线了。但是,噩梦以此为始,线上问题开始不断冒出来。( 我太南了 😩 😰 )
系统会时不时的变慢。由于,所有模块都在一个进程中,老师进行对学生操作记录的统计,导出等相关操作,一旦出现问题,都可能会影响到股票模块的正常进行。
扩容造成的资源浪费。模块和模块之间的访问量是不同,比如教学模块和股票模块,由于教学模块主要是面向教师进行一些管理操作,而股票模块会有更多的学生参与交易操作。简单的讲,那么我们可能需要9台机器来部署股票模块,1台机器来部署教学模块。但是呢,我们需要把整个应用一起扩容,下面灰色部分其实是多余的模块,白白消耗了机器的资源。
开发同学们反思了一下,不能一直这样下去,我们需要进行改变。
我们对原有的系统进行了一系列的拆分。我们根据业务的功能独立性,重要程度,访问量,IO型(文件操作)和非IO型进行了拆分,服务和服务之间通过HTTP接口相互交互。现在系统变成了这样。
服务和服务之间完全被隔离开来。我们来看看拆分后的结果。
对于稳定性, 由于每个微服务都是独立的进程,通过进程的隔离,我们能够保证在某个服务不可用的时候,其他服务还是可以正常的使用。
对于伸缩性,我们对于业务资源的调整粒度更加的小,我们更加方便的进行业务资源的扩容和缩减,同时扩容和缩减所需要的时间也更短。
对于可修改性,我们将系统进行拆分,每个服务之间必须通过接口来进行远程调用,这样子能够保证服务之间的耦合只存在于接口。同时,由于拆分,每个服务专注于自己的业务实现,保持了高内聚的特性。我们在做修改的时候只需要关注修改操作不要对接口产生影响即可。
到这里,我们使用拆分解决了刚刚在单体应用中遇到的几个问题。但是,这样就好了???
( Naive!!!!)
服务扩容的时候,如何把新机器的IP地址告诉调用方?
如何防止某个服务不可用带来的雪崩效应?
如何将请求在服务之间的调用链路显示出来呢?
如何管理配置和动态的将配置发布出去?
接下来我们要针对这几个问题,一一的进行解答。
上述问题都可以通过引入微服务框架得到解决。
我们来想象一下这样的场景:用户量激增,原来的几个实例不够用了,我们需要新加几个股票实例怎么办?或者上课时间请求量多,非上课时间段请求量少,那么我们应该在非上课时间下线几个实例吧。
我们找了几台服务器把应用部署了上去,这个时候还要去把新的IP地址配置到Nginx中,下线的时候从Nginx当中去除掉。手工操作太麻烦,太累,容易出错。
同时,集中式的Nginx又会成为我们的瓶颈。怎么办?
首先,我们可以将Nginx的负载均衡功能内嵌到我们的应用当中,减少集中式的流量。
其次,我们需要部署一个注册中心来动态的维护所有服务实例的地址信息,其需要具备以下功能:
服务启动的时候,将自己的地址信息注册到注册中心
服务方定时报告健康状态给注册中心,如果服务掉线,注册中心就将该实例剔除出去
调用方定时的从注册中心获取最新的地址列表
雪崩效应:服务之间存在着层层的依赖关系,服务A的不可用将会引起服务B的不可用,同时不可用将像滚雪球一样放大到C和D。
在分布式系统中,故障无处不在,我们要应该拥抱故障,在故障出现时合理的处理它。对于调用其他服务,我们要始终保持怀疑,其他服务可能会是个“坏人”。
下图中可以看到,由于股票服务出现故障不可用,将会导致交易中心会不断重连,并会在连接的超时时间内建立很多的连接请求,最终占满交易中心的连接池,导致交易中心的服务也不可用。
交易中心不可用后,将会将错误蔓延到学生端。最终导致服务的全面崩溃。
那么我们应该如何去处理雪崩效应呢?
我们可以想象一下,家里的电路。当我们家里的电器发生短路时候,电路就会自动跳闸,从而保护其他电器。
断路器模式也一样的,当我们发现其他服务出现问题的时候,我们应该拉下电闸,不去请求对方,保护我们自己服务的正常进行。
使用断路器时,当对下游资源的发起的请求连续失败过多,达到阈值时,断路器会打开。
这个时候,接下来的所有请求都会快速地失败,不再去调用下游资源。
同时,每隔一段时间,客户端发送一些请求试着访问一下下游服务,看看是不是能够访问,如果它得到了正常的响应,将关闭断路器。接下去的请求就又能够成功的通过了。
我们知道,微服务之间通过网络进行通信 。 我们需要快速的定位到问题出在哪个服务上。如果能够跟踪每个请求,了解请求经过哪些
微服务(从而了解信息是如何在服务之间流动) 、 请求耗费时间、网络延迟 、 业务逻辑
耗费时间等指标,那么就能更好地分析系统瓶颈、解决系统问题。
下图是zipkin的界面展示图,我们可以很清楚的看到请求在服务之间调用的先后关系和耗时。
要完成这个效果,我们需要将效果转换成可以实现的数据结构,如下图所示。整个链路我们称之为Trace,每个请求的分支一一对应的就是Span。
首先,通过记录Trace和Span的父子关系,我们可以建立一个树形的结构。由于我们在一次请求中共享同一个TraceId,然后根据TraceId查找出所有关联的日志信息。同时,我们可以根据SpanId的父子关系重构出当时请求的结构。
TraceId:TraceId标识一次请求中的所有日志。
SpanId:标识一个分支节点,用于单独计算持续时间。比如远程调用,方法调用等。
ParentSpanId:父节点的spanId。 根据和SpanId的关系我们可以整理出一个树形的结构。通常Root节点的ParentSpanId即为TraceId。
接着,通过记录几个时间戳,来分析请求的耗时和先后次序。
cs(client send):客户端发起请求的时间。
cr(client recive):客户端接收响应的时间。
sr(server recive):服务端接收到请求的时间。
ss(server send):服务端发送响应的时间。
另外,我们根据各个端点收集到时间戳信息。可以进行以下的计算,
请求总耗时 = cr - cs
网络耗时 = sr - cs
服务内部处理时间 = sr - sc
最后,我们需要将各个机器上的日志集中到一起
我们在每个客户端内置一个client,异步的将内存中的请求日志发送到zipkin服务端,服务端在统一将日志存储起来。
随着业务的发展、微服务架构的升级,服务的数量、程序的配置日益增多,因此集中管理配置是非常有必要的 。 同时,我们需要达到修改配置,无需重启,实时将配置推送到客户端。
很多时候,应用中包含着许多无需修改代码的配置属性。例如,我们调用其他部门的接口某天突然通知,我们的服务器地址要变了,你们也改一下吧。好吧,原先怎么做呢,配置文件需要改一下,然后重新打包发布上线。现在呢,鼠标点一点。配置中心改一下,然后推送到应用方,应用方自动动态更新,都不用重启,爽。
这个时候我们需要一个配置中心协助我们管理配置和动态发布配置功能。
那么怎么做到呢?
应用方需要在本地配置配置中心的地址。
通过配置中心修改和发布配置后,调用应用的相关接口告知进行配置刷新。
然后应用主动拉取配置中心的最新配置文件,并根据最新的配置文件动态更新本地内存中的相关数据。
刚刚所介绍的都是对微服务中几个问题的相应解决方案,那么都有一些什么框架能够实现这些方案呢。
微服务的实现框架有很多,比如Dubbo、Zookeeper等,我们为什么要选择Spring Cloud呢。
Spring Cloud是一个解决微服务架构实施的综合性解决框架,它整合了诸多被广泛实践和证明过的框架作为实施的基础部件,又在该体系基础上创建了一些非常优秀的边缘组件。所有微服务实施中需要的相应解决方案,Spring Cloud都帮我们实现了。像Dubbo或者Apollo都只是实现了微服务框架中的某个组件。
服务治理:Dubbo、Eureka、Zookeeper、Consul等
配置管理:百度的Disconf、携程的Apollo、淘宝的Diamond、Spring Cloud的Config、Netflix的Archaius等
服务跟踪:Zipkin、Uber的Jaeger、Spring Cloud的Sleuth等
组件名称组件用途
Spring Cloud Netflix Eureka服务注册和发现
Spring Cloud Netflix Hystrix服务熔断和降级
Spring Cloud Netflix Zuul服务网关
Spring Cloud OpenFeign服务调用
Spring Cloud Config配置中心
Spring Cloud Sleuth服务跟踪
典型的基于Spring Cloud的微服务应用,其需要用到如下组件:
具体每个微服务组件怎么用呢?
且待后续系列课程 一 一分解!!!
本博客所有内容仅供学习,不为商用,如有侵权,请联系博主谢谢。
[1] Martin Fowler. https://martinfowler.com/articles/microservices.html
[2] Netflix. 服务全球超 1.5 亿用户的微服务实践. https://www.infoq.cn/article/f_Q7xWtgZ93djZALqWoP
[3] 朱勇. 阿里巴巴微服务技术实践. https://www.infoq.cn/article/KGxyTJb2Cr7hguKmjg0p
[4] 周立. Spring Cloud与Docker微服务架构实战.
[5] Opentracing. https://opentracing.io/docs/getting-started/
owler. https://martinfowler.com/articles/microservices.html
[2] Netflix. 服务全球超 1.5 亿用户的微服务实践. https://www.infoq.cn/article/f_Q7xWtgZ93djZALqWoP
[3] 朱勇. 阿里巴巴微服务技术实践. https://www.infoq.cn/article/KGxyTJb2Cr7hguKmjg0p
[4] 周立. Spring Cloud与Docker微服务架构实战.
[5] Opentracing. https://opentracing.io/docs/getting-started/
[6] 微服务设计.