zoukankan      html  css  js  c++  java
  • SpringCloud之服务网关

    网关统一服务入口,可方便实现对平台众多服务接口进行管控,对访问服务的身份认证、防报文重放与防数据篡改、功能调用的业务鉴权、响应数据的脱敏、流量与并发控制,甚至基于API调用的计量或者计费等等。

    1.zuul

    1.1定义

    zuul叫路由网关,它包含对请求的路由和过滤的功能。路由负责将外部的请求转发到具体的微服务实例上,是实现外部访问统一入口的基础。而过滤是负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能基础。

    目前zuul已更新到2.0版本,但SpringCloud官方不推荐使用zuul2.0,但支持zuul2.0。

    1.2基础环境搭建

    源代码:https://github.com/zhongyushi-git/cloud-zuul-demo.git

    创建maven的父工程cloud-zuul-demo,删除src目录,在pom中导入依赖,对SpringBoot和SpringCloud版本进行锁定

     <properties>
            <spring.boot.version>2.2.2.RELEASE</spring.boot.version>
            <spring.cloud.version>Hoxton.SR1</spring.cloud.version>
        </properties>
    
        <!--  依赖管理,父工程锁定版本-->
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>${spring.boot.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring.cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>

    1.3搭建服务提供者

    1)新建maven子模块(cloud-provider8001),导入依赖

         <dependencies>
            <!--web-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
        </dependencies>

    2)新建启动类ProviderMain8001并添加注解

    package com.zys.cloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class ProviderMain8001 {
        public static void main(String[] args) {
            SpringApplication.run(ProviderMain8001.class, args);
        }
    }

    3)配置application.yml

    server:
      port: 8001
    
    spring:
      application:
        name: cloud-consul-provider
      cloud:
        consul:
          host: localhost
          port: 8500
          discovery:
            service-name: ${spring.application.name}

    4)新建controller接口

    package com.zys.cloud.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class UserController {
    
        @Value("${server.port}")
        private String port;
    
        @GetMapping("/user/get")
        public String get() {
            return "我是服务提供者,端口:" + port;
        }
    
    }

    1.4搭建路由子模块

    1)创建子模块(cloud-zuul-gateway9527),导入依赖

        <dependencies>
            <!--web-->
            <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>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
            <!-- zuul路由网关 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            </dependency>
        </dependencies>

    2)application.yml配置

    server:
      port: 9527
    
    spring:
      application:
        name: cloud-zuul-gateway
      cloud:
        consul:
          host: localhost
          port: 8500
          discovery:
            service-name: ${spring.application.name}
    
    #配置微服务代理
    zuul:
      #忽略真实的服务名,即不能使用服务名访问,只能通过代理访问以保证安全
      ignored-services: "*"
      routes:
        #指定要代理的微服务名称和路径
        cloud-consul-provider: /users/**

    这里也需要把服务注册到consul中,除此之外主要配置了网关路由的详细信息。当访问到/users路径时会转发到cloud-consul-provider服务中,根据后面的路径进行转发。

    3)创建启动类

    package com.zys.cloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableZuulProxy
    public class ZuulMain9527 {
        public static void main(String[] args) {
            SpringApplication.run(ZuulMain9527.class, args);
        }
    }

    4)启动测试

    先启动服务提供者,再启动网关服务。浏览器输入http://localhost:9527/users/user/get可以正常访问到提供者的服务,此时路由功能就完成了。

    注意:这里必须要给每个服务加一个前缀。

    2.gateway

    2.1定义

    旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤功能,如熔断、限流、重试等。三大核心概念如下:

    Route(路由) 构建网关的基本模块,由ID、URL、一系列断言和过滤器组成
    Predicate(断言) 用于匹配Http请求中的所有内容,如果匹配则进行路由
    Filter(过滤) Spring框架中GatewayFilter的实例

    2.2zuul与gateway的区别

    gateway是基于WebFlux框架实现的,而WebFlux框架底层使用了高性能的Reactor模式通信框架Netty,是基于异步非阻塞模型开发的。而zuul使用的是阻塞框架。

    2.3基础环境搭建

    源代码:https://github.com/zhongyushi-git/cloud-gateway-demo.git

    创建maven的父工程cloud-gateway-demo,删除src目录,在pom中导入依赖,对SpringBoot和SpringCloud版本进行锁定

     <properties>
            <spring.boot.version>2.2.2.RELEASE</spring.boot.version>
            <spring.cloud.version>Hoxton.SR1</spring.cloud.version>
        </properties>
    
        <!--  依赖管理,父工程锁定版本-->
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>${spring.boot.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring.cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>

    2.4搭建服务提供者

    1)新建maven子模块(cloud-provider8001),导入依赖

         <dependencies>
            <!--web-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
        </dependencies>

    2)新建启动类ProviderMain8001并添加注解

    package com.zys.cloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class ProviderMain8001 {
        public static void main(String[] args) {
            SpringApplication.run(ProviderMain8001.class, args);
        }
    }

    3)配置application.yml

    server:
      port: 8001
    
    spring:
      application:
        name: cloud-consul-provider
      cloud:
        consul:
          host: localhost
          port: 8500
          discovery:
            service-name: ${spring.application.name}

    4)新建controller接口

    package com.zys.cloud.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class UserController {
    
        @Value("${server.port}")
        private String port;
    
        @GetMapping("/user/get")
        public String get() {
            return "我是服务提供者,端口:" + port;
        }
    
    }

    根据8001再创建一个服务提供者模块,端口为8002.

    2.5搭建服务消费者

    1)新建maven子模块(cloud-consumer80),导入依赖

        <dependencies>
            <!--web-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <!--feign-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
        </dependencies>

    2)新建启动类ConsumerMain80并添加注解

    package com.zys.cloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableFeignClients
    class ConsumerMain80 {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerMain80.class, args);
        }
    }

    3)配置application.yml

    server:
      port: 80
    
    spring:
      application:
        name: cloud-consul-consumer
      cloud:
        consul:
          host: localhost
          port: 8500
          discovery:
            service-name: ${spring.application.name}
    
    feign:
      client:
        config:
          #指定全局
          default:
            #连接超时时间
            connectTimeout: 5000
            #服务等待时间
            readTimeout: 5000
            loggerLevel: full
    
    logging:
      level:
        com.zys.cloud.service.UserServiceClient: debug

    3)创建服务接口UserServiceClient,对于服务提供者接口

    package com.zys.cloud.service;
    
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    
    //指定微服务名称
    @FeignClient(value = "cloud-consul-provider")
    public interface UserServiceClient {
    
        @GetMapping("/user/get")
        String get();
    }

    4)创建controller接口,将UserServiceClient注入使用

    package com.zys.cloud.controller;
    
    import com.zys.cloud.service.UserServiceClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    
    @RestController
    @RequestMapping("/consumer")
    public class TestController {
    
        @Resource
        private UserServiceClient userServiceClient;
    
        @GetMapping("/get")
        public String get() {
            return userServiceClient.get();
        }
    }

    2.6搭建路由子模块

    1)创建子模块(cloud-gateway9527),导入依赖

        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <!--gateway-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
        </dependencies>

    需要注意的是,这里不能导入web,否则会报错

    2)yml配置

    server:
      port: 9527
    
    spring:
      application:
        name: cloud-gateway
      cloud:
        consul:
          host: localhost
          port: 8500
          discovery:
            service-name: ${spring.application.name}
        gateway:
          #开启从注册中心动态创建路由的功能,利用微服务名进行路由
          discovery:
            locator:
              enabled: true
          #配置路由列表
          routes:
            - id: provider_route      #路由的ID,没有固定规则但要求唯一
              uri: lb://cloud-consul-provider   #匹配后提供服务的路由地址,根据服务名访问,lb表示后台使用负载均衡
              predicates:
                - Path=/user/**          #断言,路径相匹配的进行路由
    
            - id: consumer_route
              uri: lb://cloud-consul-consumer
              predicates:
                  - Path=/consumer/**

    这里也需要把服务注册到consul中,除此之外主要配置了网关路由的详细信息。

    使用断言配置了路径匹配路由,当访问/user相关的请求,都会转发到服务名为cloud-consul-provider的服务上,并把路由拼接到后面,对外暴露统一的接口。如访问http://localhost:9527/user/get那么会转发到http://cloud-consul-provider/user/get。

    当然也可以对路由进行切割,需配置filters:

    #配置路由列表
    routes:
      - id: provider_route      #路由的ID,没有固定规则但要求唯一
        uri: lb://cloud-consul-provider   #匹配后提供服务的路由地址,根据服务名访问,lb表示后台使用负载均衡
        predicates:
           - Path=/api/user/**          #断言,路径相匹配的进行路由
        filters:
           - StripPrefix=1             #路由截取

    StripPrefix的意思是将路径切掉一级,这个例子中 /api被剪。若请求路径是localhost:9527/api/user/list,则最终转发的路径是lb://cloud-consul-provider/user/list。

    3)创建启动类

    package com.zys.cloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class GatewayMain9527 {
        public static void main(String[] args) {
            SpringApplication.run(GatewayMain9527.class, args);
        }
    }

    4)启动测试

    先启动服务提供者集群,再启动服务消费者,最后启动网关服务。浏览器输入http://localhost:9527/consumer/get可以正常访问到消费者的服务,此时路由功能就完成了。多刷新几次,可以看出是服务提供者轮询的。

    2.7编码方式配置路由

    除了上述使用yml的方式配置路由外,还可以通过编码方式配置(不推荐)。新建配置类GateWayConfig,这里设置别的路由,避免和yml方式混淆。

    package com.zys.cloud.config;
    
    import org.springframework.cloud.gateway.route.RouteLocator;
    import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * 编码方式配置路由
     */
    @Configuration
    public class GateWayConfig {
        @SuppressWarnings("JavaDoc")
        @Bean
        public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
            RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
            routes.route("baidu_route", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
            return routes.build();
        }
    }

    重启9527,访问地址http://localhost:9527/guonei时会自动转发到地址:http://news.baidu.com/guonei。

    2.8断言配置

    上面的小节中在yml中也配置了断言,用于路由匹配。除此之外,还可以配置其他的断言。这些配置只在路由转发时生效,对于服务之间的调用,没有任何意义。

    2.8.1 案例说明

    假如配置服务提供者请求在某个时间后生效。只需要在yml的predicates设置After即可:

    predicates:
       - Path=/user/**          #断言,路径相匹配的进行路由
       - After=2021-08-08T16:04:28.872+08:00[Asia/Shanghai]

    配置后访问http://localhost:9527/user/get,在此时间之前访问,会返回404信息,只有过了设置时间才能正常访问。

    需要注意是的,后面的时间格式必须正确,获取格式时间的方法:

        public static void main(String[] args) {
            //获取当前的时间
            ZonedDateTime zbj = ZonedDateTime.now();
            int year = 2021, month = 8, day = 10, hour = 10, minute = 20, second = 0;
            //获取指定的时间
            ZonedDateTime of = ZonedDateTime.of(year, month, day, hour, minute, second, 0, ZoneId.systemDefault());
            System.out.println(zbj);
            System.out.println(of);
        }

    可根据需求进行获取时间格式。

    2.8.2其他的配置

    1)配置Before

    表示在某个时间之前

    predicates:
        - Path=/user/**             
        - Before=2020-04-09T21:15:28.872+08:00[Asia/Shanghai]

    Between表示在某个时间段内,配置两个时间,中间用逗号隔开。

    2)配置Method

    Method用于配置请求的方法,若设置必须是post请求

    predicates:
        - Path=/user/**             
        - Method=GET

    2.9查看网关路由规则

    在开发时,若需要查看网关路由规则信息,可以在yml中进行开启,默认是关闭的:

    management:
      endpoints:
        web:
          exposure:
            #开启所有web端点暴露
            include: "*"

    配置后,重启网关服务,访问http://localhost:9527/actuator/gateway/routes,即可看到相关的信息:

    2.10过滤器配置

    在路由中提供了许多了过滤器配置,可以直接使用,但也可以根据需求自定义配置,把公共的服务放到网关中。例如在请求中header必须携带token才能访问。

    创建过滤器类MyGateWayFilter 

    package com.zys.cloud.config;
    
    import com.alibaba.fastjson.JSON;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    import java.nio.charset.StandardCharsets;
    import java.util.HashMap;
    import java.util.Map;
    
    
    /**
     * 自定义过滤器
     */
    @Component
    public class MyLogGateWayFilter implements GlobalFilter, Ordered {
        private static Logger log = LoggerFactory.getLogger(MyLogGateWayFilter.class);
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            String token = exchange.getRequest().getHeaders().getFirst("token");
            //非法用户直接拦截
            if (token == null) {
                ServerHttpResponse response = exchange.getResponse();
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                Map<String, Object> map = new HashMap<>();
                map.put("code", 401);
                map.put("msg", "未授权,无法访问");
                byte[] errs = JSON.toJSONBytes(map);
                DataBuffer buffer = response.bufferFactory().wrap(errs);
                return response.writeWith(Flux.just(buffer));
            }
            return chain.filter(exchange);
        }
    
        //order是过滤器的执行顺序,数字越小,优先级越高
        @Override
        public int getOrder() {
            return 0;
        }
    }

    重启9527,访问地址http://127.0.0.1:9527/user/get请求头中未携带token就无法访问,而携带token可以正常访问。

    为了能够让读者正常访问,故先把此过滤器注释。

    2.11跨域配置

    跨域配置有两种方式,一种是在配置文件配置,一种是通过编码方式。这里只介绍配置文件 方式,因为编码方式和SpringBoot配置跨域类似:

    spring:
      cloud:
        gateway:
          #跨域配置
          globalcors:
            corsConfigurations:
              '[/**]':
                allowedOrigins: "*"
                allowedMethods: "*"
                allowedHeaders: "*"

    只需一个配置即可。

    就是这么简单,你学废了吗?感觉有用的话,给笔者点个赞吧 !
  • 相关阅读:
    2019黑龙江省大学生程序设计竞赛 赛后总结
    HZNU Training 4 for Zhejiang Provincial Collegiate Programming Contest 2019
    浙江大学第十九届图森未来杯大学生程序设计竞赛
    HZNU Training 2 for Zhejiang Provincial Collegiate Programming Contest 2019
    whu-contest-2019(online)
    HZNU Training 1 for Zhejiang Provincial Collegiate Programming Contest
    华为实习日记——第十九天
    华为实习日记——第十八天
    华为实习日记——第十七天
    华为实习日记——第十六天
  • 原文地址:https://www.cnblogs.com/zys2019/p/12656029.html
Copyright © 2011-2022 走看看