From ce75ca6d1b2b996906d128718e2226cd6919c00c Mon Sep 17 00:00:00 2001 From: 007 <007gzs@gmail.com> Date: Sat, 4 Apr 2020 18:40:20 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=B8=89=E6=96=B9=E5=B9=B3=E5=8F=B0?= =?UTF-8?q?=E6=94=AF=E6=8C=81redis=E5=88=86=E5=B8=83=E5=BC=8F=E9=94=81?= =?UTF-8?q?=EF=BC=9BgetComponentAccessToken=20=E5=8A=A0=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 6 ++ weixin-java-common/pom.xml | 9 +++ .../util/locks/JedisDistributedLock.java | 71 +++++++++++++++++++ .../weixin/open/api/WxOpenConfigStorage.java | 6 ++ .../AbstractWxOpenInRedisConfigStorage.java | 9 ++- .../api/impl/WxOpenComponentServiceImpl.java | 19 +++-- .../api/impl/WxOpenInMemoryConfigStorage.java | 27 ++++++- .../api/impl/WxOpenInRedisConfigStorage.java | 8 +++ 8 files changed, 146 insertions(+), 9 deletions(-) create mode 100644 weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/JedisDistributedLock.java diff --git a/pom.xml b/pom.xml index 032bbee045..7f53d38a38 100644 --- a/pom.xml +++ b/pom.xml @@ -243,6 +243,12 @@ 2.9.0 provided + + com.github.jedis-lock + jedis-lock + 1.0.0 + provided + org.redisson redisson diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index 5d17c487a9..f6acd49870 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -118,6 +118,15 @@ dom4j 2.1.1 + + redis.clients + jedis + + + + com.github.jedis-lock + jedis-lock + diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/JedisDistributedLock.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/JedisDistributedLock.java new file mode 100644 index 0000000000..636e7e245f --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/JedisDistributedLock.java @@ -0,0 +1,71 @@ +package me.chanjar.weixin.common.util.locks; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import redis.clients.jedis.Jedis; +import redis.clients.util.Pool; + +/** + * JedisPool 分布式锁 + * + * @author 007 + */ +public class JedisDistributedLock implements Lock { + private final Pool jedisPool; + private final JedisLock lock; + + public JedisDistributedLock(Pool jedisPool, String key){ + this.jedisPool = jedisPool; + this.lock = new JedisLock(key); + } + + @Override + public void lock() { + try (Jedis jedis = jedisPool.getResource()) { + if (!lock.acquire(jedis)) { + throw new RuntimeException("acquire timeouted"); + } + } catch (InterruptedException e) { + throw new RuntimeException("lock failed", e); + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + try (Jedis jedis = jedisPool.getResource()) { + if (!lock.acquire(jedis)) { + throw new RuntimeException("acquire timeouted"); + } + } + } + + @Override + public boolean tryLock() { + try (Jedis jedis = jedisPool.getResource()) { + return lock.acquire(jedis); + } catch (InterruptedException e) { + throw new RuntimeException("lock failed", e); + } + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + try (Jedis jedis = jedisPool.getResource()) { + return lock.acquire(jedis); + } + } + + @Override + public void unlock() { + try (Jedis jedis = jedisPool.getResource()) { + lock.release(jedis); + } + } + + @Override + public Condition newCondition() { + throw new RuntimeException("unsupported method"); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java index 79969228ad..5a51c3bfa1 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java @@ -6,6 +6,8 @@ import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken; import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken; +import java.util.concurrent.locks.Lock; + /** * @author 007 */ @@ -53,6 +55,10 @@ public interface WxOpenConfigStorage { WxMaConfig getWxMaConfig(String appId); + Lock getComponentAccessTokenLock(); + + Lock getLockByKey(String key); + /** * 应该是线程安全的 * diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/AbstractWxOpenInRedisConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/AbstractWxOpenInRedisConfigStorage.java index 0d0b1bf255..52799da57c 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/AbstractWxOpenInRedisConfigStorage.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/AbstractWxOpenInRedisConfigStorage.java @@ -13,6 +13,9 @@ public abstract class AbstractWxOpenInRedisConfigStorage extends WxOpenInMemoryC protected final static String AUTHORIZER_REFRESH_TOKEN_KEY = "wechat_authorizer_refresh_token:"; protected final static String AUTHORIZER_ACCESS_TOKEN_KEY = "wechat_authorizer_access_token:"; + + protected final static String LOCK_KEY = "wechat_lock:"; + protected final static String JSAPI_TICKET_KEY = "wechat_jsapi_ticket:"; protected final static String CARD_API_TICKET_KEY = "wechat_card_api_ticket:"; @@ -26,6 +29,7 @@ public abstract class AbstractWxOpenInRedisConfigStorage extends WxOpenInMemoryC protected String authorizerAccessTokenKey; protected String jsapiTicketKey; protected String cardApiTicket; + protected String lockKey; @Override public void setComponentAppId(String componentAppId) { @@ -36,8 +40,9 @@ public void setComponentAppId(String componentAppId) { componentAccessTokenKey = prefix + COMPONENT_ACCESS_TOKEN_KEY.concat(componentAppId); authorizerRefreshTokenKey = prefix + AUTHORIZER_REFRESH_TOKEN_KEY.concat(componentAppId); authorizerAccessTokenKey = prefix + AUTHORIZER_ACCESS_TOKEN_KEY.concat(componentAppId); - this.jsapiTicketKey = JSAPI_TICKET_KEY.concat(componentAppId); - this.cardApiTicket = CARD_API_TICKET_KEY.concat(componentAppId); + lockKey = prefix + LOCK_KEY.concat(componentAppId); + jsapiTicketKey = prefix + JSAPI_TICKET_KEY.concat(componentAppId); + cardApiTicket = prefix + CARD_API_TICKET_KEY.concat(componentAppId); } protected String getKey(String prefix, String appId) { diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java index 27e765698b..bcc23360dd 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Map; +import java.util.concurrent.locks.Lock; import java.util.concurrent.ConcurrentHashMap; /** @@ -109,8 +110,16 @@ public boolean checkSignature(String timestamp, String nonce, String signature) @Override public String getComponentAccessToken(boolean forceRefresh) throws WxErrorException { - - if (this.getWxOpenConfigStorage().isComponentAccessTokenExpired() || forceRefresh) { + final WxOpenConfigStorage config = this.getWxOpenConfigStorage(); + if (!config.isComponentAccessTokenExpired() && !forceRefresh) { + return config.getComponentAccessToken(); + } + Lock lock = config.getComponentAccessTokenLock(); + lock.lock(); + try { + if (!config.isComponentAccessTokenExpired() && !forceRefresh) { + return config.getComponentAccessToken(); + } JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); jsonObject.addProperty("component_appsecret", getWxOpenConfigStorage().getComponentAppSecret()); @@ -118,9 +127,11 @@ public String getComponentAccessToken(boolean forceRefresh) throws WxErrorExcept String responseContent = this.getWxOpenService().post(API_COMPONENT_TOKEN_URL, jsonObject.toString()); WxOpenComponentAccessToken componentAccessToken = WxOpenComponentAccessToken.fromJson(responseContent); - getWxOpenConfigStorage().updateComponentAccessToken(componentAccessToken); + config.updateComponentAccessToken(componentAccessToken); + return config.getComponentAccessToken(); + } finally { + lock.unlock(); } - return this.getWxOpenConfigStorage().getComponentAccessToken(); } @Override diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java index 33678ea8ab..966db0612a 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java @@ -44,6 +44,9 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage { private Map authorizerAccessTokens = new ConcurrentHashMap<>(); private Map jsapiTickets = new ConcurrentHashMap<>(); private Map cardApiTickets = new ConcurrentHashMap<>(); + private Map locks = new ConcurrentHashMap<>(); + + private Lock componentAccessTokenLock = getLockByKey("componentAccessTokenLock"); @Override @@ -61,6 +64,21 @@ public void updateComponentAccessToken(WxOpenComponentAccessToken componentAcces updateComponentAccessToken(componentAccessToken.getComponentAccessToken(), componentAccessToken.getExpiresIn()); } + @Override + public Lock getLockByKey(String key){ + Lock lock = locks.get(key); + if (lock == null) { + synchronized (WxOpenInMemoryConfigStorage.class){ + lock = locks.get(key); + if (lock == null) { + lock = new ReentrantLock(); + locks.put(key, lock); + } + } + } + return lock; + } + @Override public WxMpConfigStorage getWxMpConfigStorage(String appId) { return new WxOpenInnerConfigStorage(this, appId); @@ -211,13 +229,16 @@ private static class WxOpenInnerConfigStorage implements WxMpConfigStorage, WxMa * 云环境ID */ private volatile String cloudEnv; - private Lock accessTokenLock = new ReentrantLock(); - private Lock jsapiTicketLock = new ReentrantLock(); - private Lock cardApiTicketLock = new ReentrantLock(); + private final Lock accessTokenLock; + private final Lock jsapiTicketLock; + private final Lock cardApiTicketLock; private WxOpenInnerConfigStorage(WxOpenConfigStorage wxOpenConfigStorage, String appId) { this.wxOpenConfigStorage = wxOpenConfigStorage; this.appId = appId; + this.accessTokenLock = wxOpenConfigStorage.getLockByKey(appId + ":accessTokenLock"); + this.jsapiTicketLock = wxOpenConfigStorage.getLockByKey(appId + ":jsapiTicketLock"); + this.cardApiTicketLock = wxOpenConfigStorage.getLockByKey(appId + ":cardApiTicketLock"); } @Override diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java index f1b903d472..a681e2931e 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java @@ -1,9 +1,12 @@ package me.chanjar.weixin.open.api.impl; +import me.chanjar.weixin.common.util.locks.JedisDistributedLock; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.util.Pool; +import java.util.concurrent.locks.Lock; + /** * @author 007 */ @@ -163,4 +166,9 @@ public void updateCardApiTicket(String appId, String cardApiTicket, int expiresI jedis.setex(this.getKey(this.cardApiTicket, appId), expiresInSeconds - 200, cardApiTicket); } } + + @Override + public Lock getLockByKey(String key) { + return new JedisDistributedLock(jedisPool, getKey(lockKey, key)); + } } From eb7a74aeb9298dd66273dc3122c4d7f532e5149d Mon Sep 17 00:00:00 2001 From: 007 <007gzs@gmail.com> Date: Sat, 4 Apr 2020 18:53:17 +0800 Subject: [PATCH 2/2] =?UTF-8?q?getAuthorizerAccessToken=20=E5=8A=A0?= =?UTF-8?q?=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/impl/WxOpenComponentServiceImpl.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java index bcc23360dd..4f1fc90069 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java @@ -357,8 +357,16 @@ public void setAuthorizerOption(String authorizerAppid, String optionName, Strin @Override public String getAuthorizerAccessToken(String appId, boolean forceRefresh) throws WxErrorException { - - if (this.getWxOpenConfigStorage().isAuthorizerAccessTokenExpired(appId) || forceRefresh) { + WxOpenConfigStorage config = getWxOpenConfigStorage(); + if (!config.isAuthorizerAccessTokenExpired(appId) && !forceRefresh){ + return config.getAuthorizerAccessToken(appId); + } + Lock lock = config.getWxMpConfigStorage(appId).getAccessTokenLock(); + lock.lock(); + try{ + if (!config.isAuthorizerAccessTokenExpired(appId) && !forceRefresh){ + return config.getAuthorizerAccessToken(appId); + } JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); jsonObject.addProperty("authorizer_appid", appId); @@ -366,9 +374,11 @@ public String getAuthorizerAccessToken(String appId, boolean forceRefresh) throw String responseContent = post(API_AUTHORIZER_TOKEN_URL, jsonObject.toString()); WxOpenAuthorizerAccessToken wxOpenAuthorizerAccessToken = WxOpenAuthorizerAccessToken.fromJson(responseContent); - getWxOpenConfigStorage().updateAuthorizerAccessToken(appId, wxOpenAuthorizerAccessToken); + config.updateAuthorizerAccessToken(appId, wxOpenAuthorizerAccessToken); + return config.getAuthorizerAccessToken(appId); + }finally { + lock.unlock(); } - return this.getWxOpenConfigStorage().getAuthorizerAccessToken(appId); } @Override