使用Zuul构建API Gateway || 使用API Gateway代替Zuul

纠错帖:Zuul & Spring Cloud Gateway & Linkerd性能对比

开端

网上搜索了一下,说Spring Cloud Gateway性能比Zuul差的言论来自:http://www.servicemesh.cn/?/article/45

作者使用ab 进行benchmark,操作非常标准。从结果来看,确实Spring Cloud Gateway比Zuul差了一大截。

但,让我们打开官方的Issue:Throughput problems when compared with Netflix Zuul and Nginx ,里面官方人员回答道:

reactor-netty has issues with http 1.0 and hence ab. reactor/reactor-netty#21

不妨跟踪到reactor/reactor-netty#21 ,看看说了啥:

As discussed recently about the issue raised on https://jira.spring.io/browse/SPR-14964, a very simple ab -n 1 -c 1 http://localhost:8082/items/10 on Spring + Reactor Netty based server block forever likely because Reactor Netty does not support HTTP 1.0.

里面说了,Reactor Netty不支持HTTP 1.0,而Spring Cloud Gateway依赖了reactor-netty

也就是说——由于reactor-netty的bug,使用ab 压测结果并不准确!

正确姿势

官方建议使用wrk 进行benchmark测试。不仅如此,官方人员还十(丧)分(心)贴(病)心(狂)地创建了一个benchmark的项目:spring-cloud-gateway-bench ,其中对比了:

  • Spring Cloud Gateway
  • Zuul
  • Linkerd

三者的性能。

思路非常简单:static 项目是一个使用Go语言编写的简单服务器;然后分别使用Gateway/Zuul/Linkerd来代理该服务的端点,并对比。

最终结果:

组件 RPS(request per second)
Spring Cloud Gateway Requests/sec: 32213.38
Zuul Requests/sec: 20800.13
Linkerd Requests/sec: 28050.76

从结果可知,Spring Cloud Gateway的RPS是Zuul的1.6倍!比Linkerd性能还好!

展望

  • 本文的Zuul,指的是Zuul 1.x,是一个基于阻塞io的API Gateway。
  • Spring Cloud Gateway是一个很有前途的项目,上手简单,功能也比较强大。
  • Linkerd也是一个非常有前途的项目,是基于Scala实现的、目前市面上仅有的生产级别的Service Mesh(其他诸如Istio、Conduit暂时还不能用于生产)。
  • Zuul已经发布了Zuul 2.x,基于Netty,也是非阻塞的,支持长连接,但Spring Cloud暂时还没有整合计划。

一  微服务网关背景及简介

不同的微服务一般有不同的网络地址,而外部的客户端可能需要调用多个服务的接口才能完成一个业务需求。比如一个电影购票的收集APP,可能回调用电影分类微服务,用户微服务,支付微服务等。如果客户端直接和微服务进行通信,会存在一下问题:

# 客户端会多次请求不同微服务,增加客户端的复杂性

# 存在跨域请求,在一定场景下处理相对复杂

# 认证复杂,每一个服务都需要独立认证

# 难以重构,随着项目的迭代,可能需要重新划分微服务,如果客户端直接和微服务通信,那么重构会难以实施

# 某些微服务可能使用了其他协议,直接访问有一定困难

上述问题,都可以借助微服务网管解决。微服务网管是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过微服务网关,架构演变成:

这样客户端只需要和网关交互,而无需直接调用特定微服务的接口,而且方便监控,易于认证,减少客户端和各个微服务之间的交互次数

二 Zuul 简介

Zuul是Netflix开源的微服务网关,他可以和Eureka,Ribbon,Hystrix等组件配合使用。Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:

# 身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求

# 审查与监控:

# 动态路由:动态将请求路由到不同后端集群

# 压力测试:逐渐增加指向集群的流量,以了解性能

# 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求

# 静态响应处理:边缘位置进行响应,避免转发到内部集群

# 多区域弹性:跨域AWS Region进行请求路由,旨在实现ELB(ElasticLoad Balancing)使用多样化

Spring Cloud对Zuul进行了整合和增强。目前,Zuul使用的默认是Apache的HTTP Client,也可以使用Rest Client,可以设置ribbon.restclient.enabled=true.

三 编写Zuul微服务网关

# 添加依赖

Zuul的依赖肯定是要加的,如何和Eureka配合使用, Zuul需要注册到Eureka上,但是Zuul的依赖不包含Eureka Discovery客户端,所以还需要添加Eureka的客户端依赖

<dependency>

      <groupId>org.springframework.cloud</groupId>

      <artifactId>spring-cloud-starter-eureka</artifactId>

</dependency>

<dependency>

      <groupId>org.springframework.cloud</groupId>

      <artifactId>spring-cloud-starter-zuul</artifactId>

</dependency>

# 启动类加上注解@EnableZuulProxy

它默认加上了@EnableCircuitBreaker和@EnableDiscoveryClient

@SpringBootApplication

@EnableZuulProxy

public class ZuulApplication {

      public static void main(String[] args) throws Exception {

           SpringApplication.run(ZuulApplication.class, args);

      }

}

# 配置application.yml

server:

  port: 8040

spring:

  application:

    name: microservice-gateway-zuul

eureka:

  client:

    register-with-eureka: true

    fetch-registry: true

    service-url:

      defaultZone:http://nicky:123abcABC@localhost:8761/eureka

  instance:

    ip-address: true

zuul:

  ignoredServices: '*'

  routes:

    microservice-provider-user: /ecom/**

zuul:

 ignoredServices: '*' // 忽略所有请求

  routes:

    服务名: /ecom/** //允许将服务名映射到ecom

# 启动Eureka,Zuul和 其他应用

访问http://localhost:8040/ecom/user/1

或者

http://localhost:8040/microservice-provider-user/user/1

都可以

四 微服务网关相关的配置

4.1 路由路径的配置

zuul:

 ignoredServices: '*' // 忽略所有请求

  routes:

服务名: /ecom/** //允许将服务名映射到ecom

为了更加细粒度控制路由路径:

// 表示只要HTTP请求是 /ecom开始的,就会forward到服务id为users_service的服务上面

zuul:

  routes:

    users:

      path:/ecom/**  // 路由路径

     serviceId: users_service // 服务id

zuul:

  routes:

   users:  // 路由名称,随意,唯一即可

      path: /ecom/**  // 路由路径

      url:http://localhost:9000

4.2 不破坏Zuul的Hystrix和Ribbon特性

上述简单的url路由配置,不会作为Hystrix Command执行,也不会进行ribbon的负载均衡。为了同时指定path 和 url,但是不破坏Zuul的Hystrix和Ribbon特性:

zuul:

  routes:

    users:

      path:/ecom/**

     serviceId: microservice-provider-user

ribbon:

  eureka:

    enabled:false // ribbon禁掉Eureka

microservice-provider-user:

  ribbon:

listOfServers: localhost:9000,localhost:9001

4.3 使用正则表达式指定Zuul的路由匹配规则

借助PatternServiceRoute    Mapper实现从微服务到映射路由的正则配置,例如:

      @Bean

      publicPatternServiceRouteMapper serviceRouteMapper(){

           // servicePattern: 指的是微服务的pattern

           // routePattern: 指的是路由的pattern

           // 当你访问/microservice-provider-user/v1 其实就是

           // localhost:8040/v1/microservice-provider-user/user/1

           return newPatternServiceRouteMapper(

                 "(?<name>^.+)-(?<version>v.+$)","${version}/${name}"

           );

      }

4.4 路由的前缀

zuul.prefix: 我们可以指定一个全局的前缀

strip-prefix: 是否将这个代理前缀去掉

zuul:

  prefix: /ecom

  strip-prefix: false

  routes:

    microservice-provider-user: /provider/**

比如你访问http://localhost:8040/ecom/microservice-provider-user/user/1,其实真实访问路径是/ecom/user/1

zuul:

  prefix: /ecom

  strip-prefix: true

  routes:

    microservice-provider-user: /provider/**

比如你访问http://localhost:8040/ecom/microservice-provider-user/user/1,其实真实访问路径是/user/1,因为我们可以将前缀去掉

如果strip-prefix只是放在路由下面,那么就是局部的,不会影响全局

zuul:

  prefix: /ecom

  routes:

    abc:

      path: /provider/**

      service-id: microservice-provider-user

      strip-prefix: true

比如你访问http://localhost:8040/ecom/microservice-provider-user/user/1

其实真实访问路径是/user/1,因为我们可以将前缀去掉

zuul:

  prefix: /ecom

  routes:

    abc:

      path: /provider/**

      service-id: microservice-provider-user

      strip-prefix: false

比如你访问http://localhost:8040/ecom/provider/user/1

其实真实访问路径是/provider/user/1,因为我们可以将前缀去掉

4.5 忽略某些路径

zuul:

  ignoredPatterns: /**/admin/**

  routes:

users: /myusers/**

过滤掉path包含admin的请求

五 Zuul的安全和Header

5.1 敏感的Header设置

在同一个系统中微服务之间共享Header,但是某些时候尽量防止让一些敏感的Header外泄。因此很多场景下,需要通过为路由指定一系列敏感的Header列表。例如:

zuul:

  routes:

    abc:

      path: /provider/**

      service-id: microservice-provider-user

      sensitiveHeaders:Cookie,Set-Cookie,Authorization

      url: https://downstream

5.2 忽略Header

被忽略的Header不会被传播到其他的微服务去。其实敏感的Heade最终也是走的这儿

zuul:

  ignored-headers: Header1,Header2

默认情况下,ignored-headers是空的

六strangle模式

在迁移现有应用程序或API时,一种常见的模式是“扼杀”旧的端点,用不同的实现慢慢替换它们。Zuul代理是一个有用的工具,因为您可以使用它来处理来自旧端点客户端的所有流量,但是将一些请求重定向到新端点。

zuul:

  routes:

    first:

      path: /first/**

      url: http://first.example.com

    second:

      path: /second/**

      url: forward:/second

    third:

      path: /third/**

      url: forward:/3rd

    legacy:

      path: /**

      url: http://legacy.example.com  // 遗留的或者剩余的都走这个路径

七 使用Zuul上传文件

7.1 如果我们不使用Zuul上传文件,即通过web框架,也可以实现文件上传,即原始的文件上传功能:

# 添加对应的依赖

<dependency>

 <groupId>org.springframework.cloud</groupId>

  <artifactId>spring-cloud-starter-eureka</artifactId>

</dependency>

<dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

 <groupId>org.springframework.boot</groupId>

  <artifactId>spring-boot-starter-actuator</artifactId>

</dependency>

# 创建上传文件的controller

@Controller

public classFileUploadController {

      @RequestMapping(value="/upload",method=RequestMethod.POST)

      public @ResponseBody StringhandleFileUpload(

                 @RequestParam(value="file",required=true)MultipartFile file){

           try {

                 byte[] in = file.getBytes();

                 File out = new File(file.getOriginalFilename());

                 FileCopyUtils.copy(in, out);

                 return out.getAbsolutePath();

           } catch (IOException e) {

                 // TODO Auto-generatedcatch block

                 e.printStackTrace();

           }

           return null;

      }

}

# 编写配置文件application.yml

# 使用工具CURL测试

curl -F"file=@微服务简介.docx"localhost:8086/upload

7.2 通过Zuul上传文件

# 不添加/zuul,上传小文件没有问题

curl -F"[email protected]" http://localhost:8050/microservice-consumer/upload

# 不添加/zuul前缀上传大文件

curl -F"[email protected]" http://localhost:8050/microservice-consumer/upload

如果不加/zuul前缀就会报错

{"timestamp":1507123311527,"status":500,"error":"InternalServerError","exception":"org.springframework.web.multipart.MultipartException","message":"Couldnot parse multipart servlet request; nested exception isjava.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException:the request was rejected because its size (132780234) exceeds the configuredmaximum(10485760)","path":"/microservice-consumer/upload"}

# 添加/zuul前缀,上传大文件

curl -F"[email protected]"http://localhost:8050/zuul/microservice-consumer/upload

{"timestamp":1507123418018,"status":500,"error":"InternalServerError","exception":"com.netflix.zuul.exception.ZuulException","message":"TIMEOUT"}

此时已经不是文件大小的错误了,我们可以将上传大文件的超时时间设置长一些

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds:60000

ribbon:

  ConnectTimeout: 3000

  ReadTimeout: 60000

八 Zuul的容错与回退

在Spring Cloud, Zuul默认已经整合了Hystrix, 而且如果启动了Dashborad,也可以知道Zuul对Hystrix监控的粒度是微服务,而不是某一个API; 同时也说明所有经过Zuul的请求都会被Hystrix保护起来。

8.1 为Zuul添加回退

想要为Zuul添加回退,需要实现ZuulFallbackProvider接口。在实现类中,指定为哪一个微服务提供回退,并且提供一个ClientHttpResponse作为回退响应。

编写Zuul回退类:

@Component

public classUserFallbackProvider implementsZuulFallbackProvider{

      @Override

      public String getRoute(){

           // 指定为哪一个微服务提供回退

           return "microservice-provider-user";

      }

      @Override

      publicClientHttpResponse fallbackResponse() {

           return newClientHttpResponse() {

                 @Override

                 public HttpHeadersgetHeaders() {

                      // 设置header

                      HttpHeaders headers = new HttpHeaders();

                      MediaType mediaType = new MediaType("application",

                                  "json",Charset.forName("UTF-8"));

                      headers.setContentType(mediaType);

                      return headers;

                 }

                 @Override

                 public InputStreamgetBody() throws IOException {

                      // 响应体

                      return newByteArrayInputStream(

                            "Usermicro-service is unavailable, please try it again later!".getBytes());

                 }

                 @Override

                 public StringgetStatusText() throws IOException {

                      // 返回状态文本

                      return this.getStatusCode().getReasonPhrase();

                 }

                 @Override

                 public HttpStatusgetStatusCode() throws IOException {

                      return HttpStatus.OK;

                 }

                 @Override

                 public intgetRawStatusCode() throws IOException {

                      // 返回数字类型的状态码

                      return this.getStatusCode().value();

                 }

                 @Override

                 public void close() {

                 }

           };

      }

}

重新启动Eureka Server,microservice-gateway-zuul-fallback以及microservice-provider-user

我们正常通过zuul访问microservice-provider-user微服务

http://localhost:8040/microservice-provider-user/user/1 没有问题

然后我们关掉microservice-provider-user微服务

在访问http://localhost:8040/microservice-provider-user/user/1,则会出现User micro-service is unavailable, please try it again later!

而不是以前不友好的那个页面了

九 Zuul的高可用
Zuul的高可用非常关键,因为外部请求到后端的微服务的流量都会经过Zuul。故而在生产环境中一般都需要部署高可用的Zuul以避免单点故障

9.1 Zuul客户端注册到了Eureka Server上

此种情况,Zuul的高可用实现比较简单,只需将多个Zuul节点注册到Eureka Server,就可以实现Zuul高可用。此时,Zuul与其他的微服务高可用没啥区别。Zuul客户端会自动从Eureka Server中查询Zuul Server的列表,并使用Ribbon负载均衡的请求Zuul集群

9.2 Zuul客户端未注册到Eureka Server上

如果Zuul客户端未注册到Eureka上,因为微服务可能被其他微服务调用,也可能直接被终端调用,比如手机App。此种情况下,我们需要借助额外的负载均衡器来实现Zuul的高可用,比如Nginx,比如HAProxy或者F5等

九 Sidecar

我们可以使用Sidecar整合非JVM微服务,比如C++、Python、PHP等语言写的。其他非JVM微服务可操作Eureka的REST端点,从而实现注册与发现。事实上,也可以使用sidecar更加方便整合非JVM微服务


# 首先添加依赖

<dependency>

 <groupId>org.springframework.cloud</groupId>

  <artifactId>spring-cloud-starter-eureka</artifactId>

</dependency>

<dependency>

 <groupId>org.springframework.cloud</groupId>

 <artifactId>spring-cloud-starter-zuul</artifactId>

</dependency>

<dependency>

 <groupId>org.springframework.cloud</groupId>

  <artifactId>spring-cloud-netflix-sidecar</artifactId>

</dependency>

# 添加启动类,在启动类上加上@EnableSidecar注解,声明这是一个Sidecar

这个注解整合了三个注解即:

@EnableCircuitBreaker

@EnableDiscoveryClient

@EnableZuulProxy

@SpringBootApplication

@EnableSidecar

public classZuulSidecarApplication {

      public static void main(String[] args) throws Exception {

           SpringApplication.run(ZuulSidecarApplication.class, args);

      }

}

# 编写application.yml

Sidecar.port指的就是其他语言微服务的端口

Sidecar与其他语言的微服务分离部署

上面我们是将其他语言微服务和sidecar放在同一个机器上,现实中,常常会将Sidecar与IVM微服务分离部署,部署在不同的主机上面或者容器中,这时候应该如何配置呢?

方法一:

eureka:

  instance:

hostname: # 非JVM微服务所在的hostname

方法二:

sidecar:

  hostname: # 非JVM微服务所在的hostname

  ip-address: # 非JVM微服务所在的IP 地址

注意:如果这种微服务太多,而且还涉及到集群的话使用sidecar我们应该权衡一下。因为一个sidecar只能对应一个其他语言写的微服务,如果很多,那表示就多个sidecar了。

十 Zuul的过滤器

过滤器是Zuul的核心组件,Zuul大部分功能都是通过过滤器来实现的。

10.1 过滤器类型和请求生命周期

Zuul中定义了四种标准的过滤器类型,这些过滤器类型对应于典型的生命周期。

PRE: 这种过滤器在请求被路由之前调用。可利用其实现身份验证等

ROUTING: 这种过滤器将请求路由到微服务,用于构建发送给微服务的请求,并使用Apache Http Client或者Netflix Ribbon请求微服务

POST: 这种过滤器在路由到微服务以后执行,比如为响应添加标准的HTTP Header,收集统计信息和指标,将响应从微服务发送到客户端等

ERROR: 在其他阶段发生错误时执行该过滤器

除了默认的过滤器类型,Zuul还允许创建自定义的过滤器类型。

10.2 编写Zuul过滤器

我们只需要继承抽象类ZuulFilter过滤器即可,让该过滤器打印请求日志

public classPreRequestLogFilter extends ZuulFilter {

      private static final Logger logger = LoggerFactory.getLogger(

                 PreRequestLogFilter.class);

      @Override

      public Object run() {

           RequestContext context = RequestContext.getCurrentContext();

           HttpServletRequest reqeust = context.getRequest();

           PreRequestLogFilter.logger.info(

                      String.format("send %srequest to %s",

                      reqeust.getMethod(),

                      reqeust.getRequestURL().toString()));

           return null;

      }

      @Override

      public boolean shouldFilter() {

           // 判断是否需要过滤

           return true;

      }

      @Override

      public int filterOrder() {

           // 过滤器的优先级,越大越靠后执行

           return 1;

      }

      @Override

      public StringfilterType() {

           // 过滤器类型

           return "pre";

      }

}

修改启动类,为启动类添加:

@SpringBootApplication

@EnableZuulProxy

public classZuulFilterApplication {

      @Bean

      publicPreRequestLogFilter preRequestLogFilter(){

           return newPreRequestLogFilter();

      }

      public static void main(String[] args) throws Exception {

           SpringApplication.run(ZuulFilterApplication.class, args);

      }

}

10.3 禁用Zuul过滤器

Spring Cloud默认为Zuul编写并启用了一些过滤器,例如DebugFilter,FromBodyWrapperFilter,PreDecorationFilter等,这些过滤器都存放在spring-cloud-netflix-core这个jar里的

在某些场景下,希望禁掉一些过滤器,该怎办呢?

只需设置zuul.<SimpleClassName>.<filterType>.disable=true即可,比如

zuul.PreRequestLogFilter.pre.disable=true

十一 Zuul聚合微服务

许多场景下,一个外部请求,可能需要查询Zuul后端多个微服务。比如说一个电影售票系统,在购票订单页上,需要查询电影微服务,还需要查询用户微服务获得当前用户信息。如果让系统直接请求各个微服务,就算Zuul转发,网络开销,流量耗费,时长都不是很好的。这时候我们就可以使用Zuul聚合微服务请求,即应用系统只发送一个请求给Zuul,由Zuul请求用户微服务和电影微服务,并把数据返给应用系统。

--------------------- 本文来自 happy19870612 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/zhanglh046/article/details/78651993?utm_source=copy

Spring Cloud Gateway替代zuul作为API网关

Spring Cloud Finchley版本的gateway比zuul 1.x系列的性能和功能整体要好。

特别提醒:Spring Cloud Finchley版本中,即使你引入了spring-cloud-starter-netflix-zuul,也不是2.0版本的zuul

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
  • 1
  • 2
  • 3
  • 4

我们的介绍分为3个部分, 第一,pom文件,第二项目的基本架构,第三源码和截图。

  • 第一,pom文件

因为使用Eureka作为服务注册和发现,因此在pom中引入了eureka,各位可根据自己的实际情况修改。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.yq</groupId>
    <artifactId>GatewayDemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

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

        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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

        <!-- fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.1.33</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.5</version>
        </dependency>

    </dependencies>


    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 第二,项目结构

总共有3个项目,
第一个项目是eureka 注册中心,非常简单,基本就一个Application类。 端口7700
第二个项目是User service,也非常简单提供两个rest api,为了简略不连接数据,直接在内从中生成一组数据。端口6601
第三个项目就是我们的网关。端口6604

目前项目中集成websocket服务配置,本文咱不介绍可直接忽略。

  • 第三,项目代码和运行截图

网关的主代码

package com.yq;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;


@SpringCloudApplication
public class APIGatewayApplication  {
    private static final Logger logger = LoggerFactory.getLogger(APIGatewayApplication.class);

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route(r -> r.path("/baidu")
                        .uri("http://baidu.com:80/")
                )
               .route("websocket_route", r -> r.path("/apitopic1/**")
                .uri("ws://127.0.0.1:6605"))
                .route(r -> r.path("/userapi3/**")
                        .filters(f -> f.addResponseHeader("X-AnotherHeader", "testapi3"))

                        .uri("lb://user-service/")
                )
                .build();
    }

    public static void main(String[] args) {
        SpringApplication.run(APIGatewayApplication.class, args);
        logger.info(" Start APIGatewayApplication Done");
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

网关的配置文件application.yml

server:
  port: 6604


#服务名
spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      filter:
        remove-non-proxy-headers:
          headers:
          - dummy
      routes:
        - id:  apiuser
        # 重点!/info必须使用http进行转发,lb代表从注册中心获取服务
          uri: lb://user-service
          predicates:
          # 重点!转发该路径!,/userapi/**,
          - Path=/userapi/**
          # http://localhost:6601/userapi/user/users/2, 必须加上StripPrefix=1,否则访问服务时会带上userapi
          #而不是我们期望的去掉userapi,只保留**部分
          filters:
          - StripPrefix=1
        - id:  api2user
          uri: lb://user-service
          predicates:
          - Path=/userapi2/**
          filters:
          - StripPrefix=1


eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:7700/eureka/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

我们简要分析分析一下配置文件
- id: apiuser
# 重点!/info必须使用http进行转发,lb代表从注册中心获取服务
uri: lb://user-service
predicates:
# 重点!转发该路径!,/userapi/**,
- Path=/userapi/**
# http://localhost:6601/userapi/user/users/2, 必须加上StripPrefix=1,否则访问服务时会带上userapi
#而不是我们期望的去掉userapi,只保留**部分
filters:
- StripPrefix=1

配置了一个路由apiuser, 当路径( - Path=/userapi/**),就转发到服务(lb://user-service),同时把路径中的userapi这部分去掉(- StripPrefix=1)。

  • 运行效果图

直接访问User service
http://localhost:6601/user/users/2

通过网关访问user service
http://localhost:6604/userapi/user/users/2

这里写图片描述

--------------------- 本文来自 russle 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/russle/article/details/80962066?utm_source=copy

猜你喜欢

转载自blog.csdn.net/singgel/article/details/82855489