发布订阅者模式

本文章书写的场景是,学习到用户发布动态,关注着可以接收到用户发布的动态
使用的是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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值