在传统单体式应用中管理配置很直接,配置文件基本位于应用所在服务器上。如果需要更新配置文件,只需要修改属性文件并重启应用就可以。但是对于微服务来说,事情稍微有些复杂。。
微服务由很多小的匿名服务构成,每个都有其配置文件,分布在不同的多个服务器的多个服务中。在生产环境,每个服务都可能有多个实例,配置管理更是很复杂的任务。
云应用会更加复杂,云环境倾向于规模不确定的扩展式应用,这种不确定性给升级和确保正确配置带来挑战。
顾名思义,每个实例运行时从配置集中管理库中获得其配置文件。Spring通过Spring Cloud的子项目Spring Cloud Config完成这一任务。可以通过它创建通过REST API提供服务的Spring Boot 应用。服务通过REST API获得应用配置文件,此文件是保存在Git库中,因此可以进行版本控制。Spring Cloud Config可以选择本地库或者远程库。生产环境中,最好通过访问Git私有库。
本篇文章将会配置一个可以从Github上拉配置的服务,将演示一个银行账号服务如何使用其它服务提供的配置。参考如下架构图。源代码可以从 github 上获得。
【图一】
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
```
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns=" http://maven.apache.org/POM/4.0.0" xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" ;>
<modelVersion>4.0.0</modelVersion>
<groupId>com.briansjavablog.microservices</groupId>
<artifactId>config-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>config-server</name>
<description>Demo config server provides centralised configuration for various micro services</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>-
```
spring.application.name=config-server server.port=8888
spring.cloud.config.server.git.uri=https://github.com/briansjavablog/micro-services-spring-cloud-config
spring.cloud.config.server.git.searchPaths: configuration
logging.level.org.springframework.web=INFO
现在可以运行一个测试,可以在Eclipse或者命令行启动应用,并在端口8888上看到它。
【图二】
用默认属性调用 http://localhost:8888/bank-account-service/default .
收到请求后,Config Service使用GIT URI复制远程的库。参看下图:
【图三】
Bank-account-service.properties返回配置信息,并以JSON方式显示如下:
{
"name": "bank-account-service",
"profiles": ["default"],
"label": null,
"version": "7b0732778b442726f8dd0bf7d1a36fc00f15c5b8",
"state": null,
"propertySources": [{
"name": "https://github.com/briansjavablog/micro-services-spring-cloud-config/configuration/bank-account-service.properties",
"source": {
"bank-account-service.minBalance": "99",
"bank-account-service.maxBalance": "200"
}
}]
}
现在创建一个bank account service调用以上属性。Bank Account Service有两个服务,一个是创建账号,一个是接受账号。
@RestController
@Slf4j
public class BankAccountController {
@Autowired
public BankAccountService bankAccountService;
@PostMapping("/bank-account")
public ResponseEntity << ? > createBankAccount(@RequestBody BankAccount bankAccount, HttpServletRequest request) throws URISyntaxException {
bankAccountService.createBankAccount(bankAccount);
log.info("created bank account {}", bankAccount);
URI uri = new URI(request.getRequestURL() + "bank-account/" + bankAccount.getAccountId());
return ResponseEntity.created(uri).build();
}
@GetMapping("/bank-account/{accountId}")
public ResponseEntity<BankAccount> getBankAccount(@PathVariable("accountId") String accountId) {
BankAccount account = bankAccountService.retrieveBankAccount(accountId);
log.info("retrieved bank account {}", account);
return ResponseEntity.ok(account);
}
}
生成一个新账号时,服务会通过一系列最大最小值检查账号是否有结余。
/**
* Add account to cache
*
* @param account
*/
public void createBankAccount(BankAccount account) {
/* check balance is within allowed limits */
if(account.getAccountBlance().doubleValue() >= config.getMinBalance() &&
account.getAccountBlance().doubleValue() <= config.getMaxBalance()) {
log.info("Account balance [{}] is is greater than lower bound [{}] and less than upper bound [{}]",
account.getAccountBlance(), config.getMinBalance(), config.getMaxBalance());
accountCache.put(account.getAccountId(), account);
}
else {
log.info("Account balance [{}] is outside of lower bound [{}] and upper bound [{}]",
account.getAccountBlance(), config.getMinBalance(), config.getMaxBalance());
throw new InvalidAccountBalanceException("Bank Account Balance is outside of allowed thresholds");
}
}
最大最小值可以配置,并从输入的Configuration对象读入。
@Service
@Slf4j
public class BankAccountService {
@Autowired
private Configuration config;
Configuration对象的定义如下:
@Component
@ConfigurationProperties(prefix="bank-account-service")
public class Configuration {
@Setter
@Getter
private Double minBalance;
@Setter
@Getter
private Double maxBalance;
}
Github的截屏显示@Configuration类中的名字和属性值
【图四】
使用Bank Account Service配置之前, 还是有一些需要本地配置的地方:
spring.application.name=bank-account-service server.port=8080 spring.config.cloud.uri=htp://localhost:8888 spring.cloud.config.profile=uat management.endpoints.web.exposure.include=*
最终调用命令:
http://localhost:8888/bank-account-service/uat.
运行以下命令生成一个bank account:
curl -i -H "Content-Type: application/json" -X POST -d '{"accountId":"B12345","accountName":"Joe Bloggs","accountType":"CURRENT_ACCOUNT","accountBlance":1250.38}' localhost:8080/bank-account
注意现在的账号余额是1250.38英镑,处在UAT定义账户操作范围之内。
【图五】
从日志中可以看出,账号成功创建,最小最大值分别是501.0和15002.0,符合UAT定义范围。
通过git上传配置,并且能够在客户端启动时候自动获得最新配置信息。但是如果运行中,如何更新配置信息呢?幸运的是Spring Boot提供了一个机制可以在运行实例中更新配置的机制。
curl localhost:8080/actuator/refresh -d {} -H "Content-Type: application/json
从截屏可以看到,/refresh被调用,可以通过UAT来接收最新属性。
【图六】
尽管可以手动更新配置,但是我们也可以写一个shell自动获取更新。这样可以大大减少把精力投入更新配置的过程中。
本篇文章中使用Spring Cloud Config创建一个基于Github的集中配置管理机制。也介绍了管理不同环境下属性的方法。有任何问题,可以和本博主联系。