Spring Cloud Config 分佈式配置

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配置服務器。原因包括以下幾個:

  1. Spring Cloud配置服務器易於搭建和使用。
  2. Spring Cloud配置與Spring Boot緊密集成。
  3. Spring Cloud配置服務器提供多個後端用於存儲配置數據。如果讀者已經使用了Eureka和Consul等工具,那麼可以將它們直接插入Spring Cloud配置服務器中。
  4. 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,查詢許可證信息,如下圖所示:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章