copy一份redis出來并分別啟動(dòng)服務(wù)端:
redis2的啟動(dòng)方式要以指定端口啟動(dòng)皇钞,或者conf修改了端口后挑童,指定conf文件啟動(dòng)(即使在conf中設(shè)置了端口旬牲,以默認(rèn)方式啟動(dòng)也是啟動(dòng)的6379端口):
Jedis實(shí)現(xiàn)分布式客戶端并添加數(shù)據(jù):
在Redis在java客戶端的使用--jedis中介紹了jedis的基本使用废酷。分布式操作也差不多淋昭,不過這里使用的就不是JedisPool
鏈接池了遂黍,而是使用的ShardedJedisPool
终佛。
具體代碼實(shí)現(xiàn):
public class RedisShardedPool {
private static ShardedJedisPool pool;//sharded jedis連接池
private static Integer maxTotal = Integer.parseInt(PropertiesUtil.getProperty("redis.max.total","20")); //最大連接數(shù)
private static Integer maxIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.max.idle","20"));//在jedispool中最大的idle狀態(tài)(空閑的)的jedis實(shí)例的個(gè)數(shù)
private static Integer minIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.min.idle","20"));//在jedispool中最小的idle狀態(tài)(空閑的)的jedis實(shí)例的個(gè)數(shù)
private static Boolean testOnBorrow = Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.borrow","true"));//在borrow一個(gè)jedis實(shí)例的時(shí)候,是否要進(jìn)行驗(yàn)證操作雾家,如果賦值true查蓉。則得到的jedis實(shí)例肯定是可以用的。
private static Boolean testOnReturn = Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.return","true"));//在return一個(gè)jedis實(shí)例的時(shí)候榜贴,是否要進(jìn)行驗(yàn)證操作豌研,如果賦值true妹田。則放回jedispool的jedis實(shí)例肯定是可以用的。
private static String redis1Ip = PropertiesUtil.getProperty("redis1.ip");
private static Integer redis1Port = Integer.parseInt(PropertiesUtil.getProperty("redis1.port"));
private static String redis2Ip = PropertiesUtil.getProperty("redis2.ip");
private static Integer redis2Port = Integer.parseInt(PropertiesUtil.getProperty("redis2.port"));
private static void initPool(){
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(maxTotal);
config.setMaxIdle(maxIdle);
config.setMinIdle(minIdle);
config.setTestOnBorrow(testOnBorrow);
config.setTestOnReturn(testOnReturn);
config.setBlockWhenExhausted(true);//連接耗盡的時(shí)候鹃共,是否阻塞鬼佣,false會(huì)拋出異常,true阻塞直到超時(shí)霜浴。默認(rèn)為true晶衷。
JedisShardInfo info1 = new JedisShardInfo(redis1Ip,redis1Port,1000*2);
JedisShardInfo info2 = new JedisShardInfo(redis2Ip,redis2Port,1000*2);
List<JedisShardInfo> jedisShardInfoList = new ArrayList<JedisShardInfo>(2);
jedisShardInfoList.add(info1);
jedisShardInfoList.add(info2);
pool = new ShardedJedisPool(config,jedisShardInfoList, Hashing.MURMUR_HASH, Sharded.DEFAULT_KEY_TAG_PATTERN);
}
static{
initPool();
}
public static ShardedJedis getJedis(){
return pool.getResource();
}
public static void returnBrokenResource(ShardedJedis jedis){
pool.returnBrokenResource(jedis);
}
public static void returnResource(ShardedJedis jedis){
pool.returnResource(jedis);
}
}
- 設(shè)置config參數(shù)
- 在JedisPool構(gòu)造器中傳入
config,jedisShardInfoList...
- 上述初始化完成過后就可以通過
getJedis()
獲取到ShardedJedis
對象阴孟,進(jìn)行存儲(chǔ)晌纫、查看、設(shè)置等操作,具體命令查看Redis基礎(chǔ)命令 - 對
jedis
操作后成功調(diào)用returnResource
失敗調(diào)用returnBrokenResources
方法永丝,這里真正的將之前的操作設(shè)置到了服務(wù)器锹漱。
注意:jedisShardInfoList分別添加了JedisShardInfo(info1,info2),連接兩個(gè)redis服務(wù)器
添加filter
更新token過期時(shí)間:
- 在
web.xml
中添加filter:
<filter>
<filter-name>sessionExpireFilter</filter-name>
<filter-class>com.mmall.controller.common.SessionExpireFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sessionExpireFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
所有.do方法都會(huì)走這個(gè)過濾器慕嚷。
- 創(chuàng)建SessionExpireFilter實(shí)現(xiàn)
Filter
接口,在doFilter
中執(zhí)行更新token過期時(shí)間邏輯:
public class SessionExpireFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
String loginToken = CookieUtil.readLoginToken(httpServletRequest);
if(StringUtils.isNotEmpty(loginToken)){
//判斷l(xiāng)ogintoken是否為空或者""哥牍;
//如果不為空的話,符合條件喝检,繼續(xù)拿user信息
String userJsonStr = RedisShardedPoolUtil.get(loginToken);
User user = JsonUtil.string2Obj(userJsonStr,User.class);
if(user != null){
//如果user不為空嗅辣,則重置session的時(shí)間,即調(diào)用expire命令
RedisShardedPoolUtil.expire(loginToken, Const.RedisCacheExtime.REDIS_SESSION_EXTIME);
}
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
測試:
public static void main(String[] args) {
ShardedJedis jedis = RedisShardedPool.getResource();
for(int i =0;i<10;i++){
jedis.set("key"+i,"value"+i);
}
returnResource(jedis);
System.out.println("program is end");
}
查看服務(wù)端:
兩個(gè)redis服務(wù)端都存入了數(shù)據(jù)
查看客戶端:
看到6390中存入了key9挠说、key7澡谭、key6、key4等數(shù)據(jù)损俭,剩余的存入到6380中蛙奖。
補(bǔ)充:上文完成了redis的分布實(shí)現(xiàn),但在現(xiàn)實(shí)項(xiàng)目中如果需要項(xiàng)目中每個(gè)接口都能獲取到存儲(chǔ)的信息(比如:當(dāng)前登錄用戶的信息)撩炊,還需要對cookie進(jìn)行設(shè)置:
public class CookieUtil {
private final static String COOKIE_DOMAIN = ".zou.work";
private final static String COOKIE_NAME = "login_token";
public static String readLoginToken(HttpServletRequest request){
Cookie[] cks = request.getCookies();
if(cks != null){
for(Cookie ck : cks){
log.info("read cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
if(StringUtils.equals(ck.getName(),COOKIE_NAME)){
log.info("return cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
return ck.getValue();
}
}
}
return null;
}
public static void writeLoginToken(HttpServletResponse response,String token){
Cookie ck = new Cookie(COOKIE_NAME,token);
ck.setDomain(COOKIE_DOMAIN);
//代表設(shè)置在根目錄
ck.setPath("/");
//防止腳本攻擊
ck.setHttpOnly(true);
//單位是秒外永。
//如果這個(gè)maxage不設(shè)置的話,cookie就不會(huì)寫入硬盤拧咳,而是寫在內(nèi)存伯顶。只在當(dāng)前頁面有效。
ck.setMaxAge(60 * 60 * 24 * 365);//如果是-1,代表永久
log.info("write cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
response.addCookie(ck);
}
public static void delLoginToken(HttpServletRequest request,HttpServletResponse response){
Cookie[] cks = request.getCookies();
if(cks != null){
for(Cookie ck : cks){
if(StringUtils.equals(ck.getName(),COOKIE_NAME)){
ck.setDomain(COOKIE_DOMAIN);
ck.setPath("/");
ck.setMaxAge(0);//設(shè)置成0,代表刪除此cookie壹瘟。
log.info("del cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
response.addCookie(ck);
return;
}
}
}
}
當(dāng)我們在登錄時(shí)調(diào)用writeLoginToken
,就將token
存入到了cookie
,通過瀏覽器可以看到存儲(chǔ)成功掐暮,之后只要時(shí)在COOKIE_DOMAIN
路徑下的接口都可以獲得該COKIE_NAME
,這個(gè)時(shí)候在將COOKIE_NAME作為key
政钟,用戶信息作為value
存入到redis
中就實(shí)現(xiàn)了--每個(gè)接口都能獲取到存儲(chǔ)的信息的效果(當(dāng)然用戶信息要先轉(zhuǎn)化成字符串在存入路克,取的時(shí)候在轉(zhuǎn)化成對應(yīng)的bean即可(Jackson 封裝):