六、Zuul网关
1.简介
没有Zuul之前的开发架构
2.Zuul的作用
加入Zuul之后新的微服务架构图
不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都会经过Zuul这个网关,然后再由网关来实现 鉴权、动态路由等等操作。Zuul就是我们服务的统一入口。
3.使用Zuul进行地址转换和负载均衡
在总工程师内创建一个新的Module
然后添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
编写启动类和Zuul的启动注解
package itcast;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.EnableZuulServer;
@EnableZuulProxy
@SpringCloudApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class);
}
}
编写端口号
server:
port: 10010
以kv形式配置路由规则,k就是路由id,path就是所匹配的路径,url匹配对应端口,最终让此端口的这一路径会先通过网关然后才能访问微服务,而1就是我们的路由id,叫什么都可以
server:
port: 10010
zuul:
routes:
1:
path: /user-Service/**
url: http://127.0.0.1:9091
然后启动我们的Zuul,拜访对应的端口,由于我们user-Service的Module控制层设置的请求地址为user,所以直接来访问user即可
如图;
我们可以发现,现在已经不用再访问本来的9091端口访问到数据了,而是经过了网关去转发请求地址,来到了10010。
但是有一个问题,如果将来微服务的地址发生了变化,或者启动了多台集群需要我们配置负载均衡,那么我们就要在zuul
的依赖中引入Eureka客户端的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
然后进行Zuul配置
server:
port: 10010
zuul:
routes:
1:
path: /user-Service/**
serviceID: user-Service
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
这样就可以直接获取到eureka的INSTANCE列表服务了 ,然后需要把url改换成我们的ServiceID,而我们的ServiceID就是user-Service。
重新启动
然后再介绍一种简化配置zuul的配置方法
按照此规则配置也就是这样
server:
port: 10010
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
zuul:
routes:
user-Service: /user-Service/**
ignored-services:
- consumer-Service
spring:
application:
name: Zuul
然后重新运行
总结:
当一个request请求过来之后,先进行端口和serviceID的匹配,匹配到之后将原服务转发到zuul网关的服务 ,但是服务本身没有地址,所以就要拉取Eureka的INSTANCE列表,获取具体的路径地址,然后通过负载均衡算法,这就是面向服务的路由,内部同时实现了负载均衡和地址转发。
4.过滤器
Zuul作为网关中的一个重要功能,就是实现请求的鉴权。而这个动作我们往往是通过Zuul提供的过滤器来实现的。
<1>ZuulFilter
ZuulFilter是过滤器的顶级父类,里面有四个最重要的方法
public abstract ZuulFilter implements IZuulFilter{
abstract public String filterType();//过滤器类型
abstract public int filterOrder();//过滤器顺序
boolean shouldFilter();// 来自IZuulFilter,要不要过滤
Object run() throws ZuulException;// IZuulFilter,过滤逻辑
}
-
shouldFilter
:返回一个Boolean
值,判断该过滤器是否需要执行。返回true执行,返回false不执行。 -
run
:过滤器的具体业务逻辑。 filterType
:返回字符串,代表过滤器的类型。包含以下4种:
-
pre
:请求在被路由之前执行 -
routing
:在路由请求时调用 -
post
:在routing和errror过滤器之后调用 -
error
:处理请求时发生错误调用
-
filterOrder
:通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。
<2>生命周期
- 正常流程:
- 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。
- 异常流程:
- 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给POST过滤器,最后返回给用户。
- 如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。
- 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达POST过滤器了。
<3>自定义过滤器
自定义一个过滤器,模拟一个登录的校验,如果请求中有access-token参数,则认为请求有效,放行
package itcast.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class LoginFilter extends ZuulFilter {
@Override
public String filterType() {//定义过滤器种类
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {//定义过滤器优先级
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {//定义是否过滤
return true;
}
@Override
public Object run() throws ZuulException {//写过滤逻辑,登录拦截
//获取请求上下文
RequestContext ctx = RequestContext.getCurrentContext();
//获取request
HttpServletRequest request = ctx.getRequest();
//获取请求参数
String token = request.getParameter("access-token");
//判断是否存在
if (StringUtils.isBlank(token)) {
//不存在,未登录,就拦截
ctx.setSendZuulResponse(false);
//返回403
ctx.setResponseStatusCode(HttpStatus.FORBIDDEN.value());
}
return null;
}
}
简单的进行了一下Session的模拟,现在可以来重新测试一下
填入原来的地址,发生了403禁止访问的错误,就是因为我们没有按照之前的自定义过滤器所编写的,同时提交一个请求参数,由于我们没有具体的业务实现,所以填写什么路由规则都可以
访问成功。
<4>Zuul的负载均衡和熔断
Zuul中默认就已经集成了Ribbon负载均衡和Hystix熔断机制。但是所有的超时策略都是走的默认值,比如熔断超时时间只有1S,很容易就触发了。因此建议我们手动进行配置:
hystrix:
command:
default:
execution:
isolution:
thread:
timeoutInMilliseconds: 2000
ribbon:
ReadTimeout: 60000
ConnectTimeout: 60000