Статьи

Использование Camel, CDI внутри Kubernetes с Fabric8

пролог

Недавно я писал в блоге о внедрении услуг Kubernetes с помощью CDI . В этом посте я собираюсь сделать еще один шаг вперед и представить Apache Camel . Итак, я собираюсь использовать поддержку CDI Camel для подключения моих компонентов и маршрутов, а также расширение CDI Fabric8 для автоматического внедрения сервисов Kubernetes в мои компоненты.

Я собираюсь повторно использовать материал из моего предыдущего поста (так что прочитайте его, если вы еще этого не сделали), чтобы создать отдельное приложение CIL для верблюдов, которое будет предоставлять содержимое базы данных через http (простой http для jdbc и обратно). еще раз) . Все будет работать в Docker, а оркестровка будет выполнена Kubernetes .

Итак, первым делом первым. Как работает верблюд и CDI ….

Реестр верблюдов

Apache Camel использует понятие реестра . Он использует реестр для поиска объектов, необходимых для маршрутов. Эти поиски могут по типу или по имени.

Наиболее распространенное использование реестра — когда обрабатывается конечная точка URI , Camel проанализирует схему и найдет реестр по имени для соответствующего компонента. Другие случаи включают передачу ссылок bean-компонентов на конечные точки по имени и т. Д.

Другими словами, Apache Camel может выполнять поиск в реестре бинов во время выполнения.

Любое расширение, которое должно хорошо работать с Apache Camel, должно предоставлять бинам предсказуемые имена.

Аннотация @Alias

Расширение CDI Fabric8 для любой данной службы может регистрировать более одного bean-компонента (по одному на услугу для каждого типа, на протокол…) . Таким образом, невозможно иметь служебные бины, названные в честь службы. Также пользователю не нужно запоминать соглашения об именах, которые используются внутри …

«Итак, как Fabric8 играет с фреймворками, которые полагаются на поиск по имени?»

Fabric8 предоставляет аннотацию @ Alias, которая позволяет разработчику явно указывать имя компонента внедренной службы. Вот пример:

01
02
03
04
05
06
07
08
09
10
import javax.inject.Inject;
import io.fabric8.annotations.Protocol;
import io.fabric8.annotations.ServiceName;
  
public class MysqlExampleWithAlias {
  
    public MysqlExampleWithAlias(@Inject @Alias("mysqldb") @ServiceName("mysql") String serivceUrl) {
        System.out.println("Bean Name: mysqldb. Type: String. Value:"+serviceUrl);
    }
}

«Что здесь происходит?»

Расширение Fabric8 cdi получит событие, что есть точка внедрения типа String, с 2 квалификаторами:

  1. ServiceName со значением « mysql ».
  2. Псевдоним со значением « mysqldb ».

Поэтому, когда он создает компоненты и производителей для этого сервиса, он будет использовать «mysqldb» в качестве имени. Это то, что позволяет контролировать управляемые bean-компоненты Fabric8 и делает возможным поиск имен.

Использование @Factory для создания или настройки компонентов или конечных точек Camel

В моем предыдущем посте я рассмотрел несколько примеров того, как вы можете использовать аннотацию @ Factory от Fabric8 для создания соединений jdbc. Теперь я собираюсь создать фабрику для источника данных jdbc, который затем будет добавлен в реестр Apache Camel Cdi Bean .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import io.fabric8.annotations.Configuration;
import io.fabric8.annotations.Factory;
import io.fabric8.annotations.ServiceName;
  
import javax.sql.DataSource;
  
public class DatasourceFactory {
    private static final String TCP_PROTO = "tcp";
    private static final String JDBC_PROTO = "jdbc:mysql";
  
    @Factory
    @ServiceName
    public DataSource create(@ServiceName String url, @Configuration MysqlConfiguration conf) {
        MysqlDataSource ds = new MysqlDataSource();
        ds.setURL(url.replaceFirst(TCP_PROTO, JDBC_PROTO) + "/" + conf.getDatabaseName());
        ds.setUser(conf.getUsername());
        ds.setPassword(conf.getPassword());
        return ds;
    }

Теперь, если мы хотим сослаться на этот источник данных из конечной точки Apache Camel , нам нужно будет указать « имя » источника данных для конечной точки uri. Например, « jdbc: custmersds », где customerds — это имя источника данных.

«Но как я могу назвать управляемый источник данных fabric8?»

Вот как @Alias ​​спасает день:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import io.fabric8.annotations.Alias;
import io.fabric8.annotations.ServiceName;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.cdi.ContextName;
import org.apache.camel.model.language.ConstantExpression;
  
import javax.ejb.Startup;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.sql.DataSource;
  
@ContextName("myCdiCamelContext")
@Startup
@ApplicationScoped
public class MyRoutes extends RouteBuilder {
  
    @Inject
    @ServiceName("mysql-service")
    @Alias("customerds")
    DataSource dataSource;
  
    @Override
    public void configure() throws Exception {
        from("jetty:http://0.0.0.0:8080/list/")
                .setBody(new ConstantExpression("select * from customers"))
                .to("jdbc:customerds");
    }
}

Это типичный RouteBuilder для приложения Camel на основе CDI. Что особенного в этом то, что мы внедряем источник данных с именем «customerds».

«Кто предоставляет источник данных?»

Краткий ответ : Fabric8 .

Не очень короткий ответ : аннотация @ ServiceName («mysql») сообщает Fabric8, что DataSource ссылается на службу «mysql» Kubernetes . Fabric8 получит URL для этого сервиса для нас. Поскольку типом поля является ни String, ни URL, а DataSource, Fabric8 будет искать методы @ Factory , которые способны преобразовывать String в DataSource. В нашем случае он найдет класс DataSourceFactory, который делает именно это. Поскольку это было недостаточно, DataSourceFactory также принимает @ Configuration MysqlConfiguration , чтобы мы могли указывать такие вещи, как имя базы данных, учетные данные и т. Д. (См. Мой предыдущий пост).

Настройка источника данных

Прежде чем я начну объяснять, как мы можем настроить DataSource, позвольте мне сделать один шаг назад и вспомнить MysqlConfiguration из моего предыдущего поста:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import org.apache.deltaspike.core.api.config.ConfigProperty;
import javax.inject.Inject;
  
public class MysqlConfiguration {
      @Inject
      @ConfigProperty(name = "USERNAME", defaultValue = "admin")
      private String username;
       
      @Inject
      @ConfigProperty(name = "PASSWORD", defaultValue = "admin")
      private String password;
       
      @Inject
      @ConfigProperty(name = "DATABASE_NAME", defaultValue = "mydb")
      private String databaseName;
       
      public String getUsername() {
            return username;
      }
       
      public String getPassword() {
            return password;
      }
      public String getDatabaseName() {
            return databaseName;
      }
             
}

Как я упоминал в моем предыдущем посте, мы можем использовать переменные окружения для передачи конфигурации нашему приложению. Помните, что это приложение предназначено для жизни внутри контейнера Docker….

MysqlConfiguration содержит 3 поля:

  1. Имя пользователя поля для переменной среды USERNAME
  2. Пароль поля для переменной окружения ПАРОЛЬ
  3. Поле databseName для переменной окружения DATABASE_NAME

Итак, нам нужно 3 переменные окружения, по одной для каждого поля. Тогда нашему DataSourceFactory будет передан экземпляр
MysqlConfiguration с любыми значениями, которые можно извлечь из среды, чтобы он создавал фактический источник данных.

«Но как я могу использовать MysqlConfiguration для настройки нескольких разных служб?»

Итак, идея заключается в том, что @ Factory и @ Configuration могут быть использованы повторно. В конце концов, нет необходимости иметь фабрики и классы моделей, связанные с базовыми сервисами, верно?

Fabric8 помогает, используя имя службы в качестве префикса для переменных среды. Он делает это во время выполнения и работает так:

  1. Расширение Fabric8 обнаруживает точку инъекции, помеченную @ ServiceName
  2. Он проверит тип цели и при необходимости найдет @ Factory .
  3. @ Factory принимает URL-адрес службы и экземпляр MysqlConfiguration.
  4. MysqlConfiguration будет создан с использованием значения @ ServiceName в качестве префикса переменной среды.

Таким образом, чтобы наш пример работал, нам нужно было упаковать наше приложение в контейнер Docker, а затем использовать следующую конфигурацию Kubernetes :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
{               
  "image": "camel-cdi-jdbc",
  "imagePullPolicy": "IfNotPresent",
  "name": "camel-cdi-jdbc",
  "env": [
    {
      "name": "MYSQL_SERVICE_USERNAME",
      "value": "admin"
    },
    {
      "name": "MYSQL_SERVICE_PASSWORD",
      "value": "password"
    },
    {
      "name": "MYSQL_SERVICE_DATABASE_NAME",
      "value": "customers"
    }
  ]
}

Теперь, если нам нужно создать дополнительный источник данных (скажем, для моста jdbc to jdbc) внутри того же контейнера, нам нужно будет просто указать дополнительную переменную среды для дополнительных Kubernetes . Теперь, если имя службы было «mysql-target», тогда наша конфигурация Kubernetes должна выглядеть следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{               
  "image": "camel-cdi-jdbc",
  "imagePullPolicy": "IfNotPresent",
  "name": "camel-cdi-jdbc",
  "env": [
    {
      "name": "MYSQL_SERVICE_USERNAME",
      "value": "admin"
    },
    {
      "name": "MYSQL_SERVICE_PASSWORD",
      "value": "password"
    },
    {
      "name": "MYSQL_SERVICE_DATABASE_NAME",
      "value": "customers"
    },
    {
      "name": "MYSQL_TARGET_USERNAME",
      "value": "targetUser"
    },
    {
      "name": "MYSQL_TARGET_PASSWORD",
      "value": "targetPassword"
    },
    {
      "name": "MYSQL_TARGET_DATABASE_NAME",
      "value": "targetCustomers"
    }
  ]
}

… И мы могли бы использовать это, добавив в наш проект точку внедрения с квалификатором @ ServiceName («mysql-target»).

Вы можете найти похожие примеры внутри быстрого запуска Fabric8 . А точнее, быстрый запуск camel-cdi-amq .

Оставайтесь в курсе

Надеюсь, вам понравилось. Скоро появятся и другие связанные темы (включая написание интеграционных тестов для Java-приложений, работающих на Kubernetes ).