소스 검색

更新集群模式多次重复获取微信accessToken的问题

OPGame 6 년 전
부모
커밋
86954f17d8

+ 1 - 1
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;
 }

+ 89 - 0
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();
+        }
+
+    }
+}

+ 90 - 0
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();
+        }
+    }
+}

+ 6 - 1
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;
     }
 

+ 5 - 1
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;
     }