牛客网中级项目学习笔记-点赞点踩功能实现

Redis在用户赞踩功能中的应用

用户点赞一条资讯,该资讯的点赞数量增加,点踩则踩数量增加,本项目的点赞点踩功能是通过redis数据库实现的,java操作redis数据库的工具类可用Jedis库。
1.需要在pom.xml中引入jedis的jar包:


			redis.clients
			jedis
			2.8.0

2.需要设计一个操作redis的业务类,Jedis对象的获取是通过jedis连接池JedisPool,把Jedis操作redis的方法写在业务层:

@Service
public class JedisAdapter implements InitializingBean {
	/*InitializingBean接口为bean提供了初始化方法的方式,
	它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。*/

	private JedisPool pool=null;
	
    private static final Logger logger = LoggerFactory.getLogger(JedisAdapter.class);


	@Override
	public void afterPropertiesSet() throws Exception {//在类初始化时执行此方法
		// TODO Auto-generated method stub
		pool=new JedisPool("localhost",6379);//创建Jedis连接池对象
	}
	
	public Jedis getJedis() {
		return pool.getResource();
	}
	
	public void set(String key,String value) {
		Jedis jedis=null;
		try {
			jedis=pool.getResource();
			jedis.set(key, value);
		}catch(Exception e) {
			logger.error("redis异常:"+e.getMessage());
		}finally {
			if(jedis!=null) jedis.close();
		}
	}
	
	public String get(String key) {
		Jedis jedis=null;
		try {
			jedis=pool.getResource();
			return jedis.get(key);
		}catch(Exception e) {
			logger.error("redis异常:"+e.getMessage());
			return null;
		}finally {
			if(jedis!=null) jedis.close();
		}
	}
	
	public long sadd(String set,String value) {
		Jedis jedis=null;
		try {
			jedis=pool.getResource();
			return jedis.sadd(set, value);
		}catch(Exception e) {
			logger.error("redis异常:"+e.getMessage());
			return 0;
		}finally {
			if(jedis!=null) jedis.close();
		}				
	}
	
	public long srem(String set,String value) {
		Jedis jedis=null;
		try {
			jedis=pool.getResource();
			return jedis.srem(set, value);
		}catch(Exception e) {
			logger.error("redis异常:"+e.getMessage());
			return 0;
		}finally {
			if(jedis!=null) jedis.close();
		}
	}
	
	public boolean sismember(String set,String value) {
		Jedis jedis=null;
		try {
			jedis=pool.getResource();
			return jedis.sismember(set, value);
		}catch(Exception e) {
			logger.error("redis异常:"+e.getMessage());
			return false;
		}finally {
			if(jedis!=null) jedis.close();
		}
	}
	
	public long scard(String set) {
		Jedis jedis=null;
		try {
			jedis=pool.getResource();
			return jedis.scard(set);
		}catch(Exception e) {
			logger.error("redis异常:"+e.getMessage());
			return 0;
		}finally {
			if(jedis!=null) jedis.close();
		}
	}
	
	public long lpush(String key,String value) {
		Jedis jedis=null;
		try {
			jedis=pool.getResource();
			return jedis.lpush(key, value);//返队列长度
		}catch(Exception e) {
			logger.error("redis异常:"+e.getMessage());
			return 0;
		}finally {
			if(jedis!=null) jedis.close();
		}
	}
	
	public List brpop(int timeout,String key){//时间延迟
		Jedis jedis=null;
		try {
			jedis=pool.getResource();
			return jedis.brpop(timeout,key);
		}catch(Exception e) {
			logger.error("redis异常:"+e.getMessage());
			return null;
		}finally {
			if(jedis!=null) jedis.close();
		}
	}	
}

3.设计赞踩业务层,对一条资讯的赞和踩通过redis的set集合实现,为每一条资讯创建一个set集合,set集合内存储点赞/点踩的用户数,用户点赞则把用户加入赞集合,点踩则把用户加入踩集合,通过判断用户在哪个集合来传递信息到前端应该高亮赞/踩状态。

@Service
public class LikeService {
    @Autowired
	private JedisAdapter jedisAdapter;
    
    public int getLikeStatus(int userId,int entityType,int entityId) {
    	//如果该用户喜欢这条资讯,返回1,不喜欢返-1,没表示返0
    	//用于标示前端用户喜欢则高亮点赞,不喜欢高亮踩,或者没表示
    	String likeSta=RedisKeyUtil.getLikeKey(entityId, entityType);
    	if(jedisAdapter.sismember(likeSta, String.valueOf(userId))) return 1;
    	
    	String disLikeSta=RedisKeyUtil.getDisLikeKey(entityId, entityType);
    	return jedisAdapter.sismember(disLikeSta, String.valueOf(userId))? -1:0;
    }
    
    public long like(int userId,int entityType,int entityId) {
    	//用户点赞某条资讯,先将用户加入该资讯点赞集合,并从踩集合中移除
    	String likeSta=RedisKeyUtil.getLikeKey(entityId, entityType);
    	jedisAdapter.sadd(likeSta, String.valueOf(userId));
    	
    	String disLikeSta=RedisKeyUtil.getDisLikeKey(entityId, entityType);
    	jedisAdapter.srem(disLikeSta, String .valueOf(userId));
    	
    	return jedisAdapter.scard(likeSta);//最后返回点赞集合后数量
    }
    
    public long disLike(int userId,int entityType,int entityId) {
    	//用户踩某条资讯,反过来
    	String disLikeSta=RedisKeyUtil.getDisLikeKey(entityId, entityType);
    	jedisAdapter.sadd(disLikeSta, String .valueOf(userId));
    	
    	String likeSta=RedisKeyUtil.getLikeKey(entityId, entityType);
    	jedisAdapter.srem(likeSta, String.valueOf(userId));
    	
    	return jedisAdapter.scard(likeSta);//最后返回点赞集合后数量
    }
}

4.Controller在获取用户赞/踩请求后,调用LikeService的方法把用户赞/踩信息加入redis数据库。

@Controller
public class LikeController {
	@Autowired
	private HostHolder holder;
	@Autowired
	private LikeService likeService;
	@Autowired
	private NewsService newsService;
	
	@RequestMapping(value="/like",method = {RequestMethod.GET, RequestMethod.POST})
	@ResponseBody
	public String like(@Param("newsId") int newsId) {
		int userId=holder.getUser().getId();
		long likeCount=likeService.like(userId, EntityType.ENTITY_NEWS, newsId);
		newsService.updateLikeCount(newsId,(int)likeCount);//更新点赞数
		return ToutiaoUtil.getJSONString(0, String.valueOf(likeCount));
	}
	
	@RequestMapping(value="/dislike",method = {RequestMethod.GET, RequestMethod.POST})
	@ResponseBody
	public String disLike(@Param("newsId") int newsId) {
		int userId=holder.getUser().getId();
		long likeCount=likeService.disLike(userId, EntityType.ENTITY_NEWS, newsId);
		newsService.updateLikeCount(newsId,(int)likeCount);
		return ToutiaoUtil.getJSONString(0, String.valueOf(likeCount));
	}
}

顺带一提redis的应用场景:
PV //讨论区里的浏览数在Redis中存储;
点赞 //点赞,userId放在Redis中;
关注 //人与人之间关注,关注者是个集合,放进Redis,如果取消关注,从集合中删除即可;
排行榜 //登陆数,登陆一次,数值加1;
验证码 //验证码,用到set timeout时间,后台生成验证码,放进Redis,设置过期时间,比如三分钟还没收到用户验证码,自动从Redis删除验证码;
缓存 //牛客网上访问,打开网页用户头像,用户名昵称,如果用户不更新,用户信息等在MySQL上可以做一层缓存,如果用户没更新信息,可以直接从缓存读,如果缓存没有,直接去数据库取,大大降低数据库的压力;
异步队列 //点了赞,谁评论了帖子,发生事件,放进redis,后面有线程去执行;
判题队列 //用户提交判题,放入判题队列,在Redis中,服务器连判题队列执行,如果碰到高峰期,可以加机器。

在高并发的情况下,比如秒杀,很多用户进行操作,我们要记录商品数,浏览量,评论数,如果每一次请求后都去数据库中update字段,服务器很容易卡死的。这时我们可以用redis,把数据先写入内存,再定时的更新到数据库,而且对于用户来讲一两秒的数据变化延迟不会有很大的影响。

你可能感兴趣的:(后端开发)