观察者模式(监听器模式)+线程池 实现异步发送消息

1.使用场景

大家都知道MQ有三个使用场景:1.解耦,2.同步变异步,3.消峰。在很多简单的场景,我们没有必要使用MQ来实现异步,我们可以通过spring的注解@Async或者通过多线程来解决。我在开发中遇到了这样一个使用场景:我们需要在满足条件的情况下,要同时往很多个用户小程序和公众号推送消息,由于推送消息的处理,是非常耗时的,做成同步明显是不合理的。我们需要异步的处理发消息。当然,我们可以使用MQ来实现这一场景。但我发现我项目中的场景,使用观察者模式也是能实现的。我们知道,在观察者模式也有所谓的发布者和订阅者,但我们传统的观察者模式其实是同步的,还是会阻塞,只要我们稍加改时,可以实现异步观察。

2.定义被观察者

这里的被观察者有两个:一个是用来向公众号发消息,另一个是用来向小程序发消息。由于在java.util下面提供了接口和方法来实现观察者模式,我们可以直接使用。

/**
 * 发消息给小程序主题
 *
 * @author caoyong
 * @version 1.0.0
 * @since 2019-01-25 10:41
 **/
@Component
@Getter
@Setter
public class SendMiniProgramSubject extends Observable {
    private String userId;

    /**
     * 通知发送
     */
    public void send() {
        this.setChanged();
        this.notifyObservers();
    }
}

/**
 * 发消息给公众号主题
 *
 * @author caoyong
 * @version 1.0.0
 * @since 2019-01-25 10:40
 **/
@Getter
@Setter
@Component
public class SendPublicAccountSubject extends Observable {
    private String userId;
    /**
     * 通知发送
     */
    public void send() {
        this.setChanged();
        this.notifyObservers();
    }
}

3.定义观察者

观察者在处理比较多的任务的时候,为了提高处理效率,我们可以使用线程池。

/**
 * 订阅观察者
 *
 * @author caoyong
 * @version 1.0.0
 * @since 2019-01-25 10:34
 **/
@Slf4j
@Component
//标志为多例
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class SubscriberObserver implements Observer {
    private ExecutorService executorService = Executors.newFixedThreadPool(5);
    @Autowired
    private WechatMessageService wechatMessageService;

    @Override
    public void update(Observable o, Object arg) {
        //用多线程去发消息
        executorService.execute(() -> {
            //处理小程序发送消息
            if (o instanceof SendMiniProgramSubject) {
                SendMiniProgramSubject miniProgramSubject = (SendMiniProgramSubject) o;
                log.info("send miniProgram message end.");
            }
            //处理公众号发送消息
            if (o instanceof SendPublicAccountSubject) {
                SendPublicAccountSubject publicAccountSubject = (SendPublicAccountSubject) o;
                wechatMessageService.sendCustomerdPublicAccount(publicAccountSubject.getUserId());
                log.info("send publicAccount message end.");
            }
        });
    }
}

4.定义消息发送器

在这里发送消息的时候,我们可以使用Spring提供的注解@Async来轻松实现异步。

/**
 * 消息发送器
 *
 * @author caoyong
 * @version 1.0.0
 * @since 2019-01-25 15:08
 **/
@Component
public class MessageSender {
    @Autowired
    private SendMiniProgramSubject sendMiniProgramSubject;
    @Autowired
    private SendPublicAccountSubject sendPublicAccountSubject;
    @Autowired
    private SubscriberObserver subscriberObserver;

    /**
     * 发送给公众号
     *
     * @param userId 用户id
     */
    @Async
    public void sendPublicAccount(String userId) {
        sendPublicAccountSubject.setUserId(userId);
        sendPublicAccountSubject.addObserver(subscriberObserver);
        sendPublicAccountSubject.send();
    }

    /**
     * 发送给小程序
     *
     * @param userId 用户id
     */
    @Async
    public void sendMiniProgram(String userId) {
        sendMiniProgramSubject.setUserId(userId);
        sendMiniProgramSubject.addObserver(subscriberObserver);
        sendMiniProgramSubject.send();
    }
}

要实现异步,如果你使用的是Spring Boot,需要在启动类添加:@EnableAsync

@SpringBootApplication
@EnableCaching
@EnableAsync
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

5.测试发送消息

通过SpringJUnit工具来测试我们的发消息是否成功,查看日志输出位置,可以看出是否实现异步。

/**
 * @author caoyong
 * @version 1.0.0
 * @since 2019-01-25 16:21
 **/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class TestObserver {
    @Autowired
    private MessageSender messageSender;

    @Test
    public void testSendMessage() {
        String userId = "testUserId";
        messageSender.sendPublicAccount(userId );
    }
}
  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值