提交 bce7aabb 编写于 作者: 街头小贩's avatar 街头小贩

增加MemberChangeEvent,减少CommonControllerAdvice.getSessionBean查询压力

上级 4b70cc60
......@@ -46,6 +46,12 @@ public class RedisMemberEventConfig {
mq.setQueueName("member:VipExchangeEvent");
return mq;
}
@Bean("changeEventQueue")
public RedisMessageQueue changeEventQueue(){
RedisMessageQueue mq = new com.github.davidmarquis.redisq.RedisMessageQueue();
mq.setQueueName("member:ChangeEvent");
return mq;
}
// loop Producer
@Bean("signUpEventProducer")
public MessageProducer<MemberSignUpEvent> signUpEventProducer(@Qualifier("signUpEventQueue") RedisMessageQueue signUpEventQueue){
......@@ -77,6 +83,12 @@ public class RedisMemberEventConfig {
dmq.setQueue(vipExchangeEventQueue);
return dmq;
}
@Bean("changeEventProducer")
public MessageProducer<MemberChangeEvent> changeEventProducer(@Qualifier("changeEventQueue") RedisMessageQueue changeEventQueue){
DefaultMessageProducer<MemberChangeEvent> dmq = new com.github.davidmarquis.redisq.producer.DefaultMessageProducer<>();
dmq.setQueue(changeEventQueue);
return dmq;
}
//loop Consumer
@Bean
public MessageConsumer<MemberSignUpEvent> promoteRoleConsumer(
......
......@@ -5,6 +5,7 @@ import com.apobates.forum.member.entity.Member;
import com.apobates.forum.member.entity.MemberGroupEnum;
import com.apobates.forum.member.entity.MemberRoleEnum;
import com.apobates.forum.member.entity.MemberStatusEnum;
import com.apobates.forum.member.impl.event.MemberChangeEvent;
import com.apobates.forum.utils.Commons;
import java.math.BigDecimal;
import java.time.LocalDateTime;
......@@ -13,8 +14,11 @@ import java.util.Map.Entry;
import java.util.stream.Stream;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import com.github.davidmarquis.redisq.producer.MessageProducer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
......@@ -32,6 +36,8 @@ import org.springframework.transaction.annotation.Transactional;
public class MemberDaoImpl implements MemberDao {
@PersistenceContext
private EntityManager entityManager;
@Autowired @Qualifier("changeEventProducer")
private MessageProducer<MemberChangeEvent> changeEventProducer;
private final static Logger logger = LoggerFactory.getLogger(MemberDaoImpl.class);
@Cacheable(key="#memberId", unless="#result==null")
......@@ -50,6 +56,9 @@ public class MemberDaoImpl implements MemberDao {
public boolean editMemberRole(long memberId, MemberRoleEnum role) {
int affect = entityManager.createQuery("UPDATE Member m SET m.mrole = ?1 WHERE m.id = ?2 AND m.mrole != ?3").setParameter(1, role).setParameter(2, memberId).setParameter(3, role).executeUpdate();
boolean complete = (affect == 1);
if(complete) {
changeEventProducer.create(new MemberChangeEvent(memberId, role)).submit();
}
return complete;
}
......@@ -59,6 +68,9 @@ public class MemberDaoImpl implements MemberDao {
public boolean editMemberGroup(long memberId, MemberGroupEnum group) {
int affect = entityManager.createQuery("UPDATE Member m SET m.mgroup = ?1 WHERE m.id = ?2 AND m.mgroup != ?3").setParameter(1, group).setParameter(2, memberId).setParameter(3, group).executeUpdate();
boolean complete = (affect == 1);
if(complete) {
changeEventProducer.create(new MemberChangeEvent(memberId, group)).submit();
}
return complete;
}
......@@ -68,6 +80,9 @@ public class MemberDaoImpl implements MemberDao {
public boolean editMemberStatus(long memberId, MemberStatusEnum status) {
int affect = entityManager.createQuery("UPDATE Member m SET m.status = ?1 WHERE m.id = ?2 AND m.status != ?3").setParameter(1, status).setParameter(2, memberId).setParameter(3, status).executeUpdate();
boolean complete = (affect == 1);
if(complete) {
changeEventProducer.create(new MemberChangeEvent(memberId, status)).submit();
}
return complete;
}
......
package com.apobates.forum.member.impl.event;
import com.apobates.forum.member.entity.MemberGroupEnum;
import com.apobates.forum.member.entity.MemberRoleEnum;
import com.apobates.forum.member.entity.MemberStatusEnum;
import com.apobates.forum.utils.lang.EnumArchitecture;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
* 会员变化事件
*
* @author xiaofanku
* @since 20200927
*/
public class MemberChangeEvent {
private final long memberId;
private final String key;
private final int value;
private final static Map<String,String> MAP_STRUCT = Map.ofEntries(Map.entry("MemberStatusEnum", "status"), Map.entry("MemberGroupEnum", "group"), Map.entry("MemberRoleEnum", "role"));
public MemberChangeEvent(long memberId, MemberStatusEnum status) {
this(memberId, status, MemberStatusEnum.class);
}
public MemberChangeEvent(long memberId, MemberRoleEnum role) {
this(memberId, role, MemberRoleEnum.class);
}
public MemberChangeEvent(long memberId, MemberGroupEnum group) {
this(memberId, group, MemberGroupEnum.class);
}
private MemberChangeEvent(long memberId, EnumArchitecture archite, Class<?> cls){
Optional<String> k = getMapKey(cls);
if(!k.isPresent()){
throw new IllegalStateException("不支持的Key");
}
if(cls.isAssignableFrom(EnumArchitecture.class)){
throw new IllegalStateException("参数不是合法值");
}
this.memberId = memberId;
this.key = k.get();
this.value = archite.getSymbol();
}
public long getMemberId() {
return memberId;
}
public String getKey() {
return key;
}
public int getValue() {
return value;
}
/**
* 变化事件允许的映射Key
*
* @return
*/
public static Set<String> allowKey(){
return Set.copyOf(MAP_STRUCT.values());
}
/**
* 根据类查询映射Key
*
* @param <T> EnumArchitecture的实现枚举类型
* @param cls EnumArchitecture的实现枚举.class
* @return
*/
public static <T> Optional<String> getMapKey(Class<T> cls){
String cCN = cls.getSimpleName();
if(MAP_STRUCT.containsKey(cCN)){
return Optional.ofNullable(MAP_STRUCT.get(cCN));
}
return Optional.empty();
}
/**
*
* @param <T> EnumArchitecture的实现枚举类型
* @param cls EnumArchitecture的实现枚举.class
* @param mapping 映射的值集合
* @return
*/
public static <T> Optional<T> get(Class<T> cls, Map<String,Integer> mapping){
Optional<String> mapKey = getMapKey(cls);
if(mapKey.isPresent() && mapping.containsKey(mapKey.get())){
return EnumArchitecture.getInstance(mapping.get(mapKey.get()), cls);
}
return Optional.empty();
}
@Override
public String toString() {
return "MemberChangeEvent{" + "memberId=" + memberId + ", key=" + key + ", value=" + value + '}';
}
}
package com.apobates.forum.thrones;
import com.apobates.forum.core.impl.event.*;
import com.apobates.forum.member.impl.event.MemberPenalizeEvent;
import com.apobates.forum.member.impl.event.MemberSignInEvent;
import com.apobates.forum.member.impl.event.MemberSignUpEvent;
import com.apobates.forum.member.impl.event.MemberVipExchangeEvent;
import com.apobates.forum.member.impl.event.*;
import com.apobates.forum.thrones.event.*;
import com.github.davidmarquis.redisq.RedisMessageQueue;
import com.github.davidmarquis.redisq.consumer.MessageConsumer;
......@@ -129,4 +126,15 @@ public class ThronesRedisEventConfig {
messageConsumer.setMessageListener(bornNotice);
return messageConsumer;
}
@Bean
public MessageConsumer<MemberChangeEvent> changeEventConsumer(
@Qualifier("changeEventQueue") RedisMessageQueue changeEventQueue,
@Qualifier("asyncInfoCache") MemberChangeEventListener asyncInfoCache){
MessageConsumer<MemberChangeEvent> messageConsumer = new com.github.davidmarquis.redisq.consumer.MessageConsumer<>();
messageConsumer.setQueue(changeEventQueue);
messageConsumer.setConsumerId("MemberChangeEvent:asyncInfo");
messageConsumer.setMessageListener(asyncInfoCache);
//messageConsumer.setThreadingStrategy(new com.github.davidmarquis.redisq.consumer.MultiThreadingStrategy(4));
return messageConsumer;
}
}
......@@ -2,11 +2,14 @@ package com.apobates.forum.thrones.controller.helper;
import com.apobates.forum.core.security.exception.StrategyException;
import com.apobates.forum.core.security.exception.VerificaFailException;
import com.apobates.forum.member.entity.Member;
import com.apobates.forum.member.service.MemberService;
import com.apobates.forum.member.entity.MemberGroupEnum;
import com.apobates.forum.member.entity.MemberRoleEnum;
import com.apobates.forum.member.entity.MemberStatusEnum;
import com.apobates.forum.member.impl.event.MemberChangeEvent;
import com.apobates.forum.member.storage.OnlineMemberStorage;
import com.apobates.forum.member.storage.core.MemberSessionBean;
import com.apobates.forum.member.storage.core.MemberSessionBeanBuilder;
import com.apobates.forum.thrones.event.MemberChangeEventListener;
import com.apobates.forum.thrones.exception.BorbidMemberRegisterException;
import com.apobates.forum.thrones.exception.ForumValidateException;
import com.apobates.forum.thrones.exception.MemberFreezeException;
......@@ -17,7 +20,10 @@ import com.apobates.forum.utils.TipMessage;
import com.apobates.forum.utils.TipMessageLevelEnum;
import com.apobates.forum.utils.lang.ReplicableException;
import java.io.IOException;
import java.io.PrintWriter;import java.util.Optional;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
......@@ -25,6 +31,8 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
......@@ -50,7 +58,7 @@ public class CommonControllerAdvice {
@Autowired
private OnlineMemberStorage onlineMemberStorage;
@Autowired
private MemberService memberService;
private RedisTemplate<String, Object> template;
@Value("${site.domain}")
private String siteDomain;
private final static Logger logger = LoggerFactory.getLogger(CommonControllerAdvice.class);
......@@ -63,18 +71,39 @@ public class CommonControllerAdvice {
// Cookie
MemberSessionBean mbean = onlineMemberStorage.getInstance(request, "CommonControllerAdvice").orElse(MemberSessionBeanBuilder.empty().build(Commons.getRequestIp(request), "CommonControllerAdvice"));
if (mbean.getMid()>0) {
//总要查一下看一看有没变化
Optional<Member> data = memberService.get(mbean.getMid());
if (data.isPresent()) {
Member obj = data.get();
if(obj.getMrole()!=mbean.getRole() || obj.getMgroup()!=mbean.getGroup() || obj.getStatus()!=mbean.getStatus()){
mbean = mbean.refact(obj.getMgroup(), obj.getMrole(), obj.getStatus()); //为什么要更新一下角色,组,状态?这三者随时可能发生变化@20200502
onlineMemberStorage.refresh(request, response, obj.getStatus(), obj.getMgroup(), obj.getMrole());
}
Optional<MemberSessionBean> data = refactMemberSessionBean(mbean);
data.ifPresent(newMSB -> {
onlineMemberStorage.refresh(request, response, newMSB.getStatus(), newMSB.getGroup(), newMSB.getRole());
});
if(data.isPresent()){
mbean = data.get();
}
}
return mbean;
}
private Optional<MemberSessionBean> refactMemberSessionBean(MemberSessionBean oldBean) {
Map<String, Integer> cachedata = getEventPushDate(oldBean.getMid());
if (!cachedata.isEmpty()) {
Optional<MemberGroupEnum> newGroup = MemberChangeEvent.get(MemberGroupEnum.class, cachedata);
Optional<MemberStatusEnum> newStatus = MemberChangeEvent.get(MemberStatusEnum.class, cachedata);
Optional<MemberRoleEnum> newRole = MemberChangeEvent.get(MemberRoleEnum.class, cachedata);
//
MemberSessionBean newBean = oldBean.refact(newGroup.orElse(oldBean.getGroup()), newRole.orElse(oldBean.getRole()), newStatus.orElse(oldBean.getStatus()));
return Optional.of(newBean);
}
return Optional.empty();
}
private Map<String,Integer> getEventPushDate(long memberId){
String redisKey = MemberChangeEventListener.getEventRedisKey(memberId);
HashOperations<String, String, Integer> operation = template.opsForHash();
Map<String, Integer> cachedata = operation.entries(redisKey);
if (null != cachedata && !cachedata.isEmpty()) { //总有一个变了
template.delete(redisKey);
return cachedata;
}
return Collections.emptyMap();
}
//https://stackoverflow.com/questions/24498662/howto-handle-404-exceptions-globally-using-spring-mvc-configured-using-java-base
//https://stackoverflow.com/questions/2066946/trigger-404-in-spring-mvc-controller
@ExceptionHandler(value={ResourceNotFoundException.class, NoHandlerFoundException.class, ReplicableException.class})
......
package com.apobates.forum.thrones.event;
import com.apobates.forum.member.impl.event.MemberChangeEvent;
import com.github.davidmarquis.redisq.Message;
import com.github.davidmarquis.redisq.consumer.MessageListener;
import com.github.davidmarquis.redisq.consumer.retry.RetryableMessageException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component("asyncInfoCache")
public class MemberChangeEventListener implements MessageListener<MemberChangeEvent> {
@Autowired
private RedisTemplate<String, Object> template;
private final static Logger logger = LoggerFactory.getLogger(MemberChangeEventListener.class);
@Override
public void onMessage(Message<MemberChangeEvent> message) throws RetryableMessageException {
logger.info("[Member][ChangeEvent][1]会员变更侦听器处理开始");
MemberChangeEvent event = message.getPayload();
Map<String, Integer> gm = Map.ofEntries(Map.entry(event.getKey(), event.getValue()));
template.opsForHash().putAll(getEventRedisKey(event.getMemberId()), gm);
logger.info("[Member][ChangeEvent][1]会员变更侦听器结束处理");
}
public static String getEventRedisKey(long memberId){
return "memberEvent:"+memberId;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册