① API网关从入门到放弃
假设你正在开发一个电商网站,那么这里会涉及到很多后端的微服务,比如会员、商品、推荐服务等等。
那么这里就会遇到一个问题,APP/Browser怎么去访问这些后端的服务? 如果业务比较简单的话,可以给每个业务都分配一个独立的域名(https://service.api.company.com),但这种方式会有几个问题:
更好的方式是采用API网关,实现一个API网关接管所有的入口流量,类似Nginx的作用,将所有用户的请求转发给后端的服务器,但网关做的不仅仅只是简单的转发,也会针对流量做一些扩展,比如鉴权、限流、权限、熔断、协议转换、错误码统一、缓存、日志、监控、告警等,这样将通用的逻辑抽出来,由网关统一去做,业务方也能够更专注于业务逻辑,提升迭代的效率。
通过引入API网关,客户端只需要与API网关交互,而不用与各个业务方的接口分别通讯,但多引入一个组件就多引入了一个潜在的故障点,因此要实现一个高性能、稳定的网关,也会涉及到很多点。
API 注册
业务方如何接入网关?一般来说有几种方式。
协议转换
内部的API可能是由很多种不同的协议实现的,比如HTTP、Dubbo、GRPC等,但对于用户来说其中很多都不是很友好,或者根本没法对外暴露,比如Dubbo服务,因此需要在网关层做一次协议转换,将用户的HTTP协议请求,在网关层转换成底层对应的协议,比如HTTP -> Dubbo, 但这里需要注意很多问题,比如参数类型,如果类型搞错了,导致转换出问题,而日志又不够详细的话,问题会很难定位。
服务发现
网关作为流量的入口,负责请求的转发,但首先需要知道转发给谁,如何寻址,这里有几种方式:
服务调用
网关由于对接很多种不同的协议,因此可能需要实现很多种调用方式,比如HTTP、Dubbo等,基于性能原因,最好都采用异步的方式,而Http、Dubbo都是支持异步的,比如apache就提供了基于NIO实现的异步HTTP客户端。
因为网关会涉及到很多异步调用,比如拦截器、HTTP客户端、bbo、redis等,因此需要考虑下异步调用的方式,如果基于回调或者future的话,代码嵌套会很深,可读性很差,可以参考zuul和spring cloud gateway的方案,基于响应式进行改造。
优雅下线
性能
网关作为所有流量的入口,性能是重中之重,早期大部分网关都是基于同步阻塞模型构建的,比如Zuul 1.x。但这种同步的模型我们都知道,每个请求/连接都会占用一个线程,而线程在JVM中是一个很重的资源,比如Tomcat默认就是200个线程,如果网关隔离没有做好的话,当发生网络延迟、FullGC、第三方服务慢等情况造成上游服务延迟时,线程池很容易会被打满,造成新的请求被拒绝,但这个时候其实线程都阻塞在IO上,系统的资源被没有得到充分的利用。另外一点,容易受网络、磁盘IO等延迟影响。需要谨慎设置超时时间,如果设置不当,且服务隔离做的不是很完善的话,网关很容易被一个慢接口拖垮。
而异步化的方式则完全不同,通常情况下一个CPU核启动一个线程即可处理所有的请求、响应。一个请求的生命周期不再固定于一个线程,而是会分成不同的阶段交由不同的线程池处理,系统的资源能够得到更充分的利用。而且因为线程不再被某一个连接独占,一个连接所占用的系统资源也会低得多,只是一个文件描述符加上几个监听器等,而在阻塞模型中,每条连接都会独占一个线程,而线程是一个非常重的资源。对于上游服务的延迟情况,也能够得到很大的缓解,因为在阻塞模型中,慢请求会独占一个线程资源,而异步化之后,因为单条连接所占用的资源变的非常低,系统可以同时处理大量的请求。
如果是JVM平台,Zuul 2、Spring Cloud gateway等都是不错的异步网关选型,另外也可以基于Netty、Spring Boot2.x的webflux、vert.x或者servlet3.1的异步支持进行自研。
缓存
对于一些幂等的get请求,可以在网关层面根据业务方指定的缓存头做一层缓存,存储到Redis等二级缓存中,这样一些重复的请求,可以在网关层直接处理,而不用打到业务线,降低业务方的压力,另外如果业务方节点挂掉,网关也能够返回自身的缓存。
限流
限流对于每个业务组件来说,可以说都是一个必须的组件,如果限流做不好的话,当请求量突增时,很容易导致业务方的服务挂掉,比如双11、双12等大促时,接口的请求量是平时的数倍,如果没有评估好容量,又没有做限流的话,很容易服务整个不可用,因此需要根据业务方接口的处理能力,做好限流策略,相信大家都见过淘宝、网络抢红包时的降级页面。
因此一定要在接入层做好限流策略,对于非核心接口可以直接将降级掉,保障核心服务的可用性,对于核心接口,需要根据压测时得到的接口容量,制定对应的限流策略。限流又分为几种:
稳定性
稳定性是网关非常重要的一环,监控、告警需要做的很完善才可以,比如接口调用量、响应时间、异常、错误码、成功率等相关的监控告警,还有线程池相关的一些,比如活跃线程数、队列积压等,还有些系统层面的,比如CPU、内存、FullGC这些基本的。
网关是所有服务的入口,对于网关的稳定性的要求相对于其他服务会更高,最好能够一直稳定的运行,尽量少重启,但当新增功能、或者加日志排查问题时,不可避免的需要重新发布,因此可以参考zuul的方式,将所有的核心功能都基于不同的拦截器实现,拦截器的代码采用Groovy编写,存储到数据库中,支持动态加载、编译、运行,这样在出了问题的时候能够第一时间定位并解决,并且如果网关需要开发新功能,只需要增加新的拦截器,并动态添加到网关即可,不需要重新发布。
熔断降级
熔断机制也是非常重要的一项。若某一个服务挂掉、接口响应严重超时等发生,则可能整个网关都被一个接口拖垮,因此需要增加熔断降级,当发生特定异常的时候,对接口降级由网关直接返回,可以基于Hystrix或者Resilience4j实现。
日志
由于所有的请求都是由网关处理的,因此日志也需要相对比较完善,比如接口的耗时、请求方式、请求IP、请求参数、响应参数(注意脱敏)等,另外由于可能涉及到很多微服务,因此需要提供一个统一的traceId方便关联所有的日志,可以将这个traceId置于响应头中,方便排查问题。
隔离
比如线程池、http连接池、redis等应用层面的隔离,另外也可以根据业务场景,将核心业务部署带单独的网关集群,与其他非核心业务隔离开。
网关管控平台
这块也是非常重要的一环,需要考虑好整个流程的用户体验,比如接入到网关的这个流程,能不能尽量简化、智能,比如如果是bbo接口,我们可以通过到git仓库中获取源码、解析对应的类、方法,从而实现自动填充,尽量帮用户减少操作;另外接口一般是从测试->预发->线上,如果每次都要填写一遍表单会非常麻烦,我们能不能自动把这个事情做掉,另外如果网关部署到了多个可用区、甚至不同的国家,那这个时候,我们还需要接口数据同步功能,不然用户需要到每个后台都操作一遍,非常麻烦。
这块个人的建议是直接参考阿里云、aws等提供的网关服务即可,功能非常全面。
其他
其他还有些需要考虑到的点,比如接口mock,文档生成、sdk代码生成、错误码统一、服务治理相关的等,这里就不累述了。
目前的网关还是中心化的架构,所有的请求都需要走一次网关,因此当大促或者流量突增时,网关可能会成为性能的瓶颈,而且当网关接入的大量接口的时候,做好流量评估也不是一项容易的工作,每次大促前都需要跟业务方一起针对接口做压测,评估出大致的容量,并对网关进行扩容,而且网关是所有流量的入口,所有的请求都是由网关处理,要想准确的评估出容量很复杂。可以参考目前比较流行的ServiceMesh,采用去中心化的方案,将网关的逻辑下沉到sidecar中,
sidecar和应用部署到同一个节点,并接管应用流入、流出的流量,这样大促时,只需要对相关的业务压测,并针对性扩容即可,另外升级也会更平滑,中心化的网关,即使灰度发布,但是理论上所有业务方的流量都会流入到新版本的网关,如果出了问题,会影响到所有的业务,但这种去中心化的方式,可以先针对非核心业务升级,观察一段时间没问题后,再全量推上线。另外ServiceMesh的方案,对于多语言支持也更友好。
② 什么是微服务架构主流的微服务如何实现
简单地说,微服务架构就是以业务域或业务功能为边界,将一个大而全的应用拆分为可以独立开发,独立部署,独立测试,独立运行的一组小的应用,并且使用轻量级,通用的机制在这组应用间进行通信。
主流的微服务包括:
1、SpringCloud
Spring Cloud , 来自Spring,具有Spring 社区的强大支撑,还有Netflix强大的后盾与技术输出。Netflix作为一家成功实践微服务架构的互联网公司在几年前就把几乎整个微服务框架栈开源贡献给了社区,这些框架开源的整套服务架构套件是Spring Cloud的核心。
- Eureka:服务注册发现框架;
- Zuul:服务网关;
- Karyon:服务端框架;
- Ribbon:客户端框架;
- Hystrix:服务容错组件;
- Archaius:服务配置组件;
- Servo:Metrics组件;
- Blitz4j:日志组件;
2、Dubbo
Dobbo是一个分布式服务框架,是阿里开放的微服务化治理框架,致力于提高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。其核心部分(官网)
- 远程通讯: 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式;
- 集群容错: 提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持;
- 自动发现: 基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。
Dubbo 也是采用全 Spring 配置方式,透明化接入应用,对应用没有任何 API 侵入,只需用 Spring 加载 Dubbo的配置即可,Dubbo 基于 Spring 的 Schema 扩展进行加载。当然也支持官方不推荐的 API 调用方式。
3、lstio
lstio 作为用于微服务聚合层管理的新锐项目,是Google、IBM、Lyft(海外共享出行公司、Uber劲敌),首个共同联合开源的项目,提供了统一的连接,安全,管理和监控微服务的方案。
目前首个测试版是针对Kubernetes环境的,社区宣称在未来几个月内会为虚拟机和Cloud Foundry 等其他环境增加支持。lstio将 流量管理添加到微服务中,并为增值功能(如安全性、监控、路由、连接管理和策略)创造了基础。
- HTTP、gRPC 和 TCP 网络流量自动负载均衡;
- 提供了丰富的路由规则,实现细颗粒度的网络流量行为控制;
- 流量加密、服务件认证,以及强身份声明;
- 全范围(Fleet-wide)的策略执行;
- 深度遥测和报告。
③ 高并发网站架构的设计方案是怎样的
技术这玩意儿,你不深入使用它,你就不知道它有多牛,更不知道会有多难!
并发:指定时间段内的请求数!
高并发:指定时间段内的超多请求数!
比如tomcat,单机最大支持并发数为8000左右,redis理论值可达到几万!
那么怎么设计一套可支持高并发的系统呢?使用技术如下:
1,分布式系统,微服务:使用springcloud家族包括eureka,zuul,feign,hysrix等或者bbo搭建一套微服务框架!
2,前后端分离:使用node.js搭建前端服务系统!
3,静态化处理:将页面,后台枚举,数据库定义表等使用静态处理方式做处理!
4,文件服务器剥离:采用单独的文件服务器,防止页面加载的阻塞!
5,缓存:使用redis,memcache等将运行时数据缓存,代替频繁的操作数据库!
6,数据库:读写分离或者分库分表,采用druid等有性能监控系统的数据库连接框架!
7,消息中间件:使用xxxmq,kafka等消息中间件,解耦服务,而且异步处理效率更高!
8,反向代理:使用nginx等负载均衡服务!
9,代码层:避免大量创建对象,避免阻塞IO,避免多层for循环,避免线程死锁,避免大量同步!
10,各种优化:包括jvm优化,表结构优化,sql优化,关键字段加索引(注意避免索引失效),连接池优化等等!
11,搜索引擎:sql有大量的like语句,有必要切换成solr等搜索引擎!
12,cdn:使用CDN技术将请求分发到最合适的主机上,避免网络传输的延迟!
13,使用batch:增删改能一次做的别分为两次,但要注意batch合理设计,防止数据丢失!
14,限流,削峰!
大型网站遇到的挑战,主要是大量的用户,高并发的访问,就算一个简单的增删查改的功能,如果面对的是百万、千万甚至亿级的用户,都是一件难度很大的事情。
数据从数据库到浏览器的过程:数据库->应用数据集->内存对象->动态页面->HTTP服务器->用户浏览器。 那么我们可以把高并发的设计分成几个层次:
前端是指,用户的请求还没有到服务前的环节。
系统架构大了,部署的服务器多了,很多事情不可能通过人工完成了,比如一个接口调用发生了错误,不可能人工登录到服务器上去查日志吧,所以这些东西也是必不可少的。
都是说个大概,后面有机会的话,会把每一项都展开详细说明。
希望我的回答能够帮助到你!
我们通过这些架构要素来衡量我们整体系统架构设计的优劣,来判断是否达到了我们的要求。
性能是大型网站架构设计的一个重要方面,任何软件架构设计方案都必须考虑可能带来的性能问题,也正因为性能问题几乎无处不在,在请求链路的任何一个环节,都是我们去做极致性能优化方案中的切入点。
衡量一个系统架构设计是否满足高可用的目标,就是假设系统中任何一台或者多台服务器宕机时,以及出现各种不可预期的问题时,系统整体是否依然可用。
网站的伸缩性是指不需要改变服务器的硬件设计,仅仅靠改变应用服务器的部署数量,就可以扩大或缩小服务器的处理能力。
网站快速发展,功能不断扩展,如何设计网站的架构使其能够快速响应需求变化,是网站可扩展架构的主要目标。
互联网跟传统软件不同,它是开放的,任何人在任何地方都可以访问网站。网站的安全架构就是保护网站不受恶意访问和攻击,保护网站的重要数据不被窃取。
安全性架构,具体来说说就是保证数据的保密性、完整性、真实性、占有性。
要完全掌握大型网站的架构设计方案,或许你可以点击我头像,进入我的专栏"深入大型网站核心架构实战"。
这期专栏是笔者总结了当下这些互联网行业中相对成熟且经过大型网站检验的技术和方案,内容涵盖构建大型互联网系统服务所需的关键技术。
④ Spring微服务灰度发布(热部署)的实现(二)
接着上篇说,我们微服务中用到的nepxion discovery主要采用了三种灰度发布方式,一种是web图形化界面发布,二是zuul过滤器灰度发布,三是业务参数策略灰度发布。下面将重点介绍三种方式的实现。
一、web图形化界面灰度发布
因为我们项目用到了eureka注册中心,所以选择web图形化界面灰度发布比较合适。
1) 首先需要建立一个discovery控制台工程console, 端口为2222,控制台工程负责web图形化界面请求的处理,运行console工程。
2) 下载discovery ui,地址:https://github.com/Nepxion/DiscoveryUI,运行discovery UI,端口为8090
3)浏览器中输入localhost:8090,即可打开控制台,如下
注意:全链路灰度发布需要在“配置中心”下才可用。灰度发布配置中心,负责存储全链路灰度发布规则,并将规则推送到各个微服务中。而配置中心可用nacos,redis等,Discovery 中提供了相应配置中心的插件包。
二、zuul网关过滤器灰度发布
通过网关过滤器传递Http Header的方式传递全链路灰度路由规则。下面代码只适用于Zuul和Spring Cloud Gateway网关,Service微服务不需要加该方式。
三、业务参数在策略类中自定义灰度路由规则
通过策略方式自定义灰度路由规则。下面代码既适用于Zuul和Spring Cloud Gateway网关,也适用于Service微服务,同时全链路中网关和服务都必须加该方式
上面说了具体灰度规则发布方式,那究竟怎么定义灰度规则呢??
规则是基于XML或者Json为配置方式,存储于本地文件或者远程配置中心,可以通过远程配置中心修改的方式达到规则动态化。其核心代码参考discovery-plugin-framework以及它的扩展、discovery-plugin-config-center以及它的扩展和discovery-plugin-admin-center等,规则示例
XML示例(Json示例见discovery-springcloud-example-service下的rule.json)
黑/白名单的IP地址注册的过滤规则
微服务启动的时候,禁止指定的IP地址注册到服务注册发现中心。支持黑/白名单,白名单表示只允许指定IP地址前缀注册,黑名单表示不允许指定IP地址前缀注册。规则如何使用,见示例说明
最大注册数的限制的过滤规则
微服务启动的时候,一旦微服务集群下注册的实例数目已经达到上限(可配置),将禁止后续的微服务进行注册。规则如何使用,见示例说明
黑/白名单的IP地址发现的过滤规则
微服务启动的时候,禁止指定的IP地址被服务发现。它使用的方式和“黑/白名单的IP地址注册的过滤规则”一致
版本访问的灰度发布规则
版本权重的灰度发布规则
全局版本权重的灰度发布规则
区域权重的灰度发布规则
全局区域权重的灰度发布规则
网关端全链路路由策略的灰度发布规则
注意 路由策略的入口有三个(以{"discovery-springcloud-example-a":"1.0", "discovery-springcloud-example-b":"1.0", "discovery-springcloud-example-c":"1.0;1.2"})为例:
其作用的优先级为外界传入>网关Filter指定>配置中心或者本地rule.xml配置
您可以根据自己需求,自由定义灰度发布规则,灵活实现微服务的灰度发布。
源码位置:https://github.com/Nepxion/Discovery