盘点 2020 | 微服务架构下该如何技术选型呢?
文章来源:InfoQ 写作平台,2020 年 12 月 18 日发布
为了实现基于微服务开发的产品,或者说为了将单体应用重构为微服务架构时,将面临着众多技术框架的选择。大公司往往会有专门的部门或团队来负责自主研发自己的框架,以满足产品的需要,但是对于一般的中小型企业,选择合适的开源框架就显得更接地气了。本章将简单介绍微服务中,在技术选型时需要注意哪些原则,一些常用的开源技术框架,希望能够为大家在进行技术选型、调研时提供一些思路方向。
笔者面试过很多程序员,一提及微服务,就会具体说道 Spring Boot、Spring Cloud,然后就是“背诵”各种具体的用法和配置文件。并不是说这样不对,但我们更希望知道的是这些技术框架的原理,为什么选择它,它与其他类似框架又有何不同呢。
至于一个技术框架该怎么用,它适用于什么场景,笔者建议可以直接阅读官方或对应的 github 上的文档,有需要时还可以阅读下关注点的源码,这样对正确的理解它,是很有必要的,毕竟官方发布的东西是相对权威的,其他地方的资料或许存在片面性,对大家的使用、理解存在一定的误导。(这只是笔者对大家在技术选型时,查阅资料的一些建议)
在软件开发领域,几乎每天都有新的技术框架诞生、更新,一些新的概念更是层出不穷,技术选型时,难免让人无从抉择。对于技术选型,我个人有以下几点建议:
在微服务架构中,各类组件 / 技术很多很多,但并不意味着所有的都应该引入你的项目,倘若单纯为了覆盖全技术栈或组件而全部引入,这将是一种很不明智的选择。后续将会成为你项目的累赘,让你苦不堪言。
只要你记住这六个字:“有需求,再引入”,就 OK 了。伴随着项目体系架构的完善、功能的健全,当有某方面的需求时,在逐步考虑是否引入某些技术组件。
“一个新项目里最好不要使用超过 30% 的新技术”,我觉得这句话是有一定道理的。对于你完全不知道、不了解的技术,你是无法预估、掌控在使用过程中会出现的任何风险,一旦出现问题,短时间内解决不了,你将会变得很难堪。
在这里不是说拒绝使用、接触新技术,新技术是值得大家去追捧、了解、学习,一些新技术在很大程度上能给我们带来前所未有的利处,解决其他技术框架解决不了的问题。这里所说的“新技术”,是指没有经过充分的考察、技术验证、存在种种疑惑的技术,而是一味的拿来主义,这样的风险可想而知。
确保选择的技术,是业界使用最多的、被大家认可的技术,即使出现了问题,也能应对自如。至少在团队内部小范围是非常认可的。
GitHub 上 star 的数量是一个重要指标,同时参考近年来代码、文档、issues 等更新频率,各大技术博客是否有相关技术分享记载,这些都是能够说明该技术是否活跃、受欢迎程度、使用人群多少等。
拥有强大社区支持的技术,在选型后,倘若使用出现疑问、问题、bug 等,能够有地方可提、可修复、可深究探讨,毕竟现在的技术社区都是足够开放的。
慎选个人开源的技术框架、组件等,里面到底有多少坑,没几个人能说清楚的,况且说不定哪天就不复存在了呢。
任何技术的出发点都是为最终业务而服务的,不同业务、不同项目规模,对技术的要求指标都是不同的。处于初创期的业务,选型的基准是相对灵活,毕竟业务相对简单,支撑业务不是很大,只要够用、开发效率足够高就好。处于复杂业务而重构的项目,选型就需谨慎,往往伴随着一些复杂需求诞生、规模大小的不确定性,不得不考虑选型技术可能伴随着一些小修小补或者螺旋式上升的重构,则需选型便于适配、切换、替换,耦合度低的技术。
正因为技术选型和业务相关,我们能够观察到一些很明显的现象:新技术往往被早期创业团队或大公司的新兴业务使用;中大型公司的核心业务则更倾向于用一些稳定了几年的技术;一个公司如果长期使用一种技术,就会倾向于一直使用下去,甚至连版本都不更新的使用下去。
学会从业务端思考。首先我们需要充分地理解业务,理解用户需求,理解当下需要解决的首要问题,以及可能的风险有哪些,再将目标进行分解,进行具体的技术选型、模型设计、架构设计。
对于未经验证的新技术、新理念的引入一定要慎重,一定要在全方位的验证过后,再大规模的使用,最终确定选型。新技术、新理念的出现,自然有它的诱惑,慎重并不代表保守,技术总是在不断前进,拥抱变化本身没有问题,但是引入不成熟的技术看似能带来短期的收益,但是它的风险或者是后期的成本可能远远大于收益。
验证后,才有说服力,用着更放心。
每种技术架构都有其优缺点, 存在即合理, 不同的业务场景使用不同的应用架构,技术框架,不一定说最新的架构、技术就是最适合你的。
微服务架构中常提及到的主要技术框架选型如下表所示,本文后面将基于此展开说明对比。
Dubbo 是一款高性能、轻量级的开源 JAVA RPC 框架,以及 SOA 服务治理方案。简单的说,Dubbo 就是个服务框架,说白了就是个远程服务调用的分布式框架。它提供了三大核心能力:面向接口的远程方法调用、智能容错和负载均衡、以及服务自动注册和发现,很容易和 Spring 框架无缝集成。
Dubbo 逻辑架构如下图所示:
Provider:暴露服务的提供方,可以通过 jar 或者容器的方式启动服务。
Consumer:调用远程服务的服务消费方。
Registry:服务注册中心和发现中心。
Monitor:统计服务和调用次数,调用时间监控中心。(dubbo 的控制台页面中可以显示,目前只有一个简单版本)
Container:服务运行的容器。
远程通讯: 提供对多种基于长连接的 NIO 框架抽象封装(非阻塞 I/O 的通信方式,Mina/Netty/Grizzly),包括多种线程模型,序列化(Hessian2/ProtoBuf),以及“请求 - 响应”模式的信息交换方式。
在现有的微服务架构下,Dubbo 只能说是一个服务治理框架,或者说是一个 RPC 框架,是以接口为粒度,一个接口类就就是一个服务。如果直接用 Dubbo 来实现微服务架构,还缺少以下几个功能:
分布式配置:可以使用 Spring Cloud Config、Apollo 等来实现。
链路追踪:可以使用 Zipkin、CAT 等来实现。
批量任务:可以使用当当网开源的 Elastic-Job 来实现。
Spring Cloud 是目前最主流的微服务架构落地首选方案之一,是基于 Spring Boot 实现的开源框架,是一个全家桶,是微服务的整体技术栈。
Spring Boot 是 Spring 的一套快速配置脚手架,使用默认大于配置的理念,用于快速开发单个微服务。
它为服务注册发现、动态路由、负载均衡、配置管理、消息总线、熔断器、分布式链路追踪、大数据操作等提供了简单的实现,让我们可以更简洁的使用它。
正如我们前面说过的,微服务是可以独立部署、水平扩展、独立访问的服务单元,而 Spring Cloud 就是这些微服务的“大管家”,采用了微服务这种架构之后,项目的数量会非常多,调用链路复杂,从而管理成了很大的问题,而 Spring Cloud 框架恰恰提供了各种组件用于管理和治理微服务。理所应当的,就成了大家首选框架了。
Spring Cloud 的整体架构如下图所示,提供一站式的微服务架构解决方案。
使用 Spring Cloud 来构建微服务架构可以省去你整合各家技术的成本,Spring Cloud 为我们构建微服务架构提供了一站式的解决方案,就好比当初 Spring 诞生是为解决 EJB 企业应用开发的众多问题而提供的一站式轻量级企业应用开发解决方案一样,随着使用 Spring Cloud 的产品数量增加,Spring Cloud 在微服务架构中已一统江湖。
下面是 Spring Cloud 的完整技术栈,看完你就知道它为啥会在微服务架构中一统江湖了。
通过上表对比,很容易发现 Spring Cloud 拥有很多的项目模块,包含了微服务系统的方方面面。Dubbo 是一个非常优秀的服务治理和服务调用框架,但缺少很多功能模块,例如网关、链路追踪等。在项目模块上,Spring Cloud 占据着更大的优势。对比并不是否定谁,推崇谁,只是说明在不同场景下,有利优劣,需客观来看。
如果仅关注于服务治理的这个层面,Dubbo 其实还优于 Spring Cloud 很多:
支持多种序列化协议,如 Hessian、HTTP、WebService。
Dobbo Admin 后台管理功能强大,提供了路由规则、动态配置、访问控制、权重调节、均衡负载等功能。
在国内影响力比较大,中文社区文档较为全面。
阿里最近重启维护,成为 Apache 孵化项目。
Dubbo 使用 RPC 协议效率更高,在极端压力测试下,Dubbo 的效率会高于 Spring Cloud 效率一倍多。
如果对效率有极高的要求建议使用 Dubbo,相对比 RPC 的效率会比 Restful 高很多,如果选择微服务架构去重构整个技术体系,那么 Spring Cloud 是当仁不让之选,它可以说是目前最好的微服务框架没有之一,并且可以断言,将来 Dubbo 可以很好的整合到 Spring Cloud 中。
API 网关作为微服务中所有服务的唯一入口,免得业界各类成熟的技术框架组件,在进行技术选型时,需要特别考虑是否拥有以下特性:
高可用:网关是对外的唯一关口,必须保证 7 * 24 小时可用,持续提供稳定可靠的服务。
高性能:所有的请求都会经过网关,它承受的压力是巨大的,所以必须保证它具备良好的性能,以应对高并发请求。
安全性:网关必须能够防止外部的恶意访问,确保内部各个微服务的安全。
扩展性:网关是一个处理非业务功能的绝佳场所,必须能够提供流量管控、协议转发、日志监控等服务,同时能够为以后对非业务功能的扩展提供良好的兼容性。
Zuul 作为 Spring Cloud 中的核心组件之一,充当 API 网关的重要角色,所有请求都可以通过 Zuul 达到后端的应用程序、服务。Zuul 提供了动态路由、监控、弹性负载和安全等特性,其核心是一系列的 Filter,其作用类似于 Servlet 框架中的 Filter,或者 AOP。
Zuul 底层利用各种 Filter 实现了如下功能:
动态路由:根据需要将请求动态路由到后端集群。
身份认证和安全性:识别每个需要认证的资源,拒绝不符合要求的请求,如:鉴权。
统计监测:在服务边界追踪并统计数据,提供精确的统计监测视图。
压力测试:逐渐增加对集群的流量以了解其性能。
负载卸载:预先为每种类型的请求分配容量,当请求超过容量时自动丢弃。
静态资源处理:直接在边界返回某些响应。
基于上述这些功能特性,使得 Zuul 作为 API 网关的不二之选。
Zuul 的逻辑架构如下图所示:
Zuul 的过滤器之间是不直接通信的,而是通过一个 RequestContext 的类来进行数据传统,RequestContext 继承 ConcurrentHashMap,使用 ThreadLocal 变量来记录每个 Request 需要传递的数据。
Zuul 的过滤器是由 Groovy 来实现的,这些过滤器文件被存放在 Zuul Server 的特定目录下,Zuul 会定期轮询这些目录,修改过的过滤器会动态加载到 Zuul Server 中,以便过滤请求使用。
Zuul 的大部分功能都是通过过滤器来实现的,其中定义了 4 种标准的过滤器类型 (pre、route、post、error),以满足应用于请求的不同阶段。
(如果想更清晰深入的了解 Zuul,可以参考上图的 Zuul 逻辑架构图,并结合 Zuul 源码深入研究下。)
在了解 traefik 之前,不妨先看看它的整体架构图,如下所示:
从上图不难看出,traefik 充当了 HTTP 反向代理的角色,使得发布的服务变得轻松有趣。在微服务中,实质上是一个为了让部署微服务变得更加便捷而诞生的 HTTP 反向代理、负载均衡工具。,它支持多种后台 (Docker, Swarm, Kubernetes, Marathon, Mesos, Consul, Etcd, Zookeeper, BoltDB, Rest API, file…) 来自动化、动态的应用它的配置文件设置。
除了众多功能之外,traefik 的与众不同之处还在于它会自动发现适合您服务的配置。无需维护和同步单独的配置文件, 一切都会自动,实时地进行(无需重新启动,不会中断连接)。使用 traefik 后,你可以将更多的精力、时间花费在开发和部署上面,而不是在配置和维护其工作状态上。
高性能
无需安装其他依赖,通过 Go 语言编写的单一可执行文件
支持 Restful API 接口
多种后台支持:Docker, Swarm, Kubernetes, Marathon, Mesos, Consul, Etcd
后台监控, 可以监听后台变化进而自动化应用新的配置文件设置
配置文件热更新。(无需重启)
正常结束 http 连接
后端断路器
轮询,rebalancer 负载均衡
Rest Metrics
支持最小化官方 docker 镜像
后台支持 SSL
前台支持 SSL(包括 SNI)
清爽的 AngularJS 前端页面
支持 Websocket
支持 HTTP/2
网络错误重试
支持 Let’s Encrypt (自动更新 HTTPS 证书)
高可用集群模式
OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
OpenResty 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。
OpenResty 的目标是让你的 Web 服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求, 甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。
Kong 是一个在 Nginx 中运行的 Lua 应用程序,并且可以通过 lua-nginx 模块实现,Kong 不是用这个模块编译 Nginx,而是与 OpenResty 一起发布,OpenResty 已经包含了 lua-nginx-module,OpenResty 不是 Nginx 的分支,而是一组扩展功能的模块。
是一个 Api Gateway,通过插件的形式提供负载均衡,日志记录,身份验证,速率限制,转换等功能。可以很轻松扩展功能,模块化,可以运行在任何基础设施上。
它的核心是实现数据库抽象,路由和插件管理,插件可以存在于单独的代码库中,并且可以在几行代码中注入到请求生命周期的任何位置。很方便地为路由和服务提供各种插件,网关所需要的基本特性,Kong 都如数支持:
云原生:与平台无关,Kong 可以从裸机运行到 Kubernetes。
动态路由:Kong 的背后是 OpenResty+Lua,所以从 OpenResty 继承了动态路由的特性。
熔断
健康检查
日志:可以记录通过 Kong 的 HTTP,TCP,UDP 请求和响应。
鉴权:权限控制,IP 黑白名单,同样是 OpenResty 的特性。
SSL: 可以为基础服务或 API 设置特定的 SSL 证书。
监控:Kong 提供了实时监控插件。
认证:如数支持 HMAC, JWT, Basic, OAuth2.0 等常用协议。
限流
REST API:通过 Rest API 进行配置管理,从繁琐的配置文件中解放。
可用性: 天然支持分布式。
高性能: 背靠非阻塞通信的 nginx,性能自不用说。
插件机制: 提供众多开箱即用的插件,且有易于扩展的自定义插件接口,用户可以使用 Lua 自行开发插件。
上面这些特性中,反复提及了 Kong 背后的 OpenResty,实际上,使用 Kong 之后,Nginx 可以完全摒弃,因为 Kong 的功能是 Nginx 的父集。
综上对比,从开源社区活跃度和学习成本来看,无疑是 Zuul 和 Traefik 较好;从成熟度来看,较好的是 Kong、Traefik;从性能角度来看,Kong 要比其他几个领先一些,从架构优势的扩展性来看,Kong 丰富的插件,而 Zuul 是完全需要自研各类 Filter,但 Zuul 由于与 Spring Cloud 深度集成,使用度也很高。
现如今,各类服务注册与发现的框架、组件很多 (Zookeeper、Eureka、Consul、etcd 等),在选择上更是眼花缭乱。在服务注册与发现的技术选型上,我觉得我们应该还是有一定遵循原则和关注要点的。通常可从以下几个方面出发,进行重点关注、抉择。
数据一致性
负载均衡
健康检查
性能与容量
易用性
集群扩展性
用户扩展性
在微服务架构中,服务的数量以及配置信息的日益增多,比如各种服务器参数配置、各种数据库访问参数配置、各种环境下配置信息的不同、配置信息修改之后实时生效等等,传统的配置文件方式或者将配置信息存放于数据库中的方式已无法满足开发人员对配置管理的要求,如:
安全性:配置跟随源代码保存在代码库中,容易造成配置泄漏。
时效性:修改配置,需要重启服务才能生效。
局限性:无法支持动态调整:例如日志开关、功能开关。
在微服务架构中,使用配置中心之前,上述的问题或麻烦,你肯定也会遇到过,所以,是否引入配置中心,取决于你是否有下面的需求:
配置集中化统一管理
配置实时生效
一般完善的配置中心,都会从以下两个方面设计出发,以发挥配置中心的作用。
1)配置实时生效
传统的静态配置方式要想修改某个配置只能修改之后重新发布应用,要实现动态性,可以选择使用数据库,通过定时轮询访问数据库来感知配置的变化。轮询频率低感知配置变化的延时就长,轮询频率高,感知配置变化的延时就短,但比较损耗性能,需要在实时性和性能之间做折中。配置中心专门针对这个业务场景,兼顾实时性和一致性来管理动态配置。
2)配置管理流程
配置的权限管控、灰度发布、版本管理、格式检验和安全配置等一系列的配置管理相关的特性也是配置中心不可获取的一部分。(这也算是配置中心的高级特性作用)
Spring Cloud Config 作为 Spring Cloud 中的一个组件,其功能开放,可开发性强,常是各类配置中心自我研发的基石。
从 Spring Cloud Config 的源码 (spring-cloud-config-server) 中,可以看出目前支持本地存储、Git 仓库存储、SVN 仓库存储、数据库存储方式,其他存储方式可参考源码自行实现即可。
以 Git 存储方式为例说明,Spring Cloud Config 包含 config-server、Git 和 Spring Cloud Bus 三大组件:
config-server 提供给客户端获取配置。
Git 用于存储和修改配置。
Spring Cloud Bus 通知客户端配置变更。
本地测试模式下,Spring Cloud Bus 和 config-server 需要部署一个节点,Git 使用 GitHub 就可以。在生产环境中,Spring Cloud Config,config-server 需要部署至少两个节点。Spring Cloud Bus 如果使用 RabbitMQ,普通集群模式至少需要两个节点。
Git 服务如果使用 GitHub 就不用考虑高可用问题,如果考虑到安全性要自建 Git 私有仓库,整体的成本比较高。Web 服务可以部署多节点支持高可用,由于 Git 有数据的一致性问题,可以通过以下的方式来支持高可用:
Git+Keepalived 冷备模式,当主 Git 挂了可以马上切到备 Git。
Git 多节点部署,存储使用网络文件系统或者通过 DRBD 实现多个 Git 节点的数据同步。
Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
Apollo 分为 MySQL,Config Service,Admin Service,Portal 四个模块:
MySQL:存储 Apollo 元数据和用户配置数据。
Config Service:提供配置的读取、推送等功能,客户端请求都是落到 Config Service 上。
Admin Service:提供配置的修改、发布等功能,Portal 操作的服务就是 Admin Service。
Portal:提供给用户配置管理界面。
本地测试 Config Service,Admin Service,Portal 三个模块可以合并一起部署,MySQL 单独安装并创建需要的表结构。在生产环境使用 Apollo,Portal 可以两个节点单独部署,稳定性要求没那么高的话,Config Service 和 Admin Service 可以部署在一起,数据库支持主备容灾。
Nacos 是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。这正是 Nacos 官方给出的定义:
an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.
核心功能:
动态配置服务:
动态配置服务让您能够以中心化、外部化和动态化的方式管理所有环境的配置。动态配置消除了配置变更时重新部署应用和服务的需要。配置中心化管理让实现无状态服务更简单,也让按需弹性扩展服务更容易。
服务发现及管理:
动态服务发现对以服务为中心的(例如微服务和云原生)应用架构方式非常关键。Nacos 支持 DNS-Based 和 RPC-Based(Dubbo、gRPC)模式的服务发现。Nacos 也提供实时健康检查,以防止将请求发往不健康的主机或服务实例。借助 Nacos,您可以更容易地为您的服务实现断路器。
动态 DNS 服务:
通过支持权重路由,动态 DNS 服务能让您轻松实现中间层负载均衡、更灵活的路由策略、流量控制以及简单数据中心内网的简单 DNS 解析服务。动态 DNS 服务还能让您更容易地实现以 DNS 协议为基础的服务发现,以消除耦合到厂商私有服务发现 API 上的风险。
Nacos 部署需要 Nacos Service 和 MySQL:
Nacos Service:对外提供服务,支持配置管理和服务发现。
MySQL:提供 Nacos 的数据持久化存储。
单机模式下,Nacos 可以使用嵌入式数据库部署一个节点,就能启动。如果对 MySQL 比较熟悉,想要了解整体数据流向,可以安装 MySQL 提供给 Nacos 数据持久化服务。生产环境使用 Nacos,Nacos 服务需要至少部署三个节点,再加上 MySQL 主备。
整体来看,Nacos 的部署结构比较简单,运维成本较低。Apollo 部署组件较多,运维成本比 Nacos 高。Spring Cloud Config 易于定制化二次开发,生产高可用的成本最高。
总的来说,Apollo 和 Nacos 相对于 Spring Cloud Config 的生态支持更广,在配置管理流程上做的更好。Apollo 相对于 Nacos 在配置管理做的更加全面,不过使用起来也要麻烦一些。Nacos 使用起来相对比较简洁,在对性能要求比较高的大规模场景更适合。
点个在看少个 bug 👇