李锋镝的博客 - LiFengdi.Com

  • 首页
  • 时间轴
  • 留言
  • 左邻右舍
  • 我的日常
  • 关于我
青衿之志 履践致远
霁月光风 不萦于怀
  1. 首页
  2. 原创
  3. 正文

从零搭建Spring Cloud Gateway网关(一)

2020年3月18日 14380点热度 2人点赞 6条评论

新建Spring Boot项目

怎么新建Spring Boot项目这里不再具体赘述,不会的可以翻看下之前的博客或者直接百度。这里直接贴出对应的pom文件。

pom依赖如下:

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>com.lifengdi</groupId>
    <artifactId>gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>gateway</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR3</spring-cloud.version>
    </properties>

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

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

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

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>

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

    <dependencyManagement>
        <dependencies>
            <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>

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

</project>

由于是网关项目,所以不需要spring-boot-starter-web相关的依赖。

配置文件如下:

server:
  port: 8080
spring:
  application:
    name: spring-cloud-gateway-demo
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #启用路由访问
      routes:
        - id: path_route
          # 指定域名
          uri: http://localhost:8081
          predicates:
            - Path=/jar/**
          filters:
            # 熔断配置
            - name: Hystrix
              args:
                name: default
                fallbackUri: forward:/fallback
        - id: path_route2
          # 指定域名
          uri: http://localhost:8082
          predicates:
            - Path=/war/**
          filters:
            # 熔断配置
            - name: Hystrix
              args:
                name: hystrix1
                fallbackUri: forward:/fallback

  mvc:
    throw-exception-if-no-handler-found: true

# 默认熔断超时时间30s
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
    hystrix1:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 1000

熔断(接口或者项目)

熔断相关jar包如下:

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

默认的熔断回调接口:

package com.lifengdi.gateway.hystrix;

import com.lifengdi.gateway.exception.BaseException;
import com.lifengdi.gateway.response.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author: Li Fengdi
 * @date: 2020-03-18 16:35
 */
@RestController
@Slf4j
public class DefaultHystrixController {
    @RequestMapping("/fallback")
    public ResponseResult<Object> fallback(){

        log.error("触发熔断......");
        return ResponseResult.fail(BaseException.DEFAULT_HYSTRIX.build());
    }
}

具体配置文件说明如下:

      routes:
        - id: path_route
          # 指定域名
          uri: http://localhost:8081
          predicates:
            - Path=/jar/**
          filters:
            # 熔断配置
            - name: Hystrix
              args:
                name: default
                fallbackUri: forward:/fallback
        - id: path_route2
          # 指定域名
          uri: http://localhost:8082
          predicates:
            - Path=/war/**
          filters:
            # 熔断配置
            - name: Hystrix
              args:
                name: hystrix1
                fallbackUri: forward:/fallback

  mvc:
    throw-exception-if-no-handler-found: true

# 默认熔断超时时间30s
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
    hystrix1:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 1000

default、hystrix1为自定义的参数,可以配置多个熔断策略,不同的接口、服务可以单独配置对应的超时时间,不需要额外的进行开发,不过需要增加额外的配置文件。

全局session共享

依赖jar包:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

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

相关yml配置:

spring:
  redis:
    database: 0
    host: localhost
    port: 6379
    password: 123456
    lettuce:
      pool:
        max-active: 300
        max-idle: 8
        max-wait: -1ms
        min-idle: 0
  session:
    store-type: redis

spring.session.store-typeSpring默认就是redis实现的,也有其他的,配置不同罢了。

增加代码如下:

权限相关,这里默认全部放行:

package com.lifengdi.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

@Configuration
@EnableWebFluxSecurity
public class GatewaySecurityConfig {
    @Bean
    SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity serverHttpSecurity)
            throws Exception {
        serverHttpSecurity
                .csrf().disable()
                .authorizeExchange().pathMatchers("/**").permitAll()
                .anyExchange()
                .authenticated();
        return serverHttpSecurity.build();
    }
}

session相关:

package com.lifengdi.gateway.config;

import com.lifengdi.gateway.resolver.MyCookieWebSessionIdResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseCookie;
import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession;
import org.springframework.web.server.session.CookieWebSessionIdResolver;
import org.springframework.web.server.session.WebSessionIdResolver;

import java.util.function.Consumer;

@Configuration
@EnableRedisWebSession(maxInactiveIntervalInSeconds = 10*60*60, redisNamespace = "my:spring:session")
public class WebSessionConfig {

    @Bean
    public WebSessionIdResolver webSessionIdResolver() {
        CookieWebSessionIdResolver resolver = new MyCookieWebSessionIdResolver();
        resolver.setCookieName("SESSIONID");

        Consumer<ResponseCookie.ResponseCookieBuilder> consumer = responseCookieBuilder -> {
            responseCookieBuilder.path("/");
        };
        resolver.addCookieInitializer(consumer);
        return resolver;
    }

}

注意这里使用的是@EnableRedisWebSession注解,而不是@EnableRedisHttpSession,这个是和zuul不一样的地方。

用zuul做网关的时候,直接使用@EnableRedisHttpSession在配置里面就可以通过redis共享session信息

Spring同时提供了@EnableRedisWebSession来对WebFlux的支持。

值得一提的是这两个注解内部实现并不相同,需要自定义的配置也不一样。

这里自定义cookieName、path等是自定义了webSessionIdResolver来实现的,而不是cookieSerializer。如果使用cookieSerializer的话,对@EnableRedisWebSession来说是不起作用的。这个坑之前坑了好半天!

MyCookieWebSessionIdResolver代码如下:

package com.lifengdi.gateway.resolver;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpCookie;
import org.springframework.session.web.http.DefaultCookieSerializer;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.session.CookieWebSessionIdResolver;

import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 自定义WebSessionId解析器,以兼容{@link org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession}
 * <p>
 * 使用EnableRedisHttpSession时{@link DefaultCookieSerializer}中useBase64Encoding默认为true,将cookie中的sessionId使用base64
 * 加密,但是如果使用{@link org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession},默认
 * 的解析器没有将sessionId解密,导致获取不到正确的session
 * </p>
 *
 * @author: Li Fengdi
 * @date: 2020/3/16 15:41
 */
@Slf4j
public class MyCookieWebSessionIdResolver extends CookieWebSessionIdResolver {

    @Override
    public List<String> resolveSessionIds(ServerWebExchange exchange) {
        MultiValueMap<String, HttpCookie> cookieMap = exchange.getRequest().getCookies();
        List<HttpCookie> cookies = cookieMap.get(getCookieName());
        if (cookies == null) {
            return Collections.emptyList();
        }
        return cookies.stream().map(HttpCookie::getValue).map(this::base64Decode).collect(Collectors.toList());
    }

    /**
     * base64解码
     *
     * @param base64Value base64Value
     * @return 解码后的字符串
     */
    private String base64Decode(String base64Value) {
        try {
            byte[] decodedCookieBytes = Base64.getDecoder().decode(base64Value);
            return new String(decodedCookieBytes);
        } catch (Exception ex) {
            log.debug("Unable to Base64 decode value: " + base64Value);
            return null;
        }
    }

}

其实这段代码本就是参考了cookieSerializer中的代码来实现的。

如果指定了useBase64Encoding为false,即不加密sessionId,那么就不需要这一段代码了。

代码已上传到git上,需要的可以去看看。

git代码地址:https://github.com/lifengdi/spring-cloud-gateway-demo

除非注明,否则均为李锋镝的博客 - LiFengdi.Com原创文章,转载必须以链接形式标明本文链接
本文链接:https://www.lifengdi.com/archives/article/1776
本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: JAVA Spring Cloud Spring Cloud Gateway SpringBoot 网关
最后更新:2021年5月12日

李锋镝

既然选择了远方,便只顾风雨兼程。

打赏 点赞
< 上一篇
下一篇 >
guest
您的姓名(必填)
您的邮箱(必填)
您的站点
guest
您的姓名(必填)
您的邮箱(必填)
您的站点
6 评论
最热
最新 最旧
Inline Feedbacks
查看所有评论
叶子
叶子
VIP
2021年5月28日 14:17

你好,刚接触,代码下载了,请问一下,如何测试 :han:

0
0
回复
李锋镝
李锋镝
博主
回复  叶子
2021年5月28日 15:39

@叶子 yaml文件中有配置,修改对应的配置,就可以将请求转发到配置的接口

0
0
回复
贺龙
贺龙
VIP
2021年5月7日 18:50

你好!朋友!

0
0
回复
李锋镝
李锋镝
博主
回复  贺龙
2021年5月7日 19:02

@贺龙 :haha: 欢迎新朋友~

0
0
回复
贺龙
贺龙
VIP
回复  李锋镝
2021年5月12日 08:48

@李锋镝 看了你的文章,下了你的git代码,学到了很多!顶你!

0
0
回复
李锋镝
李锋镝
博主
回复  贺龙
2021年5月12日 09:04

@贺龙 哈哈~很荣幸能给你提供帮助,希望我们都能早日财务自由,哈哈~~

0
0
回复
支付宝红包

人间四月芳菲尽,山寺桃花始盛开。
长恨春归无觅处,不知转入此中来。

最新 热点 随机
最新 热点 随机
回忆是一条没有尽头的路 这样的日子什么时候才是个头 MySQL 中的 distinct 和 group by 哪个效率更高? 开工啦~ 今晚,回家过年! 图数据库选型:Neo4j、Janus、HugeGraph
看病难~取药难~~阳了...开工啦~RocketMQ的push消费方式实现详解国庆节过的也很累~~MybatisCodeHelperPro激活
关闭apache httpclient4.5 DEBUG日志 【漫画】戏说外行对程序员的误会有多深 Elasticsearch中的多索引、多类型搜索 查看占用 CPU 最高的线程(Java) MySQL深度分页 MySQL 的自增 ID 用完了,怎么办?
最近评论
李锋镝 发布于 2 周前(03月10日) 已添加~欢迎回访喔
博客录(boke.lu) 发布于 2 周前(03月10日) 已添加贵站0.0 名称:博客录(boke.lu) 简介:boke.lu · 博客收录展示平台~ 链接...
李锋镝 发布于 3 周前(03月05日) 系统版本是win11吗?
HJQ 发布于 4 周前(02月28日) 同问,注册表都没有楼主说的值。
林羽凡 发布于 1 个月前(02月16日) 开工大吉。
有情链接
  • 志文工作室
  • 临窗旋墨
  • 旧时繁华
  • 城南旧事
  • 强仔博客
  • 林三随笔
  • 徐艺扬的博客
  • 云辰博客
  • 韩小韩博客
  • 知向前端
  • 阿誉的博客
  • 林羽凡
  • 情侣头像
  • 哥斯拉
  • 博客录

COPYRIGHT © 2022 lifengdi.com. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

豫ICP备16004681号-2