SpringCloud入门(5)Zuul

网上很多资源都把API网关,是什么,能做什么解释得非常清楚,但是对于初学者来说我觉得是不够友好的,Zuul就是SpringCloud微服务中的网关。

对于初学者入门来说,只需要知道Zuul就是当服务增多之后,就要对API进行一个统一的管理,某个类型的API就会调用某个类型的服务,除此之外还能对请求过来的API进行一个过滤。更进一步才是Zuul其它作用,具体有哪些作用如图所示:

SpringCloud入门(5)Zuul

本文重点讲解的是「路由转发」「过滤器」

1 如何引入Zuul

一样的,建立一个Zuul模块,本例中没有什么消费端,所以就没有采取之前建立空父模块再建立具体子模块的方法。然后往Zuul中的pom文件中添加依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
</dependencies>

到现在整个项目的目录结构如图所示:

SpringCloud入门(5)Zuul

2 主启动类和配置文件

因为不涉及服务消费等,只是做api的处理,所以主启动类还是比较简单的

@SpringBootApplication
@EnableZuulProxy  //开启Zuul
@EnableEurekaClient
public class ZuulMain9401 {
    public static void main(String[] args) {
        SpringApplication.run(ZuulMain9401.classargs);
    }
}

配置文件的话和常规的Eureka客户端是一样的

spring:
  application:
    name: zuul9401
server:
  port: 9401

eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://localhost:8001/eureka/
  instance:
    instance-id: zuul9401

3 路由转发

路由转发主要是通过配置文件来修改,往上面配置文件中「增加内容」 ,下面会讲3种方式的转发路由。

设置注册Eureka的服务id

「增加」第一波配置文件,是在原有的配置文件上增加了以下内容。

zuul:
  routes:
    user-a:
      path: /api-a/**
      serviceId: eureka-provide

user-a「随便定义」path 是外部访问的路径,serviceId是微服务配置文件的spring.application.name 的值。

「所以上面增加的配置文件整体意思是,当外部访问/api-a/ 相关路径时候,会转发给名字为eureka-provid 的服务提供服务。」

开启Eureka服务注册中心EurekaServer8001 ,服务提供者EurekaProvide7001/2/3 ,API网关ZuulMain9401

SpringCloud入门(5)Zuul

接着访问http://localhost:9401/api-a/eureka/provide,按照分析,应该会被转发到eureka-provide 服务里的eureka/provide路径。

「为了防止有点混,贴一次第一个项目的代码,详情请看本系列的第一篇文章。」

@SpringBootApplication
@RestController
@EnableEurekaClient
public class EurekaProvide7001 {

    @Value("${server.port}")
    int port;

    @GetMapping("/eureka/provide")
    public String getInfo() {
        return "hello, i am eureka provide, the provide service. My port: " + port;
    }

    @GetMapping("/eureka/delayProvide")
    public String delayGetInfo() throws InterruptedException {
        Thread.sleep(3000);
        return "hello, delay to do something";
    }
    public static void main(String[] args) {
        SpringApplication.run(EurekaProvide7001.classargs);
    }
}

可以看到能够成功「转发路由」

SpringCloud入门(5)Zuul

设置URL

「增加」第二波配置文件

zuul:
  routes:
#    user-a:
#      path: /api-a/**
#      serviceId: eureka-provide
    user-b:
      path: /api-b/**
      url: http://localhost:7101/

其它如上,url 需要转发到哪个服务

通过Edit Configurations更改端口以及服务名以「模拟新的服务」,具体操作同样在第一篇文章中有清晰的gif图。

SpringCloud入门(5)Zuul
SpringCloud入门(5)Zuul

其它服务不用关闭,继续开启刚新建的Provide7101「重启」ZuulMain9401服务,访问http://localhost:9401/api-b/eureka/provide,同样能过够看到成功转发

SpringCloud入门(5)Zuul

设置非注册Eureka的服务id

之前在学习Ribbon的时候也说过,我们可以通过Ribbon设置访问一些「没有注册进Eureka」的服务,同样在API网关也能通过配置文件设置Ribbon来达到一样的效果。

「增加」第三波配置文件

zuul:
  routes:
#    user-a:
#      path: /api-a/**
#      serviceId: eureka-provide
#    user-b:
#      path: /api-b/**
#      url: http://localhost:7101/
    user-c:
      path: /api-c/**
      serviceId: provide-without-eureka

#一定需要这个才行
ribbon:
  eureka:
    enabled: false
provide-without-eureka:
  ribbon:
    ServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
    listOfServers: localhost:7201, localhost:7202
    ConnectTimeout: 1000
    ReadTimeout: 3000
    MaxTotalHttpConnections: 500
    MaxConnectionsPerHost: 100

如果取消user-a 的相关注释,此时访问user-a是无法转发的,会报500的错误,我猜是因为设置了ribbon.eureka.enabled = false 的缘故。

serviceId同样是微服务的名称,然后对这个微服务设置,所以是设置微服务名[provid-without-eureka].ribbon ,其它属性都是相关属性,最重要的同样是listOfServers ,表示这个访问这个服务名会在这些服务列表中进行分配。

为了简单还是用同一个服务,用上面的方法修改配置文件即可,修改端口号7201,修改eureka.client.register.with.eureka = false 来模拟没有注册进Eureka的服务。

SpringCloud入门(5)Zuul

接着再复制一份配置,其它都不变,把端口号改成7202,总共是新建了端口为7201,7202的「两个服务」

其它服务不用关,开启ProvideWithoutEureka7201/2 服务,「重启」ZuulMain9401服务,此时所有的服务开启如下

SpringCloud入门(5)Zuul

访问http://localhost:9401/api-c/eureka/provide,服务还是一样能进行成功转发

SpringCloud入门(5)Zuul

4 查看路由状态

顺便简单说下查看路由状态,首先还是需要增加配置文件,是「一定要增加」

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: ALWAYS

接着访问http://localhost:9401/actuator/routes,正常情况会出现以下

SpringCloud入门(5)Zuul

如果想得到详细信息,那么只需要访问http://localhost:9401/actuator/routes/details

SpringCloud入门(5)Zuul

5 转发路由时的Fallback

和Hystrix,当转发路由发现服务不能够正常提供服务的时候,就可以fallback。

新建一个类MyFallbackProvider 实现FallbackProvider 接口

@Component
public class MyFallbackProvider implements FallbackProvider {
    @Override
    public String getRoute() {
        //为所有的路由提供回退
        return "*"
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable 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("something wrong, fallback now".getBytes());
            }

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

现在来手动关闭ProvideWithoutEureka7201/2服务模拟服务宕机,来看看是否能回退

SpringCloud入门(5)Zuul

6 过滤器

之所以Zuul能完成验证、授权、静态资源处理等,就是得益于下面要讲的过滤器,但是主要是讲最基本的过滤,以后可能以后进阶的时候可能再深入讲。

创建过滤器

首先创建filter包,然后创建一个过滤器类MyPreFilter「需要实现ZuulFilter 接口」

public class MyPreFilter extends ZuulFilter {
    @Override
    public String filterType() //过滤器类型
        return FilterConstants.PRE_TYPE; //请求前处理
    }

    @Override
    public int filterOrder() {  //过滤器顺序,越小越优先
        return 0;
    }

    @Override
    public boolean shouldFilter() //是否开启过滤
        return true;
    }

    @Override
    public Object run() throws ZuulException //执行逻辑
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        System.out.println("[ PreFilter" + " ]" + String.format("send %s request to %s",request.getMethod(),request.getRequestURL()));
        return null;
    }
}

FilterConstants 类中定义了一系列常量,其中对于过滤器就是以下几种

public static final String ERROR_TYPE = "error"//出错时执行
public static final String POST_TYPE = "post";  //请求后请求
public static final String PRE_TYPE = "pre";  //请求前请求
public static final String ROUTE_TYPE = "route"//处理目标请求

同时再建立一个后置请求

public class MyPostFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.POST_TYPE;
    }

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

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

    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        //这里把PreFilter改为PostFilter
        System.out.println("[ PostFilter" + " ]" + String.format("send %s request to %s",request.getMethod(),request.getRequestURL())); 
        return null;
    }
}

注入容器中

新建一个config包,在包下创建一个类ZuulConfiguration

@Configuration
public class ZuulConfiguration {

    @Bean
    public MyPreFilter getZuulPreFilterBean() {
        return new MyPreFilter();
    }

    @Bean
    public MyPostFilter getZuulPostFilterBean() {
        return new MyPostFilter();
    }
}

此时Zuul模块的目录结构如下

「注意,这里有个坑,就是当开启了过滤器后,会发现前一小节的fallback失效。」

SpringCloud入门(5)Zuul

重启ZuulMain9401服务,并且「清空」IDEA输出控制台

如果是接着上面一节的内容,那么此时应该是转发的是「非注册进Eureka服务」的路由

访问http://localhost:9401/api-c/eureka/provide,查看控制台输出

SpringCloud入门(5)Zuul

「创作不易,如果对你有帮助,欢迎点赞,收藏和分享啦!」


原文始发于微信公众号(咖啡编程):SpringCloud入门(5)Zuul

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/23253.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!