学习SpringCloud(4)路由网关 Zuul

1、Zuul简介spring

一、Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分,好比/api/user转发到到user服务,/api/shop转发到到shop服务。zuul默认和Ribbon结合实现了负载均衡的功能。sql

二、Netflix使用Zuul进行如下操做:后端

  • 认证
  • 洞察
  • 压力测试
  • 金丝雀测试
  • 动态路由
  • 服务迁移
  • 负载脱落
  • 安全
  • 静态响应处理
  • 主动/主动流量管理

三、Zuul的规则引擎容许任何JVM语言编写规则和过滤器,内置支持Java和Groovy。api

四、配置属性zuul.max.host.connections已被两个新属性zuul.host.maxTotalConnections和zuul.host.maxPerRouteConnections取代,它们分别默认为200和20。安全

五、全部路由的默认Hystrix隔离模式(ExecutionIsolationStrategy)是SEMAPHORE。 若是首选此隔离模式,则能够将zuul.ribbonIsolationStrategy更改成THREAD。bash

2、动态路由网络

直接拿上面的项目进行修改app

一、pom 文件加入相关依赖:负载均衡

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
复制代码

二、启动类加上注解@EnableZuulProxy,开启zuul的功能:nosql

@SpringBootApplication
@EnableDiscoveryClient //开启服务注册客户端
@EnableFeignClients //开启feign client
@EnableZuulProxy //开启zuul的功能
复制代码

三、修改配置文件application.yml,加入相关配置:

zuul:
  routes:
    api:
      path: /api/**
      serviceId: spring-cloud-eureka-pro
复制代码

意思就是以/api/ 开头的请求都转发给spring-cloud-eureka-pro服务

重启项目进行访问

3、服务认证

继续修改项目,建立MyZuulFilter 类

@Component
public class MyZuulFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return "pre";
    }

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

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

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        Object token = request.getParameter("token");
        if(token == null) {
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("token is empty");
            }catch (Exception e){}

        }else{
            if(!"zuul".equals(token.toString())){
                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(401);
                try {
                    ctx.getResponse().getWriter().write("token is error");
                }catch (Exception e){}

            }
        }
        return null;
    }
}

复制代码

一、filterType:返回一个字符串表明过滤器的类型,在zuul中定义了四种不一样生命周期的过滤器类型,具体以下:

  • pre:路由以前
  • routing:路由之时
  • post: 路由以后
  • error:发送错误调用

二、filterOrder:过滤的顺序

三、shouldFilter:这里能够写逻辑判断,是否要过滤,本文true,永远过滤。

四、run:过滤器的具体逻辑。可用很复杂,包括查sql,nosql去判断该请求到底有没有权限访问。


重启项目进行访问

4、路由熔断

当咱们的后端服务出现异常的时候,咱们不但愿将异常抛出给最外层,指望服务能够自动进行一降级。Zuul给咱们提供了这样的支持。当某个服务出现异常时,直接返回咱们预设的信息。

咱们经过自定义的fallback方法,而且将其指定给某个route来实现该route访问出问题的熔断处理。主要继承FallbackProvider接口来实现,FallbackProvider默认有两个方法,一个用来指明熔断拦截哪一个服务,一个定制返回内容。

public interface FallbackProvider {
    String getRoute();

    ClientHttpResponse fallbackResponse(String route, Throwable cause);
}
复制代码

实现本身的FallbackProvider 方法:

/**
 * Zuul 目前只支持服务级别的熔断,不支持具体到某个URL进行熔断。
 */
@Component
public class MyFallbackProvider implements FallbackProvider {

    //指定要处理的 service。
    //若是要为全部路由提供默认回退,能够建立FallbackProvider类型的bean并使getRoute方法返回*或null
    @Override
    public String getRoute() {
        return "*";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        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 {
                return new ByteArrayInputStream("服务不可用".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
                return headers;
            }
        };

    }
}

复制代码

当服务出现异常时,返回“服务不可用”,这的逻辑能够本身定义。

重启项目进行测试,先试一下正常状况

将provider停掉,在试一下

能够看到此时已经对provider进行了熔断处理

5、路由重试

有时候由于网络或者其它缘由,服务可能会出现暂时不可用,这个时候咱们但愿能够主动对服务进行重试,Zuul也帮咱们实现了此功能,须要结合Spring Retry 一块儿来实现

pom文件添加依赖

<dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
复制代码

修改配置文件中,启用Zuul Retry

zuul:
  routes:
    api:
      path: /api/**
      serviceId: spring-cloud-eureka-pro

  retryable: true #是否开启重试功能


ribbon:
  MaxAutoRetries: 2 #对当前服务的重试次数
  MaxAutoRetriesNextServer: 1 #切换相同Server实例的次数
  ConnectTimeout: 250 #ribbon重试超时时间
  ReadTimeout: 1000 #创建链接后的超时时间
复制代码

将第一个provider项目关闭,第二个provider2项目进行修改并重启

@RequestMapping("/hi")
    public String hi(@RequestParam String name)
    {
        System.out.println(name+"发送了请求。。。");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "hi " + name + ",i am from port:" + port;
    }
复制代码

此时客户端的请求即便被路由到provider2,也须要等待10s才能返回结果,在服务请求端已经超时,因此不会等到它返回结果,就进行下一次的重试了,最终结果

一共是三次打印,第一次请求+两次重试

可是“重试”功能须要慎用,好比当压力过大,一个实例中止响应时,zuul路由将请求转到另外一个实例,颇有可能致使最终全部的实例全被压垮

相关文章
相关标签/搜索