Jfinal Redis哨兵模式插件

jfinal哨兵模式插件代码模仿了RedisPlugin,开发时发现了jfina源码有几个地方需要修改,看是否描述正确。

public class RedisSentinelPlugin implements IPlugin {

private String cacheName;

private String masterName = null;

private int connectionTimeout = Protocol.DEFAULT_TIMEOUT;

// 下一jedis版本预留字段

// private int soTimeout = Protocol.DEFAULT_TIMEOUT;

private String password = null;

private int database = Protocol.DEFAULT_DATABASE;

// 下一jedis版本预留字段

// private String clientName = null;

private Set<String> sentinels = new HashSet<String>();


private ISerializer serializer;

private IKeyNamingPolicy keyNamingPolicy;

private GenericObjectPoolConfig poolConfig;


public RedisSentinelPlugin(String cacheName, String masterName, Set<HostAndPort> sentinels, final GenericObjectPoolConfig poolConfig) {

this(cacheName, masterName, sentinels, poolConfig, Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE);

}


public RedisSentinelPlugin(String cacheName, String masterName, Set<HostAndPort> sentinels) {

this(cacheName, masterName, sentinels, new GenericObjectPoolConfig(), Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE);

}


public RedisSentinelPlugin(String cacheName, String masterName, Set<HostAndPort> sentinels, String password) {

this(cacheName, masterName, sentinels, new GenericObjectPoolConfig(), Protocol.DEFAULT_TIMEOUT, password);

}


public RedisSentinelPlugin(String cacheName, String masterName, Set<HostAndPort> sentinels, final GenericObjectPoolConfig poolConfig, int timeout, final String password) {

this(cacheName, masterName, sentinels, poolConfig, timeout, password, Protocol.DEFAULT_DATABASE);

}


public RedisSentinelPlugin(String cacheName, String masterName, Set<HostAndPort> sentinels, final GenericObjectPoolConfig poolConfig, final int timeout) {

this(cacheName, masterName, sentinels, poolConfig, timeout, null, Protocol.DEFAULT_DATABASE);

}


public RedisSentinelPlugin(String cacheName, String masterName, Set<HostAndPort> sentinels, final GenericObjectPoolConfig poolConfig, final String password) {

this(cacheName, masterName, sentinels, poolConfig, Protocol.DEFAULT_TIMEOUT, password);

}


public RedisSentinelPlugin(String cacheName, String masterName, Set<HostAndPort> sentinels, final GenericObjectPoolConfig poolConfig, int timeout, final String password, final int database) {

this(cacheName, masterName, sentinels, poolConfig, timeout, timeout, password, database);

}


public RedisSentinelPlugin(String cacheName, String masterName, Set<HostAndPort> sentinels, final GenericObjectPoolConfig poolConfig, int timeout, final String password, final int database, final String clientName) {

this(cacheName, masterName, sentinels, poolConfig, timeout, timeout, password, database, clientName);

}


public RedisSentinelPlugin(String cacheName, String masterName, Set<HostAndPort> sentinels, final GenericObjectPoolConfig poolConfig, final int timeout, final int soTimeout, final String password, final int database) {

this(cacheName, masterName, sentinels, poolConfig, timeout, soTimeout, password, database, null);

}


public RedisSentinelPlugin(String cacheName, String masterName, Set<HostAndPort> sentinels, final GenericObjectPoolConfig poolConfig, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName) {

if (StrKit.isBlank(cacheName))

throw new IllegalArgumentException("cacheName can not be blank.");

if (StrKit.isBlank(masterName))

throw new IllegalArgumentException("masterName can not be blank.");

if (null == sentinels || sentinels.isEmpty())

throw new IllegalArgumentException("sentinels can not be blank.");

if (null == poolConfig)

throw new IllegalArgumentException("poolConfig can not be null.");

for (HostAndPort hp : sentinels) {

this.sentinels.add(hp.toString());

}

this.cacheName = cacheName.trim();

this.masterName = masterName.trim();

this.poolConfig = poolConfig;

this.connectionTimeout = connectionTimeout;

// this.soTimeout = soTimeout;

this.password = password;

this.database = database;

// this.clientName = clientName;

}


@Override

public boolean start() {

JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName, sentinels, poolConfig, connectionTimeout, password, database);

if (serializer == null)

serializer = FstSerializer.me;

if (keyNamingPolicy == null)

keyNamingPolicy = IKeyNamingPolicy.defaultKeyNamingPolicy;


Cache cache = new CachePlus(cacheName, jedisSentinelPool, serializer, keyNamingPolicy);

Redis.addCache(cache);

return true;

}


@Override

public boolean stop() {

CachePlus cache = (CachePlus) Redis.removeCache(cacheName);

if (cache == Redis.mainCache)

Redis.mainCache = null;

cache.pool.destroy();

return true;

}


public void setSerializer(ISerializer serializer) {

this.serializer = serializer;

}

}

该插件想完全使用Cache类,但是由于Cache类中提供的构造方法中没有JedisSentinelPool,所以继承后添加构造

public class CachePlus extends Cache {

protected Pool<Jedis> pool;


public CachePlus(String name, Pool<Jedis> pool, ISerializer serializer, IKeyNamingPolicy keyNamingPolicy) {

this.name = name;

this.pool = pool;

this.serializer = serializer;

this.keyNamingPolicy = keyNamingPolicy;

}


@Override

public Jedis getJedis() {

Jedis jedis = threadLocalJedis.get();

return jedis != null ? jedis : pool.getResource();

}

}

在stop()方法中,由于Redis类中mainCache是包内部访问权限,无法访问到,所以修改Redis源码,改为public

/**

 * Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */


package com.jfinal.plugin.redis;


import java.util.concurrent.ConcurrentHashMap;


import com.jfinal.kit.StrKit;


import redis.clients.jedis.Jedis;


/**

 * Redis. redis 工具类

 * 

 * <pre>

 * 例如:

 * Redis.use().set("key", "value");

 * Redis.use().get("key");

 * </pre>

 */

public class Redis {


public static Cache mainCache = null;


private static final ConcurrentHashMap<String, Cache> cacheMap = new ConcurrentHashMap<String, Cache>(32, 0.5F);


public static void addCache(Cache cache) {

if (cache == null)

throw new IllegalArgumentException("cache can not be null");

if (cacheMap.containsKey(cache.getName()))

throw new IllegalArgumentException("The cache name already exists");


cacheMap.put(cache.getName(), cache);

if (mainCache == null)

mainCache = cache;

}


public static Cache removeCache(String cacheName) {

return cacheMap.remove(cacheName);

}


/**

* 提供一个设置设置主缓存 mainCache 的机会,否则第一个被初始化的 Cache 将成为 mainCache

*/

public static void setMainCache(String cacheName) {

if (StrKit.isBlank(cacheName))

throw new IllegalArgumentException("cacheName can not be blank");

cacheName = cacheName.trim();

Cache cache = cacheMap.get(cacheName);

if (cache == null)

throw new IllegalArgumentException("the cache not exists: " + cacheName);


Redis.mainCache = cache;

}


public static Cache use() {

return mainCache;

}


public static Cache use(String cacheName) {

return cacheMap.get(cacheName);

}


public static <T> T call(ICallback callback) {

return call(callback, use());

}


public static <T> T call(ICallback callback, String cacheName) {

return call(callback, use(cacheName));

}


private static <T> T call(ICallback callback, Cache cache) {

Jedis jedis = cache.getThreadLocalJedis();

boolean notThreadLocalJedis = (jedis == null);

if (notThreadLocalJedis) {

jedis = cache.jedisPool.getResource();

cache.setThreadLocalJedis(jedis);

}

try {

return callback.call(cache);

} finally {

if (notThreadLocalJedis) {

cache.removeThreadLocalJedis();

jedis.close();

}

}

}

}

使用方法:

 Set<HostAndPort> sentinels = new HashSet<HostAndPort>();

 sentinels.add(new HostAndPort("test174", 26556));

 sentinels.add(new HostAndPort("test175", 26556));

 RedisSentinelPlugin dsRedis = new RedisSentinelPlugin("main", "mymaster", sentinels, "密码");

 dsRedis.setSerializer(MySerializer.me);

 startPlugins(dsRedis);

搭建Redis哨兵模式,这里不做描述

评论区

zzutligang

2021-03-05 11:02

@JFinal 看来还是有不少人很期待JFinal的Redis插件能支持哨兵或则集群模式的。强烈建议官方能跟根据redisson重新封装一套redis插件。除了能对redis读写,还能支持例如分布式锁这些高级功能。

SamUU

2021-03-05 16:49

整天想着白嫖!先充个会员,再来聊你的高级功能需求。

热门反馈

扫码入社