Spring Cloud Config 是 Spring Cloud 家族中最早的配置中心,虽然后来又发布了 Consul 可以代替配置中心功能,但是 Config 依然适用于 Spring Cloud 项目,通过简单的配置即可实现功能。

配置文件是我们再熟悉不过的了,尤其是 Spring Boot 项目,除了引入相应的 maven 包之外,剩下的工作就是完善配置文件了,例如 mysql、redis 、security 相关的配置。除了项目运行的基础配置之外,还有一些配置是与我们业务有关系的,比如说七牛存储、短信相关、邮件相关,或者一些业务上的开关。

对于一些简单的项目来说,我们一般都是直接把相关配置放在单独的配置文件中,以 properties 或者 yml 的格式出现,更省事儿的方式是直接放到 application.properties 或 application.yml 中。但是这样的方式有个明显的问题,那就是,当修改了配置之后,必须重启服务,否则配置无法生效。

目前有一些用的比较多的开源的配置中心,比如携程的 Apollo、蚂蚁金服的 disconf 等,对比 Spring Cloud Config,这些配置中心功能更加强大。有兴趣的可以拿来试一试。

接下来,我们开始在 Spring Boot 项目中集成 Spring Cloud Config,并以 github 作为配置存储。除了 git 外,还可以用数据库、svn、本地文件等作为存储。主要从以下三块来说一下 Config 的使用。

1.基础版的配置中心(不集成 Eureka);

2.结合 Eureka 版的配置中心;

3.实现配置的自动刷新;

实现最简单的配置中心

最简单的配置中心,就是启动一个服务作为服务方,之后各个需要获取配置的服务作为客户端来这个服务方获取配置。

先在 github 中建立配置文件

我创建的仓库地址为:配置中心仓库

目录结构如下:

配置文件的内容大致如下,用于区分,略有不同。

data:
env: config-eureka-dev
user:
username: eureka-client-user
password: 1291029102

注意文件的名称不是乱起的,例如上面的 config-single-client-dev.yml 和 config-single-client-prod.yml 这两个是同一个项目的不同版本,项目名称为 config-single-client, 一个对应开发版,一个对应正式版。config-eureka-client-dev.yml 和 config-eureka-client-prod.yml 则是另外一个项目的,项目的名称就是 config-eureka-client 。

创建配置中心服务端

1、新建 Spring Boot 项目,引入 config-server 和 starter-web

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <!-- spring cloud config 服务端包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

2、配置 config 相关的配置项

bootstrap.yml 文件

spring:
application:
name: config-single-server # 应用名称
cloud:
config:
server:
git:
uri: https://github.com/huzhicheng/config-only-a-demo #配置文件所在仓库
username: github 登录账号
password: github 登录密码
default-label: master #配置文件分支
search-paths: config #配置文件所在根目录

application.yml

server:
port: 3301

3、在 Application 启动类上增加相关注解 @EnableConfigServer

@SpringBootApplication
@EnableConfigServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

启动服务,接下来测试一下。

Spring Cloud Config 有它的一套访问规则,我们通过这套规则在浏览器上直接访问就可以。

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

{application} 就是应用名称,对应到配置文件上来,就是配置文件的名称部分,例如我上面创建的配置文件。

{profile} 就是配置文件的版本,我们的项目有开发版本、测试环境版本、生产环境版本,对应到配置文件上来就是以 application-{profile}.yml 加以区分,例如application-dev.yml、application-sit.yml、application-prod.yml。

{label} 表示 git 分支,默认是 master 分支,如果项目是以分支做区分也是可以的,那就可以通过不同的 label 来控制访问不同的配置文件了。

上面的 5 条规则中,我们只看前三条,因为我这里的配置文件都是 yml 格式的。根据这三条规则,我们可以通过以下地址查看配置文件内容:

http://localhost:3301/config-single-client/dev/master

http://localhost:3301/config-single-client/prod

http://localhost:3301/config-single-client-dev.yml

http://localhost:3301/config-single-client-prod.yml

http://localhost:3301/master/config-single-client-prod.yml

通过访问以上地址,如果可以正常返回数据,则说明配置中心服务端一切正常。

创建配置中心客户端,使用配置

配置中心服务端好了,配置数据准备好了,接下来,就要在我们的项目中使用它了。

1、引用相关的 maven 包。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <!-- spring cloud config 客户端包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

2、初始化配置文件

bootstrap.yml

spring:
profiles:
active: dev ---
spring:
profiles: prod
application:
name: config-single-client
cloud:
config:
uri: http://localhost:3301
label: master
profile: prod ---
spring:
profiles: dev
application:
name: config-single-client
cloud:
config:
uri: http://localhost:3301
label: master
profile: dev

配置了两个版本的配置,并通过 spring.profiles.active 设置当前使用的版本,例如本例中使用的 dev 版本。

application.yml

server:
port: 3302
management:
endpoint:
shutdown:
enabled: false
endpoints:
web:
exposure:
include: "*" data:
env: NaN
user:
username: NaN
password: NaN

其中 management 是关于 actuator 相关的,接下来自动刷新配置的时候需要使用。

data 部分是当无法读取配置中心的配置时,使用此配置,以免项目无法启动。

3、要读取配置中心的内容,需要增加相关的配置类,Spring Cloud Config 读取配置中心内容的方式和读取本地配置文件中的配置是一模一样的。可以通过 @Value 或 @ConfigurationProperties 来获取。

@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

4、要读取配置中心的内容,需要增加相关的配置类,Spring Cloud Config 读取配置中心内容的方式和读取本地配置文件中的配置是一模一样的。可以通过 @Value 或 @ConfigurationProperties 来获取。

使用 @Value 的方式:

@Data
@Component
public class GitConfig { @Value("${data.env}")
private String env; @Value("${data.user.username}")
private String username; @Value("${data.user.password}")
private String password; }

使用 @ConfigurationProperties 的方式:

@Component
@Data
@ConfigurationProperties(prefix = "data")
public class GitAutoRefreshConfig { public static class UserInfo {
private String username; private String password; public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} @Override
public String toString() {
return "UserInfo{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
} private String env; private UserInfo user;
}

4、增加一个 RESTController 来测试使用配置

@RestController
public class GitController { @Autowired
private GitConfig gitConfig; @Autowired
private GitAutoRefreshConfig gitAutoRefreshConfig; @GetMapping(value = "show")
public Object show(){
return gitConfig;
} @GetMapping(value = "autoShow")
public Object autoShow(){
return gitAutoRefreshConfig;
}
}

5、项目启动类

@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

启动项目,访问 RESTful 接口

http://localhost:3302/show,结果如下:

{
"env": "localhost-dev-edit",
"username": "fengzheng-dev",
"password": "password-dev"
}

http://localhost:3302/autoShow,结果如下:

{
"env": "localhost-dev-edit",
"user": {
"username": "fengzheng-dev",
"password": "password-dev"
}
}

实现自动刷新

Spring Cloud Config 在项目启动时加载配置内容这一机制,导致了它存在一个缺陷,修改配置文件内容后,不会自动刷新。例如我们上面的项目,当服务已经启动的时候,去修改 github 上的配置文件内容,这时候,再次刷新页面,对不起,还是旧的配置内容,新内容不会主动刷新过来。

但是,总不能每次修改了配置后重启服务吧。如果是那样的话,还是不要用它了为好,直接用本地配置文件岂不是更快。

它提供了一个刷新机制,但是需要我们主动触发。那就是 @RefreshScope 注解并结合 actuator ,注意要引入 spring-boot-starter-actuator 包。

1、在 config client 端配置中增加 actuator 配置,上面大家可能就注意到了。

management:
endpoint:
shutdown:
enabled: false
endpoints:
web:
exposure:
include: "*"

其实这里主要用到的是 refresh 这个接口

2、在需要读取配置的类上增加 @RefreshScope 注解,我们是 controller 中使用配置,所以加在 controller 中。

@RestController
@RefreshScope
public class GitController { @Autowired
private GitConfig gitConfig; @Autowired
private GitAutoRefreshConfig gitAutoRefreshConfig; @GetMapping(value = "show")
public Object show(){
return gitConfig;
} @GetMapping(value = "autoShow")
public Object autoShow(){
return gitAutoRefreshConfig;
}
}

注意,以上都是在 client 端做的修改。

之后,重启 client 端,重启后,我们修改 github 上的配置文件内容,并提交更改,再次刷新页面,没有反应。没有问题。

接下来,我们发送 POST 请求到 http://localhost:3302/actuator/refresh 这个接口,用 postman 之类的工具即可,此接口就是用来触发加载新配置的,返回内容如下:

[
"config.client.version",
"data.env"
]

之后,再次访问 RESTful 接口,http://localhost:3302/autoShow 这个接口获取的数据已经是最新的了,说明 refresh 机制起作用了。

http://localhost:3302/show 获取的还是旧数据,这与 @Value 注解的实现有关,所以,我们在项目中就不要使用这种方式加载配置了。

在 github 中配置 Webhook

这就结束了吗,并没有,总不能每次改了配置后,就用 postman 访问一下 refresh 接口吧,还是不够方便呀。 github 提供了一种 webhook 的方式,当有代码变更的时候,会调用我们设置的地址,来实现我们想达到的目的。

1、进入 github 仓库配置页面,选择 Webhooks ,并点击 add webhook;

2、之后填上回调的地址,也就是上面提到的 actuator/refresh 这个地址,但是必须保证这个地址是可以被 github 访问到的。如果是内网就没办法了。这也仅仅是个演示,一般公司内的项目都会有自己的代码管理工具,例如自建的 gitlab,gitlab 也有 webhook 的功能,这样就可以调用到内网的地址了。

使用 Spring Cloud Bus 来自动刷新多个端

Spring Cloud Bus 将分布式系统的节点与轻量级消息代理链接。这可以用于广播状态更改(例如配置更改)或其他管理指令。一个关键的想法是,Bus 就像一个扩展的 Spring Boot 应用程序的分布式执行器,但也可以用作应用程序之间的通信渠道。

—— Spring Cloud Bus 官方解释

如果只有一个 client 端的话,那我们用 webhook ,设置手动刷新都不算太费事,但是如果端比较多的话呢,一个一个去手动刷新未免有点复杂。这样的话,我们可以借助 Spring Cloud Bus 的广播功能,让 client 端都订阅配置更新事件,当配置更新时,触发其中一个端的更新事件,Spring Cloud Bus 就把此事件广播到其他订阅端,以此来达到批量更新。

1、Spring Cloud Bus 核心原理其实就是利用消息队列做广播,所以要先有个消息队列,目前官方支持 RabbitMQ 和 kafka。

这里用的是 RabbitMQ, 所以先要搭一套 RabbitMQ 环境。请自行准备环境,这里不再赘述。我是用 docker 直接创建的,然后安装了 rabbitmq-management 插件,这样就可以在浏览器访问 15672 查看 UI 管理界面了。

2、在 client 端增加相关的包,注意,只在 client 端引入就可以。

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

3、在配置文件中增加 RabbitMQ 相关配置,默认的端口应该是 5672 ,因为我是用 docker 创建的,所以有所不同。

spring:
rabbitmq:
host: localhost
port: 32775
username: guest
password: guest

4、启动两个或多个 client 端,准备来做个测试

在启动的时候分别加上 vm option:-Dserver.port=3302 和 -Dserver.port=3303 ,然后分别启动就可以了。

5、分别打开 http://localhost:3302/autoShowhttp://localhost:3303/autoShow,查看内容,然后修改 github 上配置文件的内容并提交。再次访问这两个地址,数据没有变化。

6、访问其中一个的 actuator/bus-refresh 地址,注意还是要用 POST 方式访问。之后查看控制台输出,会看到这两个端都有一条这样的日志输出

o.s.cloud.bus.event.RefreshListener: Received remote refresh request. Keys refreshed

7、再次访问第 5 步的两个地址,会看到内容都已经更新为修改后的数据了。

综上所述,当我们修改配置后,使用 webhook ,或者手动触发的方式 POST 请求一个 client 端的 actuator/bus-refresh 接口,就可以更新给所有端了。

结合 Eureka 使用 Spring Cloud Config

以上讲了 Spring Cloud Config 最基础的用法,但是如果我们的系统中使用了 Eureka 作为服务注册发现中心,那么 Spring Cloud Config 也应该注册到 Eureka 之上,方便其他服务消费者使用,并且可以注册多个配置中心服务端,以实现高可用。

好的,接下来就来集成 Spring Cloud Config 到 Eureka 上。

在 github 仓库中增加配置文件

启动 Eureka Server

首先启动一个 Eureka Server,之前的文章有讲过 Eureka ,可以回过头去看看。Spring Cloud Eureka 实现服务注册发现,为了清楚,这里还是把配置列出来

1、pom 中引入相关包

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

2、设置配置文件内容

bootstrap.yml

spring:
application:
name: kite-eureka-center
security:
user:
name: test # 用户名
password: 123456 # 密码
cloud:
inetutils: ## 网卡设置
ignoredInterfaces: ## 忽略的网卡
- docker0
- veth.*
- VM.*
preferredNetworks: ## 优先的网段
- 192.168

application.yml

server:
port: 3000
eureka:
instance:
hostname: eureka-center
appname: 注册中心
client:
registerWithEureka: false # 单点的时候设置为 false 禁止注册自身
fetchRegistry: false
serviceUrl:
defaultZone: http://test:123456@localhost:3000/eureka
server:
enableSelfPreservation: false
evictionIntervalTimerInMs: 4000

3、Application 启动类

@EnableEurekaServer
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

4、启动服务,在浏览器访问 3000 端口,并输出用户名 test,密码 123456 即可进入 Eureka UI

配置 Spring Cloud Config 服务端

服务端和前面的相比也就是多了注册到 Eureka 的配置,其他地方都是一样的。

1、在 pom 中引入相关的包

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <!-- spring cloud config 服务端包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency> <!-- eureka client 端包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2、配置文件做配置

application.yml

server:
port: 3012
eureka:
client:
serviceUrl:
register-with-eureka: true
fetch-registry: true
defaultZone: http://test:123456@localhost:3000/eureka/
instance:
preferIpAddress: true
spring:
application:
name: config-eureka-server
cloud:
config:
server:
git:
uri: https://github.com/huzhicheng/config-only-a-demo
username: github 用户名
password: github 密码
default-label: master
search-paths: config

相比于不加 Eureka 的版本,这里仅仅是增加了 Eureka 的配置,将配置中心注册到 Eureka ,作为服务提供者对外提供服务。

3、启动类,在 @EnableConfigServer 的基础上增加了 @EnableEurekaClient,另外也可以用 @EnableDiscoveryClient 代替 @EnableEurekaClient

@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

4、启动服务,之后访问 Eureka ,可以看到服务已注册成功

配置 Spring Cloud Config 客户端

客户端的配置相对来说变动大一点,加入了 Eureka 之后,就不用再直接和配置中心服务端打交道了,要通过 Eureka 来访问。另外,还是要注意客户端的 application 名称要和 github 中配置文件的名称一致。

1、pom 中引入相关的包

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency> <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2、配置文件

bootstrap.yml

eureka:
client:
serviceUrl:
register-with-eureka: true
fetch-registry: true
defaultZone: http://test:123456@localhost:3000/eureka/
instance:
preferIpAddress: true spring:
profiles:
active: dev ---
spring:
profiles: prod
application:
name: config-eureka-client
cloud:
config:
label: master
profile: prod
discovery:
enabled: true
service-id: config-eureka-server ---
spring:
profiles: dev
application:
name: config-eureka-client
cloud:
config:
label: master #指定仓库分支
profile: dev #指定版本 本例中建立了dev 和 prod两个版本
discovery:
enabled: true # 开启
service-id: config-eureka-server # 指定配置中心服务端的server-id

除了注册到 Eureka 的配置外,就是配置和配置中心服务端建立关系。

其中 service-id 也就是服务端的application name。

application.yml

server:
port: 3011
management:
endpoint:
shutdown:
enabled: false
endpoints:
web:
exposure:
include: "*" data:
env: NaN
user:
username: NaN
password: NaN

3、启动类,增加了 @EnableEurekaClient 注解,可以用 @EnableDiscoveryClient 代替

@SpringBootApplication
@EnableEurekaClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

4、另外的配置实体类和 RESTController 和上面的一样,没有任何更改,直接参考即可。

5、启动 client 端,访问 http://localhost:3011/autoShow,即可看到配置文件内容。

这个例子只是介绍了和 Eureka 结合的最基础的情况,还可以注册到高可用的 Eureka 注册中心,另外,配置中心服务端还可以注册多个实例,同时保证注册中心的高可用。

注意事项

1. 在 git 上的配置文件的名字要和 config 的 client 端的 application name 对应;

2. 在结合 eureka 的场景中,关于 eureka 和 git config 相关的配置要放在 bootstrap.yml 中,否则会请求默认的 config server 配置,这是因为当你加了配置中心,服务就要先去配置中心获取配置,而这个时候,application.yml 配置文件还没有开始加载,而 bootstrap.yml 是最先加载的。

如果你觉得写的还可以的话,请点个「推荐」吧

欢迎关注,不定期更新本系列和其他文章

古时的风筝 ,进入公众号可以加入交流群

Spring Cloud Config 实现配置中心,看这一篇就够了的更多相关文章

  1. Spring Cloud Config的配置中心获取不到最新配置信息的问题

    Spring Cloud Config的配置中心获取不到最新配置信息的问题 http://blog.didispace.com/spring-cloud-tips-config-tmp-clear/

  2. 跟我学SpringCloud | 第六篇:Spring Cloud Config Github配置中心

    SpringCloud系列教程 | 第六篇:Spring Cloud Config Github配置中心 Springboot: 2.1.6.RELEASE SpringCloud: Greenwic ...

  3. Spring Cloud Config(配置中心)

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 一.简介 Spring Cloud Config为分布式系统中的外部配置提供服务器和客 ...

  4. Spring Cloud Config 分布式配置中心【Finchley 版】

    一. 介绍 1,为什么需要配置中心? 当服务部署的越来越多,规模越来越大,对应的机器数量也越来越庞大,靠人工来管理和维护服务的配置信息,变得困难,容易出错. 因此,需要一个能够动态注册和获取服务信息的 ...

  5. Spring Cloud Config 分布式配置中心使用教程

    一.简介 在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件.在Spring Cloud中,有分布式配置中心组件spring cloud config ...

  6. Spring Cloud Config的配置中心使用非对称性加密

    首先,我们需要通过keytool工具来生成密钥对. keytool是JDK中的一个密钥和证书管理工具.它使用户能够管理自己的公钥/私钥对及相关证书,用于(通过数字签名)自我认证(用户向别的用户/服务认 ...

  7. spring cloud config将配置存储在数据库中

    Spring Cloud Config Server最常见是将配置文件放在本地或者远程Git仓库,放在本地是将将所有的配置文件统一写在Config Server工程目录下,如果需要修改配置,需要重启c ...

  8. spring cloud学习(六) 配置中心-自动更新

    上一篇学习了spring cloud config的基本使用,但发现有个问题,就是每次更改配置后,都需要重启服务才能更新配置,这样肯定是不行的.在上网查资料了解后,spring cloud支持通过AM ...

  9. Spring Cloud 2-Config 分布式配置中心(七)

    Spring Cloud  Config  1.github配置 2.服务端配置 pom.xml application.xml Application.java 3.配置和命名 1. 配置加载顺序 ...

随机推荐

  1. 用Go语言异常机制模拟TryCatch异常捕捉1

    有的同学看到Go和TryCatch一起出现,心里可能会说,难道Go语言升级了,加入了try...catch语句.哈哈,其实Go语言从创建之初就没打算加入try...catch语句,因为创建Go的那帮大 ...

  2. 3021Java_数据类型

    1.分类 Java数据类型 基本数据类型 数值型 整数类型 浮点类型 字符型 布尔型 引用数据类型 类 接口 数组 2.基本数据类型 2.1 综述 java的8种基本数据类型(简单数据类型) bool ...

  3. Docker+ Kubernetes已成为云计算的主流(二十五)

    前言 最近正在抽时间编写k8s的相关教程,很是费时,等相关内容初步完成后,再和大家分享.对于k8s,还是上云更为简单.稳定并且节省成本,因此我们需要对主流云服务的容器服务进行了解,以便更好地应用于生产 ...

  4. 在网页中添加动画,使用WOW.js来实现

    [来源] 页面在向下滚动的时候,有些元素会产生细小的动画效果.虽然动画比较小,但却能吸引你的注意 刚知道wow.js这个插件,之前写的类似滚动时页面效果都是自己用jQuery写的,现在有了插件,开发更 ...

  5. Hive 学习之路(五)—— Hive 分区表和分桶表

    一.分区表 1.1 概念 Hive中的表对应为HDFS上的指定目录,在查询数据时候,默认会对全表进行扫描,这样时间和性能的消耗都非常大. 分区为HDFS上表目录的子目录,数据按照分区存储在子目录中.如 ...

  6. Mac上使用brew update会卡住的问题

    Mac上使用brew update会卡住的问题 brew默认的源是Github,会非常慢,建议换为国内的源.推荐中科大的镜像源,比较全面. 解决方案 Homebrew Homebrew源代码仓库 替换 ...

  7. Ruby中的各种比较方式对比

    Ruby中设计了很多种比较方式,所有对象都能进行==.!=.===.<=>.eql?.equal?这几种比较.此外,当实现了<=>之后,如果还include了Comparabl ...

  8. Vue的生命周期函数

    详解Vue Lifecycle 先来看看vue官网对vue生命周期的介绍 Vue实例有一个完整的生命周期,也就是从开始创建.初始化数据.编译模板.挂载Dom.渲染→更新→渲染.销毁等一系列过程,我们称 ...

  9. Linux系统:centos7下安装Jdk8、Tomcat8、MySQL5.7环境

    一.JDK1.8 环境搭建 1.上传文件解压 [root@localhost mysoft]# tar -zxvf jdk-8u161-linux-x64.tar.gz [root@localhost ...

  10. code forces 1173 B. Nauuo and Chess

    本文链接:https://www.cnblogs.com/blowhail/p/10991237.html B. Nauuo and Chess  原题链接:http://codeforces.com ...