单体应用的缺点
当系统业务过大时,不使用微服务的时候,会出现很多问题
代码的冗余
网站移动端有很多相同业务逻辑的重复代码
数据共享及数据库问题
1.数据有时候通过数据库共享,有时候通过接口调用传输。接口调用关系杂乱。
2.数据库表结构被多个应用依赖,无法重构和优化。
3.所有应用都在一个数据库上操作,数据库出现性能瓶颈。
4.单点故障
接口调用
1.单个应用为了给其他应用提供接口,渐渐地越改越大,包含了很多本来就不属于它的逻辑。应用边界模糊,功能归属混乱。
2.随着功能的增加,出现了性能瓶颈
发布问题
即使只有一个小的地方改动,也需要整个应用一起发布,会有很多未知的风险
2.拆分服务的优点
各个应用后台只需从这些服务获取所需的数据,从而删去了大量冗余的代码,就剩个轻薄的控制层和前端。
3.数据库拆分的必要性
数据隔离
服务的异构
各个服务可以采用异构技术,比如商品服务,访问频率较大可以加入缓存机制
4.微服务的故障解决以及监控机制
单体应用中,排查问题通常是看一下日志,研究错误信息和调用堆栈。而微服务架构整个应用分散成多个服务,定位故障点非常困难,
一个服务故障可能会产生雪崩效用,导致整个系统故障,在这种情况下,事前如何监控,建立一个容错级别高的系统就变得很重要。
监控 - 发现故障的征兆
微服务架构中组件繁多,各个组件所需要监控的指标不同。
比如Redis缓存一般监控占用内存值、网络流量;数据库监控连接数、磁盘空间;业务服务监控并发数、响应延迟、错误率等。因此如果做一个大而全的监控系统来监控各个组件是不大现实的,而且扩展性会很差。
一般的做法是让各个组件提供报告自己当前状态的接口(metrics接口),这个接口输出的数据格式应该是一致的。然后部署一个指标采集器组件,定时从这些接口获取并保持组件状态,同时提供查询服务。最后还需要一个UI,从指标采集器查询各项指标,绘制监控界面或者根据阈值发出告警。
网上有很多类似的开源组件:RedisExporter和MySQLExporter,分别提供了Redis缓存和MySQL数据库的指标接口,微服务可以根据各个服务的业务逻辑实现自定义的指标接口。然后采用Prometheus作为指标采集器,Grafana配置监控界面和邮件告警等等,就可以建立一套微服务监控系统。
![](https://img.haomeiwen.com/i15726274/bc2d1b19967c4120.png)
定位问题 - 链路跟踪
在微服务架构下,一个用户的请求往往涉及多个内部服务调用。为了方便定位问题,需要记录每个用户请求时,微服务内部产生了多少服务调用,及其调用关系。这个叫做链路跟踪。
要实现链路跟踪,每次服务调用会在HTTP的HEADERS中记录至少记录四项数据:
traceId:traceId标识一个用户请求的调用链路。具有相同traceId的调用属于同一条链路。
spanId:标识一次服务调用的ID,即链路跟踪的节点ID。
parentId:父节点的spanId。
requestTime & responseTime:请求时间和响应时间。
另外,还需要调用日志收集与存储的组件,以及展示链路调用的UI组件。
详细的链路追踪理论:
http://bigbully.github.io/Dapper-translation/
分析问题 - 日志分析
在单体应用架构中,当访问数变大、或服务器规模增多时,日志文件的大小会膨胀到难以用文本编辑器进行访问,更糟的是它们分散在多台服务器上面。排查一个问题,需要登录到各台服务器去获取日志文件,一个一个地查找(而且打开、查找都很慢)想要的日志信息。因此,在应用规模变大时,我们需要一个日志的“搜索引擎”。以便于能准确的找到想要的日志。另外,数据源一侧还需要收集日志的组件和展示结果的UI组件:
在这里,ELK是比较广泛使用的,ELK是Elasticsearch、Logstash和Kibana三个组件的缩写。
网关 - 权限控制
服务治理拆分成微服务后,出现大量的服务,大量的接口,使得整个调用关系会比较乱。也会出现在开发过程中,写着写着,忽然想不起某个数据应该调用哪个服务。或者调用了不该调用的服务等等,为了应对这些情况,微服务的调用需要一个把关的东西,也就是网关。在调用者和被调用者中间加一层网关,每次调用时进行权限校验。另外,网关也可以作为一个提供服务接口文档的平台。
服务注册于发现 - 动态扩容
一个服务发现服务,它提供所有已注册服务的地址信息的服务,然后各个应用服务在启动时自动将自己注册到服务发现服务上,并且应用服务启动后会实时(定期)从服务发现服务同步各个应用服务的地址列表到本地。服务发现服务也会定期检查应用服务的健康状态,去掉不健康的实例地址。这样新增实例时只需要部署新实例,实例下线时直接关停服务即可,服务发现会自动检查服务实例的增减。(例如Zookeeper 、Eureka)
实现的功能
熔断、服务降级、限流
熔断
当一个服务因为各种原因停止响应时,调用方通常会等待一段时间,然后超时或者收到错误返回。如果调用链路比较长,可能会导致请求堆积,整条链路占用大量资源一直在等待下游响应。所以当多次访问一个服务失败时,应熔断,标记该服务已停止工作,直接返回错误。直至该服务恢复正常后再重新建立连接。
当一个服务因为各种原因停止响应时,调用方通常会等待一段时间,然后超时或者收到错误返回。如果调用链路比较长,可能会导致请求堆积,整条链路占用大量资源一直在等待下游响应。所以当多次访问一个服务失败时,应熔断,标记该服务已停止工作,直接返回错误。直至该服务恢复正常后再重新建立连接。
![](https://img.haomeiwen.com/i15726274/7f16da434e56622c.png)
服务降级
当下游服务停止工作后,如果该服务并非核心业务,则上游服务应该降级,以保证核心业务不中断。比如网上超市下单界面有一个推荐商品凑单的功能,当推荐模块挂了后,下单功能不能一起挂掉,只需要暂时关闭推荐功能即可。
限流
一个服务挂掉后,上游服务或者用户一般会习惯性地重试访问。这导致一旦服务恢复正常,很可能因为瞬间网络流量过大又立刻挂掉,。因此服务需要能够自我保护——限流。限流策略有很多,最简单的比如当单位时间内请求数过多时,丢弃多余的请求。另外,也可以考虑分区限流。仅拒绝来自产生大量请求的服务的请求。例如商品服务和订单服务都需要访问促销服务,商品服务由于代码问题发起了大量请求,促销服务则只限制来自商品服务的请求,来自订单服务的请求则正常响应。
网友评论