API Gateway(APIGW / API 网关),顾名思义,是出如今系统边界上的一个面向API的、串行集中式的强管控服务,这里的边界是企业IT系统的边界,能够理解为企业级应用防火墙
,主要起到隔离外部访问与内部系统的做用
。在微服务概念的流行以前,API网关就已经诞生了,例如银行、证券等领域常见的前置机系统,它也是解决访问认证、报文转换、访问统计等问题的。前端
API网关的流行,源于近几年来,移动应用与企业间互联需求的兴起。移动应用、企业互联,使得后台服务支持的对象,从之前单一的Web应用,扩展到多种使用场景,且每种使用场景对后台服务的要求都不尽相同。这不只增长了后台服务的响应量,还增长了后台服务的复杂性。随着微服务架构概念的提出,API网关成为了微服务架构的一个标配组件
。java
如上图所示:网关该具有的最基本的四大功能:统一接入,流量管控,协议适配转发,安全防御。mysql
网关的技术选型nginx
SpringCloud-Zuul :git
社区活跃,基于 SrpingCloud 完整生态, 是构建微服务体系前置网关服务的最佳选型.github
Kong : 基于OpenResty的 API 网关服务和网关服务管理层.web
自建网关服务: 如 谈谈基于 OpenResty 的接口网关设计[https://www.zybuluo.com/yishuailuo/note/844059?utm_source=tool.lu]redis
网关的设计要素算法
系统级别spring
高可用性
均衡负载: 容错,防止雪崩.
并发控制 : 错峰流控
动态路由制定和修改
应用级别
监控统计
版本控制
认证 鉴权
数据安全: 防篡改,参数脱敏…
协议转换: 如 HTTP => RPC协议.
其余(我的 YY)
基于机器学习, 预测流量高峰.
网关(API Gateway)技术选型
zuul
kong
nginx+lua
网关(API Gateway)的设计要素
限流:实现微服务访问流量计算,基于流量计算分析进行限流,能够定义多种限流规则。
缓存:数据缓存。
日志:日志记录。
监控:记录请求响应数据,api耗时分析,性能监控。
鉴权:权限身份认证。
灰度:线上灰度部署,能够减少风险。
路由:路由是API网关很核心的模块功能,此模块实现根据请求,锁定目标微服务并将请求进行转发。
简单介绍下你的网关实施方案
开发语言:java + groovy,groovy的好处是网关服务不须要重启就能够动态的添加filter来实现一些功能;
微服务基础框架:springboot;
网关基础组件:netflix zuul;
服务注册中心:consul;
权限校验:jwt;
API监控:prometheus + grafana;
API统一日志收集:logback + ELK;
压力测试:Jmeter;
你好,请教一下: 1.为何网关须要数据缓存,是由于须要鉴权,因此须要缓存用户数据?还有其余数据须要缓存么? 2.网关路由方面,只须要作路由的转发,仍是须要作服务的聚合? 谢谢
好比限流 你须要缓存一些限流的策略,主要是缓存网关功能用到的一些数据,不涉及业务数据。 路由主要是作转发
目前,咱们业务代码是多语言的环境,网关则是用go写的,目前主要是作到了对于HTTP和Thrift的业务服务的转发(HTTP利用了fasthttp,Thrift用的网关启动客户端调用业务服务端的形式)过滤器是环绕的,系通通一的过滤和针对API级别的过滤。虽然用了go比较轻巧,可是目前功能还很值得完善
go语言体积小,性能高。大家这个设计方案不错,又支持多语言,对技术栈没有限制,很值得借鉴。
设计要素:
#1,高可用很是重要;
#2,网关须要支持动态修改路由规则;
#3,与服务注册中心整合,经过注册中心实现路由转发;
#4,过滤器链适配不一样的路由。
以上是我的愚见
选型
所使用的网关架构必须灵活,由于咱们可能须要不少与咱们业务相关的定制话的东西
有平台背书,获取有足够的证据证实他是一个能抗的住咱们需求的并发的性能
根据需求选择最好的方案
设计要素
结构必须灵活,方便扩展
基础的功能应该由框架提供或者抽象,好比动态路由,权限校验,限流
个人
咱们使用zuul做为网关并对他进行了必定定制化的开发,由于咱们使用springcloud技术栈,同时zuul基于filter来处理一切的结构也是很是灵活的,而且由netflix背书。咱们在网关利用filter加入权限校验,统一访问日志记录,访问异常请求记录,聚合请求处理器等相关功能
负载均衡能够经过在以前加入一个nginx或者dns解析来作,高可用能够经过keepalived加虚拟ip与nginx结合或者直接与zuul结合来作
1.能处理一些公共的逻辑,好比获取token
2.能支持动态的修改路由规则
3.对各服务结果和异常进行统一处理后返给调用方
目前实施了几套方案,本身封装的gateway层,准备用zuul进行替代
首先是稳定且性能好,能支持海量请求,这次是安全,最后是功能完善且易于扩展。
网关智能路由,请求过滤,日志记录,流量控制,权限审查,负载均衡,实时监控,多协议支持等是一个好的网关方案应具有的。
目前公司采用zuul做为网关的,作了路由转发,权限控制,再加上自定义日志记录功能跟踪后端服务执行状况。
zuul多开,前端再加上nginx作端口转发和静态缓存,待解决高可用和偶尔超时问题。
在跟踪后端执行状况上增长了请求执行时间,后端服务地址,当前用户,请求参数,错误截取等内容,以便后续跟踪问题。
而后经过logback发给elk进行记录分析监控
网关的技术选型
1. SpringCloud-Zuul :社区活跃,基于 SrpingCloud 完整生态, 是构建微服务体系前置网关服务的最佳选型.
2. Kong : 基于OpenResty的 API 网关服务和网关服务管理层.
3. Nginx+Lua:成熟度也算能够
4. 自建网关:成本较高
网关(API Gateway)的设计要素(高可用,安全)
* 性能:API高可用,负载均衡,容错机制。
* 安全:权限身份认证、脱敏,流量清洗,后端签名(保证全链路可信调用),黑名单(非法调用的限制)。
* 日志:日志记录(spainid,traceid)一旦涉及分布式,全链路跟踪必不可少。
* 缓存:数据缓存。
* 监控:记录请求响应数据,api耗时分析,性能监控。
* 限流:流量控制,错峰流控,目前有漏桶算法、令牌桶算法也能够定制限流规则。
* 灰度:线上灰度部署,能够减少风险。
* 路由:动态路由规则。
* 静态:代理
简单介绍下你的网关实施方案
* 微服务基础框架:springboot;
* 网关基础组件:zuul;
* 服务注册中心:consul;
* API监控:prometheus + grafana or 自建;
* API统一日志收集:时序db + ELK;
* 压力测试:Jmeter,AB,阿里压测;
网关(API Gateway)技术选型
zuul
nginx
网关(API Gateway)的设计要素
高可用
灰度
负载
限流
反向代理
简单介绍下你的网关实施方案
Spring Cloud Zuul
开发环境使用Spring cloud 技术栈 使用zuul方便
在网关上作限流 负载 限流。网关做为整个系统的入口不该该作不少事 身份认证 权限认证等都放在下面的聚合层
(Low逼的见解 大佬别喷)
首先,网关做为微服务的入口,其并发压力都在网关,当网关的并发能力没法支撑用户量的时候咱们就须要部署多个网关,而后在网关的前面加一个负载均衡服务器,可是在负载均衡服务器的选择上不能简单的选择nginx,由于若是选择nginx的话,客户端链接nginx,nginx再链接网关,网关再链接后端的微服务,这样下来光是TCP链接的三次握手就比较耗时,因此咱们应该在网关的前面选用支持四层负载均衡的服务器,譬如Haproxy或者lvs,使用他们的在tcp第一次握手的时候修改源IP地址和目的地址的模式,这样客户端至关于直连网关,能够作到性能最大化。
网关(API Gateway)技术选型
咱们本身写了一个,利用反射技术,链接同一个zk
网关(API Gateway)的设计要素
在库里边配置相应的信息便可
group varchar(255) NOT NULL,
service varchar(255) NOT NULL,
method varchar(255) DEFAULT NULL,
parameter varchar(255) NOT NULL,
value varchar(255) NOT NULL,
is_deleted tinyint(4) NOT NULL DEFAULT ‘0’,
简单介绍下你的网关实施方案
自有的devops 部署
1.首先部署多台gateway的网关 固然都是zuul的,好比部署3台,端口分别为 90,91,92.
2.使用ngnix代理80端口,而后配置权重把请求分发到90,91,92端口的网关,api网关本身再路由
首先服务网关 = 路由转发 + 过滤器
一、路由转发:接收一切外界请求,转发到后端的微服务上去;
二、过滤器:在服务网关中能够完成一系列的横切功能,例如权限校验、限流以及监控等,这些均可以经过过滤器完成(其实路由转发也是经过过滤器实现的)。
三、增长了网关,多了一层转发(本来用户请求直接访问open-service便可),性能会降低一些(可是降低不大,一般,网关机器性能会很好,并且网关与open-service的访问一般是内网访问,速度很快);
四、 网关的单点问题:在整个网络调用过程当中,必定会有一个单点,多是网关、nginx、dns服务器等。防止网关单点,能够在网关层前边再挂一台nginx,nginx的性能极高,基本不会挂,这样以后,网关服务就能够不断的添加机器。可是这样一个请求就转发了两次,因此最好的方式是网关单点服务部署在一台牛逼的机器上),并且nginx与zuul的性能比较其实相差不大,zuul是netflix开源的一个用来作网关的开源框架;
五、网关要尽可能轻。
http://www.spring4all.com/question/62
Nginx是由IgorSysoev为俄罗斯访问量第二的Rambler.ru站点开发的,一个高性能的HTTP和反向代理服务器。Ngnix一方面能够作反向代理,另一方面作能够作静态资源服务器。
可是准确的来讲,在我看来,这种方案不是真正意义上的网关,并且即便自研网关的目标也是干掉Ngnix。
Kong是Mashape提供的一款API管理软件,它自己是基于Ngnix+lua的,但比nginx提供了更简单的配置方式,数据采用了 ApacheCassandra/PostgreSQL存储,而且提供了一些优秀的插件,好比验证,日志,调用频次限制等。
Kong的一个很是诱人的地方就是提供了大量的插件来扩展应用,经过设置不一样的插件能够为服务提供各类加强的功能。Kong默认插件插件包括:
优势:Kong自己也是基于Nginx的,因此在性能和稳定性上都没有问题。Kong做为一款商业软件,在Nginx上作了很扩展工做,并且还有不少付费的商业插件。Kong自己也有付费的企业版,其中包括技术支持、使用培训服务以及API 分析插件。
缺点:Kong的缺点就是,若是你使用Spring Cloud,Kong如何结合目前已有的服务治理体系?
Zuul 是Netflix公司开源的一个API网关组件,Spring Cloud对其进行二次基于Spring Boot的注解式封装作到开箱即用。目前来讲,结合Sring Cloud提供的服务治理体系,能够作到请求转发,根据配置的或者默认的路由规则进行路由和Load Balance,集成Hystrix。详细能够参考Spring Cloud Zuul的URL转发和路由规则。
Spring Cloud Zuul处理每一个请求的方式是针对每一个请求是用一个线程来处理。PS,根据统计数据目前Zuul最多能达到(1000-2000)QPS。使用过Netty的都知道,通常都会使用Boos组和work组,一般状况下,为了提升性能,全部请求会被放处处理队列中,从线程池中选取空闲线程来处理该请求。
Spring Cloud Zuul须要作一些灰度,降级,标签路由,限流,WAF封禁,须要自定义Filter去或者作一些定制化实现。详细文章能够参考在Spring Cloud中实现降级之权重路由和标签路由
虽然能够经过自定义Filter实现,咱们想要的功能,可是因为Zuul自己的设计和基于
单线程的接收请求和转发处理
,在我看来目前来看Zuul 就显得很鸡肋,随着Zuul2一直跳票,Spring Cloud推出本身的Spring Cloud Gateway.
The API Gateway is Dead! Long Live the API Gateway!
大意:Zuul已死,Spring Cloud Gateway永生。
A Gateway built on Spring Framework 5.0 and Spring Boot 2.0 providing routing and more。
Spring Cloud Gateway是基于Spring 框架5.0版本和Spring Boot 2.0的版本构建,提供路由等功能。
Spring Cloud GateWay具备如下特征
因为Spring 5.0支持Netty,Http2,而Spring Boot 2.0支持Spring 5.0,所以Spring Cloud Gateway支持Netty和Http2瓜熟蒂落。至于2017年Q4季度是否发布完整的Spring Cloud Gateway咱们拭目以待,
可是至于最终落地看最终使用状况
。
详细信息能够参考:Spring Cloud Gateway离开孵化器的变化
如上图所示:Kong+Zuul实现的网关方案,在加上阿里云的SLB,整个调用链路多了好几层,为何要这么作呢?发挥Kong+Spring Cloud Zuul各自的优势造成“聚合网关”。我的不建议这样使用网关,所以自研网关中间件,显得尤为重要。
用Spring Cloud Zuul构建网关其实至关鸡肋,好比动态Filter,好比标签路由,降级,好比动态Filter,好比带管控审计流程,易操做的UI界面等。
zuul是netfix的api 网关,主要特点有:filter的PRPE(pre,route,post,error)模型、groovy的fitler机制,其中spring cloud对其有比较好的扩展,可是spring cloud对其的扩展感受不是很完美,存在路由规则没法只能是经过配置文件来存储,而没法动态配置的目的,其中有一我的写了一个starter插件来解决路由规则配置到Cassandra的问题,详细请看:将路由规则配置到KV分布式存储系统Cassandra
这里主要是作了流控及协议转化的工做,这里主要是http->grpc的转换;
LimitAccessFilter:利用redis令牌桶算法进行流控
GrpcRemoteRouteFilter:http转化为grpc的协议转换
实现动态路由有两种实现方式:
1.第一是DiscoveryClientRouteLocator的从新覆盖,推荐是,Spring Cloud整合GRPC,REST协议适配转发为内部GRPC服务时采用此种方法扩展修改。
2.第二是实现了RefreshableRouteLocator接口,可以实现动态刷新,能够参考 spring cloud Zuul动态路由
为何要基于事件更新,原理以下所示:
在org.springframework.cloud.netflix.zuul.ZuulConfiguration.java中228-250行
@Configuration @EnableConfigurationProperties({ ZuulProperties.class }) @ConditionalOnClass(ZuulServlet.class) // Make sure to get the ServerProperties from the same place as a normal web app would @Import(ServerPropertiesAutoConfiguration.class) public class ZuulConfiguration { //zuul的配置信息,对应了application.properties或yml中的配置信息 @Autowired protected ZuulProperties zuulProperties; @Autowired protected ServerProperties server; @Autowired(required = false) private ErrorController errorController; @Bean public HasFeatures zuulFeature() { return HasFeatures.namedFeature("Zuul (Simple)", ZuulConfiguration.class); } @Bean @ConditionalOnMissingBean(RouteLocator.class) public RouteLocator routeLocator() { //默认配置的实现是SimpleRouteLocator.class return new SimpleRouteLocator(this.server.getServletPrefix(), this.zuulProperties); } @Bean public ZuulController zuulController() { return new ZuulController(); } @Bean public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) { ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController()); mapping.setErrorController(this.errorController); return mapping; } //注册了一个路由刷新监听器,默认实现是ZuulRefreshListener.class @Bean public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() { return new ZuulRefreshListener(); } @Bean @ConditionalOnMissingBean(name = "zuulServlet") public ServletRegistrationBean zuulServlet() { ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet(), this.zuulProperties.getServletPattern()); // The whole point of exposing this servlet is to provide a route that doesn't // buffer requests. servlet.addInitParameter("buffer-requests", "false"); return servlet; } // pre filters @Bean public ServletDetectionFilter servletDetectionFilter() { return new ServletDetectionFilter(); } @Bean public FormBodyWrapperFilter formBodyWrapperFilter() { return new FormBodyWrapperFilter(); } @Bean public DebugFilter debugFilter() { return new DebugFilter(); } @Bean public Servlet30WrapperFilter servlet30WrapperFilter() { return new Servlet30WrapperFilter(); } // post filters @Bean public SendResponseFilter sendResponseFilter() { return new SendResponseFilter(); } @Bean public SendErrorFilter sendErrorFilter() { return new SendErrorFilter(); } @Bean public SendForwardFilter sendForwardFilter() { return new SendForwardFilter(); } @Configuration protected static class ZuulFilterConfiguration { @Autowired private Map<String, ZuulFilter> filters; @Bean public ZuulFilterInitializer zuulFilterInitializer() { return new ZuulFilterInitializer(this.filters); } } private static class ZuulRefreshListener implements ApplicationListener<ApplicationEvent> { @Autowired private ZuulHandlerMapping zuulHandlerMapping; private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor(); @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextRefreshedEvent || event instanceof RefreshScopeRefreshedEvent || event instanceof RoutesRefreshedEvent) { this.zuulHandlerMapping.setDirty(true); } else if (event instanceof HeartbeatEvent) { if (this.heartbeatMonitor.update(((HeartbeatEvent) event).getValue())) { this.zuulHandlerMapping.setDirty(true); } } } } }
如上所示,当使用ApplicationEventPublisher发送的Event为ContextRefreshedEvent,RefreshScopeRefreshedEvent,RoutesRefreshedEvent才会通知Zuul去刷新路由。
此插件针对的spring cloud zuul版本比较老,所以须要对其进行改进,将路由配置能够配置到mysql这样的关系型数据库中,详细请看Zuul的改动点。
对DiscoveryClientRouteLocator的从新覆盖,该类的做用就是从yml或属性文件中读取路由规则;
具体参看源码org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator,主要方法以下,浅显易懂,就不作多余解释。
@Override protected LinkedHashMap<String, ZuulRoute> locateRoutes() { LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>(); routesMap.putAll(super.locateRoutes()); if (this.discovery != null) { Map<String, ZuulRoute> staticServices = new LinkedHashMap<String, ZuulRoute>(); for (ZuulRoute route : routesMap.values()) { String serviceId = route.getServiceId(); if (serviceId == null) { serviceId = route.getId(); } if (serviceId != null) { staticServices.put(serviceId, route); } } // Add routes for discovery services by default List<String> services = this.discovery.getServices(); String[] ignored = this.properties.getIgnoredServices() .toArray(new String[0]); for (String serviceId : services) { // Ignore specifically ignored services and those that were manually // configured String key = "/" + mapRouteToService(serviceId) + "/**"; if (staticServices.containsKey(serviceId) && staticServices.get(serviceId).getUrl() == null) { // Explicitly configured with no URL, cannot be ignored // all static routes are already in routesMap // Update location using serviceId if location is null ZuulRoute staticRoute = staticServices.get(serviceId); if (!StringUtils.hasText(staticRoute.getLocation())) { staticRoute.setLocation(serviceId); } } if (!PatternMatchUtils.simpleMatch(ignored, serviceId) && !routesMap.containsKey(key)) { // Not ignored routesMap.put(key, new ZuulRoute(key, serviceId)); } } } if (routesMap.get(DEFAULT_ROUTE) != null) { ZuulRoute defaultRoute = routesMap.get(DEFAULT_ROUTE); // Move the defaultServiceId to the end routesMap.remove(DEFAULT_ROUTE); routesMap.put(DEFAULT_ROUTE, defaultRoute); } LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>(); for (Entry<String, ZuulRoute> entry : routesMap.entrySet()) { String path = entry.getKey(); // Prepend with slash if not already present. if (!path.startsWith("/")) { path = "/" + path; } if (StringUtils.hasText(this.properties.getPrefix())) { path = this.properties.getPrefix() + path; if (!path.startsWith("/")) { path = "/" + path; } } values.put(path, entry.getValue()); } return values; }
数据变动对网关的稳定性来讲,也是一个很大的挑战。当对路由信息进行CRUD操做以后,须要Spring Cloud Zuul从新刷新路由规则,实现方式经过spring的event来实现。
1.实现基于ApplicationEventPublisherAware的事件生产者的代码片断
private ApplicationEventPublisher publisher; publisher.publishEvent(new InstanceRegisteredEvent<>(this, this.environment));
2.Spring Cloud netflix内部的事件消费者
org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent
@SuppressWarnings("serial") public class RoutesRefreshedEvent extends ApplicationEvent { private RouteLocator locator; public RoutesRefreshedEvent(RouteLocator locator) { super(locator); this.locator = locator; } public RouteLocator getLocator() { return this.locator; } }
因为Spring Cloud Gateway未彻底成熟,并且性能,稳定性等,如今无从考证,没有使用案例
,基于Spring Cloud Gateway方案构建本身的网关风险比较大
,并且PS不知道到年末是否成熟可用
。故在这里不作过多说明。
能够参考架构图以下:
企业级API网关的设计
微服务与API 网关(上): 为何须要API网关?
http://blog.csdn.net/u013815546/article/details/68944039
http://www.javashuo.com/article/p-ntexbnsy-gg.html
http://xujin.org/janus/gw-solution/?from=timeline