单体架构
下图简单展示了单体架构的工作流程
单体架构是把所有的模块和功能集中到一起,部署到一台服务器中,这种一把梭的方式,赢了还好,输了就下海干活。如果请求过大,一台机器撑不住,也只能通过添加机器的方式来进行横向扩展。
微服务架构
微服务架构中我们的应用往往是拆分成不同的模块,取而代之的是多个不同的Service独立部署。他们之间的通信通过http或者rpc等方式,这样每个模块我们就可以独立开发,互不影响。
可以看到之前的单体系统提供的功能被我们拆分成了很多个模块,分别部署成不同的服务。
手机端用户直接通过Nginx访问不同的后端服务,但是在手机端需要完成聚合功能。比如首页需要显示的数据需要在不同的服务中获取数据,就需要将数据请求回来组合之后才能进行显示,所以一般我们会在手机客户端和微服务之间增加一层。
这一层主要做什么呢?聚合数据之后返回统一格式的数据,还可以根据不同设备类型进行裁剪(比如平板和手机显示就不一样),由于增加了BFF(backend for frontend,为前端开发的后端),APP和后端API就解除了强耦合的关系,两边是可以独立变化的,不会受到另一方影响。
有的服务返回的数据可能是xml格式,有的有可能是json格式
微服务看起来很棒,但是也存在一些挑战,在微服务架构之下,服务被拆的非常零散,降低了耦合度的同时也给服务的统一管理增加了难度。
在旧的服务治理体系之下,鉴权,限流,日志,监控等通用功能需要在每个服务中单独实现,这使得系统维护者没有一个全局的视图来统一管理这些功能。而计算机的问题都可以通过增加一层来解决这个问题,所以我们可以增加一层API网关来容纳这些通用的功能,在此基础上提供系统可扩展性。
可以看到这里又提出来了一层gateway,而对于BFF,有些公司可能将其和gateway合并了,具体怎么处理,得看实际情况是怎样的了。
API 网关模式意味着你要把API 网关放到你的微服务们的最前端,并且要让API 网关变成由应用所发起的每个请求的入口。这样就可以明显的简化客户端实现和微服务应用程序之间的沟通方式。
没有网关之前,客户端将商品加入购物车不得不去请求用户服务,然后再到商品服务,然后是购物车服务。客户端需要去知道怎么去一起来消费这三个不同的service。使用API网关,我们可以抽象所有这些复杂性,并创建客户端们可以使用的优化后的端点,并向那些模块们发出请求。
你还可以通过API网关中心化中间件的能力。当你开始创建越来越多的服务时,你会发现自己面临了一个新的问题 – 就是你发现你需要对一些服务进行身份验证和流量控制。
有的服务是public的;有的是private的;有的则是合作伙伴的API,这些你只能提供给一些特定的用户。迟早你会发现自己在实现每个微服务时总是一次次的重复编写一些相同的代码,这些代码其实都是可以抽象为中间件的。
这显然不是每个微服务应该去关注的事情。API网关才应该把这件事情揽下,也就是说微服务只负责接收进来的request-然后返回一个类似JSON格式的response即可。然后API网关就把这些例如身份验证、日志(logging)以及流量控制都归于麾下。
微服务并不都是优点,它同样有一长串需要考虑的问题,比如日志、监控、异常处理、容错、回滚、通信、消息格式、容器、服务发现、备份、测试、报警、跟踪、工具、文档、扩展、时区、API版本、网络延迟、健康检查、负载均衡等等问题,一个新的方式解决问题的同时也会面临新的问题,所以不要觉得微服务就一定好,每个阶段面临的问题不一样,我们处理问题,看待问题的方式也不一样。
微服务要关心的事情太多,所以如果你的公司准备转成微服务就一定要有具备这些解决微服务面临问题的能力。
云原生服务
微服务之后,又兴起了云原生服务,什么是云原生服务呢?
云原生应用定义: 基于微服务原理而开发的应用,以容器方式打包。在运行时,容器由运行于云基础设施之上的平台(比如kubernetes)进行调度。应用开发采用持续交付和DevOps实践。
云原生服务还是离不开微服务,只是它是运行在具备云原生基础的平台中的,而且采用的是持续交付和DevOps实践。这个云原生平台有什么作用呢?用过Kubernetes的都知道它不用担心扩展、服务发现、负载均衡、容错、回滚、更新等等问题,并且对于gateway,监控等等都有配套的成熟解决方案。真的是谁用谁知道。
这里就不展开说了,感兴趣的可以去了解下Kubernetes
API Gateway选择
网关需要考虑哪些内容
- 限流熔断
- 动态路由和负载均衡
- 基于path的路由,比如 example.com/user 访问问用户服务, example.com/shopping 访问购物服务
- 截获器链
- 日志采集和Metrics埋点
- 响应流优化
- 可编程API
- Header头重写
各大网关对比
支持公司 | 实现语言 | 亮点 | 不足 | |
---|---|---|---|---|
Nginx(2004) | Nginx Inc | C/Lua | 高性能,成熟稳定 | 门槛高,偏运维,可编程弱 |
Zuul1(2012) | Netflix/Pivotal | Java | 成熟,简单门槛低 | 性能一般,可编程一般 |
Spring Cloud Gateway(2016) | Pivotal | Java | 异步,配置灵活 | 早期产品 |
Envoy(2016) | Lyft | C++ | 高性能,可编程API/ServiceMesh集成 | 门槛较高 |
Kong(2014) | Kong Inc | OpenResty/Lua | 高性能,可编程API | 门槛较高 |
Traefik(2015) | Containous | Golang | 云原生,可编程API/对接各种服务发现 | 生产案例不太多 |
实际中我们使用的是云原生服务,而Zuul和Spring Cloud Gateway结合Spring Cloud全家桶结合使用效果较好,所以它不太适合我们现在的选择。
我们将目光集中在Kong和traefik中,经过对比发现,我们最终还是选择了traefik,相比较于Kong,traefic的优势如下:
1. traefik较为轻量,非常易于使用和设置
2. traefic通过Kubernetes存储状态(Kong要使用Postgres或者Cassandra来存储状态),并利用Ingress通过https将所有流量路由到对应的服务
3. 已在全球范围内用户生产环境,并经过了严格的测试和基准测试,在我司其他项目中有运用
4. Kong仪表板,它是单独开发的,与最新版本的Kong兼容需要花点时间。Traefik带有自己的仪表板,它始终与最新的Traefik版本兼容,Traefik的用户界面也比Kong的用户界面好看。
traefik的Middlewares真心好用,traefik建议升级到traefik2
并且traefik还可以作为Kubernetes ingress controller,可以完全替代我们之前说过的nginx controller
如何使用traefick作为网关进行用户验证
ingress route的配置:
代码:
通过上面的配置之后,当我们想要访问/api/orders这个api的时候就会先去/api/auth中进行授权验证用户是否登录,如果登录后则会将携带对应的http header到对应的后端服务,后端服务在根据这个header进行验证