20

微服务去中心化架构下为何还要用API网关(201127)

 3 years ago
source link: http://blog.sina.com.cn/s/blog_493a84550102zaay.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

今天准备再谈下API网关,在前面我已经写过几篇关于API网关基础介绍,API网关和ESB服务总线区别,微服务架构下API网关的应用方面的文章。但是发现很多人对去中心化的微服务架构下为何还要用API网关本身存在诸多的疑惑,因此准备在对一些关键的问题点进行说明。

微服务架构下API网关的位置

上图是一个标准的基于SpringCLoud的微服务架构体系图,在图中可以明确的看到API网关或微服务网关所处的位置,即前面谈到过的API网关的定义。

即API网关是内部微服务的统一对外出口,所有需要外部的应用或APP访问的接口都通过API网关暴露一个统一的地址给外部使用。

因此API网关最基本的作用首先是要实现一次接口服务的代理,实现内部的微服务对外界透明。否则所有的内部微服务将全部暴露给外部应用,这个风险是相当大的。

如果仅仅是实现接口转发,那么Ngnix反向代理也可以完全实现,因此当前架构也可以看到很多的微服务架构体系并没有采用API网关。

微服务注册和微服务API接口注册

在微服务架构里面的注册一定要将微服务注册和微服务API接口注册两个概念分开。

微服务注册中心只到微服务模块级注册,而API网关可以到API接口级别注册。

比如我们常说的Eureka服务注册中心,实际是整个微服务模块注册,而不是该微服务提供的具体多个API接口服务的注册。因此后续的心跳监控,负载均衡等也全部是在微服务这个颗粒度上面。

而微服务API接口注册,即微服务提供出来的Http Rest API接口的具体注册动作,比如一个微服务本身提供了100个接口,但是只有10个API接口需要外部APP访问,那么这个时候实际上只需要将这10个接口注册和接入到API网关即可。

去中心化和中心化的思路区别

对于微服务架构下的服务管控治理,要首先考虑中心化和去中心化的区别。

对于中心化架构,简单来说就是所有的API接口服务全部都要注册和接入到API网关中,等于是API网关基于原始的业务服务API接口做了一层封装,然后发布代理服务。

那么对于所有API接口服务的调用,涉及到的调用请求,消息的输入和输出,都可以在API网关中进行拦截。

因此API网关要实现安全,日志,限流熔断,服务路由等能力,全部都都以这个拦截为基础。同时各个能力的实现又可以配置为拦截过程中的各个独立插件。这个是当前主流的API网关产品的实现和API管控治理思路。

而在非中心化架构下,实际服务调用请求和数据流并不会通过类似服务注册中心或管控中心,而注册中心仅仅起到服务注册发现作用。

在这种情况下要实现负载均衡,路由,限流熔断等各种服务治理管控能力,那么就必须在微服务的消费端植入代理来完成,类似下图。

也就是说在去中心化架构下,实际上管控流和数据流已经分离,为了实现最终的管控要求,那么就必须在服务请求端对相关请求进行拦截和处理。类似可以看到Ribbon,Hystrix,Sentinel等基本也都是上述的处理思路和逻辑。

而上图去中心化的思路也正是当前ServiceMesh实现微服务治理的一个核心思路,即通过去中心化架构下,下放Sidecar来实现拦截和控制处理。

有了类似Sentinel和Hystrix为何还用网关限流?

在前面我已经画过一个同时存在API网关和注册中心的混合架构,如下:

从这个图实际可以很明显的看到,对于外部APP实际上是无法通过内部的Hystrix进行限流的。外部APP本身可能并没有通过标准的SpingCloud框架实现,也无法下放各种JAR包和代理。那么外部APP访问请求的限流就必须在API网关上面来做。

因此在存在内外访问的场景下,两处的限流都是必须的。

API网关是否需要具备ESB总线的协议转换能力

简单来说在一个遗留系统的微服务架构改造过程中,如果整体的IT应用集成已经有了ESB总线平台,那么API网关只做Rest API接口服务接入即可。

但是如果原来并没有这类平台,那么API网关需要承担部分轻量的ESB总线能力。

轻量的能力主要包括两个方面:

一个是协议转换,对于协议转换并不需要支持很复杂,但是可以将传统遗留系统的SOAP接口,JMS接口转换发布为Rest API接口。

其次是数据映射,不需要复杂的数据映射,但是存在提供方和消费方需要的数据格式存在差异的时候,能够通过间的映射模板来完成数据删减或丰富的动作。

对于当前主流的API网关往往都会提供简单的数据映射能力,但是对于协议转换能力不提供。而对于协议转换能力也可以方便的通过插件定义来实现。

服务运行监控和日志分析

前面谈到,对于API网关的中心化,既有形成新得集中化单点影响可靠性和性能得劣势,同时也存在关键请求输入和输出拦截的优势。

由于可以拦截服务运行请求报文数据,因此一方面可以应用到具体的接口调用日志审计需求,也可以详细的记录到接口服务运行的性能数据信息,包括前面一篇文章谈到的服务运行时长,数据量,调用IP等。

对每一次的服务运行实例日志信息都进行详细记录,是后续进行服务运行监控,统计分析,监控预警,限流熔断等的基础。

从这个点来说,去中心化架构没有完全ServiceMesh化的时候实际上很难做到这点。

因此也看到在采用SpingCLoud微服务架构又没有启用ServiceMesh的时候,实际上是没有一个统一的地方可以查询到服务运行实例日志信息,进行统一的服务运行分析统计的。

异构微服务框架的兼容性

要看到对于API网关实际是独立在微服务框架外的一个组件。虽然在类似SpingCLoud框架体系里面也提供了Zuul和SpringCloud Gateway的微服务网关。

但是也有各种独立在微服务框架体系之外的API网关产品。

我们可以想一个最简单的场景,比如我现在有一个采用传统的Java SpringMVC+CXF框架来开发实现的Rest API接口服务。那么这个接口服务在标准的微服务架构体系里面是无法纳入到注册中心,限流熔断,负载均衡等进行统一的管控。

包括不同的微服务开发框架,本身也存在各自的微服务治理管控策略上的差异。

其次,在当前去中心化的架构下,往往是多个组件协同来完成整体的服务注册发现,服务配置,服务限流熔断,服务安全等多个管控能力。同时在这些管控能力的实现过程中,实际对微服务模块本身是存在侵入性的,即要么需要将管控的Jar包下放到具体的微服务模块里面并添加依赖,要么需要在接口实现方法上增加注解等。

即标准微服务体系下的微服务治理能力实际依赖微服务框架,同时对代码实现存在一定的侵入性。那么在这种情况下管控规则变更,或者更改微服务框架等,都会导致对应的微服务治理管控部分的配置文件或代码修改。

而对于中心化的API网关来说,希望达到的效果是,前面谈到的服务安全,日志,限流熔断等各自API接口的管控能力全部能够通过配置的方式来完成。在API接口注册到API网关后,所有的管控规则都可以通过配置方式动态变化,并实时生效。

即使你微服务实现替换了框架,对API网关已有的能力和规则实现并没有任何影响。API网关只关心接入的是Rest API接口,但是如何实现的接口,采用哪种框架实现的接口并不关心,也不会去限制使用。

也正是这个原因可以看到,在遗留系统朝微服务迁移的时候,API网关往往更加适合。

为何需要在Ngnix后面再加一层API网关

在前面已经谈到,Ngnix本身也可以起到接口服务代理和路由的作用。在简单的一些场景往往使用Ngnix就可以了。但是在更加严格要求安全和控制粒度的场景仍然需要API网关。

对于Ngnix重点是来做负载均衡,也可以做接口代理路由,但是一般只做URL大的路径级别,而不会做到具体的API接口服务级别。比如一个微服务需要暴露30个API接口服务给前端APP端使用,这个时候一般只配置到一个统一的路径,通过路径进行代理路由,而不是配置到具体的一个个接口服务地址。那么在这种场景下这个路径如果本身提供了100个API接口服务,就会导致其他70个服务也同时暴露到外网。

而API网关则可以管控到每一个细粒度的API接口服务。

其次,内部微服务暴露给外部使用的API接口,一种场景是只暴露给自己的APP端使用,还有一种场景就是需要暴露给外部第三方的应用系统使用。

在第二种场景下,就需要进一步对API接口的安全进行细粒度管控,即需要管控到每一个API接口的安全控制。比如我们常说的,虽然API网关上暴露了100个服务,但是只有A01-A10这10个服务开放给第三方物流系统使用,A01-A10和B01-B10的20个服务开放给招投标平台使用。在这种情况下,就必须实现到具体的业务系统消费方+API接口服务粒度的访问控制能力。

这种到API接口级别细粒度安全控制Ngnix无法做,简单的使用Auth2.0也无法满足,因此就需要一个统一的地方对这种安全进行管理。

API网关的开源实现

对于API网关的开源实现,当前主要有两类,一类是基于Ngnix+Lua语言的Openresty来实现,一类是基于Go语言来实现。

当前Kong网关也是大家采用比较多的一个开源API网关产品,底层基于Ngnix和OpenRestry,语言是Lua语言,最重要的是整个架构设计中的可插拔的插件机制,这种插件机制很方便我们自定义插件扩展。比如我们现在对于安全,日志,运行监控等功能都能够很方便的通过插件扩展来实现。

在上次也谈到另外一个国人研发,进入到NCNF开源基金会的API网关,APISIX,这个网关底层架构实际和Kong很类似,但是从作者公布的性能数据来看比Kong网关好了很多,具体可以参考作者发布的一篇文章如下:

https://zhuanlan.zhihu.com/p/103236688

对于Go语言开发的API网关,如国内的GoKu API Gateway,中文名:悟空API网关,该网关是eoLinker旗下、国内首个企业级开源的go语言API网关,帮助企业进行API服务治理与API性能安全维护,为企业数字化赋能。GoKu支持OpenAPI与微服务管理,支持私有云部署,实现API转发、请求参数转换、数据校验等功能,提供图形化界面管理,能够快速管理多个API网关,提高API业务安全性。

而个人理解Go语言开发的API网关应该具备更好的并发性能,这个需要后续做了压测后再给出具体的测试数据说明。当然Kong网关本身的性能也不弱,已经能够满足大部分的业务需求和场景,也是我个人推荐采用的API网关选型参考。

跨团队协同必须使用API网关

一个企业有两个独立的开发部门,分别开发企业两个独立的应用,而两个应用本身都基于微服务架构进行开发。这个时候两个应用之间存在接口集成。

而这个接口集成个人建议是不能走微服务注册中心,即使类似Nacos这种独立的注册中心也不建议。其核心的一个原因就是对于两个独立团队和独立应用来说,两者的边界不再应该是微服务这个粒度,而应该是微服务API接口这个粒度。

如果完全将微服务全部开放,那么等于是内部应用很多API接口能力也会暴露给其他的应用,这个从安全管控上显然是不合适的。

这也是我们常说的场景,即即使都是内部环境,内部应用,也不需要提供统一的API接入地址给对外的APP应用使用和访问。但是由于内部是独立的开放团队,独立的应用,考虑到各自的边界,还是应该启用API网关来管理两个应用的API接口服务。

API网关和微服务网关

对于API网关和微服务网关实际实现的核心功能基本一致,但是要注意到微服务网关一般是在微服务架构体系里面的内容。而API网关一般是可以独立在微服务架构体系之外的内容。

也就是说API网关和微服务整体框架体系更加的松耦合。

API网关一般具备独立的服务注册接入,负载均衡和路由能力,而微服务网关一般则是通过和服务注册中心的集成来实现服务注册发现,负载均衡和路由。

简单来说如果当前微服务A模块有100个接口服务。

在有服务注册发现中心的情况下,微服务A模块部署后会被注册中心自动发现,并加入到可用集群列表中。因此在微服务网关和注册中心集成后,所有的接口服务也自动的注册和接入到了微服务网关中。

当用户访问网关提供的服务地址时候整体过程如下图:

  1. 调用网关暴露地址:http://ip/gateway/api
  2. 网关将请求转发到注册中心
  3. 注册中心返回可用的服务注册地址列表
  4. 网关基于负载均衡算法选择一个最终地址并路由

在这种场景下可以看到实际并不用一个个的API接口在网关上面注册。但是也无法控制一个微服务哪些具体的接口要接入网关,哪些不接入。同时这里的微服务网关实际上本身也是整体微服务架构体系里面的一个微服务模块,充当了服务消费方的角色。

也就是说APP应用无法受整体微服务框架管辖,那么对应的依赖包,代理SDK等无法下放到外部应用中,那么这部分内容实际是转移到微服务网关上来帮助外部APP应用完成。

而对于相对独立的API网关来说,整体的注册和接入过程是在API网关上面独立完成的,而是是控制到API接口服务粒度进行。

那么这个时候如果微服务模块出现了动态扩展,在API网关上是否需要手工添加新节点?

这里实际两种方法:

其一是微服务模块节点变化的时候,应该能够调用API网关提供的接口,对节点地址集群进行动态更新。其二是微服务上层构建一个集群IP地址,API网关只对接这个集群IP地址出口,比如在微服务和容器云融合后,实际可以暴露Kurbernetes集群IP地址给API网关接入。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK