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

目录

 

redis安装:

常用启动,关闭命令:

reids常用命令:

使用的是jedis客户端

jackson序列化

单点登录conding

redis分布式

spring session解决单点登录

springMVC全局异常控制

springMVC _RESTful 架构


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配置文件添加./reut (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

使用的是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 static <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

  • 出现的问题:
  1. 因为服务器是集群的,所以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节点的分布不均匀(有的数据多,有的数据少),一致性算法会虚拟出很多虚拟节点,这样数据库量越大越平衡

分布式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>
  1. 在spring配置文件里面注入bean
  • redisHttpSessionConfiguration  配置redis key的失效时间
  • jedisPoolConfig redis的连接池配置
  • jedisConnectionFactory redis的链接信息
  • defaultCookieSerializer 配置cookie

springMVC全局异常控制

实现HandlerExceptionResolver类,重写resolveException,然后注入该bean
package 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是一种接口设计风格,请求路径传值

猜你喜欢

转载自blog.csdn.net/qq_35035078/article/details/82720863