五阶段--day02--zuul 请求过滤/降级容错/限流/日志监控仪表盘

目录

一 zuul 请求过滤,统一权限的校验

1 sp06-zuul工程创建filter包,创建继承ZuulFilter过滤器的类

2 zuul 继承 ribbon的负载均衡和重试:

3 zuul启用Ribbon的重试

3.1  sp06工程pom.xml文件添加重试依赖,配置文件启动重试

4 zuul 集成Hystrix的容错和限流

4.1 容错降级的实现

  4.2 hystrix限流--熔断

4.3 Hystrix 熔断的监控

 二 搭建 Hystrix Dashboard  断路器仪表盘,对熔断进行监控



一 zuul 请求过滤,统一权限的校验

通过zuul的过滤器,判断用户权限

  • 有权限,继续转发调用
  • 没有权限,从网关直接返回结果

 模拟判断是否登录:

  • http://localhost:3001/item-service/123456       -----没有令牌代表没有登录,不允许访问
  • http://localhost:3001/item-service/123456?token=u6y54t ---携带token,已登录,可访问

步骤:

1. 继承ZuulFilter 过滤器

2. 添加@Component注解,使spring自动创建实例

  • Zuul的自动配置类,可以从spring容器自动发现过滤器实例,完成自动配置

1 sp06-zuul工程创建filter包,创建继承ZuulFilter过滤器的类

package cn.tedu.sp06zuul.filter;

import cn.tedu.web.util.JsonResult;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class AccessFilter extends ZuulFilter {

    //pre,routing,post,error
    @Override
    public String filterType() {
        //return "pre";
        return FilterConstants.PRE_TYPE;//返回字符串常量
    }

    //过滤器的顺序号
    @Override
    public int filterOrder() {
        //有5个默认的过滤器,自己的过滤器加到末尾
        return 6;//加到第六个,不能放到前五个之间
    }

    //针对当前请求,判断是否要执行过滤代码
    /*如果调用item-service,检查是否登录
    *否则,不检查登录,直接访问*/
    @Override
    public boolean shouldFilter() {
        //1.先获取当前请求的上下文对象
        // RequestContext它在web项目中传参时使用,涉及后端获取前端参数.
        // 而Context为当前对象在程序中所处的一个环境,一个与系统交互的过程,
        //存放一些数据,对象, 如果很多服务需要,都从这里面拿就行
        RequestContext ctx = RequestContext.getCurrentContext();
        //2.然后从当前请求,调用的服务id
        String serviceId = (String)
                ctx.get(FilterConstants.SERVICE_ID_KEY);//"serviceId",可能是商品/用户/订单
        //3.判断调用的是否是item-service
        return "item-service".equals(serviceId);
    }

    //过滤代码--进行权限判断
    @Override
    public Object run() throws ZuulException {
        //1.获得上下文对象
        RequestContext ctx = RequestContext.getCurrentContext();
        //2.获得 request 对象
        HttpServletRequest request = ctx.getRequest();
        //3.接受 token 参数
        String token = request.getParameter("token");
        //4.若没有token,就要阻止其继续调用,从网关直接返回响应
        //""空串,null值,"  "都是没有token
        if(StringUtils.isBlank(token)){
            //如果没有token,阻止继续调用
            ctx.setSendZuulResponse(false);//关闭发送zuul响应
            //直接向客户端返回响应--JsonResult--{code:1,msg:xxx,data:null}
            ctx.addZuulResponseHeader(
                    "Content-Type","application/json;charset=UTF-8");
            ctx.setResponseBody(JsonResult
                    .err().code(400)
                    .msg("Not login! / 未登录!")
                    .toString());//转成json串
        }
        return null;//当前zuul版本中,这个返回值没有使用,没有任何作用,so返回null
    }
}

启动服务,进行测试:

此处会发生超时504错误,需要反复刷新:

2 zuul 继承 ribbon的负载均衡和重试:

  • zuul默认启动负载均衡,会设置两个或多个相同服务的id,其中一个失败,可以调用另一个的
  • zuul默认不启用重试,zuul不推荐启用重试(所以,请求时会报504超时,是因为设置了90%的随机阻塞,需要反复刷新请求),一般不在网关执行重试,否则,会造成后台大面积服务器压力倍增,重试尽量往后放

3 zuul启用Ribbon的重试

步骤:

  1. 添加 spring-retry 依赖
  2. yml配置启用 zuul 重试: zuul.retryable=true
  3. (可选)配置重试参数

3.1  sp06工程pom.xml文件添加重试依赖,配置文件启动重试

 

测试:

 默认共重试2次也失败,就需要反复刷新,才能获得

4 zuul 集成Hystrix的容错和限流

zuul默认已启用了Hystrix,可以直接写降级代码,降级用与容错;通过熔断实现限流

步骤:

  1. 实现 FallbackProvider 接口
  2. 添加@COnponent: zuul会用自动配置类,对降级类进行自动配置

4.1 容错降级的实现

第一步: 定义过滤器,继承 ZuulFilter

package cn.tedu.sp06zuul.filter;

import cn.tedu.web.util.JsonResult;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component  
public class AccessFilter extends ZuulFilter {

    //pre,routing,post,error
    @Override
    public String filterType() {
        //return "pre";
        return FilterConstants.PRE_TYPE;//返回字符串常量
    }

    //过滤器的顺序号
    @Override
    public int filterOrder() {
        //有5个默认的过滤器,自己的过滤器加到末尾
        //在第5个过滤器中,向Context对象放入了 serviceId,
        //后面组件中,才能访问这个数据
        return 6;//加到第六个,不能放到前五个之间
    }

    //针对当前请求,判断是否要执行过滤代码
    /*如果调用item-service,检查是否登录
    *否则,不检查登录,直接访问*/
    @Override
    public boolean shouldFilter() {
        //1.先获取当前请求的上下文对象
        RequestContext ctx = RequestContext.getCurrentContext();
        //2.然后从当前请求,调用的服务id
        String serviceId = (String)
                ctx.get(FilterConstants.SERVICE_ID_KEY);//"serviceId",可能是商品/用户/订单
        //3.判断调用的是否是item-service
        return "item-service".equals(serviceId);
    }

    //过滤代码--进行权限判断
    @Override
    public Object run() throws ZuulException {
        //1.获得上下文对象
        // RequestContext它在web项目中传参时使用,涉及后端获取前端参数
        // 而Context为当前对象在程序中所处的一个环境,一个与系统交互的过程
        存放一些数据,对象, 如果很多服务需要,都从这里面拿就行
        RequestContext ctx = RequestContext.getCurrentContext();
        //2.获得 request 对象
        HttpServletRequest request = ctx.getRequest();
        //3.接受 token 参数
        String token = request.getParameter("token");
        //4.若没有token,就要阻止其继续调用,从网关直接返回响应
        //""空串,null值,"  "都是没有token
        if(StringUtils.isBlank(token)){
            //如果没有token,阻止继续调用
            ctx.setSendZuulResponse(false);//关闭发送zuul响应
            //直接向客户端返回响应--JsonResult--{code:1,msg:xxx,data:null}
            ctx.addZuulResponseHeader(
                    "Content-Type","application/json;charset=UTF-8");
            ctx.setResponseBody(JsonResult
                    .err().code(400)
                    .msg("Not login! / 未登录!")
                    .toString());//转成json串
        }
        return null;//当前zuul版本中,这个返回值没有使用,没有任何作用,so返回null
    }
}

第二步: 调用商品的后台服务item-service,出错时调用此降级服务

package cn.tedu.sp06zuul.fb;

import cn.tedu.web.util.JsonResult;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
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.nio.charset.StandardCharsets;

/**调用商品的后台服务,出错时调用此降级服务*/
@Component
public class ItemFB implements FallbackProvider {
    /**返回一个 service-id,表示当前降级类是针对哪个后台服务的降级类
    * item-service ----只针对商品服务降级
    * *星号--对所有服务都应用这个降级类
    * null---等同于*星号
    * */
    @Override
    public String getRoute() {
        return "item-service";
    }

    //调用后台服务出错,发送降级响应给客户端
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            //比如:返回封装 500 error 的对象
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR;
            }
            //只返回 500 状态
            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR.value();
            }
            //只返回 error 文本
            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
            }

            @Override
            public void close() {


            }


            @Override
            public InputStream getBody() throws IOException {
                //JsonResult ----{code:500,msg:xxx,data:null}
                String json = JsonResult.
                        err().code(500).msg("调用后台服务出错").toString();
                return new ByteArrayInputStream(json.getBytes("UTF-8"));
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders h = new HttpHeaders();
                h.add("Content-Type","application/json;charset=UTF-8");
                return h;
            }
        };
    }
}

如果调用订单服务,就编辑一个订单服务的容错降级类

package cn.tedu.sp06zuul.fb;

import cn.tedu.web.util.JsonResult;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

/**调用订单的后台服务,出错时调用此降级服务*/
@Component
public class OrderFB implements FallbackProvider {
    /**返回一个 service-id,表示当前降级类是针对哪个后台服务的降级类
    * item-service ----只针对商品服务降级
    * *星号--对所有服务都应用这个降级类
    * null---等同于*星号
    * */
    @Override
    public String getRoute() {
        return "order-service";
    }

    //调用后台服务出错,发送降级响应给客户端
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            //比如:返回封装 500 error 的对象
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR;
            }
            //只返回 500 状态
            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR.value();
            }
            //只返回 error 文本
            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
            }

            @Override
            public void close() {


            }


            @Override
            public InputStream getBody() throws IOException {
                //JsonResult ----{code:500,msg:xxx,data:null}
                String json = JsonResult.
                        err().code(500).msg("调用后台服务出错").toString();
                return new ByteArrayInputStream(json.getBytes("UTF-8"));
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders h = new HttpHeaders();
                h.add("Content-Type","application/json;charset=UTF-8");
                return h;
            }
        };
    }
}

第三步:访问测试

  4.2 hystrix限流--熔断

1.当流量过大,后台服务器出现故障,可以断开后台服务器的链路,等待后台服务恢复

2.短路器的打开条件:

  1. 10秒20次请求(必须首先满足20次请求)
  2. 再判断是否是50%失败,是50%才能执行的降级代码(商品服务设置了90%的阻塞,满足了此要求)

3.半开状态:

  • 断路状态打开一段时间后,会进入半开状态
  • 会尝试发生一次客户端调用,若成功,则关闭断路器,恢复正常;若失败,则继续保持打开状态

测试:F5快速刷新请求,转圈在请求属于半开状态,出现500,属于短路状态,但是此测试效果不明显,需要hstrix监控,使用图标展现

4.3 Hystrix 熔断的监控

对Hystrix降级和熔断的情况进行监控,可以快速定位错误,查找系统中的问题.

在sp06工程中,Hystrix利用actuator来暴露自己的监控日志数据:

  1. 添加actuator 依赖,(这个依赖已经集成在了zuul依赖中了)
  2. yml配置暴露监控日志
#暴露所有监控日志
m.e.w.e.i="*"
m.e.w.e.i=health,bean,mappings
m.e.w.e.i=hystrix.stream

     3.

 第一步:pom文件添加actuator 依赖,(这个依赖已经集成在了zuul依赖中了)

第二步:配置文件设置暴露所有监控日志

management:
  endpoints:
    web:
      exposure:
        include: "*"

第三步:保持六个服务启动状态,sp06工程重启,访问url查看所有监控日志

http://localhost:3001/actuator    ,所有的监控url都在这里面

访问熔断日志:

第四步: 访问http://localhost:3001/item-service/123456?token=123456

再访问http://localhost:3001/actuator/hystrix.stream   ,访问什么服务就会有什么日志

 二 搭建 Hystrix Dashboard  断路器仪表盘,对熔断进行监控

1. 新建 spring模块: sp07-hystrix-dashboard

2. 添加依赖: Hystrix Dashboard

3. yml配置文件配置:

允许抓取的服务器列表: localhost,a,b,c

4. 启动类注解: @EnableHystrixDashboard

5. 访问: http://localhost:4001/hystrix   ,在输入框中填写日志数据的地址

第一步:新建sp07工程

 不加任何依赖直接finish

第二步:修改parent标签的版本

 添加依赖,以及依赖版本设置:

      #加在dependencies标签
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
      # dependencyManagement标签
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR8</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

第三步: 修改配置文件的后缀为.yml,编辑允许抓取服务器的列表

server:
  port: 4001
hystrix:
  dashboard:
    #允许抓取的服务器列表
    proxy-stream-allow-list: localhost

第四步:启动类添加@EnableHystrixDashboard  注解,搭建仪表盘

 第五步:启动启动类,访问  localhost:4001/hystrix

 第六步:http://localhost:3001/actuator  ,能看到监控所有服务,访问熔断的url

http://localhost:3001/actuator/hystrix.stream  再复制粘贴到hystrix dashboard的url地址

 monitor stream进入监控日志仪表盘

第七步:访问服务,hystrix进行输出日志,hystrix dashboard仪表盘进行日志监控

第八步: 在Apache24 的bin目录下,cdm启动dos窗口命令,测试

  • 用 ab 工具,以并发50次,来发送20000个请求
ab -n 20000 -c 50 http://localhost:3001/item-service/123456?token=123456

  • 断路器状态为 Open,所有请求会被短路,直接降级执行 fallback 方法
  • 断路状态打开一段时间后,会进入半开状态
  • 会尝试发生一次客户端调用,若成功,则关闭断路器,恢复正常;若失败,则继续保持打开状态

熔断

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值