入门微服务spring-cloud(第二章)

15 篇文章 0 订阅

zuul

Zuul 是 Netflix OSS 中的一员,是一个基于 JVM 路由和服务端的负载均衡器. 提供路由、监控、弹性、安全等方面的服务框架。Zuul 能够与 Eureka、Ribbon、Hystrix 等组件配合使用。 Zuul 的核心是过滤器,通过这些过滤器我们可以扩展出很多功能,比如:
  1. 动态路由:

    动态地将客户端的请求路由到后端不同的服务,做一些逻辑处理,比如聚合多个服务的数据返回
  2. 请求监控:

    可以对整个系统的请求进行监控,记录详细的请求响应日志,可以实时统计出当前系统的访问量以及监控状态。
  3. 认证鉴权

    对每一个访问的请求做认证,拒绝非法请求,保护好后端的服务
  4. 压力测试

    通过 Zuul 可以动态地将请求转发到后端服务的集群中,还可以识别测试流量和真实流量,从而做一些特殊处理
  5. 灰度发布

    灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度

使用(路由配置)

坐标
-------------------
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

yml
-------------------
zuul:
  routes:
    microservicecloud-dept:
      path: /api/**      #将服务名称换了一个名称,一个*代表一个层级,两个代表多个层级
  prefix: /rest   #配置路径前缀
  ignored-services: microservicecloud-dept  #上面配置了服务名称成了路径访问,但是服务名称依然可以访问,忽略服务名称访问,可通过配置 * 来表示所有的服务名称,防止服务过多,太臃肿

microservicecloud-dept是一个服务对外暴露的名称
上面的配置表示了通过微服务名称是不可以访问的,需要通过配置的/api/**代替

注意,此时网关这个服务想访问服务提供者者就不能使用服务名称去访问,而是  /api/**  接上其他的路径
通过zuul去访问就是ip:端口/api/**8  去访问才可以     换成服务名称是不行得,
注意,要是加了前缀,应该要在/api前面加上/rest


还有的一种配置的方式:
zuul:
  routes:
    mydept.serviceId: microservicecloud-dept    #微服务名称
    mydept.path: /myway/**  路径
这种方式配置也是可以的,mydept是自定义自己随便写的


还有
spring:
  application:
    name: springcloud_zuul_getway
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka




启动类
-------------------
@EnableZuulProxy

过滤器

Zuul 中的过滤器跟我们之前使用的 javax.servlet.Filter 不一样,javax.servlet.Filter 只有一种类型,可以通过配置 urlPatterns 来拦截对应的请求.而 Zuul 中的过滤器总共有 4 种类型,且每种类型都有对应的使用场景。
  • pre

    可以在请求被路由之前调用。适用于身份认证的场景,认证通过后再继续执行下面的流程
  • route

    在路由请求时被调用。适用于灰度发布场景,在将要路由的时候可以做一些自定义的逻辑。
  • post

    在 route 和 error 过滤器之后被调用。这种过滤器将请求路由到达具体的服务之后执行。适用于需要添加响应头,记录响应日志等应用场景。
  • error

    处理请求时发生错误时被调用。在执行过程中发送错误时会进入 error 过滤器,可以用来统一记录错误信息
他们的顺序是先进入pre,然后route,最后post,其中这三个合何时抛出异常都会进入error 在ZuulServlet的源码中有一个service的方法可以看得出来他们的调用顺序

使用过滤器

创建一个 pre 过滤器,我们需要继承一个抽象类ZuulFilter ,需要覆写他的很多个方法
filterType():  过滤器的类型,就是上面所说的四种
filterOrder():  过滤器的执行顺序,越小的越先执行
shouldFilter() :为false的话不会被转发到后端服务器
run():   自己的过滤器逻辑处理

代码示例:
public class IpFilter extends ZuulFilter {

    public IpFilter() {
        super();
    }

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        String remoteHost = request.getRemoteHost();
        String remoteAddr = request.getRemoteAddr();
        if ("127.0.0.1".equals(remoteHost)){
            currentContext.setSendZuulResponse(false);
            Result data = new Result();
            data.setCode(401);
            data.setMsg("非法请求");
            currentContext.setResponseBody(JSON.toJSONString(data));
            currentContext.getResponse().setContentType("application/json; charset=utf-8");
            return null;
        }
        return null;
    }
}


主要起作用的是这个currentContext.setSendZuulResponse(false);
他表示不需要将当前请求转发到后端的服务,所以直接就返回了
创建了这么一个类,我们还需要一个配置类
@Configuration
public class IpConfig {

    @Bean
    public IpFilter getIpConfig(){
        return new IpFilter();
    }
}

此时该过滤器就生效了,
,配置多个过滤器的方式也是一样的,但是现在问题来了,有多个过滤器,也有先后顺序,那么如何设置如果请求不满足前面的过滤器要求,就不会进入到后面的其他的过滤器逻辑中呢

首先我们要知道 shouldFilter()方法返回的布尔值决定了是否会进入自己的run方法逻辑中
而currentContext.setSendZuulResponse(false)则决定了是否会被转发到后端服务器进行

因此有多个过滤器时,要注意,最高优先级的过滤器的shouldFilter()方法我们直接返回true,保证可以进自己的run方法,去执行他的逻辑,
如果符合我们的逻辑我们放行,currentContext.setSendZuulResponse(true),如果不符合我们就拦截改为false,其实默认就是true,

那么后面的过滤器呢,他的shouldFilter()方法就是有点不一样了,我们需要通过
        RequestContext currentContext = RequestContext.getCurrentContext(); 获取当前的上下文
        return !currentContext.sendZuulResponse();   
          //注意,如果上个选择器设置了它fasle,也就是上个过滤器就不符合逻辑,被拦截了, 没有被拦截那就是true,会执行当前的run方法,以此类推。
          注意:第一个拦截器要直接放回==返回true

禁用过滤器

当我们因为一些原因需要关闭某个过滤器功能时,我们可以这样;
在配置文件中

zuul.IpFilter.pre.disable=true  #注意IpFilter是我们的过滤器的类名

过滤器之间的值传递

设置
currentContext.set(,);

取值
currentContext.get();

它的原理就是ThreadLocal

容错回退

Zuul 默认整合了 Hystrix,当后端服务异常时可以为 Zuul 添加回退功能,返回默认的数据。
实现回退机制需要实现 ZuulFallbackProvider 接口去覆写他的两个方法:
如:
@Component
public class MyZuulFallbackProvider implements ZuulFallbackProvider {
    @Override
    public String getRoute() {
        return "*";  //表示对所有服务进行回退操作,单个的回退需要放回服务的名称,必须是已经注册到eureka的服务
    }

    @Override
    public ClientHttpResponse fallbackResponse() {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                //返回响应的状态码
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return this.getStatusCode().value();
            }

            @Override
            public String getStatusText() throws IOException {
                //返回响应状态码对应的文本
                return this.getStatusCode().getReasonPhrase();
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                //返回回退的内容
                RequestContext currentContext = RequestContext.getCurrentContext();
                Result result = new Result();
                result.setMsg("服务异常");
                result.setCode(500);
                return new ByteArrayInputStream(JSON.toJSONString(result).getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                //返回响应的请求头信息
                HttpHeaders headers = new HttpHeaders();
                MediaType mt = new MediaType("application", "json", Charset.forName("UTF-8"));
                headers.setContentType(mt);
                return headers;
            }
        };
    }
}


当配置好后,我们先将注册中心和网关启动,服务提供者启动,先访问一次,通过网关去访问服务提供者的接口,是没有问题的,那么现在把服务提供方关了,然后在访问就会出现我们配好的回退信息
{"code":500,"msg":"服务异常"}

zuul自带了ribbon
现在我们启动两个服务提供者,然后的他们的接口是一样的路径也是一样的,
然后我们通过zuul去访问服务,这时就会发现它会轮询着去访问这两个服务的接口,默认是轮询

注意:如果两个服务提供者我先关了一个,然后去访问,依然是轮询,只不过访问到服务关闭了的会进行一个回退,返回我们上面写好的返回数据,如果zuul的配置文件里加了重试的配置的话,那么就有可能不是轮询的一种表现状态了,
此时有可能访问到宕机的服务,然后他会重试,再次访问,还可能在重试,


重试
zuul.retryable=true 

高可用

routes 路由端点

当 @EnableZuulProxy 与 Spring Boot Actuator 配合使用时,Zuul 会暴露一个路由管理端点 /routes,我们可以借助这个端点可以查看和管理路由
添加配置
management:
  endpoints:
    web:
      exposure:
        include: "*"
  security:
    enabled: false


访问端口:ip:端口/routes就可以了

filters 过滤器信息

/fliters 端点会返回 Zuul 中所有过滤器的信息
ip:端口/filters

补充:

服务状态监听
public class EurekaStateChangeListener {

    @EventListener
    public void listen(EurekaInstanceCanceledEvent event) {
        System.err.println(event.getServerId() + "\t" + event.getAppName() + " 服务下线 ");
    }
    @EventListener
    public void listen(EurekaInstanceRegisteredEvent event) {
        InstanceInfo instanceInfo = event.getInstanceInfo();
        System.err.println(instanceInfo.getAppName() + " 进行注册 ");
    }
    @EventListener
    public void listen(EurekaInstanceRenewedEvent event) {
        System.err.println(event.getServerId() + "\t" + event.getAppName() + " 服务进行续约 ");
    }
    @EventListener
    public void listen(EurekaRegistryAvailableEvent event) {
        System.err.println(" 注册中心启动 ");
    }
    @EventListener
    public void listen(EurekaServerStartedEvent event) {
        System.err.println("Eureka Server启动 ");
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值