Redis是个好的缓冲服务器,但它的集群却实现的不怎么样(一家之言,没有仔细研究,只读了下它的官方文档)。只有两种方法:
主从复制,从服务器只读
集群方式,但却是把数据分片放到不同服务器上。该方式对物理位置在一起的服务器而言很好用,但如果业务上的需要,集群分布在天南地北呢?
为此,我的业务规划为,缓冲写时指到到Redis Master上,通过Redis的主从复制,将数据复制到远程Redis Slave上,而远程的Jfianl就近读取Redis Slave。
实现用到两个类,请大神指正:
package com.ext.jfinal;
import com.jfinal.plugin.redis.Cache;
import com.jfinal.plugin.redis.IKeyNamingPolicy;
import com.jfinal.plugin.redis.serializer.ISerializer;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class xCache extends Cache {
protected JedisPool jedisPool2;
protected final ThreadLocal<Jedis> threadLocalJedis2 = new ThreadLocal<Jedis>();
public xCache(String name, JedisPool jedisPool,JedisPool jedisPool2, ISerializer serializer, IKeyNamingPolicy keyNamingPolicy) {
this.name = name;
this.serializer = serializer;
this.keyNamingPolicy = keyNamingPolicy;
this.jedisPool = jedisPool;
this.jedisPool2 = jedisPool2;
}
public Jedis getJedis2() {
Jedis jedis = threadLocalJedis2.get();
return jedis != null ? jedis : jedisPool2.getResource();
}
@Override
public <T> T get(Object key) {
Jedis jedis = getJedis2();
try {
return (T)valueFromBytes(jedis.get(keyToBytes(key)));
}
finally {close(jedis);}
}
}2. xRedisPlugin,直接抄了波总的原插件,改了点
/**
* Copyright (c) 2011-2016, James Zhan 詹波 (jfinal@126.com).
* <p/>
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.ext.jfinal;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.IPlugin;
import com.jfinal.plugin.redis.Cache;
import com.jfinal.plugin.redis.IKeyNamingPolicy;
import com.jfinal.plugin.redis.Redis;
import com.jfinal.plugin.redis.serializer.FstSerializer;
import com.jfinal.plugin.redis.serializer.ISerializer;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Protocol;
/**
* xRedisPlugin.
* xRedisPlugin 支持多个读写分离
*/
public class xRedisPlugin implements IPlugin {
private String cacheName;
private String[] master; //主服务器连接参数
private String[] slave; //从服务器连接参数,只读
private ISerializer serializer = null;
private IKeyNamingPolicy keyNamingPolicy = null;
private JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
public xRedisPlugin(String cacheName, String[] redisServer) {
if (StrKit.isBlank(cacheName))
throw new IllegalArgumentException("cacheName can not be blank.");
this.cacheName = cacheName.trim();
this.master = redisServer[0].split(",");
if (redisServer.length==1){
this.slave = this.master;
} else {
this.slave = redisServer[1].split(",");
}
}
public boolean start() {
JedisPool jedisPool=null,jedisPool2=null;
// 设置主服务器
if(master.length==1){ //只传了IP
jedisPool = new JedisPool(jedisPoolConfig, master[0]);
} else if(master.length==2) { //传了IP和Port
jedisPool = new JedisPool(jedisPoolConfig, master[0], Integer.parseInt(master[1]));
} else if(master.length==3) { //传了IP,Port,timeout
jedisPool = new JedisPool(jedisPoolConfig, master[0], Integer.parseInt(master[1]), Integer.parseInt(master[2]));
} else if(master.length==4) { //传了IP,Port,timeout, password
jedisPool = new JedisPool(jedisPoolConfig, master[0], Integer.parseInt(master[1]), Integer.parseInt(master[2]),master[3]);
} else if(master.length==5) { //传了IP,Port,timeout, password,database
jedisPool = new JedisPool(jedisPoolConfig, master[0], Integer.parseInt(master[1]), Integer.parseInt(master[2]),master[3],Integer.parseInt(master[4]));
} else if(master.length==6) { //传了IP,Port,timeout, password,database,clientName
jedisPool = new JedisPool(jedisPoolConfig, master[0], Integer.parseInt(master[1]), Integer.parseInt(master[2]),master[3],Integer.parseInt(master[4]),master[5]);
}
// 设置从服务器
if(slave.length==1){ //只传了IP
jedisPool2 = new JedisPool(jedisPoolConfig, slave[0]);
} else if(slave.length==2) { //传了IP和Port
jedisPool2 = new JedisPool(jedisPoolConfig, slave[0], Integer.parseInt(slave[1]));
} else if(slave.length==3) { //传了IP,Port,timeout
jedisPool2 = new JedisPool(jedisPoolConfig, slave[0], Integer.parseInt(slave[1]), Integer.parseInt(slave[2]));
} else if(slave.length==4) { //传了IP,Port,timeout, password
jedisPool2 = new JedisPool(jedisPoolConfig, slave[0], Integer.parseInt(slave[1]), Integer.parseInt(slave[2]),slave[3]);
} else if(slave.length==5) { //传了IP,Port,timeout, password,database
jedisPool2 = new JedisPool(jedisPoolConfig, slave[0], Integer.parseInt(slave[1]), Integer.parseInt(slave[2]),slave[3],Integer.parseInt(slave[4]));
} else if(slave.length==6) { //传了IP,Port,timeout, password,database,clientName
jedisPool2 = new JedisPool(jedisPoolConfig, slave[0], Integer.parseInt(slave[1]), Integer.parseInt(slave[2]),slave[3],Integer.parseInt(slave[4]),slave[5]);
}
if (serializer == null)
serializer = FstSerializer.me;
if (keyNamingPolicy == null)
keyNamingPolicy = IKeyNamingPolicy.defaultKeyNamingPolicy;
xCache cache = new xCache(cacheName, jedisPool,jedisPool2,serializer, keyNamingPolicy);
Redis.addCache(cache);
return true;
}
public boolean stop() {
// Cache cache = Redis.removeCache(cacheName);
// if (cache == Redis.mainCache)
// Redis.mainCache = null;
// cache.jedisPool.destroy();
return true;
}
/**
* 当RedisPlugin 提供的设置属性仍然无法满足需求时,通过此方法获取到
* JedisPoolConfig 对象,可对 redis 进行更加细致的配置
* <pre>
* 例如:
* redisPlugin.getJedisPoolConfig().setMaxTotal(100);
* </pre>
*/
public JedisPoolConfig getJedisPoolConfig() {
return jedisPoolConfig;
}
// ---------
public void setSerializer(ISerializer serializer) {
this.serializer = serializer;
}
public void setKeyNamingPolicy(IKeyNamingPolicy keyNamingPolicy) {
this.keyNamingPolicy = keyNamingPolicy;
}
// ---------
public void setTestWhileIdle(boolean testWhileIdle) {
jedisPoolConfig.setTestWhileIdle(testWhileIdle);
}
public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
jedisPoolConfig.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
}
public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
}
public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
}
}3. AppConfig中启动插件
//配置redis缓存, 要注意生产环境的配置, 服务器参数为:ip,端口,超时,密码,数据库编号,客户端
//String redisServer = PropKit.get("redisServer", "127.0.0.1");
xRedisPlugin redis = new xRedisPlugin("Redis",new String[]{"127.0.0.1,6379","127.0.0.1,6380"});
redis.setSerializer(JdkSerializer.me); // 设置Redis序列化方式
JedisPoolConfig jedisPoolConfig = redis.getJedisPoolConfig();
jedisPoolConfig.setMaxTotal(1000);
jedisPoolConfig.setMaxIdle(50);
jedisPoolConfig.setMinIdle(20);
jedisPoolConfig.setMaxWaitMillis(10*1000);
me.add(redis);还需要什么改进,可能产生什么Bug,请各位指正