JAVA SDK通过proxy对接google: GCS/FCM
前言:因为国内调用google相关api需要通过代理访问(不想设置全局代理),所以在代理这里经常遇到问题,先说一下结论 GCS 需要设置全局代理或自定义代理选择器, FCM sdk admin 在初始化firebaseApp时是支持设置的。
GCS: 开始时尝试在使用sdk时传入代理的配置,但没有实际效果。
参考如下资料:
https://stackoverflow.com/questions/49980303/how-to-set-proxy-for-storage-service-in-gcp-using-java
最终选择如下资料中的方案使用自定义代理选择器解决了问题:
Java Networking and Proxies
@Slf4j
public class CustomProxySelector extends ProxySelector {
private final ProxySelector defaultSelector;
private final Proxy proxy;
private final Set<String> targetDomains;
public CustomProxySelector(ProxySelector defaultSelector, Proxy proxy, Set<String> targetDomains) {
this.defaultSelector = defaultSelector;
this.proxy = proxy;
this.targetDomains = targetDomains;
}
// 根据 URL 选择代理
@Override
public List<Proxy> select(URI uri) {
if (uri != null && uri.getHost() != null) {
log.info("uri:{}", uri.getHost());
for (String domain : targetDomains) {
if (uri.getHost().endsWith(domain)) {
log.info("uri google:{} proxy:{}", uri.getHost(), proxy.toString());
return List.of(proxy);
}
}
}
return defaultSelector.select(uri);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
defaultSelector.connectFailed(uri, sa, ioe);
}
}
@Slf4j
@Component
public class ProxyConfigurer {
@Value("${gateway.google.map-proxy.enable:false}")
private boolean proxyEnabled;
@Value("${gateway.google.map-proxy.host:}")
private String proxyHost;
@Value("${gateway.google.map-proxy.port:}")
private int proxyPort;
@PostConstruct
public void configureProxy() {
if (!proxyEnabled) {
return;
}
log.info("proxyHost:{} port:{}", proxyHost, proxyPort);
ProxySelector defaultSelector = ProxySelector.getDefault();
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
Set<String> targetDomains = new HashSet<>(List.of(".googleapis.com"));
CustomProxySelector customSelector = new CustomProxySelector(defaultSelector, proxy, targetDomains);
ProxySelector.setDefault(customSelector);
log.info("Proxy selector configured successfully.");
}
}
FCM:
这个因为是后面我才开始对接我直接就选择了代理选择器方式发现本地好用,但服务器上就不好用,报错如下:
Caused by: java.io.IOException: Unknown exception in request
at com.google.firebase.internal.ApacheHttp2Request.execute(ApacheHttp2Request.java:134)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1012)
at com.google.firebase.internal.ErrorHandlingHttpClient.send(ErrorHandlingHttpClient.java:97)
... 38 more
Caused by: java.util.concurrent.ExecutionException: java.net.SocketException: Network is unreachable
at org.apache.hc.core5.concurrent.BasicFuture.getResult(BasicFuture.java:72)
at org.apache.hc.core5.concurrent.BasicFuture.get(BasicFuture.java:85)
at com.google.firebase.internal.ApacheHttp2Request.execute(ApacheHttp2Request.java:123)
... 40 more
Caused by: java.net.SocketException: Network is unreachable
开始怀疑是proxy有问题,这时候一般选择用curl测试proxy是否连通
curl -I --proxy xxx:3128 "https://oauth2.googleapis.com/token"
返回404 代表网络上是通的,后来查询文档Firebase: Accessing Firestore and Firebase through a proxy server | by Hiranya Jayathilaka | FAUN — Developer Community 🐾
其实SDK是支持动态设置的,但文档中与我的实现有出入所以参照源码加了一个动态的令牌鉴权改造代码如下:
@PostConstruct
private void initializeFirebase() {
try {
InputStream inputStream = fileServiceUtil.downloadFile(fcmPath);
if (inputStream == null) {
log.error("fcm file download fail");
throw new GeneraliPluginException("fcm file download fail");
}
InetSocketAddress address = new InetSocketAddress(proxyHost, proxyPort);
final HttpTransport transport = new NetHttpTransport.Builder()
.setProxy(new Proxy(Proxy.Type.HTTP, address))
.build();
HttpTransportFactory transportFactory = () -> transport;
FirebaseOptions options =
FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(inputStream, transportFactory))
.setHttpTransport(transport)
.build();
FirebaseApp.initializeApp(options);
} catch (IOException e) {
Log.error("PushNotificationServiceAndroidImpl.initializeFirebase", e);
}
}
解决问题。
思考:还是要以官网文档和各种社区讨论来编写代码,经常查询各种社区是非常有帮助的。