zuul简介
Zuul 是从设备和网站到 Netflix 流媒体应用程序后端的所有请求的前门。作为边缘服务应用程序,Zuul 旨在支持动态路由、监控、弹性和安全性。路由是微服务架构不可或缺的一部分。例如,/可能被映射到您的web应用程序,/api/users被映射到用户服务,/api/shop被映射到商店服务。 Zuul是Netflix的基于JVM的路由器和服务器端负载平衡器。
zuul 的主要作用(为什么使用)
Netflix API 流量的数量和多样性有时会导致生产问题在没有警告的情况下迅速出现。我们需要一个允许我们快速改变行为以应对这些情况的系统。
Zuul 使用一系列不同类型的过滤器,使我们能够快速灵活地将功能应用到我们的边缘服务。这些过滤器帮助我们执行以下功能:
1.身份验证和安全性 - 确定每个资源的身份验证要求并拒绝不满足要求的请求。
2.洞察力和监控 - 在边缘跟踪有意义的数据和统计数据,以便为我们提供准确的生产视图。
3.动态路由 - 根据需要将请求动态路由到不同的后端集群。
4.压力测试 - 逐渐增加集群的流量以衡量性能。
5.减载 - 为每种类型的请求分配容量并丢弃超出限制的请求。
6.静态响应处理 - 直接在边缘构建一些响应,而不是将它们转发到内部集群
zuul过滤实现:
1.引入jar包
<!--zuul jar-->
<dependency>
<groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId></dependency>
<!--nacos客户端jar-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2启动类:
@SpringBootApplication
@EnableZuulProxy //开启zuul的代理功能
@SpringBootApplication
@MapperScan("com.aaa.car.dao")
@EnableTransactionManagement
//@EnableEurekaClient //开启Eureka客户端注册功能 使用eureka做注册中心时,使用该注解
@EnableDiscoveryClient //开启客户端发现功能 不管使用什么注册中心都可以使用该注解
@EnableFeignClients //开启Openfeign客户端功能
public class CarFApp {
public static void main(String[] args) {
SpringApplication.run(CarFApp.class,args);
}
}
3配置application.yml **前没有空格
server:
port: 14251
spring:
application:
#注册中心的名称
name: ZuulServer
cloud:
nacos:
discovery:
#nacous的服务地址
server-addr: http://localhost:8848
zuul:
host:
#配置全局的最大连接数,默认200
max-total-connections: 300
#每一个路由支持的最大连接数
max-per-route-connections: 50
#连接超时时间
connect-timeout-millis: 10000
routes:
#和后台微服务的名称对应
member:
#固定名称 值必须是微服务到注册中心的名称
serviceId: MemberServer
#固定名称 动态路由配置 路径值随便写 和使用zuul访问后端服务是的地址栏对应 **通配符,动态统配任何路径
#localhost:14251/mbs./order/selectOne?id=1 localhost:14251/mbs./order/selectAll?id=1
path: /mbs/ **
car:
serviceId: CarServerRibbon
path: /crsr/ **
car1:
serviceId: CarServerFeign
path: /crsf/ **
market:
serviceId: SentinelServer
path: /mks/ **
#自定义非法IP
illegalIp: 192.168.33.103,192.168.33.238,192.168.33.6,192.168.33.174
illegalChar: tmd,nmd,wukelan,taiwan
4路由测试:
http://localhost:14151/mbs/order/selectOne?id=1
5.过滤实现
jar包搞里头
<!--springboot web包 过滤器需要-->
<dependency>
<groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>
filter过滤方法详解:
ip过滤
/**
* @ fileName:IllegalIpFilter
* @ description:
* @ author:葛爷独创代码
* @ createTime:2022/3/4 16:02
* @ version:1.0.0
*/
@Component //交给ioc
public class IllegalIpFilter extends ZuulFilter {
@Value("${illegalIp}")
private String illegalIp;
/**
* 指定当前filter使用·时机类型
* */
@Override
public String filterType() {
//在业务方法执行之前执行 pre
//执行中执行 route
//执行错误时执行 error
//执行错误后执行post
return "pre";
}
/**
* 当需要多个fliter时,使用该方法,数字越小,优先级越高
* */
@Override
public int filterOrder() {
return 0;
}
/**
* 是否启用过滤器filter true启用 false失效
* */
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器功能业务的实现
* */
@Override
public Object run() throws ZuulException {
System.out.println("执行非法类型过滤。。。。。。。。。。。。。。");
//获取客户请求的IP地址
//request.getRemoteAddr();
//通过zuul包提供的 RequestContext对象获取上下文对象
RequestContext currenContext = RequestContext.getCurrentContext();
//获取http
HttpServletRequest request = currenContext.getRequest();
HttpServletResponse response = currenContext.getResponse();
String remoteAddr = request.getRemoteAddr();
System.out.println("----------请求IP为:" + remoteAddr);
System.out.println(illegalIp + "............");
if (StrUtil.isNotEmpty(this.illegalIp)) {
//非法ip中是否包含指定请求ip
if (this.illegalIp.contains(remoteAddr)) {
//阻止程序往下运行
currenContext.setSendZuulResponse(false);
response.setCharacterEncoding("GBK");
try {
response.getWriter().println("ip非法禁止访问");
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
字符过滤器 parameterNames在项目里面是从数据库取的
@Component //交给ioc
public class IllegalCharIpFilter extends ZuulFilter {
@Value("${illegalChar}")
private String illegalChar;
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
System.out.println("执行非法字符过滤。。。。。。。。。。。");
//实例化zuul封装请求上下文对象
RequestContext currentContext = RequestContext.getCurrentContext();
//通过上下问对象获取request,response
HttpServletRequest request = currentContext.getRequest();
HttpServletResponse response = currentContext.getResponse();
//获取请求的所有参数
// ?a=1&b=2&c=3&aa=11 parameterNames [a,b,c,aa]
Enumeration<String> parameterNames = request.getParameterNames();
//根据逗号分割 非法字符串 [tmd,nnd,nm,yyds]
String[] illegalCharAarray = illegalChar.split(",");
//循环遍历所有参数名称
while(parameterNames.hasMoreElements()){
//第1次获取a 第2次获取b ...
String parameterName = parameterNames.nextElement();
//根据名称获取参数值 第1次获取1 第2次获取2 ...
String parameterValue = request.getParameter(parameterName);
for (String illegalChar : illegalCharAarray) {
if(parameterValue.contains(illegalChar)){
//直接阻止程序运行
currentContext.setSendZuulResponse(false);
//回显错误
response.setCharacterEncoding("GBK");
try {
response.getWriter().println("请求中含有非法参数,禁止访问");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return null;
}
}
后台跨域过滤器 只管用就行
/**
* @fileName:CrossDomainFilter
* @description:后台跨域过滤器
* @author:zz
* @createTime:2020/11/6 16:20
* @version:1.0.0
*/
@Component
public class CrossDomainFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
// 获取request对象
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
HttpServletResponse response = ctx.getResponse();
// 这些是对请求头的匹配,网上有很多解释
response.setHeader("Access-Control-Allow-Origin",request.getHeader("origin"));
response.setHeader("Access-Control-Allow-Credentials","true");
response.setHeader("Access-Control-Allow-Methods","GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH");
response.setHeader("Access-Control-Allow-Headers","authorization, content-type");
response.setHeader("Access-Control-Expose-Headers","X-forwared-port, X-forwarded-host");
response.setHeader("Vary","Origin,Access-Control-Request-Method,Access-Control-Request-Headers");
// 跨域请求一共会进行两次请求 先发送options 是否可以请求
// 调试可发现一共拦截两次 第一次请求为options,第二次为正常的请求 eg:get请求
// equalsIgnoreCase 忽略大小写
if ("OPTIONS".equalsIgnoreCase(request.getMethod())){
ctx.setSendZuulResponse(false); //验证请求不进行路由
ctx.setResponseStatusCode(HttpStatus.OK.value());//返回验证成功的状态码
ctx.set("isSuccess", true);
return null;
}
// 第二次请求(非验证,eg:get请求不会进到上面的if) 会走到这里往下进行
// 不需要token认证
ctx.setSendZuulResponse(true); //对请求进行路由
ctx.setResponseStatusCode(HttpStatus.OK.value());
ctx.set("isSuccess", true);
return null;
}
public static void main(String[] args) {
System.out.println("a".equals("A"));
System.out.println("a".equalsIgnoreCase("A"));
}
}