Как подключить Java-приложение Heroku к облачной базе данных

Привет, друзья! Я вернулся из короткого отпуска и готов продолжить свой любимый проект: гео-распределенный мессенджер на Java!

В прошлой статье я запустил первую версию своего приложения, которое работает на Heroku и использует YugabyteDB Managed в качестве облачной распределенной базы данных. Теперь я уверен, что гео-мессенджер может выдержать перебои на уровне зоны. Сегодня я рассмотрю несколько вариантов подключения для Heroku и YugabyteDB Managed.

Вы можете спросить: «Что не так с подключением между этими двумя SaaS-продуктами?».

Во-первых, после развертывания ваш экземпляр YugabyteDB Managed не будет виден всему Интернету. Вы должны предоставить IP-адреса приложений, служб, виртуальных машин и т.д. для подключения к базе данных. Это обычное и разумное требование для облачных баз данных. Оно справедливо для MongoDB Atlas, Amazon Aurora и для любой другой облачной базы данных, которая серьезно относится к безопасности.

По умолчанию список разрешенных управляемых IP-адресов YugabyteDB пуст. Это означает, что запросы моего гео-мессенджера будут отклонены.

Вы можете улыбнуться и сказать: «Да ладно, Денис, просто найди статический IP-адрес своего приложения в Heroku и добавь его в YugabyteDB!».

Именно так я и думал, дружище! Однако Heroku не предоставляет статические IP-адреса в Common Runtime Environment. Поэтому мне нужно либо перейти на корпоративный тарифный план, включающий частные пространства, либо найти другой вариант. Будучи экономным (т.е. дешевым), я выбрал последнее.

Итак, если вы все еще со мной в этом путешествии, то, как говорили пираты, «All Hand Hoy!», что означает «Все на палубу!». Давайте рассмотрим различные варианты подключения, которые я подтвердил в своем приложении.

Разрешить все подключения

Решение для грубой силы — разрешить все подключения к моему экземпляру YugabyteDB Managed. Я сделал это, добавив адрес 0.0.0.0/0 в список разрешенных IP-адресов.

Я рекомендую этот вариант, если вы находитесь на ранней стадии разработки (и хотите сосредоточиться на кодировании, а не на настройке инфраструктуры) или если вы развертываете полное решение в сети VPC. Я кодил без остановки и не хотел отвлекаться, поэтому я использовал решение 0.0.0.0/0 в качестве краткого пути.

Использование прокси SOCK5

Когда моя работа над кодом замедлилась, я решил найти более элегантное решение проблемы соединения Heroku и YugabyteDB Managed. Очевидно, я не хотел, чтобы мой экземпляр базы данных оставался открытым для всего интернета.

Каким был мой следующий шаг? Ну, как опытный инженер с почти 20-летним стажем работы в технологической отрасли, я точно знал, что делать: Я открыл браузер и погуглил «heroku static ip address java».

Вскоре я понял, что эта проблема настолько распространена, что на рынке Heroku полно дополнений, которые могут проксировать запросы Heroku через статические IP-адреса. (Кстати, хорошая возможность для бизнеса, если вы хотите предложить свой прокси: как насчет основания стартапа?)

Затем я потратил целый час, пробуя различные прокси. Ничего не помогло. YugabyteDB отклонял запросы от моего гео-мессенджера. Я почесал голову, затем почесал ее снова. Прежде чем почесать голову в третий раз, я понял, что все эти прокси-дополнения работают только для HTTP-трафика: REST, GraphQL и другие протоколы, которые полагаются на HTTP. Мое приложение использует драйвер JDBC, который открывает прямое сокетное соединение с базой данных и обменивается сообщениями в протоколе PostgreSQL на уровне проводов.

Каким был мой следующий шаг? Уверен, вы уже догадались! Я снова обратился к Google, на этот раз искал «heroku socks5 proxy for postgres», и наконец наткнулся на дополнение Fixie Socks, которое мне подошло.

Пошаговая инструкция выглядит следующим образом:

  1. Я установил дополнение Fixie Socks heroku addons:create fixie-socks:handlebar -a geo-distributed-messenger. Вы можете поменять «handlebar» на другой уровень. Этот стоит мне 9 долларов в месяц за 2 000 запросов. Есть также бесплатный вариант на 100 запросов в месяц под названием «grip».
  2. Я нашел свои статические IP на приборной панели, которые можно запустить с помощью этой команды: heroku addons:open fixie-socks.
  3. Я добавил эти IP в разрешительные списки управляемых IP YugabyteDB.
  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 включает прокси Fixie Socks только для управляемых URI YugabyteDB. Наконец, экземпляр селектора создается при запуске приложения:

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 Private Spaces также должна работать (если верить технической документации Heroku). Для полноты картины я добавил эту возможность в статью. Теоретически, вы можете настроить эти частные пространства в Heroku и сопоставить их с сетью YugabyteDB Managed VPC, но я предоставлю вам проверить это!

Что на горизонте?

Хорошо, друзья. Эта статья завершает мои размышления о текущей версии приложения, работающей в одном облаке. Теперь позвольте мне сделать небольшой перерыв, прежде чем я перейду к следующему этапу: далее мне нужно, чтобы гео-мессенджер функционировал в нескольких облачных регионах. Я буду изучать инструменты Google Cloud для автоматизации развертывания. Буду держать вас в курсе!

Тем временем, если вам интересно, как начинался (и продолжается) мой путь разработчика, ознакомьтесь с предыдущими статьями этой серии.

Оцените статью
devanswers.ru
Добавить комментарий