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 );
}
}