mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2026-03-10 00:13:40 +08:00
🎨 #3840 小程序和公众号的多租户starter添加多租户共享模式以优化资源使用
This commit is contained in:
@@ -4,18 +4,25 @@ import com.binarywang.spring.starter.wxjava.mp.properties.WxMpMultiProperties;
|
||||
import com.binarywang.spring.starter.wxjava.mp.properties.WxMpSingleProperties;
|
||||
import com.binarywang.spring.starter.wxjava.mp.service.WxMpMultiServices;
|
||||
import com.binarywang.spring.starter.wxjava.mp.service.WxMpMultiServicesImpl;
|
||||
import com.binarywang.spring.starter.wxjava.mp.service.WxMpMultiServicesSharedImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.mp.api.WxMpService;
|
||||
import me.chanjar.weixin.mp.api.impl.*;
|
||||
import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl;
|
||||
import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpComponentsImpl;
|
||||
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
|
||||
import me.chanjar.weixin.mp.api.impl.WxMpServiceJoddHttpImpl;
|
||||
import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl;
|
||||
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
|
||||
import me.chanjar.weixin.mp.config.WxMpHostConfig;
|
||||
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -34,6 +41,7 @@ public abstract class AbstractWxMpConfiguration {
|
||||
log.warn("微信公众号应用参数未配置,通过 WxMpMultiServices#getWxMpService(\"tenantId\")获取实例将返回空");
|
||||
return new WxMpMultiServicesImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 appId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。
|
||||
*
|
||||
@@ -50,9 +58,26 @@ public abstract class AbstractWxMpConfiguration {
|
||||
throw new RuntimeException("请确保微信公众号配置 appId 的唯一性");
|
||||
}
|
||||
}
|
||||
WxMpMultiServicesImpl services = new WxMpMultiServicesImpl();
|
||||
|
||||
// 根据配置选择多租户模式
|
||||
WxMpMultiProperties.MultiTenantMode mode = wxMpMultiProperties.getConfigStorage().getMultiTenantMode();
|
||||
if (mode == WxMpMultiProperties.MultiTenantMode.SHARED) {
|
||||
return createSharedMultiServices(appsMap, wxMpMultiProperties);
|
||||
} else {
|
||||
return createIsolatedMultiServices(appsMap, wxMpMultiProperties);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建隔离模式的多租户服务(每个租户独立 WxMpService 实例)
|
||||
*/
|
||||
private WxMpMultiServices createIsolatedMultiServices(
|
||||
Map<String, WxMpSingleProperties> appsMap,
|
||||
WxMpMultiProperties wxMpMultiProperties) {
|
||||
|
||||
WxMpMultiServicesImpl services = new WxMpMultiServicesImpl();
|
||||
Set<Map.Entry<String, WxMpSingleProperties>> entries = appsMap.entrySet();
|
||||
|
||||
for (Map.Entry<String, WxMpSingleProperties> entry : entries) {
|
||||
String tenantId = entry.getKey();
|
||||
WxMpSingleProperties wxMpSingleProperties = entry.getValue();
|
||||
@@ -63,9 +88,78 @@ public abstract class AbstractWxMpConfiguration {
|
||||
WxMpService wxMpService = this.wxMpService(storage, wxMpMultiProperties);
|
||||
services.addWxMpService(tenantId, wxMpService);
|
||||
}
|
||||
|
||||
log.info("微信公众号多租户服务初始化完成,使用隔离模式(ISOLATED),共配置 {} 个租户", appsMap.size());
|
||||
return services;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建共享模式的多租户服务(单个 WxMpService 实例管理多个配置)
|
||||
*/
|
||||
private WxMpMultiServices createSharedMultiServices(
|
||||
Map<String, WxMpSingleProperties> appsMap,
|
||||
WxMpMultiProperties wxMpMultiProperties) {
|
||||
|
||||
// 创建共享的 WxMpService 实例
|
||||
WxMpMultiProperties.ConfigStorage storage = wxMpMultiProperties.getConfigStorage();
|
||||
WxMpService sharedService = createWxMpServiceByType(storage.getHttpClientType());
|
||||
configureWxMpService(sharedService, storage);
|
||||
|
||||
// 准备所有租户的配置,使用 TreeMap 保证顺序一致性
|
||||
Map<String, WxMpConfigStorage> configsMap = new HashMap<>();
|
||||
String defaultTenantId = new TreeMap<>(appsMap).firstKey();
|
||||
|
||||
for (Map.Entry<String, WxMpSingleProperties> entry : appsMap.entrySet()) {
|
||||
String tenantId = entry.getKey();
|
||||
WxMpSingleProperties wxMpSingleProperties = entry.getValue();
|
||||
WxMpDefaultConfigImpl config = this.wxMpConfigStorage(wxMpMultiProperties);
|
||||
this.configApp(config, wxMpSingleProperties);
|
||||
this.configHttp(config, storage);
|
||||
this.configHost(config, wxMpMultiProperties.getHosts());
|
||||
configsMap.put(tenantId, config);
|
||||
}
|
||||
|
||||
// 设置多配置到共享的 WxMpService
|
||||
sharedService.setMultiConfigStorages(configsMap, defaultTenantId);
|
||||
|
||||
log.info("微信公众号多租户服务初始化完成,使用共享模式(SHARED),共配置 {} 个租户,共享一个 HTTP 客户端", appsMap.size());
|
||||
return new WxMpMultiServicesSharedImpl(sharedService);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据类型创建 WxMpService 实例
|
||||
*/
|
||||
private WxMpService createWxMpServiceByType(WxMpMultiProperties.HttpClientType httpClientType) {
|
||||
switch (httpClientType) {
|
||||
case OK_HTTP:
|
||||
return new WxMpServiceOkHttpImpl();
|
||||
case JODD_HTTP:
|
||||
return new WxMpServiceJoddHttpImpl();
|
||||
case HTTP_CLIENT:
|
||||
return new WxMpServiceHttpClientImpl();
|
||||
case HTTP_COMPONENTS:
|
||||
return new WxMpServiceHttpComponentsImpl();
|
||||
default:
|
||||
return new WxMpServiceImpl();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置 WxMpService 的通用参数
|
||||
*/
|
||||
private void configureWxMpService(WxMpService wxMpService, WxMpMultiProperties.ConfigStorage storage) {
|
||||
int maxRetryTimes = storage.getMaxRetryTimes();
|
||||
if (maxRetryTimes < 0) {
|
||||
maxRetryTimes = 0;
|
||||
}
|
||||
int retrySleepMillis = storage.getRetrySleepMillis();
|
||||
if (retrySleepMillis < 0) {
|
||||
retrySleepMillis = 1000;
|
||||
}
|
||||
wxMpService.setRetrySleepMillis(retrySleepMillis);
|
||||
wxMpService.setMaxRetryTimes(maxRetryTimes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置 WxMpDefaultConfigImpl
|
||||
*
|
||||
@@ -76,37 +170,9 @@ public abstract class AbstractWxMpConfiguration {
|
||||
|
||||
public WxMpService wxMpService(WxMpConfigStorage configStorage, WxMpMultiProperties wxMpMultiProperties) {
|
||||
WxMpMultiProperties.ConfigStorage storage = wxMpMultiProperties.getConfigStorage();
|
||||
WxMpMultiProperties.HttpClientType httpClientType = storage.getHttpClientType();
|
||||
WxMpService wxMpService;
|
||||
switch (httpClientType) {
|
||||
case OK_HTTP:
|
||||
wxMpService = new WxMpServiceOkHttpImpl();
|
||||
break;
|
||||
case JODD_HTTP:
|
||||
wxMpService = new WxMpServiceJoddHttpImpl();
|
||||
break;
|
||||
case HTTP_CLIENT:
|
||||
wxMpService = new WxMpServiceHttpClientImpl();
|
||||
break;
|
||||
case HTTP_COMPONENTS:
|
||||
wxMpService = new WxMpServiceHttpComponentsImpl();
|
||||
break;
|
||||
default:
|
||||
wxMpService = new WxMpServiceImpl();
|
||||
break;
|
||||
}
|
||||
|
||||
WxMpService wxMpService = createWxMpServiceByType(storage.getHttpClientType());
|
||||
wxMpService.setWxMpConfigStorage(configStorage);
|
||||
int maxRetryTimes = storage.getMaxRetryTimes();
|
||||
if (maxRetryTimes < 0) {
|
||||
maxRetryTimes = 0;
|
||||
}
|
||||
int retrySleepMillis = storage.getRetrySleepMillis();
|
||||
if (retrySleepMillis < 0) {
|
||||
retrySleepMillis = 1000;
|
||||
}
|
||||
wxMpService.setRetrySleepMillis(retrySleepMillis);
|
||||
wxMpService.setMaxRetryTimes(maxRetryTimes);
|
||||
configureWxMpService(wxMpService, storage);
|
||||
return wxMpService;
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,15 @@ public class WxMpMultiProperties implements Serializable {
|
||||
* </pre>
|
||||
*/
|
||||
private int retrySleepMillis = 1000;
|
||||
|
||||
/**
|
||||
* 多租户实现模式.
|
||||
* <ul>
|
||||
* <li>ISOLATED: 为每个租户创建独立的 WxMpService 实例(默认)</li>
|
||||
* <li>SHARED: 使用单个 WxMpService 实例管理所有租户配置,共享 HTTP 客户端</li>
|
||||
* </ul>
|
||||
*/
|
||||
private MultiTenantMode multiTenantMode = MultiTenantMode.ISOLATED;
|
||||
}
|
||||
|
||||
public enum StorageType {
|
||||
@@ -155,4 +164,19 @@ public class WxMpMultiProperties implements Serializable {
|
||||
*/
|
||||
JODD_HTTP
|
||||
}
|
||||
|
||||
public enum MultiTenantMode {
|
||||
/**
|
||||
* 隔离模式:为每个租户创建独立的 WxMpService 实例.
|
||||
* 优点:线程安全,不依赖 ThreadLocal
|
||||
* 缺点:每个租户创建独立的 HTTP 客户端,资源占用较多
|
||||
*/
|
||||
ISOLATED,
|
||||
/**
|
||||
* 共享模式:使用单个 WxMpService 实例管理所有租户配置.
|
||||
* 优点:共享 HTTP 客户端,节省资源
|
||||
* 缺点:依赖 ThreadLocal 切换配置,异步场景需注意
|
||||
*/
|
||||
SHARED
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.binarywang.spring.starter.wxjava.mp.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.chanjar.weixin.mp.api.WxMpService;
|
||||
|
||||
/**
|
||||
* 微信公众号 {@link WxMpMultiServices} 共享式实现.
|
||||
* <p>
|
||||
* 使用单个 WxMpService 实例管理多个租户配置,通过 switchover 切换租户。
|
||||
* 相比 {@link WxMpMultiServicesImpl},此实现共享 HTTP 客户端,节省资源。
|
||||
* </p>
|
||||
* <p>
|
||||
* 注意:由于使用 ThreadLocal 切换配置,在异步或多线程场景需要特别注意线程上下文切换。
|
||||
* </p>
|
||||
*
|
||||
* @author Binary Wang
|
||||
* created on 2026/1/9
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class WxMpMultiServicesSharedImpl implements WxMpMultiServices {
|
||||
private final WxMpService sharedWxMpService;
|
||||
|
||||
@Override
|
||||
public WxMpService getWxMpService(String tenantId) {
|
||||
if (tenantId == null) {
|
||||
return null;
|
||||
}
|
||||
// 使用 switchover 检查配置是否存在,保持与隔离模式 API 行为一致(不存在时返回 null)
|
||||
if (!sharedWxMpService.switchover(tenantId)) {
|
||||
return null;
|
||||
}
|
||||
return sharedWxMpService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWxMpService(String tenantId) {
|
||||
if (tenantId != null) {
|
||||
sharedWxMpService.removeConfigStorage(tenantId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加租户配置到共享的 WxMpService 实例
|
||||
*
|
||||
* @param tenantId 租户 ID
|
||||
* @param wxMpService 要添加配置的 WxMpService(仅使用其配置,不使用其实例)
|
||||
*/
|
||||
public void addWxMpService(String tenantId, WxMpService wxMpService) {
|
||||
if (tenantId != null && wxMpService != null) {
|
||||
sharedWxMpService.addConfigStorage(tenantId, wxMpService.getWxMpConfigStorage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user