diff --git a/yami-shop-common/src/main/java/com/yami/shop/common/annotation/RedisLock.java b/yami-shop-common/src/main/java/com/yami/shop/common/annotation/RedisLock.java index 3eccfec..9eca6ef 100644 --- a/yami-shop-common/src/main/java/com/yami/shop/common/annotation/RedisLock.java +++ b/yami-shop-common/src/main/java/com/yami/shop/common/annotation/RedisLock.java @@ -45,5 +45,5 @@ public @interface RedisLock { * * @return 秒 */ - TimeUnit timeUnit() default TimeUnit.SECONDS; + TimeUnit timeUnit() default TimeUnit.MILLISECONDS; } diff --git a/yami-shop-mp/src/main/java/com/yami/shop/mp/component/WxMaServiceClusterImpl.java b/yami-shop-mp/src/main/java/com/yami/shop/mp/component/WxMaServiceClusterImpl.java new file mode 100644 index 0000000..954ef3a --- /dev/null +++ b/yami-shop-mp/src/main/java/com/yami/shop/mp/component/WxMaServiceClusterImpl.java @@ -0,0 +1,89 @@ +package com.yami.shop.mp.component; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import com.yami.shop.common.exception.YamiShopBindException; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.BasicResponseHandler; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +/** + * WxMaServiceImpl 在集群模式获取accessToken的方式 + * @author LGH + */ +public class WxMaServiceClusterImpl extends WxMaServiceImpl { + + + private static final String REDISSON_LOCK_PREFIX = "redisson_lock:"; + + private RedissonClient redissonClient; + + public void setRedissonClient(RedissonClient redissonClient) { + this.redissonClient = redissonClient; + } + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + if (!this.getWxMaConfig().isAccessTokenExpired() && !forceRefresh) { + return this.getWxMaConfig().getAccessToken(); + } + + RLock rLock = redissonClient.getLock(REDISSON_LOCK_PREFIX + ":WxMaServiceCluster:getAccessToken"); + + boolean doingUpdateAccessToken; + + try { + doingUpdateAccessToken = rLock.tryLock(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + return this.getWxMaConfig().getAccessToken(); + } + + if (!doingUpdateAccessToken) { + throw new YamiShopBindException("服务器繁忙,请稍后再试"); + } + + if (this.getWxMaConfig().isAccessTokenExpired()) { + return this.getWxMaConfig().getAccessToken(); + } + + try { + String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(), + this.getWxMaConfig().getSecret()); + try { + HttpGet httpGet = new HttpGet(url); + if (this.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build(); + httpGet.setConfig(config); + } + try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) { + String resultContent = new BasicResponseHandler().handleResponse(response); + WxError error = WxError.fromJson(resultContent); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + this.getWxMaConfig().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + + return this.getWxMaConfig().getAccessToken(); + } finally { + httpGet.releaseConnection(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } finally { + rLock.unlock(); + } + + } +} diff --git a/yami-shop-mp/src/main/java/com/yami/shop/mp/component/WxMpServiceClusterImpl.java b/yami-shop-mp/src/main/java/com/yami/shop/mp/component/WxMpServiceClusterImpl.java new file mode 100644 index 0000000..3e48fc4 --- /dev/null +++ b/yami-shop-mp/src/main/java/com/yami/shop/mp/component/WxMpServiceClusterImpl.java @@ -0,0 +1,90 @@ +package com.yami.shop.mp.component; + +import com.yami.shop.common.exception.YamiShopBindException; +import me.chanjar.weixin.common.WxType; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.BasicResponseHandler; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +/** + * WxMpServiceImpl 在集群模式获取accessToken的方式 + * @author LGH + */ +public class WxMpServiceClusterImpl extends WxMpServiceImpl { + + + private static final String REDISSON_LOCK_PREFIX = "redisson_lock:"; + + private RedissonClient redissonClient; + + public void setRedissonClient(RedissonClient redissonClient) { + this.redissonClient = redissonClient; + } + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + if (!this.getWxMpConfigStorage().isAccessTokenExpired() && !forceRefresh) { + return this.getWxMpConfigStorage().getAccessToken(); + } + + RLock rLock = redissonClient.getLock(REDISSON_LOCK_PREFIX + ":WxMpServiceCluster:getAccessToken"); + + boolean doingUpdateAccessToken; + + try { + doingUpdateAccessToken = rLock.tryLock(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + return this.getWxMpConfigStorage().getAccessToken(); + } + + if (!doingUpdateAccessToken) { + throw new YamiShopBindException("服务器繁忙,请稍后再试"); + } + + if (this.getWxMpConfigStorage().isAccessTokenExpired()) { + return this.getWxMpConfigStorage().getAccessToken(); + } + + + Object result = null; + try { + String url = String.format(WxMpService.GET_ACCESS_TOKEN_URL, + this.getWxMpConfigStorage().getAppId(), this.getWxMpConfigStorage().getSecret()); + try { + HttpGet httpGet = new HttpGet(url); + if (this.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build(); + httpGet.setConfig(config); + } + try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) { + String resultContent = new BasicResponseHandler().handleResponse(response); + WxError error = WxError.fromJson(resultContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + this.getWxMpConfigStorage().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + return this.getWxMpConfigStorage().getAccessToken(); + } finally { + httpGet.releaseConnection(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + } finally { + rLock.unlock(); + } + } +} diff --git a/yami-shop-mp/src/main/java/com/yami/shop/mp/config/WxMaConfiguration.java b/yami-shop-mp/src/main/java/com/yami/shop/mp/config/WxMaConfiguration.java index 45af904..1e84194 100644 --- a/yami-shop-mp/src/main/java/com/yami/shop/mp/config/WxMaConfiguration.java +++ b/yami-shop-mp/src/main/java/com/yami/shop/mp/config/WxMaConfiguration.java @@ -13,7 +13,9 @@ package com.yami.shop.mp.config; import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; import com.yami.shop.mp.component.WxMaInRedisConfig; +import com.yami.shop.mp.component.WxMaServiceClusterImpl; import lombok.AllArgsConstructor; +import org.redisson.api.RedissonClient; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -30,10 +32,13 @@ public class WxMaConfiguration { private final WxMaInRedisConfig wxMaInRedisConfig; + private final RedissonClient redissonClient; + @Bean public WxMaService wxMaService() { - WxMaService service = new WxMaServiceImpl(); + WxMaServiceClusterImpl service = new WxMaServiceClusterImpl(); service.setWxMaConfig(wxMaInRedisConfig); + service.setRedissonClient(redissonClient); return service; } diff --git a/yami-shop-mp/src/main/java/com/yami/shop/mp/config/WxMpConfiguration.java b/yami-shop-mp/src/main/java/com/yami/shop/mp/config/WxMpConfiguration.java index 26b07f9..e9b7627 100644 --- a/yami-shop-mp/src/main/java/com/yami/shop/mp/config/WxMpConfiguration.java +++ b/yami-shop-mp/src/main/java/com/yami/shop/mp/config/WxMpConfiguration.java @@ -11,11 +11,13 @@ package com.yami.shop.mp.config; import com.yami.shop.mp.component.WxMpInRedisConfigStorage; +import com.yami.shop.mp.component.WxMpServiceClusterImpl; import com.yami.shop.mp.handler.MenuHandler; import lombok.AllArgsConstructor; import me.chanjar.weixin.mp.api.WxMpMessageRouter; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; +import org.redisson.api.RedissonClient; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -34,11 +36,13 @@ public class WxMpConfiguration { private final MenuHandler menuHandler; private final WxMpInRedisConfigStorage wxMpInRedisConfigStorage; + private final RedissonClient redissonClient; @Bean public WxMpService wxMpService() { - WxMpService service = new WxMpServiceImpl(); + WxMpServiceClusterImpl service = new WxMpServiceClusterImpl(); service.setWxMpConfigStorage(wxMpInRedisConfigStorage); + service.setRedissonClient(redissonClient); return service; }