zuul是spring cloud的网关组件,可以构建动态路由、服务降级、负载均衡的服务网关,通过filter链式调用进行扩展,实现统一认证、调用监控、日志管理等等功能。
核心组件:
组件 | 介绍 |
ZuulFilter | 是zuul的核心,分为pre、route、post三种类型,分别对应服务调用之前、之中、之后的处理 |
ZuulServlet | 一个HttpServlet,执行所有ZuulFilter的入口 |
Route | 路由的一个节点,通常对应某个微服务 |
RouteLocator | 获取所有Route的接口,有SimpleRouteLocator和DiscoveryClientRouteLocator两种实现,后者继承自前者,前者通过配置文件配置获得,后者扩展了通过服务发现找到所有服务,并默认为所有服务生成path为/serviceId/**的Route。 |
ZuulController | 需要路由的请求的统一处理器,继承自ServletWrappingController,把请求统一代理给ZuulServlet处理 |
ZuulHandlerMapping | 通过RouteLocator 获取到所有Route之后把route的fullPath和ZuulController注册到到HandlerMap中,把网关需要路由的路径统一映射到ZuulController |
目前主要用到请求过滤和路由功能,因此重点理解相关组件使用方法;
组件–zuulFilter:
Zuul中提供Filter的作用有哪些?
– 网关是暴露在外面的,必须要进行权限控制
– 可以针对服务做控制,在路由的时候处理,比如服务降级
– 防止爬虫,利用Filter对请求进行过滤
– 流量控制,只允许最高的并发量,保护后端的服务
– 灰度发布,可以针对不用的用户进行路由来实现灰度
怎么使用filter功能?
1.自定义新的filter类继承 ZuulFilterl基础抽象类,
**
* IP黑名单限制过滤器
*
**/
public class IpFilter extends ZuulFilter {
@Autowired
private BasicConf basicConf;
public IpFilter() {
super();
}
//shouldFilter是决定这个过滤器需不需要执行,返回false则不执行,这个也可以利用配置中心来做,达到动态的开启关闭效果,
这边是通过上下文对象,获取一个isSuccess的值来决定要不要执行的,就是说由上个过滤器告诉你要不要执行,因为上面的流程没通过,所以下面也没必要继续执行了。
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
Object success = ctx.get("isSuccess");
return success == null ? true : Boolean.parseBoolean(success.toString());
}
//filterType就是过滤器的类型了,取值分别为pre,route,post,error
@Override
public String filterType() {
return "pre";
}
//filterOrder是表示过滤器执行的顺序,数字越小,优先级越高
@Override
public int filterOrder() {
return 1;
}
//run里面就是我们自己要执行的业务逻辑了,可以直接返回null,
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
String ip = IpUtils.getIpAddr(ctx.getRequest());
// 在黑名单中禁用
if (StringUtils.isNotBlank(ip) && basicConf != null && basicConf.getIpStr().contains(ip)) {
ctx.set("isSuccess", false);
ctx.setSendZuulResponse(false);
ResponseData data = ResponseData.fail("非法请求", ResponseCode.NO_AUTH_CODE.getCode());
ctx.setResponseBody(JsonUtils.toJson(data));
ctx.getResponse().setContentType("application/json; charset=utf-8");
return null;
}
return null;
}
}
2. 注册自定义的filter给SpringBoot
@Configuration
public class FilterConfig {
@Bean
public IpFilter ipFilter() {
return new IpFilter();
}
}
Zuul有哪些内置的filter? 这些filter了解一下即可
来着重看下route路由阶段的几个过滤器
1. RibbonRoutingFilter:是route阶段第一个过滤器,仅在context中存在serviceId的情况下运行。存在serviceId,就是说需要面向服务进行路由,服务的路由信息就是我们上面讲过的两种方式,配置文件(静态)及服务注册。具体就是创建RibbonCommandContext,然后交由ribbon和hystrix向下游服务进行请求
2. SimpleHostRoutingFilter:是route阶段第二个过滤器,仅在context中存在routeHost的情况下运行。存在routeHost,就是说我们配置了具体的http或者https url的请求信息。具体逻辑就是通过HttpClient直接向目标url发起请求,不再经过ribbon及hystrix,所以也就没有负载均衡以及熔断
3. SendForwardFilter:是route阶段第三个(最后一个)过滤器,仅在context中存在forward.to的情况下运行。存在forward.to,就是说我们配置了类似forward:/index的请求信息。具体就是通过RequestDispatcher进行forward(也就是local forward到本地子定义的controller上, 可以保证老的Http接口可以继续沿用)
组件–RouteLocator:
RouteLocator主要用于实现路由能力,包括 静态路由,动态路由;
RouteLocator 获取所有Route的接口,有SimpleRouteLocator和DiscoveryClientRouteLocator两种实现,后者继承自前者,前者通过配置文件配置获得,后者扩展了通过服务发现找到所有服务,并默认为所有服务生成path为/serviceId/**的Route。
如何实现动态路由?一个基于URL(不是service id)路由的样例:
- 继承SimpleRouteLocator类,实现 RefreshableRouteLocator, 重新定义相关路由逻辑(比如从DB,Zookeeper中读取路由信息)
public class CustomRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
public final static Logger logger = LoggerFactory.getLogger(CustomRouteLocator.class);
private JdbcTemplate jdbcTemplate;
private ZuulProperties properties;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public CustomRouteLocator(String servletPath, ZuulProperties properties) {
super(servletPath, properties);
this.properties = properties;
logger.info("servletPath:{}", servletPath);
}
@Override
public void refresh() {
doRefresh();
}
@Override
protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
LinkedHashMap<String, ZuulProperties.ZuulRoute> routesMap = new LinkedHashMap<>();
//从application.properties中加载路由信息
routesMap.putAll(super.locateRoutes());
//从db中加载路由信息
routesMap.putAll(locateRoutesFromDB());
//优化一下配置
LinkedHashMap<String, ZuulProperties.ZuulRoute> values = new LinkedHashMap<>();
for (Map.Entry<String, ZuulProperties.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;
}
private Map<String, ZuulProperties.ZuulRoute> locateRoutesFromDB() {
Map<String, ZuulProperties.ZuulRoute> routes = new LinkedHashMap<>();
List<ZuulRouteVO> results = jdbcTemplate.query("select * from gateway_api_define where enabled = true ", new
BeanPropertyRowMapper<>(ZuulRouteVO.class));
for (ZuulRouteVO result : results) {
if (StringUtils.isEmpty(result.getPath()) ) {
continue;
}
if (StringUtils.isEmpty(result.getServiceId()) && StringUtils.isEmpty(result.getUrl())) {
continue;
}
ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute();
try {
BeanUtils.copyProperties(result, zuulRoute);
} catch (Exception e) {
logger.error("=============load zuul route info from db with error==============", e);
}
routes.put(zuulRoute.getPath(), zuulRoute);
}
return routes;
}
public static class ZuulRouteVO {
/**
* The ID of the route (the same as its map key by default).
*/
private String id;
/**
* The path (pattern) for the route, e.g. /foo/**.
*/
private String path;
/**
* The service ID (if any) to map to this route. You can specify a physical URL or
* a service, but not both.
*/
private String serviceId;
/**
* A full physical URL to map to the route. An alternative is to use a service ID
* and service discovery to find the physical address.
*/
private String url;
/**
* Flag to determine whether the prefix for this route (the path, minus pattern
* patcher) should be stripped before forwarding.
*/
private boolean stripPrefix = true;
/**
* Flag to indicate that this route should be retryable (if supported). Generally
* retry requires a service ID and ribbon.
*/
private Boolean retryable;
private Boolean enabled;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getServiceId() {
return serviceId;
}
public void setServiceId(String serviceId) {
this.serviceId = serviceId;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public boolean isStripPrefix() {
return stripPrefix;
}
public void setStripPrefix(boolean stripPrefix) {
this.stripPrefix = stripPrefix;
}
public Boolean getRetryable() {
return retryable;
}
public void setRetryable(Boolean retryable) {
this.retryable = retryable;
}
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
}
}
2. 把自定义的路由类注册给SpringBoot
@Configuration
public class CustomZuulConfig {
@Autowired
ZuulProperties zuulProperties;
@Autowired
ServerProperties server;
@Autowired
JdbcTemplate jdbcTemplate;
@Bean
public CustomRouteLocator routeLocator() {
CustomRouteLocator routeLocator = new CustomRouteLocator(this.server.getServletPrefix(), this.zuulProperties);
routeLocator.setJdbcTemplate(jdbcTemplate);
return routeLocator;
}
}
没有评论