SpringCloud微服务组件——Zuul快速入门

7 篇文章 0 订阅
1 篇文章 0 订阅

Zuul

Zuul的作用
  • 所有服务统一的入口,可以方便做参数校验,安全校验,权限校验。(Nginx目的不是实现业务)
  • Zuul可以通过eureka获取每一个服务的信息。(客户端自己记录/Nginx记录都很麻烦)
  • 如果服务地址信息改变了,Zuul基本不需要改变。(客户端/Nginx都需要做大量的维护信息)
  • 做统一的监控信息。(Nginx目的不是实现业务)
Zuul的基础使用

创建项目导入依赖

<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

直接测试

Zuul的常用配置

Zuul的监控界面

导入依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

编写配置文件

# 查看zuul的监控界面(开发时,配置为*,代表监控全部服务,上线的时候,不要配置)
management:
 endpoints:
   web:
    exposure:
       include: "*"

通过localhost:端口号/actuator/routes
忽略服务配置

# zuul的配置
zuul:
 # 基于服务名忽略服务,无法查看 ,如果要忽略全部的服务  "*",默认配置的全部路径都会被忽略掉(自定义服务的配置,无法忽略的)
 ignored-services: eureka
 # 监控界面依然可以查看,在访问的时候,404
 ignored-patterns: /**/search/**

自定义服务配置

# zuul的配置
zuul:
 # 指定自定义服务(方式一 , key(服务名):value(路径))
#routes:
#search: /ss/**
#customer: /cc/**
 # 指定自定义服务(方式二)
 routes:
   kehu:   # 自定义名称
     path: /ccc/**     # 映射的路径
     serviceId: customer   # 服务名称
灰度发布

启动类添加一个配置类

**@Bean
public PatternServiceRouteMapper serviceRouteMapper() {
   return new PatternServiceRouteMapper(
       "(?<name>^.+)-(?<version>v.+$)",
       "${version}/${name}");
}**

准备一个服务,提供两个版本

version: v1

#指定服务的名称
spring:
 application:
   name: CUSTOMER-${version}

第二个版本可以直接在idea启动的地方(Run/Debug Configurations)直接复制一个,在VM options中加入(-Dversion=v2 -Dserver.port=11112
修改Zuul的配置

# zuul的配置
zuul: 
 # 基于服务名忽略服务,无法查看  , 如果需要用到-v的方式,一定要忽略掉 
  # ignored-services: "*"
Zuul的原理(过滤器执行流程)

Zuul的实现原理:

  • Zuul就是由多个过滤器组成,并由RoutingFilter的过滤器将请求转发到其他服务上。

客户端请求发送到Zuul服务上,首先通过PreFilter链,如果正常放行,会吧请求再次转发给RoutingFilter,请求转发到一个指定的服务,在指定的服务响应一个结果之后,再次走一个PostFilter的过滤器链,最终再将响应信息交给客户端。

基于Zuul提供的前置,后置,异常过滤器去编写业务逻辑:

  • PreFilter:在请求路由到其他服务前,先执行。
  • RouteFilter:将请求路由到其他服务。
  • PostFilter:在响应之前,执行。
  • ErrorFilter:在出现异常时,执行。

自定义过滤器

  • 创建POJO类,继承ZuulFilter的抽象类。
  • 重写四个方法:
    • filterType:指定过滤器的类型,推荐用FilterConstants的常量指定过滤器类型。
    • filterOrder:指定过滤器的执行顺序,推荐用FilterConstants进行加减。
    • shouldFilter:指定过滤器是否开启。返回true代表开启
    • run:编写你要执行的业务逻辑代码。
  // 获取request等信息
  - RequestContext requestContext = RequestContext.getCurrentContext();
  -  HttpServletRequest request = requestContext.getRequest();
前置过滤器实现token校验

创建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;
}
Zuul的降级

创建POJO类,实现接口FallbackProvider

@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;
       }
   };
}
动态路由的实现
  • 可以基于Zuul实现动态路径,根据项目的业务动态修改路由的规则
  • 实现
    • 是前置过滤器:pre
    • 尽量放在前置过滤器的最后。
    • 在过滤器中设置RequestContext对象中的信息:
      • SERVICE_ID_KEY:找服务对应的项目路径
      • SERVICE_ID_KEY:找服务对应的项目路径
    • 在执行Route过滤器时,根据路由规则处理。
  • 代码实现
@Override
public Object run() throws ZuulException {
//  http://ip:port/xxxx?redisKey=customer  ---->  customer服务的/sys/customer/table路径
//  http://ip:port/xxxx?redisKey=search ---->  search服务的/search/customer/get/1路径
//1. 获取请求携带的redisKey参数
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String redisKey = request.getParameter("redisKey");

//2. 判断redisKey不为null,
if(!StringUtils.isEmpty(redisKey)){
    Object serviceId = redisTemplate.opsForHash().get(redisKey, "serviceid");
    Object requestURI = redisTemplate.opsForHash().get(redisKey, "requesturi");
    context.put(FilterConstants.SERVICE_ID_KEY,serviceId);
    context.put(FilterConstants.REQUEST_URI_KEY,requestURI);
}
return null;
}
Zuul整合Hystrix

避免其他服务出现问题后,问题抛到Zuul,导致给页面发送错误信息。

根据官网编写指定POJO类:

public class MyFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
    return "*";				// 指定哪些服务,在出现问题后,走这个fallback方法
}

@Override
public ClientHttpResponse fallbackResponse(String route, Throwable throwable) {
    // route:哪个服务出现了问题
    // throwable: 服务出现的问题是什么
    
    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("fallback".getBytes());
        }

        @Override
        public HttpHeaders getHeaders() {
            // 设置响应头信息
            HttpHeaders headers = new HttpHeaders();
            >headers.setContentType(MediaType.APPLICATION_JSON);
            return headers;
        }
    };
}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值