7、Spring -Cloud-路由网管Spring Cloud Zuul
7.1、为什么需要Zuul
Zuul 作为路由网关组件,在微服务架构中有着非常重要的作用:
7.2、Zuul的工作原理
Zuul 是通过 Servlet 来实现的,
Zuul 通过自定义的 Zuu!Servlet (类似于 Spring MVC的DispatcServlet )来对请求进行控制
Zuul 的核心是一系列过滤器
可以在 Http 请求的发起和响应返回期间执行 系列的过滤器。
包含以下四种过滤器:
Zuul 采取了动态读取、编译和运行这些过滤器
过滤器 间不能直接相互通信,通过RequestContext 对象来共享数据
每个请求都会创建一个RequestContext 对象
特性:
请求的生命周期图:
7.3、案例
7.3.1、搭建 Zuul 服务
新建一个项目:
pom文件:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
此时需要使用到的工程:
配置均是之前的配置:
spring.application.name=feign server.port=8087 eureka.client.service-url.defaultZone=http://localhost:8762/eureka/ #开启Hystrix的功能 feign.hystrix.enabled=true
在下方配置文件的使用:
spring.application.name=hystric server.port=8088 eureka.client.service-url.defaultZone=http://localhost:8762/eureka/ #开启Hystrix的功能 feign.hystrix.enabled=true
在下方配置文件的使用:
新建工程的配置类:
@EnableZuulProxy @EnableEurekaClient @SpringBootApplication public class EurekaZuulClientApplication { public static void main(String[] args) { SpringApplication.run(EurekaZuulClientApplication.class, args); } }
配置文件:
eureka: client: service-url: defaultZone: http://localhost:8762/eureka/ server: port: 5000 spring: application: name: zuul zuul: routes: #自定义的 ribbonapi: #路径 path: /ribbonapi/** #引入的application(上述需要使用到的工程名) serviceid: hystric feignapi: path: /feignapi/** serviceid: feign
zuul.routes.ribbonapi中的ribbonapi时自己定义的
需要指定path和serviceId两者配合使用
就可以将指定类型的请求url路由到制定的serviceId
即满足/ribbonapi 开头的请求Url都会被分发到hystric 服务
如果某个服务存在多个实例,Zul结合Ribbon会做负载均衡,将请求均分的部分路由到不同服务的实例。
此时也需要进行启动上述的两个工程,分别修改器端口号进行访问测试:
Eureka注册中心:
两个服务是可以进行访问的
此时访问:
hi是之前的请求url
访问:http://localhost:5000/feignapi/feign
这里会有负载均衡
可见Zuul在路由转发做了负载均衡。
如果不需要用 Ribbon 做负载均衡
可以指定服务实例的 Uri
zuul: routes: ribbonapi: path: /ribbonapi/** serviceid: hystric url: http://localhost:8089
重新启动此时不会负载均衡,值会使用这个端口的进行服务!!!
如果你想指定 Url 并且想做负载均衡
那么就需要自己维护负载均衡的服务注册列表。
首先、ribbon.eureka.enabled 改为 false
Ribbon 负载均衡客户端不向Eureka Client 获取服务注册列表信息
然后需要自己维护一份注册列表
该注册列表对应的服务名为hiapi-vl(这 名字可自定义)
7.3.2、Zuul 上配置 API 接口的版本号
若要给每个服务的API接口的加上前缀
如:http://localhost:5000/v1/ribbonapi/hi
此时的v1就是版本号
待补充.....
7.3.3、Zuul 上配置熔断器
Zuul 作为 Netflix 组件,可以和Ribbon、Eureka、Hystrix 等组件相结合,
实现负载均衡、熔断器的功能。
在默认情况下, Zuul和Ribbon相结合,实现了负载均衡的功能
MyFallbackProvider.class
package com.cr.eurekazuulclient.zull; import org.slf4j.LoggerFactory; 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; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.logging.Logger; @Component public class MyFallbackProvider implements FallbackProvider { @Override public String getRoute() { // 表明是为哪个微服务提供回退,*表示为所有微服务提供回退 return "hystric"; } public ClientHttpResponse fallbackResponse(){ return new ClientHttpResponse() { @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() { } @Override public InputStream getBody() throws IOException { return new ByteArrayInputStream("The service is unavailable.".getBytes()); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return headers; } }; } @Override public ClientHttpResponse fallbackResponse(String route, Throwable cause) { if (cause != null && cause.getCause() != null){ String reason = cause.getCause().getMessage(); System.out.println("exception:" + reason); } return fallbackResponse(); } }
此时启动服务!!!
访问hystrix的地址:
关闭该端口的服务:
此时的字符串是上文中的提示字符串
重启hystrix服务:
7.3.4、在Zuul中使用过滤器
过滤器的类型:
实现过滤器自需要继承ZuulFilter,并且实现其中的抽象方法
包括:filterType()、fil terOrder()、shouldFilter ()、Object run()
filterType():过滤器类型
filterOrder()是过滤顺序,值越小越早执行过滤器
shouldFilter()表示该过滤器是都过滤逻辑,为true则执行run()方法,false则不执行run()方法
run()方法写具体的过滤逻辑
本次的测试请求参数是否传送token参数
若没有传,则请求不被路由到具体的服务实例直接返回响应状态吗401
package com.cr.eurekazuulclient.zull; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.util.logging.Logger; @Component public class MyZuulFilter extends ZuulFilter { //private static Logger log= (Logger) LoggerFactory.getLogger(MyZuulFilter.class); @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); Object accessToken = request.getParameter("token"); if (accessToken == null){ System.out.println("token is empty"); //log.warning("token is empty"); ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); try { ctx.getResponse().getWriter().write("token is empty"); }catch (Exception e){ return null; } } return null; } }
Zuul 的过滤器 ZuulFilter 的使用。
注意 EnableZuulProxy 注解能注册到 eureka 服务上,是因为该注解包含了
eureka 客户端的注解,该 EnableZuulProxy 是一个复合注解。
http://localhost:8150/routes 地址可以查看该zuul微服务网关代理了多少微服务的serviceId
* 在 zuul 中定义了四种不同生命周期的过滤器类型:
*
* 1、pre:可以在请求被路由之前调用;
*
* 2、route:在路由请求时候被调用;
*
* 3、post:在route和error过滤器之后被调用;
*
* 4、error:处理请求时发生错误时被调用;
此时请求:
此时加上参数token:
MyZuulFilter这个 Bean 注入 IoC 容器之后
对请求进行了过滤
并在请求路由转发之前进行了逻辑判断
在实际开发中,可以用此过滤器进行安全验证
7.3.5、Zuul常见的使用方式
Zuul 是采用了类似于 Spring MVC的DispatchServlet 来实现的
采用的是异步阻塞模型,所以性能比 Ngnix 差
由于 Zuul和其他 Netflix 组件可以相互配合、无缝集成 Zuul 很容易
就能实现负载均衡、智能路由和熔断器等功能
大多数情况下、Zuul 都是以集群的形式存在的
由于Zuul的横向扩展能力非常好
所以当负载过高时
可以通过添加实例来解决性能瓶颈。