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,查询许可证信息,如下图所示: