网关
网关,对内,是内部服务拜访外部网站的出口;对外,是外部网站拜访内部资源的入口。
网关就像一个管家,作为内外服务的一道屏障,能够很好的保护内部服务资源的安全,流量监控、一致认证、防止直接暴露内部服务信息等。
网关作业原理
能够把网关看作一道屏障,外部恳求先通过网关,然后由网关转发至后端服务,后端服务处理后,将呼应成果经网关回来给调用方:
流量一致收口到网关,咱们能够利用网关做很多通用的工作,比方:
- 权限认证、解析
- 目标搜集
- …
日益成熟的 web 商场,现成的网关组件有很多,比方常见的 Spring Cloud Gateway、Netflix 的 zull 网关等。
网关的根本设计思路:
- 路由:能够将恳求转发至对应的服务,这是网关的中心能力,转发规矩能够多种多样
- 过滤器:网关需要易于扩展的 filter、可插拔式的,这儿就会用到常见的职责链形式。
本文首要讲解 web 服务恳求过程中经常运用的网关 zull 相关原理和实践。
zull 网关
根本装备
一份常见的 zull 网关装备:
server:
port: 8080
spring:
application:
name: zuul-gateway
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
zuul:
prefix: /api
ignored-services: '*'
routes:
service1:
path: /service1/**
serviceId: service1
stripPrefix: false
service2:
path: /service2/**
serviceId: service2
stripPrefix: false
设置了一切路由的前缀为/api,然后咱们设置了 ignored-services为'*'
,这意味着 Zuul 将不会自动创立 Eureka 服务的路由。
- 然后界说了两个路由规矩,一切以 /service1/ 最初的恳求都会被路由到 service1 服务,一切以 /service2/ 最初的恳求都会被路由到 service2 服务。
- stripPrefix 设置为 false 意味着在路由恳求时,不会去掉途径前缀。
- retryable 设置为 true 意味着当恳求失利时,Zuul 会尝试重新发送恳求。
默认规矩
假如你没有在 Zuul 的装备中清晰指定要转发的服务,那么 Zuul 默认会将一切从 Eureka 服务注册中心注册的服务都创立路由规矩。
例如,假如你有一个名为 service1 的服务在 Eureka 注册中心注册,那么 Zuul 默认会创立一个如下的路由规矩:
zuul:
routes:
service1:
path: /service1/**
serviceId: service1
这意味着一切以 /service1/ 最初的恳求都会被路由到 service1 服务。
假如你不期望 Zuul 自动创立路由规矩,你能够在装备中增加 ignored-services: '*'
,这会告诉 Zuul 疏忽一切服务:
zuul:
ignored-services: '*'
然后,你能够手动增加你期望Zuul转发的服务。例如:
zuul:
routes:
service1:
path: /my-service1/**
serviceId: service1
在这个比如中,一切以 /my-service1/ 最初的恳求都会被路由到 service1 服务。
途径形式
在 Zuul 的装备中,能够为每个服务界说一个或多个途径形式,Zuul 会将匹配这些形式的恳求转发到相应的服务。
例如,以下装备将一切以 /service1/ 最初的恳求转发到 service1 服务,将一切以 /service2/ 最初的恳求转发到 service2 服务:
zuul:
routes:
service1:
path: /service1/**
serviceId: service1
service2:
path: /service2/**
serviceId: service2
你也能够为同一个服务界说多个途径形式,例如:
zuul:
routes:
service1:
path: /service1/**,/api/service1/**
serviceId: service1
在这个比如中,一切以 /service1/或/api/service1/ 最初的恳求都会被转发到 service1 服务。
按 URL 转发
在一些特殊的场景,你可能想要隐藏后端接口,需要在网关按接口维度
做映射,比方:前端接口:/front/getUser
-> 后端接口:/user-service/baseInfo
Zull 没有做到这样细粒度,不过它的 Filter 机制很方便扩展。
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import javax.servlet.http.HttpServletRequest;
public class DynamicRouteFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre"; // 在恳求被路由之前调用
}
@Override
public int filterOrder() {
return 0; // filter履行顺序,通过数字指定
}
@Override
public boolean shouldFilter() {
// 这儿能够写逻辑判别,是否要过滤,本文true,永远过滤。
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
// ...
Map<String, Object> routeMap;
String uri = requestContext.getRequest().getRequestURI();
// ...
if(routeMap.contains(uri)) {
ctx.setSendZuulResponse(true); // 对恳求进行路由
ctx.setResponseStatusCode(200);
ctx.set("isSuccess", true);
} else {
ctx.setSendZuulResponse(false); // 不对其进行路由
ctx.setResponseStatusCode(401); // 回来过错码
ctx.setResponseBody("IP address is not allowed"); // 回来过错内容
ctx.set("isSuccess", false);
}
return null;
}
}
在以上 routeMap 中,你能够自界说前后端接口映射装备,然后在此处判别当时恳求是否装备了映射联系,进而决议是转发仍是呼应过错码。
ZuulFilter 接口界说了 Zuul 过滤器的根本结构,包括两个办法:
- shouldFilter():这个办法决议了是否需要履行 run() 办法。假如回来 true,则履行run() 办法;假如回来 false,则不履行。
- run():这是过滤器的中心办法,当 shouldFilter() 回来 true 时,该办法会被调用。这个办法能够包括过滤器的详细逻辑,例如安全验证、限流操控等。假如在履行过程中产生过错,会抛出 ZuulException 反常。
生命周期
以一条恳求为例,先看看一条网关的全生命周期:
- 预过滤(Pre Filter) :这是恳求在进入 Zuul 路由之前履行的过滤,一般用于实现身份验证、记载调试信息、决议是否需要对恳求进行路由等。
- 路由(Routing Filter) :在这个阶段,Zuul 会依据设定的规矩将恳求路由到对应的服务实例。
- 后过滤(Post Filter) :这是在路由到微服务后履行的过滤器,这个过滤器将会对回来的数据进行处理,比方增加 HTTP Header、搜集计算和目标信息、将呼应输出到客户端等。
- 过错处理(Error Filter) :在整个生命周期中任何阶段产生过错时都会进入该过滤器,该过滤器用于一致处理恳求过程中出现的反常。
// Pre Filter
public class SimplePreFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
return null;
}
}
// Post Filter
public class SimplePostFilter extends ZuulFilter {
@Override
public String filterType() {
return "post";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info("Response Status : " + ctx.getResponseStatusCode());
return null;
}
}
// Error Filter
public class SimpleErrorFilter extends ZuulFilter {
@Override
public String filterType() {
return "error";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info("Error occurred, request to " + request.getRequestURL().toString());
return null;
}
}
以上比如中,界说了三个过滤器:预过滤器、后过滤器和过错过滤器。
- 预过滤器记载了恳求的HTTP办法和URL
- 后过滤器记载了呼应的状态码
- 过错过滤器记载了产生过错的恳求URL。
以上就是 zull 网关的中心原理以及实践,欢迎交流讨论!