一、引言
开发过程中一般使用mq进行功能解耦,但是当主业务上开启许多分支功能,不可能为每一个分支功能开启各自的mq,因此使用Spring的ApplicationEventPublisher进行发布监听。
同时大多数情况下分支功能与主业务需要解耦,因此发布的事件需要异步处理。
二、工具
1、线程池
作者使用的拒绝策略是主线程执行,一般对于线程池容量都会配置的高于平时的qps,极其少的情况下流量突发才会使用主线程。
@Configuration
public class ThreadPoolConfig {
@Resource
private MeterRegistry meterRegistry;
private String commonThreadName = "common-executor-service";
/**
* 公共线程池
*
* @return
*/
@Bean(name = "commonExecutorService")
public ExecutorService getCommonExecutorService() {
return getUseMainThreadExecutorService(getCommonThreadPoolProperties(), new BaseThreadFactory(ThreadPoolEnum.COMMON), commonThreadName);
}
/**
* 公共线程池参数
*
* @return
*/
@Bean
@ConfigurationProperties(prefix = "common.thread-pool")
public ThreadPoolProperties getCommonThreadPoolProperties() {
return new ThreadPoolProperties();
}
//当队列与线程池塞满之后使用主线程执行
private ExecutorService getUseMainThreadExecutorService(ThreadPoolProperties poolProperties, ThreadFactory factory, String threadName) {
ThreadPoolExecutor executorService = new ThreadPoolExecutor(poolProperties.getCorePoolSize(), poolProperties.getMaxPoolSize(),
poolProperties.getKeepAliveSeconds(), TimeUnit.SECONDS, new ArrayBlockingQueue<>(poolProperties.getMaxQueueSize()),
factory, new ThreadPoolExecutor.CallerRunsPolicy());
ExecutorServiceMetrics.monitor(meterRegistry, executorService, threadName);
return executorService;
}
}
2、事件
一个普通的实体类就可以作为事件,发布这个实体类事件,所有的监听者都会收到并进行处理。
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ShopOnlineDTO extends BaseEntity {
private Long shopId;
private Integer yearTotalOnlineDay;
}
三、使用
1、发布者
通过ApplicationEventPublisher将事件发布出去。
@Resource
private ApplicationEventPublisher publisher;
public void shopOnline(ShopDTO shopDTO) {
//门店业务处理
*******
*******
//计算门店在线天数
publisher.publishEvent(ShopOnlineDTO
.builder()
.shopId(billReportDayDTO.getShopId())
.build());
}
2、监听者
通过@EventListener表明这是一个监听者,spring会将它加载到监听者中,并通过监听实体进行匹配。
通过@Async("commonExecutorService")表明这是异步处理,并且需要注入到线程池中执行。
由于需要控制并发,需要加分布式锁,这里使用的是作者自己封装的分布式代理锁,感兴趣的同学可以看一下作者的另外一篇博客,对于作者封装的分布式代理锁有详细解读。
@Async("commonExecutorService")
@EventListener
public void shopMath(ShopOnlineDTO shopOnlineDTO) {
//在aop开启了自动清除
LockUtil.set(shopReportDayDTO.getShopId().toString());
operateShopOnlineMath.shopMath(shopReportDayDTO);
}
3、业务处理
@RedisLock(key = RedisConsts.REPORT_SHOP_DAY_MATH_LOCK, atuoRemove = true)
public void shopMath(ShopReportDayDTO shopReportDayDTO) {
//取当年生成的在线单据
LocalDate firstDayOfYear = LocalDate.now().with(TemporalAdjusters.firstDayOfYear());
List<ShopOnlineBill> shopOnlineBillList = shopOnlineBillService.getShopYearBill(shopReportDayDTO.getShopId(), firstDayOfYear);
int day = shopOnlineBillList
.stream()
.mapToInt(ShopOnlineBill::getActualOnlineDay)
.sum();
if (day == shopReportDayDTO.getYearTotalReportDay()) {
return;
}
ShopDetail update = new ShopDetail();
update.setId(shopReportDayDTO.getId());
update.setYearTotalOnlineDay(day);
log.info("更新门店在线天数:{}",update);
ShopDetailService.update(update);
}
四、原理分析
进入publishEvent方法准备事件发布,获取事件类型,将包装好的事件与对应类型传入applicationEventMulticaster
protected void publishEvent(Object event, ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<Object>(this, event);
if (eventType == null) {
获取事件类型
eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
这里准备进行发布事件
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
multicastEvent方法获取匹配事件类型的监听者,遍历匹配的监听者并唤醒,下面先看一下getApplicationListeners如何匹配监听者
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});
}
else {
invokeListener(listener, event);
}
}
}
getApplicationListeners根据事件类型和类包装缓存键,找到事件类型对应的缓存工厂,返回工厂中的监听者
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// 找到事件类型对应的监听工厂
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
//返回工厂中的监听者
return retriever.getApplicationListeners();
}
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// Fully synchronized building and caching of a ListenerRetriever
synchronized (this.retrievalMutex) {
retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true);
Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
else {
// No ListenerRetriever caching -> no synchronization necessary
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
invokeListener唤醒监听者其实是进入onApplicationEvent方法
protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
listener.onApplicationEvent(event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || msg.startsWith(event.getClass().getName())) {
Log logger = LogFactory.getLog(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
}
doInvoke根据beanName从applicationContext获取bean,进入增强方法
protected Object doInvoke(Object... args) {
根据beanName从applicationContext获取bean
Object bean = getTargetBean();
ReflectionUtils.makeAccessible(this.bridgedMethod);
try {
进入cglib的增强方法
return this.bridgedMethod.invoke(bean, args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(this.bridgedMethod, bean, args);
throw new IllegalStateException(getInvocationErrorMessage(bean, ex.getMessage(), args), ex);
}
catch (IllegalAccessException ex) {
throw new IllegalStateException(getInvocationErrorMessage(bean, ex.getMessage(), args), ex);
}
catch (InvocationTargetException ex) {
// Throw underlying exception
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else {
String msg = getInvocationErrorMessage(bean, "Failed to invoke event listener method", args);
throw new UndeclaredThrowableException(targetException, msg);
}
}
}
intercept获取拦截器链,此处有@async方法定义的异步线程,通过cglib生成拦截方法
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// 生成cglib增强方法
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
invokeJoinpoint进入代理类
public Object proceed() throws Throwable {
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
return proceed();
}
}
else {
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
protected Object invokeJoinpoint() throws Throwable {
if (this.publicMethod) {
通过反射进入代理类
return this.methodProxy.invoke(this.target, this.arguments);
}
else {
return super.invokeJoinpoint();
}
}
代理类通过反射进入监听者执行监听逻辑
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
} catch (IllegalArgumentException var5) {
if (this.fastClassInfo.i1 < 0) {
throw new IllegalArgumentException("Protected method: " + this.sig1);
} else {
throw var5;
}
}
}
五、总结
Spring的发布监听机制封装的比较完善,使用也较为简单,还有EventBus等工具也可以实现自定义的发布监听,用哪个看个人习惯吧。