前言
Jedis
Jedis 是 Redis 官方推薦的 Java 連接開發工具,要在Java開發中使用好 Redis中間件,必須對Jedis十分了解才行,
基本使用
Jedis 的使用非常簡單,只需要創建 Jedis 物件的時候指定 host,port,password 即可,
Jedis jedis = new Jedis("ip",post);
......
jedis.close();
創建完 Jedis 物件,Jedis底層會打開一條 Socket 通道和 Redis 進行連接,所以在使用完 Jedis 物件后,需要 jedis.close() 關閉連接,不然會占用系統資源,如果每次使用都手動創建和銷毀 Jedis 物件,對應用的性能有很大影響,畢竟創建 socket 的程序是很耗時的,所以我們使用連接池的方式減少 socket 物件的創建和銷毀程序,
連接池使用
Jedis連接池是 org.apache.commons.pool2實作的,在構建連接池物件時,需要提供連接池物件的配置物件,我們可以通過這個配置物件對連接池進行相關引數的配置,比如:最大空閑連接數,最大連接總數,
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(8);
jedisPoolConfig.setMaxTotal(18);
JedisPool jedisPool = new JedisPool(jedisPoolConfig, "ip", 6379, 2000);
Jedis jedis = jedisPool.getResource();
......
jedis.close();
jedisPool.close();
使用 Jedis連接池后,每次用完連接物件都會把連接歸還給連接池,Jedis對 close() 和 getResource() 方法的實作,
//Jedis的close方法
@Override
public void close() {
if (dataSource != null) {
if (client.isBroken()) {
this.dataSource.returnBrokenResource(this);
} else {
this.dataSource.returnResource(this);
}
} else {
client.close();
}
}
// JedisPool.getResource()方法
// 從物件池中獲取Jedis連接時,將會對dataSource進行設定
public Jedis getResource() {
Jedis jedis = super.getResource();
jedis.setDataSource(this);
return jedis;
}
高可用連接
我們知道,連接池可以大大提高應用訪問Reids服務的性能,減去大量的 Socket 的創建和銷毀程序,但是 Redis 為了保障高可用,服務一般都是 Sentinel部署方式,當 Redis 服務中的主服務掛掉之后,會仲裁出另外一臺 Slaves 服務充當 Master,這個時候,我們的應用即使使用了Jedis連接池,Master服務掛了,還是無法連接新的 Master服務,為了解決這個問題,Jedis也提供了相應的 Sentinel 實作,能夠在 Redis Sentinel 主從切換時候,通知我們的應用,把我們的應用連接到新的 Master 服務,
Set<String> sentinels = new HashSet<>();
sentinels.add("ip1");
sentinels.add("ip2");
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(8);
jedisPoolConfig.setMaxTotal(18);
JedisSentinelPool jedisPool = new JedisSentinelPool("mymaster", sentinels, jedisPoolConfig);
Jedis jedis = jedisPool.getResource();
......
jedis.close();
jedisPool.close();
JedisSentinelPool 的使用很簡單,添加了設定服務器ip的 set 集合和 masterName引數,Jedis Sentinel底層基于 Redis訂閱實作 Redis主從服務的切換通知,當 Redis發生主從切換時,Sentinel 會發送通知主動通知 Jedis 進行連接的切換,JedisSentinelPool 在每次從連接池中獲取連接物件的時候,都要對連接物件進行檢測,如果此鏈接和 Sentinel 的 Master 服務連接引數不一致,則會關閉此連接,重新獲取新的 Jedis 連接物件,
@Override
public Jedis getResource() {
while (true) {
Jedis jedis = super.getResource();
jedis.setDataSource(this);
// get a reference because it can change concurrently
final HostAndPort master = currentHostMaster;
final HostAndPort connection = new HostAndPort(jedis.getClient().getHost(), jedis.getClient()
.getPort());
if (master.equals(connection)) {
// connected to the correct master
return jedis;
} else {
returnBrokenResource(jedis);
}
}
}
使用 Spring 時可以引入 spring-data-redis 包,使用 SpringBoot 時可以直接參考 spring-boot-starter-redis包,其內部都整合了 Jedis,
當然,也可以單獨參考 Jedis 包,
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
Lettuce
Lettuce 是基于 Netty框架(NIO)的事件驅動的通信,支持同步和異步呼叫的,可擴展的 redis client,多個執行緒可以共享一個 RedisConnection,執行緒安全,
基本使用
1、引 lettuce 依賴
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</dependency>
2、lettuce操作Redis
// 同步操作
@Test
public void test() {
RedisURI redisURI = RedisURI.builder().withHost(“ip”).withPort(6379).withTimeout(Duration.of(10, ChronoUnit.SECONDS)).build();
RedisClient redisClient = RedisClient.create(redisURI);
StatefulRedisConnection<String, String> connection = redisClient.connect();
RedisCommands<String, String> syncCommands = connection.sync();
String set = syncCommands.set(“k1”, “v1”);
System.out.println(set);
connection.close();
redisClient.shutdown();
}
// 異步操作
@Test
public void testAsync(){
RedisURI redisURI = RedisURI.builder().withHost(“ip”).withPort(6379).withTimeout(Duration.of(10, ChronoUnit.SECONDS)).build();
RedisClient redisClient = RedisClient.create(redisURI);
StatefulRedisConnection<String, String> connection = redisClient.connect();
RedisAsyncCommands<String, String> asyncCommands = connection.async();
RedisFuture<String> set = asyncCommands.set(“k2”, “v2”);
System.out.println(set);
connection.close();
redisClient.shutdown();
}
// 回應式API
@Test
public void testReactive() {
RedisURI redisURI = RedisURI.builder().withHost(“192.168.96.173”).withPort(6379).withTimeout(Duration.of(10, ChronoUnit.SECONDS)).build();
RedisClient redisClient = RedisClient.create(redisURI);
StatefulRedisConnection<String, String> connection = redisClient.connect();
RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();
Mono<String> set = reactiveCommands.set(“name”, “feiyangyang”);
System.out.println(set.block());
高可用連接
@Test
public void masterSlave() {
RedisURI redisURI = RedisURI.builder().withHost("ip").withPort(6379).withTimeout(Duration.of(10, ChronoUnit.SECONDS)).build();
RedisClient client = RedisClient.create(redisURI);
StatefulRedisMasterReplicaConnection<String, String> connection = MasterReplica.connect(client, StringCodec.UTF8, redisURI);
// 從節點讀主節點資料
connection.setReadFrom(ReadFrom.REPLICA);
RedisCommands<String, String> commands = connection.sync();
commands.set("name", "feiyangyang");
System.out.println(commands.get("name"));
connection.close();
client.shutdown();
}
// 哨兵
@Test
public void sentinel() {
List<RedisURI> uris = new ArrayList();
uris.add(RedisURI.builder().withSentinel("ip1", 26379).withSentinelMasterId("mymaster").withPassword("123456").build());
uris.add(RedisURI.builder().withSentinel("ip2", 26379).withSentinelMasterId("mymaster").withPassword("123456").build());
uris.add(RedisURI.builder().withSentinel("ip3", 26379).withSentinelMasterId("mymaster").withPassword("123456").build());
RedisClient client = RedisClient.create();
StatefulRedisMasterReplicaConnection<String, String> connection = MasterReplica.connect(client, StringCodec.UTF8, uris);
connection.setReadFrom(ReadFrom.REPLICA);
RedisCommands<String, String> commands = connection.sync();
commands.set("name", "feiyangyang");
System.out.println(commands.get("name"));
connection.close();
client.shutdown();
}
// 集群
@Test
public void cluster() {
Set<RedisURI> uris = new HashSet<>();
uris.add(RedisURI.builder().withHost("ip1").withPort(7000).withPassword("123456").build());
uris.add(RedisURI.builder().withHost("ip2").withPort(7001).withPassword("123456").build());
uris.add(RedisURI.builder().withHost("ip3").withPort(7000).withPassword("123456").build());
uris.add(RedisURI.builder().withHost("ip4").withPort(7001).withPassword("123456").build());
uris.add(RedisURI.builder().withHost("ip5").withPort(7000).withPassword("123456").build());
uris.add(RedisURI.builder().withHost("ip6").withPort(7001).withPassword("123456").build());
RedisClusterClient client = RedisClusterClient.create(uris);
StatefulRedisClusterConnection<String, String> connection = client.connect();
RedisAdvancedClusterCommands<String, String> commands = connection.sync();
commands.set("name", "feiyangyang");
System.out.println(commands.get("name"));
//選擇從節點,只讀
NodeSelection<String, String> replicas = commands.replicas();
NodeSelectionCommands<String, String> nodeSelectionCommands = replicas.commands();
Executions<List<String>> keys = nodeSelectionCommands.keys("*");
keys.forEach(key -> System.out.println(key));
connection.close();
client.shutdown();
}
總結
Jedis 是直連 redis server,會有執行緒安全問題,除非使用連接池,為每個 Jedis實體增加物理連接,
優點:
- 簡單易理解
- 全面的Redis操作API
缺點:
- 同步阻塞IO
- 不支持異步
- 執行緒不安全
Lettuce是基于Netty的,連接實體可以在多個執行緒間并發訪問,Lettuce還支持異步連接方式,提高網路等待和磁盤IO效率,
優點:
- 執行緒安全
- 基于Netty框架的事件驅動通信,可異步呼叫
- 適用于分布式快取
缺點:
- 學習成本高,上手相對復雜
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/295726.html
標籤:其他
上一篇:MySQL JOIN的使用
