您的位置:首页 > 数据库 > Redis

6 redis+单点登录+jackson序列化+redis分布式+spring session+springMVC全局异常控制+springMVC _RESTful 架构

2018-03-20 17:29 453 查看

redis

redis安装:
wget http://download.redis.io/releases/redis-2.8.0.tar.gz
tar -zxvf redis-2.8.0.tar.gz 

cd redis-2.8.0
make 编译
make test 测试是否正常运行,我报了一个错误,提示8.5版本什么的,需要安装依赖
 wget http://downloads.sourceforge.net/tcl/tcl8.6.1-src.tar.gz 
 tar -zxvf tcl8.6.1-src.tar.gz
执行unix目录下的./configure
make
make install
启动redis :./redis-server &  (加&表示在后台运行,就不会占用窗口了)
./redis.cli   (打开命令行输入)
[root@VM_0_2_centos src]# ./redis-cli 
127.0.0.1:6379> set a b
OK
127.0.0.1:6379> keys *
1) "a"
127.0.0.1:6379> get a
"b"

127.0.0.1:6379> 
常用启动,关闭命令:关闭redis并且使其持久化:./redis-cli shutdown
换个端口启动:./redis-server --port 6380
换个端口启动cli:./redis-cli -p 6380
关闭指定端口redis并且持久化:./redis-cli -p 6380 stutdown
通过ip远程链接cli:./redis-cli -p 6379 -h 127.0.0.1
通过配置文件启动:./redis-server ../redis.conf
设置密码:修改redis.conf配置文件添加requirepass bobo (bobo就是你的密码)
通过密码链接redis:./redis-cli -a bobo 

reids常用命令:info:常用信息 



可以设置其他数据库默认是db0
flushdb:清除当前的db空间
flushall:清除所有的db空间
dbsize:当前db空间的数据数量
save:人工保存实例化
quit:退出当前cli的连接
exists *:查看key是否存在
ttl *:查看key的存活时间,单位是秒,-1代表永久存在,-2代表已经销毁
randomkey:随机key

redis集成到项目里面(ssm):使用的是jedis客户端maven引用
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.6.0</version>
</dependency>

配置redis连接池package com.mmall.common;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class RedisPool {
//jedis连接池
private static JedisPool pool;
//最大链接数
private static Integer maxTotal=20;
//jedispool中最大的idle状态(空闲的)的jedis实例的个数
private static Integer maxIdle=10;
//在jedispool中最小的idle状态(空闲的)的jedis实例的个数
private static Integer minIdle=3;
private static Boolean testOnBorrow=true;
private static Boolean testOnReturn=true;

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);设置连接池耗尽时候,是否等待,true是等待,false是直接抛出异常,默认是true

//1000*2是超时时间
pool=new JedisPool(config, "111.231.114.12", 6379, 1000*2, "bobo");
}
static{
initPool();
}
/**
* 获得连接池
* @return
*/
public static Jedis getJedis(){
return pool.getResource();
}
/**
* 把链接放回连接池
* @param jedis
*/
public static void returnResoure(Jedis jedis){
pool.returnResource(jedis);
}
/**
* 把损坏的链接放回连接池
* @param jedis
*/
public static void returnBrokenResource(Jedis jedis){
pool.returnBrokenResource(jedis);
}
}重写jedis客服端api生成自己的工具类package com.mmall.util;

import com.mmall.common.RedisPool;

import redis.clients.jedis.Jedis;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class RedisPoolUtil {

/**
* 添加
* @param key
* @param value
* @return
*/
public static String set(String key,String value){
Jedis jedis=null;
String result=null;
try {
jedis=RedisPool.getJedis();
result=jedis.set(key, value);
} catch (Exception e) {
log.error("set key:{} value:{} error",key,value,e);
RedisPool.returnBrokenResource(jedis);
return result;
}
RedisPool.returnResoure(jedis);
return result;

}
/**
* 查询
* @param key
* @param value
* @return
*/
public static String get(String key){
Jedis jedis=null;
String result=null;
try {
jedis=RedisPool.getJedis();
result=jedis.get(key);
} catch (Exception e) {
log.error("get key:{} error",key,e);
RedisPool.returnBrokenResource(jedis);
return result;
}
RedisPool.returnResoure(jedis);
return result;

}

/**
* 添加并且设置有效期
* @param key
* @param value
* @return
*/
public static String setEx(String key,String value,int time){
Jedis jedis=null;
String result=null;
try {
jedis=RedisPool.getJedis();
result=jedis.setex(key, time, value);
} catch (Exception e) {
log.error("set key:{} value:{} error",key,value,e);
RedisPool.returnBrokenResource(jedis);
return result;
}
RedisPool.returnResoure(jedis);
return result;

}

/**
* 根据key重置有效期
* @param key
* @param value
* @return
*/
public static Long exPire(String key,int time){
Jedis jedis=null;
Long result=null;
try {
jedis=RedisPool.getJedis();
result=jedis.expire(key, time);
} catch (Exception e) {
log.error("expire key:{} error",key,e);
RedisPool.returnBrokenResource(jedis);
return result;
}
RedisPool.returnResoure(jedis);
return result;

}

/**
* 通过key删除
* @param key
* @param value
* @return
*/
public static Long del(String key){
Jedis jedis=null;
Long result=null;
try {
jedis=RedisPool.getJedis();
result=jedis.del(key);
} catch (Exception e) {
log.error("del key:{} error",key,e);
RedisPool.returnBrokenResource(jedis);
return result;
}
RedisPool.returnResoure(jedis);
return result;

}

}

jackson序列化

<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.12</version>
</dependency>
重写jackson API生成自己需要的工具类package com.mmall.util;

import java.text.SimpleDateFormat;

import lombok.extern.slf4j.Slf4j;

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig.Feature;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.type.TypeReference;

@Slf4j
public class jsonUtil {
private static ObjectMapper objectMapper=new ObjectMapper();
static{
//对象的所有字段全部列入
objectMapper.setSerializationInclusion(Inclusion.ALWAYS);
//取消日期默认转换timestamps形式
objectMapper.configure(Feature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
//忽略空bean转json的错误,空bean就是没有getset方法
objectMapper.configure(Feature.FAIL_ON_EMPTY_BEANS, false);
//序列化的时候统一日期
objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT));
//在反序列化的时候json对象存在,实体类对象不存在的错误进行忽略
objectMapper.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);

}
/**
* 序列化成String
* @param obj
* @return
*/
public static <T> String objtoString(T obj){
if(obj==null){
return null;
}
try {
return obj instanceof String ?(String) obj :objectMapper.writeValueAsString(obj);
} catch (Exception e) {
log.warn("序列化成String失败",e);
return null;
}
}
/**
* 优雅的序列化成String
* @param obj
* @return
*/
public static <T> String objtoStringPretty(T obj){
if(obj==null){
return null;
}
try {
return obj instanceof String ?(String) obj :objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (Exception e) {
log.warn("序列化成String失败",e);
return null;
}
}

/**
* 把string反序列成对象
* @param str
* @param clazz
* @return
*/
public sta
4000
tic <T> T stringToobj(String str,Class<T> clazz){
if(org.apache.commons.lang.StringUtils.isEmpty(str)||clazz==null){
return null;
}
try {
return clazz.equals(String.class)?(T)str:objectMapper.readValue(str, clazz);
} catch (Exception e) {
log.warn("反序列化失败",e);
return null;
}

}

/**
* 反序列化泛型集合
* @param str
* @param typeReference
* @return
*/
public static <T> T stringToobj(String str,TypeReference<T> typeReference){
if(org.apache.commons.lang.StringUtils.isEmpty(str)||typeReference==null){
return null;
}
try {
return (T)(typeReference.getType().equals(String.class)?str:objectMapper.readValue(str, typeReference));
} catch (Exception e) {
log.warn("反序列化泛型集合失败",e);
return null;
}
}

/**
* 反序列化泛型集合(有选择性的)
* @param str
* @param typeReference
* @return
*/
public static <T> T stringToobj(String str,Class<?> collectionClass,Class<?>... elementClasse){
JavaType javaType=objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasse);
try {
return objectMapper.readValue(str, javaType);
} catch (Exception e) {
log.warn("有选择性反序列化泛型集合失败",e);
return null;
}
}

}

单点登录conding

出现的问题:因为服务器是集群的,所以session是无法共享的,需要解决session的共享问题(用户登录一次就好了);
解决思路:1:在登录的时候给浏览器写入cookie,存入sessionId,并且把用户对象作为value,sessionId作为key存入redis,设置有效期
                 2:在用户正常访问的时候设置拦截器,如果没有cookie和session过期就强制返回到登录页,拦截该请求,如果正常访问,待请求结束后重置redis里面的sessionId的有效期
操作cookie的工具类package com.mmall.util;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Cookie工具类
*
*/
public class CookieUtil {

private CookieUtil() {
}

/**
* 添加cookie
*
* @param response
* @param name
* @param value
* @param maxAge
*/
public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) {
Cookie cookie = new Cookie(name, value);
// cookie.setDomain("localhost");//可以通过这个设置顶级域名,子级域名
cookie.setPath("/");
if (maxAge > 0) {
cookie.setMaxAge(maxAge);
}
response.addCookie(cookie);
}

/**
* 删除cookie
*
* @param response
* @param name
*/
public static void removeCookie(HttpServletResponse response, String name) {
Cookie uid = new Cookie(name, null);
uid.setPath("/");
uid.setMaxAge(0);
response.addCookie(uid);
}

/**
* 获取cookie值
*
* @param request
* @return
*/
public static String getUid(HttpServletRequest request,String cookieName) {
Cookie cookies[] = request.getCookies();
for (Cookie cookie : cookies) {
if (cookie.getName().equals(cookieName)) {
return cookie.getValue();
}
}
return null;
}
}用户登录/**
* 用户登录
* @param username
* @param password
* @param session
* @return
*/
@RequestMapping(value = "login.do",method = RequestMethod.POST)
public String login(String username, String password, HttpSession session,HttpServletResponse httpServletResponse){
ServerResponse<User> response = iUserService.login(username,password);
if(response.getStatus()==1){//出错的
return "index.jsp";
}
/* if(response.isSuccess()){
session.setAttribute(Const.CURRENT_USER,response.getData());
}*/
//存入redis并且写入cookie
RedisPoolUtil.setEx(session.getId(), jsonUtil.objtoString(response.getData()), 60*6);
CookieUtil.addCookie(httpServletResponse, "BOBOSESSION",session.getId(), 60*60*60);

return "loginSuccess.jsp";
}全局拦截器的设置 <mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<mvc:exclude-mapping path="/user/login.do"/>
<bean class="com.mmall.util.InterceptorClass"></bean>
</mvc:interceptor>
</mvc:interceptors>
package com.mmall.util;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class InterceptorClass implements HandlerInterceptor{
//请求结束
@Override
public void afterCompletion(HttpServletRequest arg0,
HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
//请求结束后重置session的有效期
String sessionid=CookieUtil.getUid(arg0, "BOBOSESSION");
RedisPoolUtil.exPire(sessionid, 60*6);

}
//请求后
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2, ModelAndView arg3) throws Exception {
System.out.println("请求都溜了哦,溜了溜了");

}

//请求之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object objec) throws Exception {
System.out.println("请求来了哟");
String sessionid=CookieUtil.getUid(request, "BOBOSESSION");
if(StringUtils.isBlank(sessionid)){
response.sendRedirect("https://localhost:8080/mmall");//重定向
return false;
}
String sessionUser=RedisPoolUtil.get(sessionid);

if(StringUtils.isBlank(sessionUser)){
response.sendRedirect("https://localhost:8080/mmall");//重定向
return false;
}
return true;
}

}
退出登录,清除cookie和session@RequestMapping(value = "logout.do",method = RequestMethod.POST)
@ResponseBody
public ServerResponse<String> logout(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse){
// session.removeAttribute(Const.CURRENT_USER);
String sessionId=CookieUtil.getUid(httpServletRequest, "BOBOSESSION");
if(org.apache.commons.lang.StringUtils.isBlank(sessionId)){
RedisPoolUtil.del("sessionId");
CookieUtil.removeCookie(httpServletResponse, "BOBOSESSION");
}
return ServerResponse.createBySuccess();
}

redis分布式

使用的是jedis操作的redis分布式,jedis默认的是hash一致性算法,可以想象成一个圆,Cache通过hash后节点落在不同的位置,需要存的对象也通过hash存在不同的位置,然后对象顺时针寻找到一个cache存入,删除cache节点后,原先存在cache节点的对象,会重新顺时针寻找一个新的缓存进行存入,因为hash的倾斜性,可能会造成cache节点的分布不均匀(有的数据多,有的数据少),一致性算法会虚拟出很多虚拟节点,这样数据库量越大越平衡
conding:分布式redis的连接池:package com.mmall.common;

import com.google.common.collect.ImmutableList;

import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.util.Hashing;
import redis.clients.util.Sharded;

public class RedisShardePool {
//jedis连接池
private static ShardedJedisPool pool;
//最大链接数
private static Integer maxTotal=20;
//jedispool中最大的idle状态(空闲的)的jedis实例的个数
private static Integer maxIdle=10;
//在jedispool中最小的idle状态(空闲的)的jedis实例的个数a a aa
private static Integer minIdle=3;
private static Boolean testOnBorrow=true;
private static Boolean testOnReturn=true;

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);设置连接池耗尽时候,是否等待,true是等待,false是直接抛出异常,默认是true

//1000*2是超时时间
//172.17.0.2
// pool=new JedisPool(config, "172.17.0.2", 6379, 1000*2, "bobo");
//pool=new JedisPool(config, "111.231.114.12", 6379, 1000*2);
JedisShardInfo jedisShardInfo=new JedisShardInfo("111.231.114.12", 6379, 1000*2);
jedisShardInfo.setPassword("bobo");
JedisShardInfo jedisShardInfo2=new JedisShardInfo("111.231.114.12", 6380, 1000*2);
jedisShardInfo2.setPassword("bobo");
ImmutableList<JedisShardInfo> jedisList=ImmutableList.of(jedisShardInfo,jedisShardInfo2);
//jedis提供二种分片策略,第一种是hash一致性算法,第二种是md5算法,默认是hash算法
//Sharded.DEFAULT_KEY_TAG_PATTERN,储存在默认的服务器当中
pool=new ShardedJedisPool(config, jedisList, Hashing.MURMUR_HASH, Sharded.DEFAULT_KEY_TAG_PATTERN);

}
static{
initPool();
}
/**
* 获得连接池
* @return
*/
public static ShardedJedis getJedis(){
return pool.getResource();
}
/**
* 把链接放回连接池
* @param jedis
*/
public static void returnResoure(ShardedJedis jedis){
pool.returnResource(jedis);
}
/**
* 把损坏的链接放回连接池
* @param jedis
*/
public static void returnBrokenResource(ShardedJedis jedis){
pool.returnBrokenResource(jedis);
}

}分布式redis的操作工具类:package com.mmall.util;

import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.ShardedJedis;

import com.mmall.common.RedisShardePool;

/**
* 分布式redis工具类
* @author bo
*
*/
@Slf4j
public class RedisShardedPoolUtil {

/**
* 添加
* @param key
* @param value
* @return
*/
public static String set(String key,String value){
ShardedJedis jedis=null;
String result=null;
try {
jedis=RedisShardePool.getJedis();
result=jedis.set(key, value);
} catch (Exception e) {
log.error("set key:{} value:{} error",key,value,e);
RedisShardePool.returnBrokenResource(jedis);
return result;
}
RedisShardePool.returnResoure(jedis);
return result;

}
/**
* 查询
* @param key
* @param value
* @return
*/
public static String get(String key){
ShardedJedis jedis=null;
String result=null;
try {
jedis=RedisShardePool.getJedis();
result=jedis.get(key);
} catch (Exception e) {
log.error("get key:{} error",key,e);
RedisShardePool.returnBrokenResource(jedis);
return result;
}
RedisShardePool.returnResoure(jedis);
return result;

}

/**
* 添加并且设置有效期
* @param key
* @param value
* @return
*/
public static String setEx(String key,String value,int time){
ShardedJedis jedis=null;
String result=null;
try {
jedis=RedisShardePool.getJedis();
result=jedis.setex(key, time, value);
} catch (Exception e) {
log.error("set key:{} value:{} error",key,value,e);
RedisShardePool.returnBrokenResource(jedis);
return result;
}
RedisShardePool.returnResoure(jedis);
return result;

}

public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
RedisShardedPoolUtil.set("key"+i,"heihei");
}
System.out.println("结束");
}

/**
* 根据key重置有效期
* @param key
* @param value
* @return
*/
public static Long exPire(String key,int time){
ShardedJedis jedis=null;
Long result=null;
try {
jedis=RedisShardePool.getJedis();
result=jedis.expire(key, time);
} catch (Exception e) {
log.error("expire key:{} error",key,e);
RedisShardePool.returnBrokenResource(jedis);
return result;
}
RedisShardePool.returnResoure(jedis);
return result;

}

/**
* 通过key删除
* @param key
* @param value
* @return
*/
public static Long del(String key){
ShardedJedis jedis=null;
Long result=null;
try {
jedis=RedisShardePool.getJedis();
result=jedis.del(key);
} catch (Exception e) {
log.error("del key:{} error",key,e);
RedisShardePool.returnBrokenResource(jedis);
return result;
}
RedisShardePool.returnResoure(jedis);
return result;

}

}

spring session解决单点登录

优点是:对项目的零切入,方便高效
缺点是:不支持分片的redis,也就是分布式redis
conding要点:
1:设置过滤器把所有请求都交由spring session管理

<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>2:在spring配置文件里面注入bean
redisHttpSessionConfiguration  配置redis key的失效时间
jedisPoolConfig redis的连接池配置
jedisConnectionFactory redis的链接信息
defaultCookieSerializer 配置cookie



springMVC全局异常控制

实现HandlerExceptionResolver类,重写resolveException,然后注入该beanpackage com.hztianque.common.exception;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import com.hztianque.common.util.ResultMsg;

import net.sf.json.JSONObject;

public class ExceptionHandler implements HandlerExceptionResolver{

private final Logger logger = LoggerFactory.getLogger(ExceptionHandler.class);

@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception exception) {
ModelAndView modelAndView = new ModelAndView();
if(exception instanceof AuthorizationException){
modelAndView.setViewName("redirect:/login.html");
}else{
logger.error("错误信息:",exception);
try {
response.setContentType("application/json;charset=utf-8");
java.io.PrintWriter out = response.getWriter();
String errorMsg = getErrorMsg(exception);
ResultMsg msg = new ResultMsg(Boolean.FALSE, errorMsg, null);
out.write(JSONObject.fromObject(msg).toString());
out.println();
out.flush();
out.close();
} catch (IOException e) {
logger.error("异常处理出问题啦!!!!",e);
}
}
return modelAndView;
}

private String getErrorMsg(Exception e){
String msg = "系统异常,请联系管理员!";
if(e instanceof BusinessException){
msg = e.getMessage();
}
return msg;
}

}

springMVC _RESTful 架构

restFul是一种接口设计风格,请求路径传值





$(".MathJax").remove();
<
94f5
/script>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: