用户点赞一条资讯,该资讯的点赞数量增加,点踩则踩数量增加,本项目的点赞点踩功能是通过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,把数据先写入内存,再定时的更新到数据库,而且对于用户来讲一两秒的数据变化延迟不会有很大的影响。