本文章书写的场景是,学习到用户发布动态,关注着可以接收到用户发布的动态
使用的是springboot框架
中间件有 redis和rocketmq
pom文件
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
用户动态
/**
* 用户动态
*
* @author gcq
* @Create 2022-05-29
*/
@Data
@TableName(value = "t_user_moments")
public class UserMoment {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField(value = "userId")
private Long userId;
private String type;
@TableField(value = "contentId")
private Long contentId;
@TableField(value = "createTime")
private Date createTime;
@TableField(value = "updateTime")
private Date updateTime;
}
spring工具类
/**
* spring工具类 方便在非spring管理环境中获取bean
*
* @author ruoyi
*/
@Component
public final class SpringUtils implements BeanFactoryPostProcessor,
ApplicationContextAware
{
/** Spring应用上下文环境 */
private static ConfigurableListableBeanFactory beanFactory;
private static ApplicationContext applicationContext;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException
{
SpringUtils.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException
{
SpringUtils.applicationContext = applicationContext;
}
/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws BeansException
*
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException
{
return (T) beanFactory.getBean(name);
}
/**
* 获取类型为requiredType的对象
*
* @param clz
* @return
* @throws BeansException
*
*/
public static <T> T getBean(Class<T> clz) throws BeansException
{
T result = (T) beanFactory.getBean(clz);
return result;
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
*
* @param name
* @return boolean
*/
public static boolean containsBean(String name)
{
return beanFactory.containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
*
* @param name
* @return boolean
* @throws NoSuchBeanDefinitionException
*
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.isSingleton(name);
}
/**
* @param name
* @return Class 注册对象的类型
* @throws NoSuchBeanDefinitionException
*
*/
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
*
* @param name
* @return
* @throws NoSuchBeanDefinitionException
*
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getAliases(name);
}
/**
* 获取aop代理对象
*
* @param invoker
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAopProxy(T invoker)
{
return (T) AopContext.currentProxy();
}
/**
* 获取当前的环境配置,无配置返回null
*
* @return 当前的环境配置
*/
public static String[] getActiveProfiles()
{
return applicationContext.getEnvironment().getActiveProfiles();
}
/**
* 获取当前的环境配置,当有多个环境配置时,只获取第一个
*
* @return 当前的环境配置
*/
public static String getActiveProfile()
{
final String[] activeProfiles = getActiveProfiles();
return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
}
}
RocketMQ 配置
/**
* RocketMQ 配置
*
* @author gcq
* @Create 2022-05-28
*/
//@Configuration
public class RocketMQConfig {
private final Logger logger = LoggerFactory.getLogger(RocketMQConfig.class);
@Value("${rocketmq.name.server.address}")
private String nameServerAddress;
@Autowired
private RedisUtils redisUtils;
@Autowired
private UserFollowingService userFollowingService;
/**
* 用户动态生产者
*
* @return
*/
@Bean("momentsProducer")
public DefaultMQProducer momentsProducer() throws MQClientException {
DefaultMQProducer mqProducer =
new DefaultMQProducer(UserMomentsConstant.GROUP_MOMENTS);
mqProducer.setNamesrvAddr(nameServerAddress);
mqProducer.start();
return mqProducer;
}
/**
* 用户动他消费者
*
* 消费用户动态呢容
* 相关业务场景:
* 电商:
* 购买后,库存扣减等等
*
* @return
* @throws MQClientException
*/
@Bean("momentsConsumer")
public DefaultMQPushConsumer momentConsumer() throws MQClientException {
System.out.println("开始消费消息");
DefaultMQPushConsumer consumer =
new DefaultMQPushConsumer(UserMomentsConstant.GROUP_MOMENTS);
consumer.setNamesrvAddr(nameServerAddress);
// 订阅生产者 二级主题 主题所有的内容
consumer.subscribe(UserMomentsConstant.TOPIC_MOMENTS, "*");
// 监听器,处理内容
consumer.registerMessageListener(new MessageListenerConcurrently() {
/**
* 处理MQ信息
*
* @param messageExtList 消息扩充
* @param consumeConcurrentlyContext 消费内容上下文
* @return 状态
*/
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> messageExtList,
ConsumeConcurrentlyContext consumeConcurrentlyContext) {
MessageExt message = messageExtList.get(0);
if(message == null) {
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
String bodyStr = new String(message.getBody());
UserMoment userMoment = JsonUtils.jsonToPojo(bodyStr, UserMoment.class);
Long userId = userMoment.getUserId();
// 获取用户粉丝,得到哪些用户订阅了当前的用户
// 更新用户动态内容
// 把用户发送的内容放到redis中,通过reids去查询
List<UserFollowing> userFanList = userFollowingService.getUserFans(userId);
for (UserFollowing fan : userFanList) {
String key = "subscribed-" + fan.getUserId();
String subscribedListStr = redisUtils.get(key);
List<UserMoment> subscribedList;
if(StringUtils.isEmpty(subscribedListStr)) {
subscribedList = new ArrayList<>();
} else {
subscribedList =
JsonUtils.jsonToList(subscribedListStr, UserMoment.class);
}
// 添加当前动态
subscribedList.add(userMoment);
redisUtils.set(key, JsonUtils.objectToJson(subscribedList));
}
for (MessageExt messageExt : messageExtList) {
System.out.println("消息" + messageExt);
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
return consumer;
}
}
RocketMQ 工具类
/**
* RocketMQ 工具类
*
* @author gcq
* @Create 2022-05-29
*/
public class RocketMQUtils {
/**
* 同步发送消息
*
* @param producer 提供者
* @param message 消息
* @throws MQBrokerException
* @throws RemotingException
* @throws InterruptedException
* @throws MQClientException
*/
public static void syncSendMsg(DefaultMQProducer producer, Message message)
throws MQBrokerException, RemotingException, InterruptedException, MQClientException {
SendResult result = producer.send(message);
System.out.println(result);
}
// 异步发送
/**
* 异步发送
*
* @param producer 生产者
* @param message 消息
* @throws RemotingException
* @throws InterruptedException
* @throws MQClientException
*/
public static void asyncSendMsg(DefaultMQProducer producer, Message message)
throws RemotingException,
InterruptedException, MQClientException {
int messageCount = 2;
CountDownLatch2 countDownLatch2 = new CountDownLatch2(messageCount);
for (int i = 0; i < messageCount; i++) {
producer.send(message, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
countDownLatch2.countDown();
System.out.println(sendResult.getMsgId());
}
@Override
public void onException(Throwable e) {
countDownLatch2.countDown();
System.out.println("发送消息发生了异常!" + e);
e.printStackTrace();
}
});
}
countDownLatch2.await(5, TimeUnit.SECONDS);
}
}
用户动态Service
/**
* 用户动态Service
*
* @author gcq
* @Create 2022-05-29
*/
public interface UserMomentsService extends IService<UserMoment> {
/**
* 新建用户动态
*
* @param userMoment 用户动态内容
* @return 结果
*/
Integer addUserMoments(UserMoment userMoment) throws MQBrokerException,
RemotingException, InterruptedException,
MQClientException, UnsupportedEncodingException;
/**
* 查询订阅状态
*
* @param userId
* @return
*/
List<UserMoment> getUserSubscribedMoments(Long userId);
}
用户动态ServiceImpl
/**
* 用户动态ServiceImpl
*
* @author gcq
* @Create 2022-05-29
*/
@Service
public class UserMomentsServiceImpl extends ServiceImpl<UserMomentsMapper, UserMoment>
implements UserMomentsService {
@Autowired
private UserMomentsMapper userMomentsMapper;
@Autowired
private RedisUtils redisUtils;
@Override
public Integer addUserMoments(UserMoment userMoment)
throws MQBrokerException, RemotingException, InterruptedException, MQClientException,
UnsupportedEncodingException {
userMoment.setCreateTime(new Date());
userMomentsMapper.insert(userMoment);
// 将动态发送到MQ中
DefaultMQProducer producer = SpringUtils.getBean("momentsProducer");
Message message = new Message(UserMomentsConstant.TOPIC_MOMENTS,
JsonUtils.objectToJson(userMoment).getBytes(CommonConstant.UTF_8));
RocketMQUtils.syncSendMsg(producer, message);
return CommonConstant.SAVE_SUCCESS;
}
@Override
public List<UserMoment> getUserSubscribedMoments(Long userId) {
String key = "subscribed-" + userId;
String listStr = redisUtils.get(key);
if(StringUtils.isEmpty(listStr)) {
return Collections.emptyList();
}
return JsonUtils.jsonToList(listStr, UserMoment.class);
}
}
用户动态Controller
/**
* 用户动态Controller
*
* @author gcq
* @Create 2022-05-29
*/
@RestController
public class UserMomentsApi {
@Autowired
private UserMomentsService userMomentsService;
@Autowired
private UserSupport userSupport;
@ApiLimitedRole(limitedRoleCodeList = {AuthRoleConstant.ROLE_LV0})
@PostMapping("user-moments")
public JsonResponse<String> addUserMoments(@RequestBody UserMoment userMoment)
throws MQBrokerException, RemotingException, InterruptedException,
MQClientException, UnsupportedEncodingException {
Long userId = userSupport.getCurrentUserId();
userMoment.setUserId(userId);
userMomentsService.addUserMoments(userMoment);
return JsonResponse.success();
}
@GetMapping("/user-subscribed-moments")
public JsonResponse<List<UserMoment>> getUserSubscribedMoments() {
Long userId = userSupport.getCurrentUserId();
List<UserMoment> userMomentList =
userMomentsService.getUserSubscribedMoments(userId);
return new JsonResponse(userMomentList);
}
}