在这篇文章中,通过查看 Heroku 和 YugabyteDB Managed 的几个连接选项,继续使用 Java 创建一个地理分布式信使。
喂,伙计!我从一个短暂的假期回来,准备继续我的宠物项目:Java 中 的地理分布式信使!
如果您对我的开发之旅是如何开始(以及将要进行的)感兴趣,请查看本系列之前的文章:
- 地理什么?地理分布式应用程序简介
- 是什么让地理分布式应用程序的架构与众不同?
- 如何使用 Vaadin、YugabyteDB 和 Heroku 在几天内构建多区域 Java 应用程序。
在上一篇文章中,我启动了我的应用程序的第一个版本,它在Heroku中运行,并使用 YugabyteDB Managed 作为云原生分布式数据库。我现在有信心地理信使可以容忍区域级中断。今天,我将介绍 Heroku 和 YugabyteDB Managed 的几个连接选项。
您可能会问,“这两个 SaaS 产品之间的连接有什么问题?”
首先,一旦部署,您的 YugabyteDB 托管实例将不会对整个 Internet 可见。您必须提供应用程序、服务、VM 等的 IP 地址才能连接到数据库。这是云原生数据库常见且合理的需求。对于 MongoDB Atlas、Amazon Aurora 以及任何其他认真对待安全性的云原生数据库来说都是如此。
默认情况下,YugabyteDB 托管 IP 允许列表为空。这意味着我的地理信使的请求将被拒绝。

你可能会笑着说:“来吧,丹尼斯,在 Heroku 中找到你的应用的静态 IP 地址,然后将其添加到 YugabyteDB!”
这是我的确切想法,伙计!但是,Heroku 在Common Runtime Environment中不提供静态 IP 地址。所以我要么需要切换到包含私人空间的企业计划,要么找到其他选择。考虑到成本(即:便宜),我选择了后者。
所以,如果你在这段旅程中仍然和我在一起,那么,就像海盗们常说的那样,“All Hand Hoy!” 这意味着,“甲板上的每个人!” 让我们回顾一下我使用我的应用程序验证的各种连接选项。
允许所有连接
蛮力解决方案是允许所有连接到我的 YugabyteDB 托管实例。我通过将0.0.0.0/0地址添加到 IP 允许列表来做到这一点。

如果您处于早期开发阶段(并且希望专注于编码而不是基础设施设置),或者如果您正在 VPC 网络中部署完整的解决方案,我建议您使用此选项。我不停地编码,不想分心,所以我用这个0.0.0.0/0解决方案作为捷径。
一旦我的编码速度变慢,我决定为 Heroku 和 YugabyteDB 托管连接问题找到一个更优雅的解决方案。显然,我不希望我的数据库实例对整个互联网保持开放。
我的下一步是什么?好吧,作为一名在科技行业拥有近 20 年经验的工程师,我完全知道该怎么做:我打开浏览器并在 Google 上搜索“ heroku static ip address java ”。
我很快意识到这个问题是如此普遍,以至于 Heroku 市场上到处都是可以通过静态 IP 地址代理 Heroku 请求的附加组件。(顺便说一句,如果您想提供代理,这是一个很好的商机:创办一家初创公司怎么样?)
然后我浪费了一个小时尝试不同的代理。没有任何效果。YugabyteDB 拒绝了我的地理信使的请求。我挠了挠头,然后又挠了挠。在第三次摸不着头脑之前,我意识到所有这些代理插件仅适用于 HTTP 流量:REST、GraphQL和其他依赖 HTTP 的协议。我的应用程序使用 JDBC 驱动程序,该驱动程序打开与数据库的直接套接字连接并在 PostgreSQL 线路级协议中交换消息。
我的下一步是什么?相信你已经猜到了!我再次求助于谷歌,这次搜索“ heroku socks5 proxy for postgres”,最后发现了对我有用的Fixie Socks插件。
分步说明如下:
1.我安装了 Fixie Socks 插件heroku addons:create fixie-socks:handlebar -a geo-distributed-messenger。您可以将“车把”换成另一层。对于 2,000 个请求,我每月花费 9 美元。还有一个每月 100 个请求的免费选项,称为“grip”。
2.我在仪表板上找到了我的静态 IP,您可以使用以下命令启动:
heroku addons:open fixie-socks.
3.我将这些 IP 添加到 YugabyteDB 托管IP 允许列表中。
4.然后我介绍了自定义环境变量USE_FIXIE_SOCKS,它指示我的应用程序使用或绕过代理heroku config:set USE_FIXIE_SOCKS=true -a geo-distributed-messenger。
当USE_FIXIE_SOCKS设置为 true 时,应用程序配置两个 JVM 级别的属性 (socksProxyHost和socksProxyPort) 要求 Java 通过代理发送所有网络请求:
if (System.getenv("USE_FIXIE_SOCKS") != null) {
boolean useFixie = Boolean.valueOf(System.getenv("USE_FIXIE_SOCKS"));
if (useFixie) {
System.out.println("Setting up Fixie Socks Proxy");
// The FIXIE_SOCKS_HOST variable is added by the add-on to the environment
String[] fixieData = System.getenv("FIXIE_SOCKS_HOST").split("@");
String[] fixieCredentials = fixieData[0].split(":");
String[] fixieUrl = fixieData[1].split(":");
String fixieHost = fixieUrl[0];
String fixiePort = fixieUrl[1];
String fixieUser = fixieCredentials[0];
String fixiePassword = fixieCredentials[1];
System.setProperty("socksProxyHost", fixieHost);
System.setProperty("socksProxyPort", fixiePort);
System.out.println("Enabled Fixie Socks Proxy:" + fixieHost);
Authenticator.setDefault(new ProxyAuthenticator(fixieUser, fixiePassword));
}
}
在 Heroku 中重新启动应用程序后,我可以连接到 YugabyteDB Managed 并查看应用程序工作区、通道和消息。

仅对 YugabyteDB 连接使用代理
即使 SOCKS5 代理方法有效,我仍然对我的实现并不完全满意。怎么了?看看这两行:
System.setProperty("socksProxyHost", fixieHost);
System.setProperty("socksProxyPort", fixiePort);
这些是与 JVM 相关的设置,要求每个 TCP/IP 连接都通过我的代理。那是矫枉过正。我只需要连接到 YugabyteDB Managed 的代理。
幸运的是,这个任务在 Java 中很容易解决。您只需要提供自定义ProxySelector的实现。这就是我通过引入自己的DatabaseProxySelector类所做的:
public class DatabaseProxySelector extends ProxySelector {
private String proxyHost;
private int proxyPort;
public DatabaseProxySelector(String proxyHost, int proxyPort) {
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
}
@Override
public List<Proxy> select(URI uri) {
// YugabyteDB Managed host always ends with `ybdb.io`
if (uri.toString().contains("ybdb.io")) {
System.out.println("Using the proxy for YugabyteDB Managed: " + uri);
final InetSocketAddress proxyAddress = InetSocketAddress
.createUnresolved(proxyHost, proxyPort);
return Collections.singletonList(new Proxy(Type.SOCKS, proxyAddress));
}
return Collections.singletonList(Proxy.NO_PROXY);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
new IOException("Failed to connect to the proxy", ioe).printStackTrace();
}
}
如您所见,DatabaseProxySelector仅对 YugabyteDB 托管 URI 启用 Fixie Socks 代理。最后,选择器实例在应用程序启动时创建:
if (System.getenv("USE_FIXIE_SOCKS") != null) {
boolean useFixie = Boolean.valueOf(System.getenv("USE_FIXIE_SOCKS"));
if (useFixie) {
System.out.println("Setting up Fixie Socks Proxy");
String[] fixieData = System.getenv("FIXIE_SOCKS_HOST").split("@");
String[] fixieCredentials = fixieData[0].split(":");
String[] fixieUrl = fixieData[1].split(":");
String fixieHost = fixieUrl[0];
String fixiePort = fixieUrl[1];
String fixieUser = fixieCredentials[0];
String fixiePassword = fixieCredentials[1];
DatabaseProxySelector proxySelector = new DatabaseProxySelector(fixieHost, Integer.parseInt(fixiePort));
ProxySelector.setDefault(proxySelector);
Authenticator.setDefault(new ProxyAuthenticator(fixieUser, fixiePassword));
System.out.println("Enabled Fixie Socks Proxy:" + fixieHost);
}
}
探索私人空间
一开始,我提到 Heroku 私人空间功能也应该可以工作(如果相信 Heroku 的技术文档的话)。为了完整起见,我在文章中添加了这个选项。从理论上讲,您可以在 Heroku 中设置这些私有空间,并使用 YugabyteDB 托管 VPC 网络对等空间,但我会让您验证这一点!
地平线上有什么?
好吧,伙计。本文总结了我对在单个云区域中运行的当前应用程序版本的想法。现在,让我在进入下一个里程碑之前稍作休息:接下来,我需要地理信使在多个云区域中运行。我将研究 Google Cloud 工具来自动化部署。我会及时向大家发布!
原文标题:How To Connect a Heroku Java App to a Cloud-Native Database
原文作者: Denis Magda
原文地址:https://dzone.com/articles/how-to-connect-a-heroku-app-to-a-yugabytedb-manage




