在GCP PubSub的Spring实现中如何无需重启应用刷新服务账号凭据
看起来你遇到的这个痛点我太有共鸣了——明明每次调用CredClient都能拿到有效的新凭据,可系统就是不加载,过期后只能重启应用,这折腾人的程度真的拉满了。咱们来拆解问题,一步步解决:
问题根源
你当前用的FixedCredentialsProvider是一次性创建就固定死凭据的;哪怕给它加了@RefreshScope,PubSubTemplate在初始化时就已经绑定了旧的CredentialsProvider实例,之后根本不会主动去检查它有没有更新,所以新凭据根本传不到PubSub的调用链路里。
最优解决方案:让凭据提供者实时获取最新凭据
与其费劲手动触发刷新,不如从根源上让CredentialsProvider每次都返回最新的凭据。咱们不用FixedCredentialsProvider,自己实现一个动态的CredentialsProvider:
@Bean public CredentialsProvider getCredentialsProvider() { return new CredentialsProvider() { @Override public Credentials getCredentials() throws IOException { // 每次需要凭据时,实时从CredClient获取最新的 return CredClient.createInstance(authURL, host, clientId, secret) .getCredential(projId, serviceAccount) .createScoped(defaultServiceScopes()); } }; }
这个思路的核心是:不缓存凭据,每次PubSubTemplate发起请求需要用凭据的时候,都会去调用CredClient拿最新的有效凭据。这样不管凭据什么时候过期,下一次请求都会自动拿到新的,完全不用手动触发刷新,也不需要重启应用。
备选方案:结合@RefreshScope让PubSubTemplate感知凭据变化
如果你坚持想用@RefreshScope的方式,那得让PubSubTemplate也能感知到CredentialsProvider的更新。因为PubSubTemplate依赖CredentialsProvider,所以需要把它也纳入刷新范围:
- 先保留带
@RefreshScope的CredentialsProvider:
@Bean @RefreshScope public CredentialsProvider getCredentialsProvider(){ return CredClient.createInstance(authURL, host, clientId, secret) .getCredential(projId,serviceAccount) .createScoped(defaultServiceScopes()); }
- 手动创建
PubSubTemplate并加上@RefreshScope,确保它会随着CredentialsProvider一起刷新:
@Bean @RefreshScope public PubSubTemplate pubSubTemplate(PubSubPublisherTemplate.Builder publisherBuilder, CredentialsProvider credentialsProvider) { publisherBuilder.setCredentialsProvider(credentialsProvider); return publisherBuilder.build(); }
- 当需要刷新时,注入
ContextRefresher触发全局刷新:
@Autowired private ContextRefresher contextRefresher; // 可以通过接口调用、定时任务或者事件触发这个方法 public void triggerCredentialsRefresh() { contextRefresher.refresh(); }
这个方案需要手动触发刷新(比如通过接口、定时任务),而且每次刷新会重建PubSubTemplate,需要注意连接池的复用问题,所以更推荐第一个方案。
为什么你之前的@RefreshScope没生效?
因为PubSubTemplate是Spring自动配置时创建的单例,它在启动时就已经持有了CredentialsProvider的初始实例。即便你刷新了CredentialsProvider,PubSubTemplate不会主动去获取新的实例,自然也就用不上新凭据。只有让PubSubTemplate也被@RefreshScope管理,或者让CredentialsProvider本身能动态返回新凭据,才能解决问题。
内容来源于stack exchange

