springcloud zuul 路由配置规则 springcloud的zuul作用
转载
一:Zuul简单介绍
- 简介
这是Netflix构建微服务的另一个组件—>智能路由网关组件Zuul。它一般用于构建
边界服务(Edge Service),主要用于动态路由,过滤,监控,弹性伸缩和安全。 - Zuul的主要作用
⑴Zuul,Ribbon以及Euraka相结合,可以实现智能路由和负载均衡的功能,Zuul能够
将请求流量按照某种策略分发到集群状态的多个服务实例。
⑵网关将所有服务的API接口统一聚合,并且统一对外暴露。外界系统调用API接口
时,都是由路由对外暴露的API接口,外界系统不需要知道微服务系统中各个服务
相互调用的复杂性。微服务系统也保护了其内部微服务单元的API接口,防止其被
外界直接调用,导致服务的敏感信息对外暴露。
⑶网关服务可以用来做用户身份认证和权限认证,防止非法请求操作API接口,对服
务器起到保护作用。
⑷网关可以实现监控功能,实时日志输出,对请求进行记录。
⑸网关可以用来实现流量监控,在高流量的情况下,对服务进行降级。
⑹API接口从内部服务分离出来,方便做测试。 - Zuul的工作原理
Zuul是通过Servlet来实现的,Zuul通过自定义的ZuulServlet(类似于Spring MVC的
DispatchServlet)来对请求进行控制,ZuulServlet是Zuul的核心Servlet,它的作用是初始
化ZuulFilter,并且去编排这些ZuulFilter的执行顺序,这个类中存在一个service()方法,执
行了过滤器执行的逻辑。Zuul的核心是一系列的过滤器,可以在Http请求的发起和响应返
回期间执行一系列的过滤器。Zuul包含了以下4种过滤器:
a:PRE过滤器—它是在请求路由到具体的服务之前执行的,这种类型的过滤器可以
做安全验证,例如身份验证,参数验证等。
b:ROUTING过滤器—它用于将请求路由到具体的微服务实例。在默认情况下,它
使用Http Client进行网络请求。
c:POST过滤器—它是在请求已经被路由到具体的微服务之后执行的。一般情况下
用作收集统计信息,指标,以及将响应传输到客户端。
d:EEOR过滤器—它是在其它过滤器发生错误时执行的。 - Zuul过滤器的关键特性
⑴简介
Zuul采取了动态读取,编译和运行这些过滤器。过滤器之间不能直接相互通信,
而是通过RequestContext对象来共享数据,每个请求都会创建一个RequestContext
对象。
⑵特性如下
a:Type(类型)—Zuul过滤器的类型,这个类型决定了过滤器在请求的哪个阶段
起作用,例如PRE,POST阶段等。
b:Execution Order(执行顺序)—这个参数规定了过滤器的执行顺序,order的值
越小,越先执行。
c:Criteria(标准)—表示Filter执行所需的条件
d:Action(行动)—表示如果符合执行条件,则执行Action(即逻辑代码) - Zuul请求的生命周期
⑴流程图如下
⑵流程图的逻辑如下
当一个客户端Request请求进入Zuul网关服务时,会先进入到PRE Filters过滤器,进行
一系列的验证,操作或者判断,当然我们也可以使用自定义放入过滤器。后面再交给过滤器
ROUTING Filters去进行请求的路由转发,将请求路由到具体的服务实例进行逻辑处理,最后
返回处理结果。当具体的服务处理完后,最后由POST Filters过滤器进行处理,这个会将结果
返回给客户端。当然PRE,ROUTING,POST这3个处理器处理请求的过程中一旦处理任何问题,
都会调用ERROR Filters进行处理,最后将问题返回到客户端。
二:案例实战
- 首先我们搭建Zuul服务
⑴创建启动类EurekaZuulClientApplication.java文件
⑵创建配置文件application.yml文件
由上面的配置我们能够知道程序名称为service-zuul
服务中心的注册地址为:http://peer1:8761/eureka/
程序的端口为5000
并且我们这里配置了zuul.routs.eurekaClient.path为/eurekaClientApi/**,
zuul.routs.eurekaClient.service-id为eureka-client,那么这两个配置表示
的就是将以/eurekaClientApi开头的Url路由到eureka-client服务。当然其中
zuul.routs.eurekaClient中的eurekaClient是自定义的。同理下面的两个服务
eureka-ribbon-client和eureka-feign-client也是一样。
⑶在pom.xml文件中添加依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.kgf</groupId>
<artifactId>chapter5-2</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>eureka-zuul-client</artifactId>
<name>eureka-zuul-client</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
- 测试效果
⑴ 依次启动工程eureka-server,eureka-client(注意这里需要启动两个实例,端口分别为8762,8763),eureka-ribbon-client,
eureka-feign-client和eureka-zuul-client
⑵ 效果
a:那么现在我们首先通过zuul服务直接访问eureka-client的接口
路径:http://peer1:5000/eurekaClientApi/hi?name=aaa 第一次:
第二次:
通过上面的效果我们是可以发现zuul默认和Ribbon结合实现了负载均衡的功能。
这说明zuul起到了路由的作用。
b:当然如果我们不需要Ribbon去做这个负载均衡,我们可以指定服务实例的Url,一旦指定
了url,这时候就不需要service-id了,如下:
效果如下,不管请求几次都是8762这个服务的实例了。
c:当然,如果我们既想要去指定Url,又想做负载均衡,那么我们就需要自己维护
负载均衡的服务注册列表。
再次看看效果:
第一次:
第二次:
三:我们在Zuul的配置上可以添加一个前缀
- 在application.yml配置文件中添加一个属性prefix即可
- 效果
四:在Zuul上配置熔断器
- 简介
在Zuul中实现熔断功能需要实现FallbackProvider的接口。实现该接口的两个方法,
一个是getRoute()方法,用于指定熔断功能应用于哪些路由的服务,另一个方法fallbackResponse()
为进入熔断功能时执行的逻辑。 - 创建接口FallbackProvider的实现类MyFallbackProvider.java
package com.kgf.eureka.zuul.hystrix;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
@Component
public class MyFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
return "eureka-client";//实现对eureka-client服务的熔断
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("Error,I am the fallback".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
};
}
}
- 依次启动工程eureka-server,eureka-client(注意这里需要启动两个实例,端口分别为8762,8763),eureka-ribbon-client,
eureka-feign-client和eureka-zuul-client - 效果
访问eureka-client接口正常展示
下面我们将eureka-client关掉效果: - 如果我们将getRoute()方法的返回改为”*”,那么将对所有的路由服务都加熔断功能
五:在Zuul中使用过滤器
- 简介
zuul不仅只是路由,并且还能过滤,做一些安全验证,实现过滤器很简单,只要继承ZuulFilter,并且实现
ZuulFilter中的抽象方法,包括filterType()和filterOrder(),以及IZuulFilter的shouldFilter()和Object run()的两个方法。 - 实例代码
⑴创建自定义的过滤器MyFilter.java
package com.kgf.eureka.zuul.filter;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
/**
* 自定义过滤器
* @author 86136
*
*/
@Component
public class MyFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(MyFilter.class);
/**
* filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
* 1:pre:路由之前
* 2:routing:路由之时
* 3:post: 路由之后
* 4:error:发送错误调用
*/
@Override
public String filterType() {
return "pre";//这里必须小写
}
/**
* 这个方法表示该过滤器是否过滤逻辑,如果为true,则执行run方法,如果为false,则不执行run方法
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 这个run方法中是主要写具体过滤的逻辑的。
* 这里我们测试的例子是判断用户是否在请求参数中传递了token这个参数,
* 如果没有传递这个参数,那么请求就不会被传递到具体的服务实例,直接返回相应状态码401
*/
@Override
public Object run() throws ZuulException {
//获取RequestContext对象,因为过滤器直接不能直接通信,必须通过RequestContext来共享数据
RequestContext context = RequestContext.getCurrentContext();
//获取HttpServletRequest对象
HttpServletRequest request = context.getRequest();
//获取我们的参数标识
String accessToken = request.getParameter("token");
if(StringUtils.isBlank(accessToken)) {
log.warn("token is empty!");
//如果参数为空,那么我们设置网关返回直接不路由到具体服务
context.setSendZuulResponse(false);
//返回状态码为401
context.setResponseStatusCode(401);
try {
//将错误信息返回到前台
context.getResponse().getWriter().write("token is empty!");
} catch (IOException e) {
return null;
}
}
log.info("ok");
return null;
}
/**
* 过滤的顺序 ,值越小越先执行该过滤器
*/
@Override
public int filterOrder() {
return 0;
}
}
- ⑵依次启动工程eureka-server,eureka-client(注意这里需要启动两个实例,端口分别为8762,8763),eureka-ribbon-client,
eureka-feign-client和eureka-zuul-client服务
a:访问浏览器路径:http://peer1:5000/v1/eurekaClientApi/hi?name=aaa
b: 访问浏览器路径:http://peer1:5000/v1/eurekaClientApi/hi?name=aaa&&token=nihao
c:总结
我们定义的pre类型的过滤器在请求被zuul路由转发之前,会对请求进行安全校验。
本文章为转载内容,我们尊重原作者对文章享有的著作权。如有内容错误或侵权问题,欢迎原作者联系我们进行内容更正或删除文章。