1、先定义连接池配置
package wzp.redis.factory;
import java.time.Duration;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
public class LettuceClientConfigurationFactory implements FactoryBean<LettuceClientConfiguration> {
@Override
public LettuceClientConfiguration getObject() throws Exception {
GenericObjectPoolConfig<?> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(200);
poolConfig.setMaxWaitMillis(2000);
poolConfig.setTestOnBorrow(true);
LettuceClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder()
.poolConfig(poolConfig).commandTimeout(Duration.ofMillis(3000)).shutdownTimeout(Duration.ZERO).build();
return lettuceClientConfiguration;
}
@Override
public Class<?> getObjectType() {
return LettuceClientConfiguration.class;
}
}
2、再配置ReactiveRedisTemplate
<bean id="redisStandaloneConfiguration"
class="org.springframework.data.redis.connection.RedisStandaloneConfiguration">
<property name="hostName" value="${redis.host}" />
<property name="port" value="${redis.port}" />
<property name="password"
value='#{T(org.springframework.data.redis.connection.RedisPassword).of("${redis.password}")}' />
bean>
<bean id="lettuceClientConfiguration"
class="wzp.redis.factory.LettuceClientConfigurationFactory" />
<bean id="lettuceConnectionFactory"
class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory">
<constructor-arg name="standaloneConfig"
ref="redisStandaloneConfiguration" />
<constructor-arg name="clientConfig"
ref="lettuceClientConfiguration" />
bean>
<bean id="redisReactiveTemplate"
class="org.springframework.data.redis.core.ReactiveStringRedisTemplate">
<constructor-arg name="connectionFactory"
ref="lettuceConnectionFactory" />
bean>
1、定义Repository接口,由spring-data-mongodb负责扫描定义并注入CURD逻辑
package wzp.mongo.repository.reactive;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import wzp.mybatis.bo.User;
public interface UserReactiveRepository extends ReactiveMongoRepository<User, String> {
}
2、定义响应式MongoClient的工厂
package wzp.mongo.factory;
import java.util.concurrent.TimeUnit;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.PojoCodecProvider;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.ReadConcern;
import com.mongodb.WriteConcern;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
//初始化bean的时候,顺便使注解生效,扫描所有Reactive Repository
@EnableReactiveMongoRepositories(basePackages = "wzp.mongo.repository.reactive", reactiveMongoTemplateRef = "mongoReactiveTemplate")
public class MongoReactiveClientFactory implements FactoryBean<MongoClient>, DisposableBean {
// 标准连接地址mongodb://test:[email protected]:27017/?authSource=test
@Value("${mongo.uri}")
private String mongoUri;
private MongoClient client;
@Override
public MongoClient getObject() throws Exception {
if (this.client != null) {
return this.client;
}
// 对POJO进行BASE格式的序列化和反序列化
CodecRegistry pojoCodecRegistry = CodecRegistries.fromRegistries(
com.mongodb.MongoClient.getDefaultCodecRegistry(),
CodecRegistries.fromProviders(PojoCodecProvider.builder().automatic(true).build()));
// 连接池最小连接数为1,最大连接数为100,3秒内未获取空闲连接则直接失败;
// 建立连接时,3秒超时;发起查询时,10秒超时
MongoClientSettings settings = MongoClientSettings.builder()
.applyConnectionString(new ConnectionString(mongoUri))
.applyToConnectionPoolSettings(
builder -> builder.minSize(1).maxSize(100).maxWaitTime(3000, TimeUnit.MILLISECONDS).build())
.applyToSocketSettings(builder -> builder.connectTimeout(3000, TimeUnit.MILLISECONDS)
.readTimeout(10000, TimeUnit.MILLISECONDS).build())
.codecRegistry(pojoCodecRegistry).readConcern(ReadConcern.MAJORITY).writeConcern(WriteConcern.MAJORITY)
.build();
this.client = MongoClients.create(settings);
return this.client;
}
@Override
public Class<?> getObjectType() {
return MongoClient.class;
}
@Override
public void destroy() throws Exception {
if (this.client != null) {
this.client.close();
}
}
}
3、配置ReactiveMongoClient,ReactiveMongoTemplate
<bean id="mongoReactiveClient"
class="wzp.mongo.factory.MongoReactiveClientFactory" />
<bean id="mongoReactiveTemplate"
class="org.springframework.data.mongodb.core.ReactiveMongoTemplate">
<constructor-arg name="mongoClient"
ref="mongoReactiveClient" />
<constructor-arg name="databaseName"
value="${mongo.database}" />
bean>
主要展示如何使用Mono、Flux流的switchIfEmpty、flatMap方法来串接各个NIO请求
@Autowired
private ReactiveStringRedisTemplate redisReactiveTemplate;
@Autowired
private UserReactiveRepository userReactiveRepository;
@GetMapping(value = "/flux.do")
@ResponseBody
public Mono<User> flux(HttpServletRequest request, HttpServletResponse response) throws Exception {
// return webClientTest();
int id = 43;
String redisKey = "wzpTestUser" + id;
User query = new User();
query.setUser_id(id);
// 用于判断是否从redis获取到了缓存的对象
List<User> cached = Lists.newArrayList();
LOGGER.info("redis get start");
Mono<User> monoRedisGet = redisReactiveTemplate.opsForValue().get(redisKey).map(strUser -> {
// 此段逻辑的处理线程,应与redis get start的线程不同
LOGGER.info("redis get map");
User user = JSON.parseObject(strUser, User.class);
cached.add(user);
return user;
}).log();
// 当redis里不存在缓存时,才到mongo查询
Mono<User> monoMongoQuery = monoRedisGet
.switchIfEmpty(userReactiveRepository.findOne(org.springframework.data.domain.Example.of(query))).log();
Mono<User> monoRedisSet = monoMongoQuery.flatMap(user -> {
if (cached.size() == 0) {
// 当redis里不存在缓存时,才写入缓存
LOGGER.info("mongo query flatmap");
return redisReactiveTemplate.opsForValue()
.set(redisKey, JSON.toJSONString(user), Duration.ofSeconds(10)).map(success -> {
// 此段逻辑的处理线程,应与mongo query flatmap的线程不同
LOGGER.info("redis set map");
return user;
});
}
return Mono.just(user);
}).log();
// 对Flux流的真正订阅,交由WebFlux框架负责
return monoRedisSet;
}