文章目录
- 前言
- 一、网关gateway选型
- 1. 响应式编程模型
- 2. 网关的特定需求
- 3. 技术栈一致性
- 4. 性能对比
- 5. 实际应用场景优势
- 二、redis的集成
- 1.引入库
- 2.配置类
- A、自定义配置类RedisAfterNacosAutoConfiguration
- B、自定义配置类RedisConfig
- 总结
前言
最近在搭建最新的springCloud + springboot3 + nacos微服务框架,有个新项目要启动了,做完二期公司都可以上市的,所以基础得搭建牢固。有建议找开源的,之前呆过的一家公司居然还花钱买框架,真是。。。。。。
以上2种方式我都不喜欢,要搞就搞最新的,就跟找朋友样,要找就找年轻的,所以我就算是今天周六也在家搞搞呗。因为都是最新的,参考真的很少啊,踩几个坑了,但是没有太多时间复盘,今天就先简单说下gateway网关用redis的事情。
这里是redis的配置也放nacos,那么问题就来了,自动配置的RedisProperties怎么在服务还在启动过程中就去nacos哪配置呢?
一、网关gateway选型
gateway选择基于 WebFlux 而非传统的 Servlet 栈(如 Spring MVC)呢?就是因为这个原因,gateway的redis我不能直接引入通用的common,因为我把数据库连接、异常统一处理、redis等都放在了common包,但是异常统一处理是基于starter-web写的,所以最后我gateway单独引入redis。
为什么我不把gateway也换成基于starter-web呢?原因如下:
1. 响应式编程模型
核心优势:
-
非阻塞 I/O:WebFlux 基于 Reactor 实现非阻塞异步处理,特别适合网关这种 I/O 密集型场景
-
高并发能力:用少量线程处理大量并发连接(相比线程池模型的 Servlet 容器)
-
资源高效:避免线程上下文切换开销,减少内存消耗
2. 网关的特定需求
功能性考量:
-
请求转发:网关需要高效处理大量 HTTP 请求转发
-
过滤器链:WebFlux 的函数式编程模型更适合实现灵活的过滤器管道
-
背压支持:内置响应式流背压机制,防止下游服务过载
3. 技术栈一致性
架构匹配:
-
Spring 5+ 生态:Gateway 作为 Spring Cloud 新一代组件,自然采用 Spring 5 的响应式体系
-
Netty 集成:默认使用 Netty 作为服务器,与 WebFlux 深度集成
-
函数式路由:支持 lambda 表达式定义路由规则,更简洁的 API 设计
4. 性能对比
指标 | WebFlux (Netty) | Servlet (Tomcat) |
---|---|---|
线程模型 | 事件循环 | 线程池 |
并发连接处理能力 | 更高 | 受限于线程池大小 |
内存占用 | 更低 | 更高 |
长连接支持 | 更优 | 一般 |
5. 实际应用场景优势
典型用例:
-
微服务架构中的边缘服务
-
需要处理大量并发连接(如 IoT 场景)
-
需要低延迟的请求转发
-
需要灵活的自定义过滤逻辑
二、redis的集成
1.引入库
引库没啥可说的,无非就是现在最新的redis的starter的配置项前缀发生了改变,不再是“spring.redis”了,而是“spring.data.redis”。
<!-- Spring Boot 3 Redis 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>${redisson.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2.配置类
这里因为是要先加载完nacos,再从nacos拉取redis,最后启动服务。正因为这个服务启动顺序,又涉及到最新的springboot、cloud,所以就出现了2种写法,写在这里也是笔记也是分享。
redis在nacos的配置
spring:
data:
redis:
host: 127.0.0.1
port: 6379
password:
database: 0
timeout: 5000ms
A、自定义配置类RedisAfterNacosAutoConfiguration
import com.alibaba.cloud.nacos.NacosConfigAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.StringUtils;
/**
* @author zwmac
*/
@AutoConfiguration(after = NacosConfigAutoConfiguration.class)
@ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", havingValue = "true", matchIfMissing = true)
public class RedisAfterNacosAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RedisConnectionFactory redisConnectionFactory(ConfigurableEnvironment env) {
String host = env.getProperty("spring.data.redis.host");
Integer port = env.getProperty("spring.data.redis.port", Integer.class, 6379);
String password = env.getProperty("spring.data.redis.password");
if (host == null) {
throw new IllegalStateException("Redis配置未从Nacos加载,请检查 redis-config.yaml 是否加载成功");
}
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(host, port);
if (StringUtils.hasText(password)) {
config.setPassword(RedisPassword.of(password));
}
return new LettuceConnectionFactory(config);
}
@Bean
@ConditionalOnMissingBean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
template.setDefaultSerializer(serializer);
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}
注意这种方式需要在启动类上加@Import(RedisAfterNacosAutoConfiguration.class),否则无效。
B、自定义配置类RedisConfig
import com.rs.gov.govgateway.service.redis.RedisService;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author zwmac
*/
@Configuration
@DependsOn("nacosConfigManager")
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {
private final RedisProperties redisProperties;
public RedisConfig(RedisProperties redisProperties) {
this.redisProperties = redisProperties;
}
@Bean
public RedisService redisService(RedisTemplate<Object, Object> redisTemplate) {
return new RedisService(redisTemplate);
}
@Bean
public LettuceConnectionFactory redisConnectionFactory(RedisProperties redisProperties) {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(redisProperties.getHost());
config.setPort(redisProperties.getPort());
config.setPassword(RedisPassword.of(redisProperties.getPassword()));
return new LettuceConnectionFactory(config);
}
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 推荐的序列化方式:不需要设置 ObjectMapper
GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
template.setDefaultSerializer(jackson2JsonRedisSerializer);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
// 可选:注册 RedissonClient
@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient() {
Config config = new Config();
SingleServerConfig singleServerConfig = config.useSingleServer();
singleServerConfig.setAddress("redis://" + redisProperties.getHost() + ":" + redisProperties.getPort());
if (StringUtils.isNotBlank(redisProperties.getPassword())) {
singleServerConfig.setPassword(redisProperties.getPassword());
}
return Redisson.create(config);
}
}
我也怕麻烦,个人认为RedisConfig更简单,无非就是要注意@DependsOn(“nacosConfigManager”)这个注解。
总结
最近的事有点多,压力很大啊,关键是BOSS对软件没有认知,部门软件角色的人员也配备不全。主责的项目啥都没有,只有一个前端截图、一个Excel文档,根据Excel文档估的工时打骨折,难啊!