微服务的概念
微服务是一种架构概念,通过将功能分解到各个离散的服务中以实现各个功能间的解耦。
下面我们通过回顾网站架构的演化过程,进而聊聊微服务到底是什么、有哪些优缺点。
网站架构演化历程
先看这样一个例子,假设有一个电子商务公司,他们有四大模块:
- 商品资料
- 库存
- 订单
- 支付系统
商品资料 肯定属于基础服务,提供商品信息的录入/修改/查询等操作;
库存 是指商品或者 SKU 的库存数量,商品采购或售出都要对库存进行修改;
订单系统 是客户购买商品生成的订单,会涉及到支付;
支付系统 是指对接支付宝/微信/银联来实现第三方支付功能的模块。
所以很明显,我们知道这里面的调用关系如下图所示:
1. 早期的 LAMP 网站架构
早期的网站架构(大概2000年初那会),很多采用 LAMP 架构(Linux、Apache、MySql、Php)架构,这种架构实现起来简单,开发速度快,部署方便,一般一台服务器就能满足,多个模块在一个项目里开发,能以最快的速度满足业务需求。在实际开发中一般是如右图所示的结构:
可以看到不同的业务模块是在一个应用项目中实现,共用同一个数据库,应用项目和数据库在一台服务器上,只需要一台服务器就能实现。
那么请思考:这种架构的优缺点是什么?
优点:
架构简单、开发速度快、开发难度低。
缺点:
各模块耦合严重,一个模块出现问题,就会影响其他模块的运行,一个模块部署也会影响其它模块。随着项目规模越来越大,后期难以维护和拆分,难以重构。(实际开发中有很多这种例子)
2. 应用服务和数据服务分离
我们已经知道“初期阶段的网站架构”架构的缺点,那么可以进一步优化,如图所示:
从上边架构图可以看到,数据库服务和应用服务分开了,看起来改动不大,代码层面没有改动,仅仅是将数据库服务单独拆分出来,那么此时就需要两台服务器了。
大家想一下这样拆分的优点和缺点各是什么?
优点:
不同特性的服务器承担不同的角色,比如应用服务器,要处理比较多的逻辑,所以就需要更快的CPU和更大的内存,而数据库服务器需要更大的磁盘容量。
缺点:
各模块耦合严重,一个模块出现问题,就会影响其他模块的运行,一个模块部署也会影响其它模块。随着项目规模越来越大,后期难以维护和拆分,难以重构。(实际开发中有很多这种例子)
3. 微服务架构
针对上一节中的架构我们继续优化,如下图所示:
我们把 “订单”、“库存”、“商品资料”、“支付系统”各自单独拆分成了一个应用服务,以实现各个模块的解耦。
拆分之后,必然有面临一个问题:
原先的方法调用,在微服务中该怎么实现呢?(因为在不同的项目/服务器上了)。
很容易可以想到:不同服务之间的调用关系,使用 http 方式调用(ip:port)。
继续思考:如果某个微服务增加节点或者减少节点,怎么知道调用的目标机器IP地址呢?
1)自己实现(心跳探测+服务自己上报,NameServer)。
2)注册中心(后面会讲)。
这样架构的优缺点是什么呢?
优点:
- 各模块互相隔离,不再耦合,每个微服务可以专注单一的一块业务逻辑,可以提高开发速度,快速迭代,持续交付,同时也更容易保证这块业务的稳定性。
- 每个微服务可独立部署。
- 技术异构性(不同的模块,可以使用不同的技术/编程语言)。
缺点/难点:
- 增加了系统的复杂性和开发门槛。
- 需要引入更多的技术和中间件(如rpc框架/日志收集/监控/链路跟踪)。
- 需要确定好微服务之间的边界(难点)。
- 需要做好架构设计/技术选型。
- 需要处理类似分布式事务/CAP相关的问题。
微服务中常用技术 – RPC 框架
微服务架构中,因为不同模块是单独部署的,因此彼此之间的调用无法再像单体服务一样直接方法调用,微服务之间方法的调用叫做“远程方法调用”,常用的框架有阿里的 Dubbo 和 spring 家族中的 SpringCloud。
我们以 Dubbo 为例,讲解一下 Dubbo 这个 RPC 框架的使用方法。
Duubo 官网文档:https://dubbo.apache.org/zh/。
Dubbo 是一款RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,致力于提供高性能和透明化的 RPC 远程服务调用方案。Dubbo 采用单一长连接和 NIO 异步通讯(保持连接/轮询处理),使用自定义报文的 TCP 协议,并且序列化使用定制 Hessian2 框架,适合于小数据量大并发的服务调用。参: https://dubbo.apache.org/zh/overview/what/overview/
Dubbo架构图:
可以看到 Dubbo 的架构设计中,有四个角色,分别是:
1)Provider。2)Consumer。3)Registry。4)Monitor。
RPC 框架一般都会涉及到上面四个角色, Provider 服务提供方,Consumer 服务调用方, Registry 作为注册中心会保存 Provider 和 Consumer 的地址/方法等信息,Dubbo 的注册中心默认是 zookeeper, Monitor 是监控器。 在上一节讲了微服务之间方法调用,需要知道服务提供方/服务消费方的地址信息和方法信息,从而引申出来注册中心的概念,而 Dubbo 默认的注册中心是 Zookeeper。
再说 Spring Cloud ,Spring Cloud官网文档:https://spring.io/projects/spring-cloud。
Spring Cloud 为开发者提供了工具来快速构建分布式系统中的一些常见模式(例如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话,集群状态)。分布式系统的协调导致了样板模式,使用 Spring Cloud 开发人员可以快速建立实现这些模式的服务和应用程序。它们在任何分布式环境中都能很好地工作。可以看到, “服务调用”仅仅是Spring Cloud 的功能之一, Spring Cloud还提供了负载均衡/全局锁/路由等功能,也就是说Spring Cloud生态丰富/功能完全。
SpringCloud与Dubbo的区别
- 定位不同:SpringCloud 定位为微服务架构下的一站式解决方案;Dubbo 是 SOA 时代的产物,它的关注点主要在于服务的调用和治理。
- 生态环境不同:SpringCloud 依托于 Spring 平台,具备更加完善的生态体系;而Dubbo 一开始只是做 RPC 远程调用,生态相对匮乏,现在逐渐丰富起来。
- 调用方式:SpringCloud 是采用 Http 协议做远程调用,接口一般是 Rest 风格,比较灵活;Dubbo 默认是采用 Dubbo 协议,接口一般是 Java 的 Service 接口,格式固定。但调用时采用 Netty 的 NIO 方式,性能较好。组件差异比较多:例如 SpringCloud 注册中心一般用 Eureka/Nacos ,而 Dubbo 用的是 Zookeeper 。
微服务中常用组件 – 消息队列
消息队列是什么?
队列可以说是一个数据结构,可以存储数据,如下图,我们从右侧(队尾)插入元素(入队),从队头获取元素(出队)。
了解了队列之后,我们来看一下什么是消息队列,消息队列就是我们常说的 MQ,英文叫 Message Queue,是作为一个单独的中间件产品存在的,独立部署。常见的消息中间件有 RocketMQ、RabbitMQ、Kafka等。
消息队列的使用场景
- 解耦
- 异步
- 削峰
使用消息队列注意点
- 增加了系统的复杂性。
- 相对来说,降低了系统的可用性。
- 消息幂等性问题。
- 消息堆积(1. 设计如此。2. 代码问题导致消息积压)。
微服务中常用组件 – 日志系统
微服务架构中为什么需要日志系统?
即使单体应用架构,当访问数变大、或服务器规模增多时,日志文件的大小会膨胀到难以用文本编辑器进行访问,更糟的是它们分散在多台服务器上面。排查一个问题,需要登录到各台服务器去获取日志文件,一个一个地查找想要的日志信息。而在微服务架构中,这种现象会更严重。
ELK(Elasticsearch、Logstash 和 Kibana) 是使用非常广泛的日志分析组件:
- Elasticsearch:搜索引擎,同时也是日志的存储。
- Logstash:日志采集器,它接收日志输入,对日志进行一些预处理,然后输出到Elasticsearch。
- Kibana:UI组件,通过Elasticsearch的API查找数据并展示给用户。Kibana 界面大致如下图所示:
微服务中常用组件 – 监控系统
为什么需要监控系统?
不管是单体应用,还是微服务架构的系统,都需要监控系统。任何一个服务如果没有监控,那就是两眼一抹黑,无法知道当前服务的运行情况,也就无法对可能出现的异常状况进行很好的处理,所以对任意一个服务来说,监控都是必不可少的。
而微服务,因为相对于单体系统来说涉及更多的节点和调用关系,所以相对单体应用来说出现了问题更难排查,监控就显得非常重要了。而微服务中涉及的组件和中间件较多,各个组件和中间件所需要监控的指标也不同,一个常见的思路是让各个组件或中间件定时上报自己的状态到服务器,服务器对上报的数据进行收集和处理,针对异常的状态进行报警。
常用监控工具:
Cat:CAT(Central Application Tracking)是美团开源的基于Java开发的实时分布式应用监控平台,提供了全面的监控服务和业务决策支持。
Prometheus:Prometheus 是一款基于时序数据库的开源监控告警系统,Prometheus的基本原理是通过HTTP 协议周期性抓取被监控组件的状态,任意组件只要提供对应的 HTTP 接口就可以接入监控。不需要任何 SDK 或者其他的集成过程。
微服务开发中的注意事项
- 确定微服务模块边界。
- 确定模块边界,是微服务开发的重点,原则是松耦合高内聚,根据业务需求确定各个模块边界。
- 技术选型(Rpc 中间件/消息队列/同步异步)。
- 成熟、稳定、高效的要求。技术选型不要过于追新,稳定第一位。
- 生态丰富性/文档要完善。
- 实际开发中,往往是到公司后看公司目前正在使用的技术(什么消息中间件?什么 Rpc 框架?什么监控?什么配置中心?等等。大厂和银行项目一般会自己封装中间件),然后学习和使用。
- 要和公司整体的技术栈相匹配(比如公司都用的 Dubbo/SpringCloud ,你们新起的项目最好也用 Dubbo/SpringCloud)。
- 规范日志格式,强制使用日志 ID 。
- 日志要清晰,要区分不同级别的日志,不能打印过多,也不可以过少。 统一 Rpc 调用返回参数的格式。