基于springcloudgateway实现微服务网关(代码片段)

Java鱼仔 Java鱼仔     2023-03-03     258

关键词:

(一)什么是微服务网关

后端写完所有的微服务之后,最终是要交给前端去调用。我们都知道每个微服务都有各自的端口号,如果前端直接通过IP加端口的方式去调用微服务会很麻烦。如果想对请求增加限制也会变得十分困难。这个时候微服务网关就出现了。

微服务网关又成为API网关,它是系统对外的唯一入口。APl网关封装了系统内部架构,为每个客户端提供一个定制的APl。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。

听起来有些抽象,下面就做一张图给大家展示一下:前端的请求统一由微服务网关进行管理,微服务网关可以调用各个微服务,同时自身还有多种功能。

API网关的职责有身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理,最重要的功能是和外界联系。

常见的API网关实现方式有Zuul,traefix,Spring Cloud Gateway等等。目前主流的微服务网关是Spring Cloud Gateway。

(二)Spring Cloud Gateway网关

Spring Cloud Gateway是Spring 官方基于Spring 5.0,Spring Boot 2.0和Project Reactor 等技术开发的网关,旨在为微服务架构提供一种简单而有效的统一的API路由管理方式,统一访问接口。Spring Cloud Gateway作为Spring Cloud 生态系中的网关,目标是替代Netflix ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。

2.1 核心概念:

路由(route):路由是网关最基础的部分,路由信息由一个ID,一个目的URL,一组断言工厂和一组Filter组成。

断言(predicates):java8中的断言函数,Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自Http Request中的任何信息。当断言为真时,则匹配路由。

过滤器(filter):对请求和响应进行过滤

下面通过实践来操作微服务网关,本项目有依赖于Nacos与Sentinel,如果不需要这部分的内容可以在配置文件中去除。

2.2 搭建环境:

1、引入工程导入依赖

创建api_gateway_server工程,在工程中的pom.xml中导入依赖,项目全局依赖如下:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.javayz</groupId>
    <artifactId>nacos-discovery-spring-cloud-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nacos-discovery-spring-cloud-gateway</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <projec.build.sourceEncoding>UTF-8</projec.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.0.RELEASE</spring-boot.version>
        <spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
    </properties>

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

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <!--springcloudgateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
            <version>1.7.2</version>
        </dependency>
    </dependencies>

    <!--当前项目继承的依赖-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>$spring-cloud-alibaba.version</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <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>Hoxton.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2、配置启动类

创建GatewayApplication类

@EnableDiscoveryClient
@SpringBootApplication
public class NacosDiscoverySpringCloudGatewayApplication 
    public static void main(String[] args) 
        SpringApplication.run(NacosDiscoverySpringCloudGatewayApplication.class, args);
    

3、编写配置文件

这里对配置文件的编写使用.yml格式的配置文件,在resource文件夹下创建applicaiion.yml。这里需要注意的是gateway下routes的配置,首先用id自定义网关;接着定义uri,意思是当访问的是9090端口(即访问的是网关时),就自动把ip和端口号变成该uri;最后写断言predicates,这里的Path=/index/**指得是自动匹配/index/**的路径。当请求地址为http://localhost:9090/index/1时,微服务网关会去访问http://nacos-discovery-consumer/index/1,这就是微服务网关调用微服务的功能。

server:
  port: 9090
spring:
  application:
    name: nacos-discovery-spring-cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  #启用discoveryClient网关集成,实现服务发现
      routes:
        - id: route1
          uri: lb://nacos-discovery-consumer  #lb://表示负载均衡
          predicates:  #判断条件
            - Path=/test,/index/**

(三) 路由配置详解

路由断言中有许多匹配规则,之前只介绍了Path,其实还有许多匹配规则

predicates:
    #匹配一个时间,在这个时间之后发生的请求才能访问(格式是ZonedDateTime)
    - After=2021-03-14T11:31:08.377+08:00[Asia/Shanghai] 
    #匹配一个时间,在这个时间之前发生的请求才能访问(ZonedDateTime)
    - Before=2021-03-14T11:31:08.377+08:00[Asia/Shanghai]
    #匹配两个时间,在这个时间之间才能访问
    - Between=2021-03-14T11:31:08.377+08:00[Asia/Shanghai],2021-03-14T12:31:08.377+08:00[Asia/Shanghai] #路由断言之间
    #路由断言Cookie匹配,匹配给定名称(如master)或者正则表达式,如下面配置,请求的cookie中必须有token=master才能访问
    - Cookie=token,master
    #根据请求头信息匹配,使用方式和cookie类似
    - Header=XXX,XXX
    #根据host地址匹配
    - Host=**.somehost.org   
    #根据请求方法匹配
    - Method=GET  
    #根据请求路径进行匹配 
    - Path=***   
    #根据参数匹配,如果配置为token,则所有的请求必须要带上token=XXX
   - Query=token
   #根据远程IP匹配
   - RemoteAddr=192.168.1.1/24    
   #权重,第一个参数是组名,第二个参数是权重,两个route下配置不同权重,会根据权重负载访问
   - Weight=group1,8

定义请求路径时,我们还可以据微服务名称拉取请求路径,下面是两种不同方法定义uri,实现同样的功能。

uri: http://127.0.0.1:9001  #当访问9090端口时,自动把ip和端口替换为此uri
uri: lb://userservice #根据微服务名称拉取请求路径

总体的配置:

server:
  port: 9090
spring:
  application:
    name: nacos-discovery-spring-cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  #启用discoveryClient网关集成,实现服务发现
      routes:
        - id: route1
          uri: lb://nacos-discovery-consumer  #lb://表示负载均衡
          predicates:  #判断条件
            - Path=/test,/index/**,/**

    nacos:
      discovery:
        server-addr: 192.168.78.128:8848
      username: nacos
      password: nacos

    sentinel:
      eager: true
      transport:
        dashboard: localhost:8080

3.1 自定义断言配置

自定义断言的实现分为两步,假设我们现在要实现一个自定义的过滤器,规则是必须携带一个token,并且token要等于指定的值。

1、定义配置类:这里包含在配置文件要传入的对象值

@Data
public class TokenConfig 
    private String token;

2、定义路由断言工厂:首先是命名必须是XXXRoutePredicateFactory,继承 AbstractRoutePredicateFactory

@Slf4j
@Component
public class TokenRoutePredicateFactory extends AbstractRoutePredicateFactory<TokenConfig> 
    public TokenRoutePredicateFactory()
        super(TokenConfig.class);
    
    //用来把配置中的值变成一个集合
    @Override
    public List<String> shortcutFieldOrder() 
        return Collections.singletonList("token");
    
    //通过jdk1.8的断言接口来返回true或者false,如果是true表示过滤器不拦截
    @Override
    public Predicate<ServerWebExchange> apply(TokenConfig config) 
        return exchange ->
            //获取request请求参数
            MultiValueMap<String,String> valueMap=exchange.getRequest().getQueryParams();
            boolean flag=false;
            List<String> list=new ArrayList<>();
            //将request请求的value保存到集合里
            valueMap.forEach((k,v)->
                list.addAll(v);
            );
            //判断有没有和配置文件中相同的值
            for (String s:list)
                log.info("Token"+s);
                if (StringUtils.equalsIgnoreCase(s,config.getToken()))
                    flag=true;
                    break;
                
            
            return flag;
        ;
    

最后在断言处写上我们自定义的内容:

predicates:  #判断条件
  - Path=/test,/index/**,/**
  - Token=javayz

只有链接是http://localhost:9090/sentinel/test1?token=javayz带上token=javayz的请求才能访问。

3.2 断言不匹配404页面自定义

SpringCloudGateway默认给出的404页面是


这样的页面实在不友好,我们可以通过自定义的方式让他返回一个JSON字符串

新建一个类叫做MyErrorWebExceptionHandler:

public class MyErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler 

    public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ErrorProperties errorProperties, ApplicationContext applicationContext) 
        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
    

    /**
     * 指定响应处理方法为JSON处理
     * @param errorAttributes
     * @return
     */
    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) 
        return RouterFunctions.route(RequestPredicates.all(),this::renderErrorResponse);
    


    /**
     * 根据code获取对应的HttpStatus
     * @param errorAttributes
     * @return
     */
    @Override
    protected int getHttpStatus(Map<String, Object> errorAttributes) 
        return (int) errorAttributes.get("status");
    

    /**
     * 构建异常信息
     * @param request
     * @param ex
     * @return
     */
    private String buildMessage(ServerRequest request,Throwable ex)
        StringBuilder builder = new StringBuilder("Failed to handle request:");
        builder.append(request.methodName());
        builder.append(" ");
        builder.append(request.uri());
        if (ex!=null)
            builder.append(ex.getMessage());
        
        return builder.toString();
    

    /**
     * 返回的json数据
     * @param status
     * @param errorMsg
     * @param data
     * @return
     */
    public static Map<String,Object> response(int status,String errorMsg,Object data)
        Map<String,Object> map=new HashMap<>();
        map.put("code",status);
        map.put("message",errorMsg);
        map.put("data",data);
        return map;
    

再编写一个配置类,将自定义的异常页面代码注入到Bean容器中:

@Configuration
public class GatewayConfiguration 
    private final ServerProperties serverProperties;
    private final ApplicationContext applicationContext;
    private final ResourceProperties resourceProperties;
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ServerProperties serverProperties, ApplicationContext applicationContext, ResourceProperties resourceProperties, ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) 
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.resourceProperties = resourceProperties;
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    

    @Bean("myErrorWebExceptionHandler")
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public ErrorWebExceptionHandler myErrorWebExceptionHandler(ErrorAttributes errorAttributes)
        MyErrorWebExceptionHandler exceptionHandler=new MyErrorWebExceptionHandler(
                errorAttributes,
                this.resourceProperties,
                this.serverProperties.getError(),
                this.applicationContext
        );
        exceptionHandler.查看详情  

springcloudgateway的作用(面试)

...授权等,常用的网关有zuul(netflix的,但是已经停更了)和springcloudgateway(springcloudalibaba)。这里主要讲springcloudgateway,springcloudgateway是一个全新的项目,其基于spring5.0以及springboot2.0和项目Reactor等技术开发的网关,其主要的目的是为微... 查看详情

物联网架构成长之路(56)-springcloudgateway+jwt实现网关鉴权

0.前言  结合前面两篇博客,前面博客实现了Gateway网关的路由功能。此时,如果每个微服务都需要一套帐号认证体系就没有必要了。可以在网关处进行权限认证。然后转发请求到后端服务。这样后面的微服务就可以直接调用,... 查看详情

微服务架构之springcloudgateway

SpringCloudGateway是springcloud中起着非常重要的作用,是终端调用服务的入口,同时也是项目中每个服务对外暴露的统一口径,我们可以在网关中实现路径映射、权限验证、负载均衡、服务聚合等业务功能。(一)版本说明a)Springboot2.0.6... 查看详情

微服务-springcloud学习系列:服务网关springcloudgateway

1.SpringCloudGateWay的使用①创建GateWay网关服务,引入依赖(这里注意GateWay使用netty和WebFlux实现,WebFlux和SpringMvc有冲突,因此不能将web依赖放在父pom中,需要单独的放在需要的子工程中,gateway中不能有mvc的web依赖)  ②配置... 查看详情

深入java微服务之网关系列4:springcloudgateway详解(史上最全)(代码片段)

1.1 SpringCloudGateway简介SpringCloudGateway是SpringCloud的一个全新项目,该项目是基于Spring5.0,SpringBoot2.0和ProjectReactor等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。SpringCloudGateway作为Sp... 查看详情

springcloudgateway基于oauth2.0的身份认证

...作。早期我们使用NetflixZuul封装网关服务,现在我们使用SpringCloudGateway来处理入口请求。  这里我会介绍如何编写一个网关服务,我会分为几个部分 查看详情

springcloud微服务网关gateway组件(代码片段)

SpringCloud微服务网关Gateway组件什么是SpringCloudGatewaySpringCloudGateway功能特征核心概念工作原理SpringCloudGateway快速开始gateway整合nacos什么是SpringCloudGateway网关作为流量的入口,常见的功能包括路由转发,权限校验,限流等... 查看详情

关于springcloudgateway集成nacos实现路由到指定微服务的方式总结(代码片段)

...现Nacos服务注册与发现的2种实现方法1.OpenAPI模式2.SDK模式SpringCloudGateWaygateway网关路由配置详解gateway动态配置网关路由规则微服务路由方式1,nacos自动下发应用请求路径中包含微服务名例如:查询微服务demo-system下的路由... 查看详情

java之springcloud微服务搭建网关springcloudgateway微服务网关gateway(第三个阶段)springboot项目实现商品服务器端是调用(代码片段)

Java之SpringCloud微服务搭建网关SpringCloudGateway微服务网关GateWay(第三个阶段)【二】【SpringBoot项目实现商品服务器端是调用】一、微服务网关GateWay1、Gateway简介(1)简介(2)核心概念二、入门案例1、入门... 查看详情

使用 Spring Cloud Gateway 实现微服务 API 网关

】使用SpringCloudGateway实现微服务API网关【英文标题】:MicorserviceAPIGatewayImplementationwithSpringCloudGateway【发布时间】:2019-07-0415:10:30【问题描述】:我一直在阅读有关在我的微服务架构中实现API网关的SpringCloudGateway。我需要阻止我... 查看详情

springcloudgateway全链路实现

背景随着微服务架构的流行,服务按照不同的维度进行拆分,一次请求往往需要涉及到多个服务。而诸多的服务可能分布在了几千台服务器,横跨多个不同的数据中心。为了快速定位和解决故障,应用性能进行分析,全链路监控... 查看详情

springcloudgateway实现服务转发2

...发配置常见的有两种方式,一种是yml配置的方式:参考:springcloudgateway服务转发本文介绍另一种方式,即配置类的方式实现,本质没啥区别,写法不同而已。只需要在项目包扫描路径下建一个配置类即可,如下:上面的例子分别... 查看详情

springcloudgateway+jwt+oauth2实现网关的鉴权操作(代码片段)

点击关注公众号,实用技术文章及时了解一、背景随着我们的微服务越来越多,如果每个微服务都要自己去实现一套鉴权操作,那么这么操作比较冗余,因此我们可以把鉴权操作统一放到网关去做,如果微服... 查看详情

springcloudgateway+jwt+oauth2实现网关的鉴权操作(代码片段)

点击关注公众号,实用技术文章及时了解一、背景随着我们的微服务越来越多,如果每个微服务都要自己去实现一套鉴权操作,那么这么操作比较冗余,因此我们可以把鉴权操作统一放到网关去做,如果微服... 查看详情

springcloudgateway+jwt+oauth2实现网关的鉴权操作(代码片段)

点击关注公众号,实用技术文章及时了解一、背景随着我们的微服务越来越多,如果每个微服务都要自己去实现一套鉴权操作,那么这么操作比较冗余,因此我们可以把鉴权操作统一放到网关去做,如果微服... 查看详情

springcloudalibaba基于nacos+feign+springcloudgateway组合来实现灰度发布(代码片段)

前言:        当今,随着web2.0移动互联网的兴起,用户量的暴涨,各类网站应用的、各种APP规模也实现跨越式增长,随之而来的是各种高并发,海量数据处理的头疼问题,此时的系统架构为了使用... 查看详情

springcloudgateway整合nacos实现服务路由及集群负载均衡

参考技术A我们都知道SpringCloudGateway是一个基于SpringBoot、SpringWebFlux、ProjectReactor构建的高性能网关,旨在提供简单、高效的API路由。SpringCloudGateway基于Netty运行,因此在传统Servlet容器中或者打成war包是不能正常运行的。这里我们... 查看详情