1.springboot 整合redisson,也依賴於spring-boot-starter-data-redis。
1.引入依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.11.6</version>
</dependency>
</dependencies>
3.11.6要依賴於2.2.0,否則會出現一些classnotFount。
2.yml配置
redisson依賴於spring-redis前綴的配置,可以在動態配置的代碼中發現
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.redisson.spring.starter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.redisson.spring.data.connection.RedissonConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Sentinel;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.ReflectionUtils;
@Configuration
@ConditionalOnClass({Redisson.class, RedisOperations.class})
@AutoConfigureBefore({RedisAutoConfiguration.class})
@EnableConfigurationProperties({RedissonProperties.class, RedisProperties.class})
public class RedissonAutoConfiguration {
@Autowired
private RedissonProperties redissonProperties;
@Autowired
private RedisProperties redisProperties;
@Autowired
private ApplicationContext ctx;
public RedissonAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean({StringRedisTemplate.class})
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean({RedisConnectionFactory.class})
public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) {
return new RedissonConnectionFactory(redisson);
}
@Bean(
destroyMethod = "shutdown"
)
@ConditionalOnMissingBean({RedissonClient.class})
public RedissonClient redisson() throws IOException {
Config config = null;
Method clusterMethod = ReflectionUtils.findMethod(RedisProperties.class, "getCluster");
Method timeoutMethod = ReflectionUtils.findMethod(RedisProperties.class, "getTimeout");
Object timeoutValue = ReflectionUtils.invokeMethod(timeoutMethod, this.redisProperties);
int timeout;
Method nodesMethod;
if (null == timeoutValue) {
timeout = 10000;
} else if (!(timeoutValue instanceof Integer)) {
nodesMethod = ReflectionUtils.findMethod(timeoutValue.getClass(), "toMillis");
timeout = ((Long)ReflectionUtils.invokeMethod(nodesMethod, timeoutValue)).intValue();
} else {
timeout = (Integer)timeoutValue;
}
if (this.redissonProperties.getConfig() != null) {
try {
InputStream is = this.getConfigStream();
config = Config.fromJSON(is);
} catch (IOException var11) {
try {
InputStream is = this.getConfigStream();
config = Config.fromYAML(is);
} catch (IOException var10) {
throw new IllegalArgumentException("Can't parse config", var10);
}
}
} else if (this.redisProperties.getSentinel() != null) {
nodesMethod = ReflectionUtils.findMethod(Sentinel.class, "getNodes");
Object nodesValue = ReflectionUtils.invokeMethod(nodesMethod, this.redisProperties.getSentinel());
String[] nodes;
if (nodesValue instanceof String) {
nodes = this.convert(Arrays.asList(((String)nodesValue).split(",")));
} else {
nodes = this.convert((List)nodesValue);
}
config = new Config();
((SentinelServersConfig)config.useSentinelServers().setMasterName(this.redisProperties.getSentinel().getMaster()).addSentinelAddress(nodes).setDatabase(this.redisProperties.getDatabase()).setConnectTimeout(timeout)).setPassword(this.redisProperties.getPassword());
} else {
Method method;
if (clusterMethod != null && ReflectionUtils.invokeMethod(clusterMethod, this.redisProperties) != null) {
Object clusterObject = ReflectionUtils.invokeMethod(clusterMethod, this.redisProperties);
method = ReflectionUtils.findMethod(clusterObject.getClass(), "getNodes");
List<String> nodesObject = (List)ReflectionUtils.invokeMethod(method, clusterObject);
String[] nodes = this.convert(nodesObject);
config = new Config();
((ClusterServersConfig)config.useClusterServers().addNodeAddress(nodes).setConnectTimeout(timeout)).setPassword(this.redisProperties.getPassword());
} else {
config = new Config();
String prefix = "redis://";
method = ReflectionUtils.findMethod(RedisProperties.class, "isSsl");
if (method != null && (Boolean)ReflectionUtils.invokeMethod(method, this.redisProperties)) {
prefix = "rediss://";
}
((SingleServerConfig)config.useSingleServer().setAddress(prefix + this.redisProperties.getHost() + ":" + this.redisProperties.getPort()).setConnectTimeout(timeout)).setDatabase(this.redisProperties.getDatabase()).setPassword(this.redisProperties.getPassword());
}
}
return Redisson.create(config);
}
private String[] convert(List<String> nodesObject) {
List<String> nodes = new ArrayList(nodesObject.size());
Iterator var3 = nodesObject.iterator();
while(true) {
while(var3.hasNext()) {
String node = (String)var3.next();
if (!node.startsWith("redis://") && !node.startsWith("rediss://")) {
nodes.add("redis://" + node);
} else {
nodes.add(node);
}
}
return (String[])nodes.toArray(new String[nodes.size()]);
}
}
private InputStream getConfigStream() throws IOException {
Resource resource = this.ctx.getResource(this.redissonProperties.getConfig());
InputStream is = resource.getInputStream();
return is;
}
}
會優先讀取RedisProperties的屬性,如果讀取不到,纔會讀取RedissonProperties的屬性,redission的配置屬性是一個路徑,屬性需要單獨寫入別的配置文件,如下
singleServerConfig:
address: "redis://127.0.0.1:6379"
本例中我們yml配置如下
spring:
redis:
host: localhost
port: 6379
採取單節點模式
package com.ly;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RMap;
import org.redisson.client.codec.StringCodec;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootRedissonTest {
@Autowired
private Redisson redisson;
@Autowired
private RedisTemplate<String,String> redisTemplate;
@Test
public void test(){
redisson.getKeys().flushall();
RMap<String,String> rMap = redisson.getMap("map", StringCodec.INSTANCE);
rMap.put("one","1");
BoundHashOperations<String,String,String> hash = redisTemplate
.boundHashOps("map");
String mapOneVal = hash.get("one");
System.out.println(mapOneVal);
}
@Test
public void distributeLockTest() throws IOException {
String distributeTestKey = "dis_test_key";
Thread t = new Thread(new ConsumerLockTest(redisson,distributeTestKey));
t.setName("thread 1");
t.start();
while (true){
}
}
@Test
public void distributeLockTest2() throws IOException {
String distributeTestKey = "dis_test_key";
Thread t = new Thread(new ConsumerLockTest(redisson,distributeTestKey));
t.setName("thread 2");
t.start();
while (true){
}
}
@Test
public void distributeLockTest3() throws IOException {
String distributeTestKey = "dis_test_key";
Thread t = new Thread(new ConsumerLockTest(redisson,distributeTestKey));
t.setName("thread 3");
t.start();
while (true){
}
}
public class ConsumerLockTest implements Runnable{
private Redisson redisson;
private String distributeTestKey;
public ConsumerLockTest(Redisson redisson, String distributeTestKey) {
this.redisson = redisson;
this.distributeTestKey = distributeTestKey;
}
@Override
public void run() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
RLock lock = redisson.getLock(distributeTestKey);
lock.lock();
try {
System.out.println(format.format(new Date())+","+Thread.currentThread().getName()+":獲取了鎖");
Thread.sleep(25000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
System.out.println(format.format(new Date())+","+Thread.currentThread().getName()+":釋放了鎖");
}
}
}
}
redisson實現了高性能的分佈式鎖,內部採用了看門狗,會自動爲鎖續航,如果檢測到連接斷開,鎖會解開。
還是支持了很多數據結構和原子類。