Zuul
Zuul是Netflix提供的一个开源组件,,致力于在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。
记录一下初学效果
传统互联网架构
服务统一管理下的架构图
在我的认知中,Zuul注册到Eureka Server,然后拉取服务清单,通过服务清单代理去访问指定服务,以达到路由的效果!下面是架构图:
配置教程
导入依赖
<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>
启动类加入注解
@EnableEurekaClient
@EnableZuulProxy
#启动类
package com.zuul;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.EnableZuulServer;
@SpringBootApplication
@EnableEurekaClient //作为eureka客户端
@EnableZuulProxy //作为网关代理
public class ZuulModelApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulModelApplication.class, args);
}
}
application.yml
server:
port: 10450
spring:
application:
name: service-zuul
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka,http://localhost:8762/eureka
Eureka Server注册服务清单
通过Zuul代理访问
Zuul服务监控+忽略服务+自定义服务配置+服务别名
配置中加入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.yml
management:
endpoints:
web:
exposure:
include: "*"
zuul:
# 基于服务名称忽略服务 不显示 也无法访问
ignored-services: server,service-consumer
# 基于路径直接限制访问,显示 但是访问的时候是404
ignored-patterns: /**/service-provider/**
# 自定义配置
routes:
# 自定义名称
consumer:
# 新的访问路径
path: /consumer/**
# 映射到指定服务
serviceId: service-consumer
原有的Eureka Server 不允许外部且在路由中不显示
service-provider 在路由中显示但是不允许访问
灰度发布
添加配置类
package com.zuul.conf;
import org.springframework.cloud.netflix.zuul.filters.discovery.PatternServiceRouteMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* create by Dannimeiyou for 2020/11/23 16:04
*/
@Configuration
public class ZuulConfig {
@Bean
public PatternServiceRouteMapper getPatternServiceRouteMapper(){
return new PatternServiceRouteMapper("(?<name>^.+)-(?<version>v.+$)","${version}/${name}");
}
}
在application.yml中添加version:
更改spring.application.name;
version: 1.0
server:
port: 10450
spring:
application:
name: service-zuul-V${version}
版本更迭只需要改版本号再次发布,这时候会多出一个服务
Zuul过滤器+Token
ZuulFilter工作原理图
创建类继承抽象过滤器ZuulFilter,交予Spring容器
PostFilter
package com.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
/**
* create by Dannimeiyou for 2020/11/23 16:20
*/
@Component
public class TestZuulFilter extends ZuulFilter {
@Override
//指定过滤器类型
public String filterType() {
return FilterConstants.POST_TYPE;
}
@Override
//同级过滤器执行优先级别
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER-1;
}
@Override
//此过滤器是否启用
public boolean shouldFilter() {
return true;
}
@Override
//过滤器的执行逻辑
public Object run() throws ZuulException {
System.out.println(" 当前执行的过滤器为TestZuulFilter,执行级别为= " +filterOrder());
return null;
}
}
PreFilter
package com.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
/**
* create by Dannimeiyou for 2020/11/23 16:32
*/
@Component
public class Test2ZuulFilter extends ZuulFilter {
@Override
//Filter TYPE
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
// LEVEL
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER-2;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
//实现拦截的操作
System.out.println("当前所执行的过滤器为Test2ZuulFilter,执行级别为" + filterOrder());
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
String tokenMessage = request.getHeader("tokenMessage");
if (StringUtils.isEmpty(tokenMessage)||!tokenMessage.equals("1214")){
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(HttpStatus.METHOD_NOT_ALLOWED.value());
currentContext.setResponseBody(HttpStatus.METHOD_NOT_ALLOWED.getReasonPhrase());
}
return null;
}
}
不存在令牌的响应
添加令牌
访问结果
Zuul服务降级
Zuul作为服务消费者崩溃的防线,当消费者服务崩溃时,自然有服务熔断降级
创建类实实现FallbackProvider接口
交付与Spring容器
package com.zuul.fallbackMethod;
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;
/**
* create by Dannimeiyou for 2020/11/23 17:14
*/
@Component
public class ProviderFallbackClass implements FallbackProvider {
@Override
//指定降级的服务 * 指的是所有的service,都走fallbackResponse(route,cause),当然你也可以创建多个分别指定
public String getRoute() {
return "*";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
System.out.println("降级的服务为 = " + route);
//在控制台输出错误信息
cause.printStackTrace();
return new ClientHttpResponse() {
@Override
//指定具体的HttpStatus
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
@Override
//指定错误状态码
public int getRawStatusCode() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR.value();
}
@Override
//错误文本
public String getStatusText() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
}
@Override
public void close() {
}
@Override
//给用户的响应信息
public InputStream getBody() throws IOException {
String msg= route+" is not request now";
return new ByteArrayInputStream(msg.getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_EVENT_STREAM);
return httpHeaders;
}
};
}
}
服务正常时的结果
当消费者奔溃的时候
结果:
服务降级成功!