1. 概述
在本教程中,我們將介紹 Spring Cloud Open Service Broker 項目,並學習如何實現 Open Service Broker API。
首先,我們將深入瞭解 Open Service Broker API 的規範。然後,我們將學習如何使用 Spring Cloud Open Service Broker 構建實現這些規範的應用。
最後,我們將探討我們可用於保護服務提供者端點的安全機制。
2. Open Service Broker API
Open Service Broker API 項目允許我們快速為在 Cloud Foundry 和 Kubernetes 等雲原生平台上運行的應用程序提供背端服務。
本質上,API 規範描述了一組 REST 端點,通過這些端點,我們可以提供和連接到這些服務。
特別是,我們可以利用雲原生平台的服務提供商,來:
- 發佈背端服務的目錄
- 提供服務實例
- 創建和刪除背端服務與客户端應用程序之間的綁定
- 取消提供服務實例
Spring Cloud Open Service Broker 為符合 Open Service Broker API 規範的實現提供基礎,通過提供所需的 Web 控制器、領域對象和配置。 此外,我們還需要通過實現適當的 服務提供商接口,來制定我們的業務邏輯。
3. 自動配置
為了在我們的應用程序中使用 Spring Cloud Open Service Broker,我們需要添加相關的 starter 構件。我們可以使用 Maven Central 搜索最新版本的 open-service-broker starter。
除了雲端 starter 之外,我們還需要包含 Spring Boot Web starter,以及 Spring WebFlux 或 Spring MVC,以啓用自動配置:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-open-service-broker</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>自動配置機制會為我們需要的服務編排器的大部分組件配置默認實現。如果需要,我們可以通過提供我們自己的 open-service-broker 與 Spring 相關的 Bean 實現來覆蓋默認行為。
3.1. 服務編排端點路徑配置
默認情況下,服務編排端點註冊的上下文路徑為“/”。
如果這不理想,並且我們希望更改它,最直接的方法是在應用程序的屬性文件中或 YAML 文件中設置 spring.cloud.openservicebroker.base-path 屬性:
spring:
cloud:
openservicebroker:
base-path: /broker在這種情況下,為了查詢服務編排終點,我們首先需要在請求前添加 /broker/ 基礎路徑。
4. 一個服務中介示例
讓我們使用 Spring Cloud Open Service Broker 庫創建一個服務中介應用程序,並探索其 API 的工作方式。
通過我們的示例,我們將使用服務中介來配置和連接到後端的郵件系統。為了簡化起見,我們將使用代碼示例中提供的虛擬郵件 API。
4.1. 服務目錄
首先,為了控制我們的服務經紀人提供的服務,我們需要定義一個服務目錄。為了快速初始化服務目錄,在我們的示例中,我們將提供一個 Spring Bean,類型為 <a href="https://docs.spring.io/spring-cloud-open-service-broker/docs/current/apidocs//org/springframework/cloud/servicebroker/model/catalog/Catalog.html">Catalog</a>。
@Bean
public Catalog catalog() {
Plan mailFreePlan = Plan.builder()
.id("fd81196c-a414-43e5-bd81-1dbb082a3c55")
.name("mail-free-plan")
.description("Mail Service Free Plan")
.free(true)
.build();
ServiceDefinition serviceDefinition = ServiceDefinition.builder()
.id("b92c0ca7-c162-4029-b567-0d92978c0a97")
.name("mail-service")
.description("Mail Service")
.bindable(true)
.tags("mail", "service")
.plans(mailFreePlan)
.build();
return Catalog.builder()
.serviceDefinitions(serviceDefinition)
.build();
}如上所示,服務目錄包含描述服務編排器可提供的所有可用服務的元數據。此外,服務的定義是故意寬泛的,因為它可能指數據庫、消息隊列,或者,在我們的例子中,郵件服務。
另一個關鍵點是,每個服務都是由計劃構建起來的,這本身就是一個通用術語。每個計劃都可能提供不同的功能併產生不同的成本。
最終,服務目錄通過服務編排器的 /v2/catalog 端點向雲原生平台提供:
curl http://localhost:8080/broker/v2/catalog
{
"services": [
{
"bindable": true,
"description": "Mail Service",
"id": "b92c0ca7-c162-4029-b567-0d92978c0a97",
"name": "mail-service",
"plans": [
{
"description": "Mail Service Free Plan",
"free": true,
"id": "fd81196c-a414-43e5-bd81-1dbb082a3c55",
"name": "mail-free-plan"
}
],
"tags": [
"mail",
"service"
]
}
]
}因此,雲原生平台將從所有服務提供商處查詢服務目錄終結點,以提供服務目錄的聚合視圖。
4.2. 服務提供
一旦我們開始推廣服務,我們也需要為我們的經紀人提供機制,以便在雲平台上提供和管理這些服務的生命週期。
此外,不同經紀人對“提供”的含義可能不同。在某些情況下,提供可能包括啓動空數據庫、創建消息代理或僅提供訪問外部 API 的帳户。
在術語方面,服務代理商創建的服務將被稱為服務實例。
通過 Spring Cloud Open Service Broker,我們可以通過實現 ServiceInstanceService 接口來管理服務生命週期。例如,為了在我們的服務代理商中管理服務提供請求,我們必須提供 createServiceInstance 方法的實現:
@Override
public Mono<CreateServiceInstanceResponse> createServiceInstance(
CreateServiceInstanceRequest request) {
return Mono.just(request.getServiceInstanceId())
.flatMap(instanceId -> Mono.just(CreateServiceInstanceResponse.builder())
.flatMap(responseBuilder -> mailService.serviceInstanceExists(instanceId)
.flatMap(exists -> {
if (exists) {
return mailService.getServiceInstance(instanceId)
.flatMap(mailServiceInstance -> Mono.just(responseBuilder
.instanceExisted(true)
.dashboardUrl(mailServiceInstance.getDashboardUrl())
.build()));
} else {
return mailService.createServiceInstance(
instanceId, request.getServiceDefinitionId(), request.getPlanId())
.flatMap(mailServiceInstance -> Mono.just(responseBuilder
.instanceExisted(false)
.dashboardUrl(mailServiceInstance.getDashboardUrl())
.build()));
}
})));
}在這裏,我們為內部映射分配一個新的郵件服務,如果存在具有相同服務實例 ID 的服務,則不執行此操作,並提供儀表盤 URL。可以將儀表盤視為我們服務實例的 Web 管理界面。
雲原生平台通過 /v2/service_instances/{instance_id} 端點進行服務配置:
curl -X PUT http://localhost:8080/broker/v2/service_instances/[email protected]
-H 'Content-Type: application/json'
-d '{
"service_id": "b92c0ca7-c162-4029-b567-0d92978c0a97",
"plan_id": "fd81196c-a414-43e5-bd81-1dbb082a3c55"
}'
{"dashboard_url":"http://localhost:8080/mail-dashboard/[email protected]"}簡而言之,當我們啓用一個新的服務時,我們需要傳遞來自服務目錄中 service_id 和 plan_id。 此外,還需要提供一個唯一的 instance_id,我們的服務代理將在未來的綁定和停用請求中使用。
4.3. 服務綁定
在配置服務後,我們希望客户端應用程序能夠與之進行通信。從服務編排人的角度來看,這被稱為服務綁定。
類似於服務實例和計劃,我們應該將綁定視為一種靈活的抽象,可以在我們的服務編排人中使用。通常,我們將提供服務綁定以暴露用於訪問服務實例的憑據。
在我們的示例中,如果宣傳的服務具有 bindable 字段設置為 true,則我們的服務編排人必須實現 ServiceInstanceBindingService 接口。 否則,雲平台將不會從我們的服務編排人調用服務綁定方法。
讓我們通過提供對 createServiceInstanceBinding 方法的實現來處理服務綁定創建請求:
@Override
public Mono<CreateServiceInstanceBindingResponse> createServiceInstanceBinding(
CreateServiceInstanceBindingRequest request) {
return Mono.just(CreateServiceInstanceAppBindingResponse.builder())
.flatMap(responseBuilder -> mailService.serviceBindingExists(
request.getServiceInstanceId(), request.getBindingId())
.flatMap(exists -> {
if (exists) {
return mailService.getServiceBinding(
request.getServiceInstanceId(), request.getBindingId())
.flatMap(serviceBinding -> Mono.just(responseBuilder
.bindingExisted(true)
.credentials(serviceBinding.getCredentials())
.build()));
} else {
return mailService.createServiceBinding(
request.getServiceInstanceId(), request.getBindingId())
.switchIfEmpty(Mono.error(
new ServiceInstanceDoesNotExistException(
request.getServiceInstanceId())))
.flatMap(mailServiceBinding -> Mono.just(responseBuilder
.bindingExisted(false)
.credentials(mailServiceBinding.getCredentials())
.build()));
}
}));
}上述代碼生成了一組唯一的憑據——用户名、密碼和 URI——通過這些憑據,我們可以連接並驗證到我們的新郵件服務實例。
Spring Cloud Open Service Broker 框架通過 /v2/service_instances/{instance_id}/service_bindings/{binding_id} 端點暴露服務綁定操作:
curl -X PUT
http://localhost:8080/broker/v2/service_instances/[email protected]/service_bindings/admin
-H 'Content-Type: application/json'
-d '{
"service_id": "b92c0ca7-c162-4029-b567-0d92978c0a97",
"plan_id": "fd81196c-a414-43e5-bd81-1dbb082a3c55"
}'
{
"credentials": {
"password": "bea65996-3871-4319-a6bb-a75df06c2a4d",
"uri": "http://localhost:8080/mail-system/[email protected]",
"username": "admin"
}
}類似於服務實例的配置,我們會在綁定請求中包含服務目錄中廣播的 service_id 和 plan_id。此外,我們還會傳遞一個唯一的 binding_id,該 binding_id 由代理作為我們憑據集合中的用户名使用。
5. 服務提供者 API 安全
通常,當服務提供者和雲原生平台相互通信時,都需要使用認證機制。
不幸的是,Open Service Broker API 規範目前並未涵蓋服務提供者端點的認證部分。因此,Spring Cloud Open Service Broker 庫也沒有實現任何安全配置。
幸運的是,如果我們需要保護我們的服務提供者端點,我們可以快速使用 Spring Security 來實施 Basic 認證或 OAuth 2.0 機制。在這種情況下,我們應該使用我們選擇的認證機制對所有服務提供者請求進行認證,並在認證失敗時返回一個 401 Unauthorized 響應。
6. 結論
本文介紹了 Spring Cloud Open Service Broker 項目。
首先,我們學習了 Open Service Broker API 的概念,以及它如何允許我們提供和連接到後端服務。隨後,我們看到了如何使用 Spring Cloud Open Service Broker 庫快速構建符合 Service Broker API 的項目。
最後,我們討論瞭如何使用 Spring Security 安全地保護我們的服務提供者端點。