- 客户端维护大量的ip和port信息,直接访问指定服务
- 认证和授权操作,需要在每一个模块中都添加认证和授权的操作
- 项目的迭代,服务要拆分,服务要合并,需要客户端进行大量的变化
- 统一的把安全性校验都放在Zuul中
创建Maven项目,修改为Springboot
导入依赖
<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
编写配置文件
# 指定Eureka服务地址
eureka:
client:
service-url:
defaultZone: http://root:root@localhost:8761/eureka,http://root:root@localhost:8762/eureka
#指定服务的名称
spring:
application:
name: ZUUL
server:
port: 80
测试
3.1Zuul的监控界面
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
编写配置文件
management:
endpoints:
web:
exposure:
include: "*"
测试
3.2 忽略服务的配置
zuul:
ignored-services: eureka
ignored-patterns: /**/search/**
3.3自定义服务配置
# zuul的配置
zuul:
routes:
kehu: # 自定义名称
path: /aaa/** # 映射的路径
serviceId: customer # 服务名称
3.4 灰度发布
添加一个配置类
@Bean
public PatternServiceRouteMapper serviceRouteMapper() {
return new PatternServiceRouteMapper(
"(?^.+)-(?v.+$)" ,
"${version}/${name}");
}
准备一个服务,提供两个版本
version: v1
#指定服务的名称
spring:
application:
name: CUSTOMER-${version}
修改Zuul的配置
# zuul的配置
zuul:
# 基于服务名忽略服务,无法查看 , 如果需要用到-v的方式,一定要忽略掉
# ignored-services: "*"
测试
客户端请求发送到Zuul服务上,首先通过PreFilter链,如果正常放行,会吧请求再次转发给RoutingFilter,请求转发到一个指定的服务,在指定的服务响应一个结果之后,再次走一个PostFilter的过滤器链,最终再将响应信息交给客户端。
创建POJO类、,继承ZuulFilter抽象类
@Component
public class TestZuulFilter 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 {
System.out.println("写具体的业务代码");
return null;
}
测试
准备访问路径,请求参数的传递token
http://localhost/v2/customer/version?token=123
创建AuthenticationFilter
@Component
public class AuthenticationFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER - 2;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
//..
}
}
在run中写具体的业务逻辑代码
@Override
public Object run() throws ZuulException {
//1. 获取Request对象
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
//2. 获取token参数
String token = request.getParameter("token");
//3. 对比token
if(token == null || !"123".equalsIgnoreCase(token)) {
//4. token校验失败,直接响应数据
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
测试
访问路径http://localhost/v2/customer/version?token=123
创建实体类
@Component
public class ZuulFallBack implements FallbackProvider {}
重写方法
@Override
public String getRoute() {
return "*"; // 代表指定全部出现问题的服务,都走这个降级方法
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
System.out.println("降级的服务:" + route);
cause.printStackTrace();
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
// 指定具体的HttpStatus
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 + "出现问题!!!";
return new ByteArrayInputStream(msg.getBytes());
}
@Override
public HttpHeaders getHeaders() {
// 指定响应头信息
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
测试
床架一个过滤器,执行顺序最好放在Pre过滤器的最后面
在run中编写业务逻辑代码
@Override
public Object run() throws ZuulException {
//1. 获取Request对象
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
//2. 获取参数,redisKey
String redisKey = request.getParameter("redisKey");
//3. 直接判断
if(redisKey != null && redisKey.equalsIgnoreCase("customer")){
// http://localhost:8080/customer
context.put(FilterConstants.SERVICE_ID_KEY,"customer-v1");
context.put(FilterConstants.REQUEST_URI_KEY,"/customer");
}else if(redisKey != null && redisKey.equalsIgnoreCase("search")){
// http://localhost:8081/search/1
context.put(FilterConstants.SERVICE_ID_KEY,"search");
context.put(FilterConstants.REQUEST_URI_KEY,"/search/1");
}
return null;
}
测试