使用Fabric8在CDI管理的bean中注入Kubernetes Services
序幕
在Kubernetes中我最喜歡的就是發(fā)現(xiàn)服務的方式。 為什么?
主要是因為用戶代碼不必處理注冊,查找服務,也沒有網(wǎng)絡意外(如果您曾經(jīng)嘗試過基于注冊表的方法,那么您就會知道我在說什么) 。
這篇文章將介紹如何使用Fabric8以便使用CDI在Java中注入Kubernetes服務。
Kubernetes服務
深入介紹Kubernetes Services超出了本文的范圍,但是我將嘗試對其進行非常簡要的概述。
在Kubernetes中,應用程序打包為Docker容器。 通常,將應用程序分成多個部分是一個好主意,因此您將擁有多個Docker容器,這些容器很可能需要彼此通信。 通過將某些容器放置在同一Pod中 ,可以將某些容器并置在一起,而另一些容器則可以位于較遠的位置,并且需要彼此通信的方式。 這就是服務進入畫面的地方。
容器可以綁定到一個或多個端口,從而為其他容器提供一個或多個“服務”。 例如:
- 數(shù)據(jù)庫服務器。
- 消息代理。
- 休息服務。
問題是“ 其他容器如何知道如何訪問這些服務? “
所以, Kubernetes讓你“標簽”每個吊艙 ,并使用這些標簽來“選擇” 吊艙提供一個邏輯服務。 這些標簽是簡單的鍵,值對。
這是一個示例,說明如何通過使用鍵名和值mysql指定標簽來“標記”容器。
{"apiVersion" : "v1beta3","kind" : "ReplicationController","metadata" : {"labels" : {"name" : "mysql"},"name" : "mysql"},"spec" : {"replicas" : 1,"selector" : { "name" : "mysql"},"template" : {"metadata" : {"labels" : {"name" : "mysql"}},"spec" : {"containers" : [ {"image" : "mysql","imagePullPolicy" : "IfNotPresent","name" : "mysql","ports" : [ {"containerPort" : 3306,"name" : "mysql"} ] }]}}}}下面是我們?nèi)绾味x一個例子服務暴露了MySQL端口。 服務選擇器正在使用我們在上面指定的鍵/值對,以便定義提供服務的Pod。
{"kind": "Service","apiVersion": "v1beta3","metadata": {"name": "mysql"},"spec": {"ports": [{"name": "mysql","protocol": "TCP","port": 3306,"targetPort": 3306}],"selector": {"name": "mysql"}} }Kubernetes將服務信息作為環(huán)境變量傳遞給每個容器。 對于創(chuàng)建的每個容器, Kubernetes將確保為容器可見的所有服務傳遞適當?shù)沫h(huán)境變量。
對于上述示例的mysql服務,環(huán)境變量將為:
- MYSQL_SERVICE_HOST
- MYSQL_SERVICE_PORT
Fabric8提供了一個CDI擴展,可以通過提供Kubernetes資源注入來簡化Kubernetes應用程序的開發(fā)。
Fabric8 CDI擴展入門
要使用cdi擴展,第一步是將依賴項添加到項目中。
<dependency><groupId>io.fabric8</groupId><artifactId>fabric8-cdi</artifactId><version>2.1.11</version> </dependency>下一步是確定要向哪個字段注入的服務,然后向其添加@ServiceName批注。
import javax.inject.Inject; import io.fabric8.annotations.ServiceName;public class MysqlExample {private static final DB = "mydb";private static final TCP_PROTO = "tcp";private static final JDBC_PROTO = "jdbc:mysql";private final Connection connection;public MysqlExample(@Inject @ServiceName("mysql") String serivceUrl) {Class.forName("com.mysql.jdbc.Driver");return DriverManager.getConnection(toJdbcUrl(serivceUrl));}private static String toJdbcUrl(String url) {return url.replaceFirst(TCP_PROTO, JDBC_PROTO) +"/" +DB;}//More stuff }在上面的示例中,我們有一個類,需要通過JDBC連接到可通過Kubernetes Services進行訪問的mysql數(shù)據(jù)庫。
注入的serivceUrl的格式為:[tcp | udp]:// [host]:[port]。 這是一個非常好的URL,但不是正確的jdbc url。 因此,我們需要一個實用程序來進行轉(zhuǎn)換。 這是toJdbcUrl的目的。
即使可以在定義服務時指定協(xié)議,但只能指定核心傳輸協(xié)議,例如TCP或UDP,而不能指定http,jdbc等。
@Protocol批注
必須查找并用應用程序協(xié)議替換“ tcp”或“ udp”值,這很臭,而且很快就會變舊。 為了刪除該樣板, Fabric8提供了@Protocol批注。 該批注允許您在注入的服務URL中選擇所需的應用程序協(xié)議。 在前面的示例中為“ jdbc:mysql”。 因此,代碼可能類似于:
import javax.inject.Inject; import io.fabric8.annotations.Protocol; import io.fabric8.annotations.ServiceName;public class MysqlExampleWithProtocol {private static final DB = "mydb";private final Connection connection;public MysqlExampleWithProtocol(@Inject @Protocol("jdbc:mysql") @ServiceName("mysql") String serivceUrl) {Class.forName("com.mysql.jdbc.Driver");return DriverManager.getConnection(serivceUrl + "/" + DB);}//More stuff }毫無疑問,這要干凈得多。 它仍然不包含有關實際數(shù)據(jù)庫的信息或通常作為JDBC Url的一部分傳遞的任何參數(shù),因此這里有改進的余地。
人們可能希望本著同樣的精神,可以使用@Path或@Parameter批注,但這兩者都是屬于配置數(shù)據(jù)的,因此不適合將其硬編碼為代碼。 而且,Fabric8的CDI擴展并不希望成為URL轉(zhuǎn)換框架。 因此,相反,它允許您直接實例化用于訪問任何給定服務的客戶端并將其注入源中,從而解決了問題。
使用@Factory注釋為Services創(chuàng)建客戶端
在前面的示例中,我們看到了如何獲取服務的URL并使用該URL創(chuàng)建JDBC連接。 任何需要JDBC連接的項目都可以復制該代碼段,并且只要用戶記得他需要設置實際的數(shù)據(jù)庫名稱,它就可以很好地工作。
如果不是復制并粘貼該代碼片段就可以對其進行組件化和重用,那豈不是很棒嗎? 這是工廠注釋的開始。您可以使用@Factory注釋任何接受服務URL作為參數(shù)并返回使用URL創(chuàng)建的對象的方法(例如,服務的客戶端)。 因此,對于前面的示例,我們可以有一個MysqlConnectionFactory:
import java.sql.Connection; import io.fabric8.annotations.Factory; import io.fabric8.annotations.ServiceName;public class MysqlConnectionFactory {@Factory@ServiceNamepublic Connection createConnection(@ServiceName @Protocol("jdbc:mysql") String url) {Class.forName("com.mysql.jdbc.Driver");return DriverManager.getConnection(serivceUrl + "/" + DB); } }然后,可以不注入URL而直接注入連接,如下所示。
import java.sql.Connection; import javax.inject.Inject; import io.fabric8.annotations.ServiceName;public class MysqlExampleWithFactory {private Connection connection;public MysqlExampleWithProtocol(@Inject @ServiceName("mysql") Connection connection) {this.connection = connection;}//More stuff }這里會發(fā)生什么?
當CDI應用程序啟動時,Fabric8擴展將接收有關所有帶注釋方法的事件。 它將跟蹤所有可用的工廠,因此對于使用@ServiceName注釋的任何非String注入點,它將創(chuàng)建一個在后臺使用匹配的@Factory的Producer。
在上面的示例中,首先將注冊MysqlConnectionFactory,并且當檢測到具有@ServiceName限定符的Connection實例時,將創(chuàng)建委托給MysqlConnectionFactory的Producer (將遵守所有限定符) 。
這很棒,但是也很簡單 。 為什么?
因為很少有這樣的工廠僅需要該服務的URL。 在大多數(shù)情況下,需要其他配置參數(shù),例如:
- 認證信息
- 連接超時
- 更多 …。
將@Factory與@Configuration一起使用
在下一節(jié)中,我們將看到使用配置數(shù)據(jù)的工廠。 我將使用mysql jdbc示例,并添加對指定可配置憑據(jù)的支持。 但是在此之前,我要問一個反問的問題?
“如何配置容器化的應用程序?”
可能的最短答案是“使用環(huán)境變量”。
因此,在此示例中,我假設使用以下環(huán)境變量將憑據(jù)傳遞到需要訪問mysql的容器:
- MYSQL_USERNAME
- MYSQL_PASSWORD
現(xiàn)在我們需要看看我們的@Factory如何使用它們。
我以前曾經(jīng)想在CDI中使用環(huán)境變量,但是很有可能已經(jīng)使用了Apache DeltaSpike 。 這個項目提供了@ConfigProperty批注,該批注允許您將環(huán)境變量注入CDI bean中(它的作用比實際更多) 。
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;}}該bean可以與@Factory方法結(jié)合使用,以便我們可以將配置傳遞給工廠本身。
但是,如果我們有多個數(shù)據(jù)庫服務器,配置了不同的憑據(jù)集或多個數(shù)據(jù)庫怎么辦? 在這種情況下,我們可以使用服務名稱作為前綴,并讓Fabric8確定應該為每個@Configuration實例查找哪些環(huán)境變量。
import javax.inject.Inject; import io.fabric8.annotations.ServiceName; import io.fabric8.annotations.Factory; import io.fabric8.annotations.Protocol; import io.fabric8.annotations.Configuration;public class MysqlExampleWithFactoryAndConfiguration {@Factory@ServiceNamepublic Connection createConnection(@ServiceName @Protocol("jdbc:mysql") String url, @Configuration MysqlConfiguration conf) {Class.forName("com.mysql.jdbc.Driver");return DriverManager.getConnection(serivceUrl + "/" + conf.getDatabaseName(), conf.getUsername(), conf.getPassword()); } }現(xiàn)在,我們有了一個可重用的組件,可以與在kubernetes內(nèi)部運行的任何mysql數(shù)據(jù)庫一起使用,并且可以完全配置。
Fabric8 CDI擴展中還有其他功能,但是由于本篇文章過長,以后的文章中將介紹它們。
敬請關注。
翻譯自: https://www.javacodegeeks.com/2015/06/injecting-kubernetes-services-in-cdi-managed-beans-using-fabric8.html
總結(jié)
以上是生活随笔為你收集整理的使用Fabric8在CDI管理的bean中注入Kubernetes Services的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 命名约定_Java命名约定
- 下一篇: 影目推出 AR 眼镜 INMO GO:M