1. 程式人生 > >《Spring微服務實戰》讀書筆記——通過配置伺服器來管理配置

《Spring微服務實戰》讀書筆記——通過配置伺服器來管理配置

管理配置

管理配置的四個原則

  • 隔離
    將服務配置資訊與服務的例項物理部署完全分離
  • 抽象
    抽象服務介面背後的配置資料訪問
  • 集中
    將應用程式配置集中到儘可能少的儲存庫中
  • 穩定
    實現高可用和冗餘

建立Spring Cloud 配置伺服器

Spring Cloud配置伺服器是構建在Spring Boot之上基於REST的應用程式。

新增一個新的專案,稱為confsvr。

新增如下依賴:

    <dependency>
			<groupId>org.springframework.cloud</groupId
>
<artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>

我們會使用license服務作為使用Spring Cloud 配置服務的客戶端。為了簡單,我們將使用三個環境來設定應用的配置資料:default,用於本地執行;dev以及prod。

在每個環境中,將會設定兩個配置屬性

  • 由license服務直接使用的示例屬性
  • 儲存license服務資料的Postgres資料的配置屬性

  1. 增加@EnableConfigServer註解
@SpringBootApplication
@EnableConfigServer
public class ConfsvrApplication {

	public static void main(String[] args) {
		SpringApplication.run(ConfsvrApplication.class, args);
	}
}
  1. 新增bootstrap.yml檔案
server:
  port: 8888
spring:
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/safika/eagle-eye-config.git
          # 查詢配置檔案的位置,可通過,分隔
          search-paths: licensea

啟動,訪問http://localhost:8888/license/dev,可以看到輸出如下:

{
	name: "license",
	profiles: [
		"dev"
	],
	label: null,
	version: "13fc97719dca692cc0fe793eb62bedfca42766da",
	state: null,
	propertySources: [{
		name: "https://gitee.com/safika/eagle-eye-config.git/license/license-dev.yml",
		source: {
			tracer.property: "I AM THE DEFAULT",
			spring.jpa.show - sql: true,
			management.security.enabled: false,
			endpoints.health.sensitive: false
		}
	}]
}

新建配置服務客戶端

  1. 新增依賴
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-client</artifactId>
		</dependency>
		
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>

我們使用h2嵌入式資料庫用於開發測試,因為Spring boot能幫我自動配置預設屬性,因此不需要我們自己寫預設資料庫名稱,驅動,使用者名稱,密碼等。

  1. 指定配置檔案
spring:
  application:
    # 和配置伺服器指定的目錄相同
    name: license
  profiles:
    active: dev
  cloud:
    config:
      uri: http://localhost:8888 # 指定配置伺服器的地址

關鍵步驟已經完成了,接下來是一些其他類的編寫

package com.learn.license.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class ServiceConfig{

  // 通過@Value註解注入配置檔案中的屬性
  @Value("${tracer.property}")
  private String exampleProperty;

  public String getExampleProperty(){
    return exampleProperty;
  }
}

package com.learn.license.controllers;

import com.learn.license.model.License;
import com.learn.license.services.LicenseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @RequestMapping 暴露Controller的根URL
 * {organizationId}是一個佔位符,表示URL在每次呼叫會傳遞一個organizationId引數
 */
@RestController
@RequestMapping(value = "/v1/organizations/{organizationId}/licenses")
public class LicenseServiceController {

    @Autowired
    private LicenseService licenseService;


    @RequestMapping(value="/",method = RequestMethod.GET)
    public List<License> getLicenses(@PathVariable("organizationId") String organizationId) {
        return licenseService.getLicensesByOrg(organizationId);
    }

    @RequestMapping(value="/{licenseId}",method = RequestMethod.GET)
    public License getLicenses( @PathVariable("organizationId") String organizationId,
                                @PathVariable("licenseId") String licenseId) {

        return licenseService.getLicense(organizationId,licenseId);
    }

    @RequestMapping(value="{licenseId}",method = RequestMethod.PUT)
    public String updateLicenses( @PathVariable("licenseId") String licenseId) {
        return String.format("This is the put");
    }

    @RequestMapping(value="/",method = RequestMethod.POST)
    public void saveLicenses(@RequestBody License license) {
        licenseService.saveLicense(license);
    }

    @RequestMapping(value="{licenseId}",method = RequestMethod.DELETE)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public String deleteLicenses( @PathVariable("licenseId") String licenseId) {
        return String.format("This is the Delete");
    }
}


package com.learn.license.model;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Data
@AllArgsConstructor
@NoArgsConstructor
@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 License withComment(String comment){
    this.setComment(comment);
    return this;
  }

}

package com.learn.license.repository;

import com.learn.license.model.License;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface LicenseRepository  extends CrudRepository<License,String> {
    List<License> findByOrganizationId(String organizationId);
    License findByOrganizationIdAndLicenseId(String organizationId,String licenseId);
}


package com.learn.license.services;

import com.learn.license.config.ServiceConfig;
import com.learn.license.model.License;
import com.learn.license.repository.LicenseRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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);
        return license.withComment(config.getExampleProperty());
    }

    public List<License> getLicensesByOrg(String organizationId){
        return licenseRepository.findByOrganizationId( organizationId );
    }

    public void saveLicense(License license){
        license.setLicenseId( UUID.randomUUID().toString());
        licenseRepository.save(license);
    }

    public void updateLicense(License license){
      licenseRepository.save(license);
    }

    public void deleteLicense(License license){
        licenseRepository.delete( license.getLicenseId());
    }

}

以及data.sql(放到src/resource目錄下,一般用於初始化資料)

DELETE FROM licenses;



INSERT INTO licenses (license_id,  organization_id, license_type, product_name, license_max, license_allocated)
VALUES ('f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a', '1', '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', '1', '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', '2', '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', '2', 'core-prod','WildCat Application Gateway', 16,16);

啟動,訪問http://localhost:8080/v1/organizations/1/licenses/f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a

得到輸出如下

{
    "licenseId": "f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a",
    "organizationId": "1",
    "productName": "customer-crm-co",
    "licenseType": "user",
    "licenseMax": 100,
    "licenseAllocated": 5,
    "comment": "I AM THE DEFAULT"
}

完成專案地址: https://gitee.com/safika/eagle-eye