Spring Cloud Config爲分佈式系統中的外部化配置提供服務器和客戶端支持。使用Config Server,您可以集中管理所有環境中應用程序的外部屬性。 當應用程序從開發人員到測試人員進入生產過程進入生產過程時,您可以管理這些環境之間的配置,並確保應用程序具有它們遷移時所需的一切。
1 快速開始
用於構建分佈式配置的開源項目如下所示:
項目名稱 |
描述 |
特點 |
Etcd |
使用Go開發的開源項目,用於服務發現和鍵值管理,使用raft協議作爲它的分佈式計算模型 |
非常快和可伸縮 可分佈式 命令行驅動 易於搭建和使用 |
Eureka |
由Netflix開發。久經測試,用於服務發現和鍵值管理 |
分佈式鍵值存儲 靈活,需要費些功夫去設置 提供開箱即用的動態客戶端刷新 |
項目名稱 |
描述 |
特點 |
Consul |
由Hashicorp開發,特性上類似於Etcd和Eureka,它的分佈式計算模型使用了不同的算法(SWIM協議) |
快速 提供本地服務發現功能,可直接與DNS集成 沒有提供開箱即用的動態客戶端刷新 |
ZooKeeper |
一個提供分佈式鎖定功能的Apache項目,經常用作訪問鍵值數據的配置管理解決方案 |
最古老的、最久經測試的解決方案 使用最爲複雜 可用作配置管理, 但只有在其他架構中已經使用了ZooKeeper的時候才考慮使用它 |
項目名稱 |
描述 |
特點 |
Spring Cloud server |
一個開源項目,提供不同後端支持的通用配置管理解決方案。它可以將Git、Eureka和Consul作爲後端進行整合 |
非分佈式鍵值存儲 提供了對Spring和非Spring服務的緊密集成 可以使用多個後端來存儲配置數據, 包括共享文件系統、Eureka、Consul和Git |
推薦使用Spring Cloud配置服務器。原因包括以下幾個:
- Spring Cloud配置服務器易於搭建和使用。
- Spring Cloud配置與Spring Boot緊密集成。
- Spring Cloud配置服務器提供多個後端用於存儲配置數據。如果讀者已經使用了Eureka和Consul等工具,那麼可以將它們直接插入Spring Cloud配置服務器中。
- Spring Cloud配置服務器可以直接與Git源控制平臺集成。Spring Cloud配置與Git的集成消除了解決方案的額外依賴,並使版本化應用程序配置數據成爲可能。
Postman
Postman是用於API開發的協作平臺。Postman的功能簡化了構建API的每個步驟並簡化了協作,因此您可以更快地創建更好的API。
MySQL數據庫
MySQL是一種開放源代碼的關係型數據庫管理系統(RDBMS),使用最常用的數據庫管理語言--結構化查詢語言(SQL)進行數據庫管理。
創建一個MySQL數據庫eagle_eye_local
創建licenses許可證表
DROP TABLE IF EXISTS licenses;
CREATE TABLE licenses (
license_id VARCHAR(100) PRIMARY KEY NOT NULL,
organization_id TEXT NOT NULL,
license_type TEXT NOT NULL,
product_name TEXT NOT NULL,
license_max INT NOT NULL,
license_allocated INT,
comment VARCHAR(100));
INSERT INTO licenses (license_id, organization_id, license_type, product_name, license_max, license_allocated)
VALUES ('f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a', 'e254f8c-c442-4ebe-a82a-e2fc1d1ff78a', 'user','customer-crm-co', 100,5);
INSERT INTO licenses (license_id, organization_id, license_type, product_name, license_max, license_allocated)
VALUES ('t9876f8c-c338-4abc-zf6a-ttt1', 'e254f8c-c442-4ebe-a82a-e2fc1d1ff78a', 'user','suitability-plus', 200,189);
INSERT INTO licenses (license_id, organization_id, license_type, product_name, license_max, license_allocated)
VALUES ('38777179-7094-4200-9d61-edb101c6ea84', '442adb6e-fa58-47f3-9ca2-ed1fecdfe86c', 'user','HR-PowerSuite', 100,4);
INSERT INTO licenses (license_id, organization_id, license_type, product_name, license_max, license_allocated)
VALUES ('08dbe05-606e-4dad-9d33-90ef10e334f9', '442adb6e-fa58-47f3-9ca2-ed1fecdfe86c', 'core-prod','WildCat Application Gateway', 16,16);
2 Spring Cloud Config 服務器
Spring Cloud Config Server爲外部配置提供了一個基於HTTP資源的API。
創建一個Spring Boot微服務作爲Spring Cloud Config 服務端。
在pom.xml中增加以下依賴jar包:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
像所有Spring Boot應用程序一樣,它默認在端口8080上運行,但是您可以通過各種方式將其切換到更傳統的端口8888。如以下示例所示:
server:
port: 8888
spring:
application:
name: configserver
profiles:
active: native
cloud:
config:
server:
native:
search-locations: classpath:config/,classpath:config/licensingservice
應用程序配置文件的命名約定是“應用程序名稱-環境名稱.yml”。 環境名稱直接轉換爲可以瀏覽配置信息的 URL。隨後,啓動許可證微服務示例時,要運行哪個服務環境是由在命令行服務啓動時傳入的 Spring Boot 的 profile 指定的。如果在命令行上沒有傳入profile,Spring Boot將始終默認加載隨應用程序打包的application.yml文件中的配置數據。
config/licensingservice/licensingservice.yml
example.property: "I AM IN THE DEFAULT"
spring.jpa.database: "MYSQL"
spring.jpa.show-sql: "true"
spring.datasource.platform: "mysql"
spring.database.driver-class-name: "com.mysql.cj.jdbc.Driver"
spring.datasource.url: "jdbc:mysql://localhost:3306/eagle_eye_local?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC"
spring.datasource.username: "root"
spring.datasource.password: "{cipher}dcd4e3e74b79da1ae5c8b383e1c40d1c4793b51e275c0ed3d9331ac64da2053b"
spring.datasource.testWhileIdle: "true"
spring.datasource.validationQuery: "SELECT 1"
spring.jpa.properties.hibernate.dialect: "org.hibernate.dialect.MySQLDialect"
licensingservice-dev.yml
spring.jpa.database: "MYSQL"
spring.datasource.platform: "mysql"
spring.jpa.show-sql: "false"
spring.database.driver-class-name: "com.mysql.cj.jdbc.Driver"
spring.datasource.url: "jdbc:mysql://localhost:3306/eagle_eye_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC"
spring.datasource.username: "root"
spring.datasource.password: "123456"
spring.datasource.testWhileIdle: "true"
spring.datasource.validationQuery: "SELECT 1"
spring.jpa.properties.hibernate.dialect: "org.hibernate.dialect.MySQLDialect"
licensingservice-test.yml
spring.jpa.database: "MYSQL"
spring.datasource.platform: "mysql"
spring.jpa.show-sql: "false"
spring.database.driver-class-name: "com.mysql.cj.jdbc.Driver"
spring.datasource.url: "jdbc:mysql://localhost:3306/eagle_eye_test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC"
spring.datasource.username: "root"
spring.datasource.password: "123456"
spring.datasource.testWhileIdle: "true"
spring.datasource.validationQuery: "SELECT 1"
spring.jpa.properties.hibernate.dialect: "org.hibernate.dialect.MySQLDialect"
licensingservice-prod.yml
spring.jpa.database: "MYSQL"
spring.datasource.platform: "mysql"
spring.jpa.show-sql: "false"
spring.database.driver-class-name: "com.mysql.cj.jdbc.Driver"
spring.datasource.url: "jdbc:mysql://localhost:3306/eagle_eye_prod?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC"
spring.datasource.username: "root"
spring.datasource.password: "123456"
spring.datasource.testWhileIdle: "true"
spring.datasource.validationQuery: "SELECT 1"
spring.jpa.properties.hibernate.dialect: "org.hibernate.dialect.MySQLDialect"
通過使用@EnableConfigServer註釋,服務器可嵌入到Spring Boot應用程序中。因此,以下應用程序是配置服務器:
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
啓動服務,訪問http://localhost:8888/licensingservice/default,查詢配置信息,如下圖所示:
3 Spring Cloud Config 客戶端
創建一個Spring Boot微服務作爲Spring Cloud Config 客戶端。
在pom.xml中增加以下依賴jar包:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
application.yml
spring:
application:
name: licensingservice
profiles:
active:
default
cloud:
config:
uri: http://localhost:8888
LicensingServiceApplication.java
@SpringBootApplication
public class LicensingServiceApplication {
public static void main(String[] args) {
SpringApplication.run(LicensingServiceApplication.class, args);
}
}
ServiceConfig.java
package com.example.licenses.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class ServiceConfig{
@Value("${example.property}")
private String exampleProperty;
public String getExampleProperty(){
return exampleProperty;
}
}
License.java
package com.example.licenses.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "licenses")
public class License {
@Id
@Column(name = "license_id", nullable = false)
private String licenseId;
@Column(name = "organization_id", nullable = false)
private String organizationId;
@Column(name = "product_name", nullable = false)
private String productName;
@Column(name = "license_type", nullable = false)
private String licenseType;
@Column(name = "license_max", nullable = false)
private Integer licenseMax;
@Column(name = "license_allocated", nullable = false)
private Integer licenseAllocated;
@Column(name = "comment")
private String comment;
public Integer getLicenseMax() {
return licenseMax;
}
public void setLicenseMax(Integer licenseMax) {
this.licenseMax = licenseMax;
}
public Integer getLicenseAllocated() {
return licenseAllocated;
}
public void setLicenseAllocated(Integer licenseAllocated) {
this.licenseAllocated = licenseAllocated;
}
public String getLicenseId() {
return licenseId;
}
public void setLicenseId(String licenseId) {
this.licenseId = licenseId;
}
public String getOrganizationId() {
return organizationId;
}
public void setOrganizationId(String organizationId) {
this.organizationId = organizationId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getLicenseType() {
return licenseType;
}
public void setLicenseType(String licenseType) {
this.licenseType = licenseType;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public License withId(String id) {
this.setLicenseId(id);
return this;
}
public License withOrganizationId(String organizationId) {
this.setOrganizationId(organizationId);
return this;
}
public License withProductName(String productName) {
this.setProductName(productName);
return this;
}
public License withLicenseType(String licenseType) {
this.setLicenseType(licenseType);
return this;
}
public License withLicenseMax(Integer licenseMax) {
this.setLicenseMax(licenseMax);
return this;
}
public License withLicenseAllocated(Integer licenseAllocated) {
this.setLicenseAllocated(licenseAllocated);
return this;
}
public License withComment(String comment) {
this.setComment(comment);
return this;
}
}
LicenseRepository.java
package com.example.licenses.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.example.licenses.model.License;
import java.util.List;
@Repository
public interface LicenseRepository extends CrudRepository<License, String> {
public List<License> findByOrganizationId(String organizationId);
public License findByOrganizationIdAndLicenseId(String organizationId, String licenseId);
}
LicenseService.java
package com.example.licenses.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.licenses.config.ServiceConfig;
import com.example.licenses.model.License;
import com.example.licenses.repository.LicenseRepository;
import java.util.List;
import java.util.UUID;
@Service
public class LicenseService {
@Autowired
private LicenseRepository licenseRepository;
@Autowired
ServiceConfig config;
public License getLicense(String organizationId, String licenseId) {
License license = licenseRepository.findByOrganizationIdAndLicenseId(organizationId, licenseId);
if (license != null) {
license.withComment(config.getExampleProperty());
}
return license;
}
public List<License> getLicensesByOrg(String organizationId) {
return licenseRepository.findByOrganizationId(organizationId);
}
public void saveLicense(License license) {
license.withId(UUID.randomUUID().toString());
licenseRepository.save(license);
}
public void updateLicense(License license) {
licenseRepository.save(license);
}
public void deleteLicense(String licenseId) {
licenseRepository.deleteById(licenseId);
}
}
LicenseServiceController.java
package com.example.licenses.controllers;
import com.example.licenses.model.License;
import com.example.licenses.services.LicenseService;
import com.example.licenses.config.ServiceConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.List;
@RestController
@RequestMapping(value = "organizations/{organizationId}/licenses")
public class LicenseServiceController {
@Autowired
private LicenseService licenseService;
@Autowired
private ServiceConfig serviceConfig;
/**
* 查詢所有對象
*
* @param organizationId
* @return
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public List<License> getLicenses(@PathVariable("organizationId") String organizationId) {
return licenseService.getLicensesByOrg(organizationId);
}
/**
* 查詢單個對象
*
* @param organizationId
* @param licenseId
* @return
*/
@RequestMapping(value = "/{licenseId}", method = RequestMethod.GET)
public License getLicenses(@PathVariable("organizationId") String organizationId,
@PathVariable("licenseId") String licenseId) {
return licenseService.getLicense(organizationId, licenseId);
}
/**
* 新增對象
*
* @param license
*/
@RequestMapping(value = "/", method = RequestMethod.POST)
public void saveLicenses(@RequestBody License license) {
licenseService.saveLicense(license);
}
/**
* 更新對象
*
* @param licenseId
* @return
*/
@RequestMapping(value = "/", method = RequestMethod.PUT)
public String updateLicenses(@RequestBody License license) {
licenseService.updateLicense(license);
return String.format("This is the put");
}
/**
* 刪除對象
*
* @param licenseId
* @return
*/
@RequestMapping(value = "{licenseId}", method = RequestMethod.DELETE)
// @ResponseStatus(HttpStatus.NO_CONTENT)
public String deleteLicenses(@PathVariable("licenseId") String licenseId) {
licenseService.deleteLicense(licenseId);
return String.format("This is the Delete");
}
}
啓動服務,訪問http://localhost:8080/organizations/e254f8c-c442-4ebe-a82a-e2fc1d1ff78a/licenses/f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a,查詢許可證信息,如下圖所示: