22、集群配置中心例子

137 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第22天,点击查看活动详情

22、集群配置中心例子

二、集群配置中心例子

5、目录配置总结 -- 鸡肋

前面的小节对服务器与客户端的配置进行了简单的演示,如果读者对相关的目录配置有所疑问,可以阅读本小节。配置服务器和客户端,主要有与 以下几个目录相关的配置。

  • 服务器 spring.cloud.config server.svn.uri = https://localhost/svn/test-project
  • 服务器 pring.cloud.config.server.default-label = default-config
  • 客户端 spring.application.name = spring-book-service
  • 客户端 spring.cloud.config.profile = dev

从以上的4个配置中不难看出,服务器的两个配置用于指定到 SVN 的路径、配置目录,而客户端的两个配置则用于指定读取哪份配置文件。根据以上配置,客户端将会去 https:// localhost/svn/test-project/default-config 目录下读取名称为 spring-book-service-dev.yml 的文件。 如果客户端想指定目录,可以通过配置 spring.cloud.config.label = book-module 实现,配置后,客户端将会去 https: // localhost/svn/tes t-project/book-module 下读取配置文件。

另外,在实际环境中有可能存在多份配置文件,例如一份文件是专门配置 Hystrix 的,另一份是专门配置 Zuul的, 可以配置为 spring .cloud.config .profile = hystrix, zuul ,此时,将会去读取名称为 spring-book-service -hystrix.yml 与 spring-book-service-zuul.yml 的配置文件。

6、刷新配置

远程 SVN 服务器上面的配置修改后,需要通知客户端来改变配置。可以访问客户端的 /refresh 端点进行刷新, 访问该端点要使用 HTTP 的 POST 方法。修改 SVN 上面的 first-test.properties ,将 test. user.name 修改为 Songrongliang,再使用 HttpClient 来发送请求,刷新配置。发送 POST 请求的代码请见代码清单:

@RestController
public class RefreshController {

    /**
     * 刷新配置 由POST 改成 GET ,可以使用浏览器直接刷新,
     * 不能再使用 http://localhost:8080/refresh 路径 Request method 'GET' not supported
     * 因为当前Controller 优先级没有系统的高
     */
    @RequestMapping(value = "/getRefresh")
    public String refresh() throws IOException {
        // 创建默认的 HttpClient
        CloseableHttpClient httpClient = HttpClients.createDefault();
        // 调用 Post 方法请求服务
        HttpPost httpPost = new HttpPost("http://localhost:8080/refresh");
        // 获取响应
        HttpResponse response = httpClient.execute(httpPost);
        // 根据响应解析出字符串
        String entity = EntityUtils.toString(response.getEntity());
        System.out.println(entity);
        //java.io.IOException: Attempted read from closed stream.
        //原因是EntityUtils.toString(HttpEntity)方法被使用了多次。所以每个方法内只能使用一次。
        return entity;
    }

}

修改配置并提交到 SVN 服务器后再运行代码清单 访问http://localhost:8080/getRefresh,可以看到控制台输出如下:

["config.client.version","test.user.name"]

客户端的 refresh 服务在接收到请求后,会重新到配置服务器获取最新的配置,然后用最新的配置与旧配置进行对比, 最终将有修改的配置 key 返回给服务调用者。在整个过程中,配置服务器、客户端都不需要重新启动,调用 refresh 刷新配置后,可以再访问客户端来查看配置是否更新。在浏览器中访问 http://localhost: 8080 ,可以看到客户端控制台输出为最新的值。

7、刷新Bean

前面的章节中介绍了 如何进行配置刷新,然而在实际应用中,往往不仅是只刷新一个配置的值那么简单。由于 Spring 容器中的很多 Bean 都是根据某个属性值来进行初始化的, 配置一旦更新,需要重建这个 Bean 的实例。 为了解决该问题, Spring Cloud 提供了@RefreshBean 注解。

在Spring 的容器中,有一个类型为 RefreshBean 的 Bean 。当 /refresh 端点被访问时,负 责处理刷新的 ContextRefresher 类会先去远程的配置服务器刷新配置,然后再调用RefreshBean 的 refreshAll 方法来处理理实例 。容器中使用了@RefreshBean 注解进行修饰的 Bean, 都会在缓存中进行销毁,当这些 Bean 被再次引用时,就会创建新的实例,以此达到一个“刷新”的效果。

在客户端(srl-book-server) 中测试刷新 Bean,新建配置类,请见代码清单 -5

@Configuration
public class MyConfig {

    @Bean
    @RefreshScope
    public Person person(Environment env) {
        // 读取名字创建Person实例
        String name = env.getProperty("test.user.name");
        // 输出person名字
        System.out.println("初始化 person bean:" + name);
        //创建一个Person
        Person person = new Person();
        person.setName(name);
        return person;
    }

    public static class Person {
        private String name;

        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
}

在配置类中会创建一个 Person 的 Bean, Bean 会读取 test.user.name 属性来创建 Person实例,该属性保存在 SYN 的配置文件中。修改一下控制器,请见代码清单 9-6。

/**
 * 测试刷新Bean
 */
@Autowired
private Person person;

@RequestMapping("/person")
public String getPerson() {
    System.out.println("输出person实例的名称:" + person.getName());
    return person.getName();
}

往控制器中注入 Person ,访问 /person 地址后,会输出当前容器中的 Person 以及名字。 当前 SVN中 first-test.properties的 test.user.name 属性值为 Songrongliang,依次启动服务器、客户端,访问 http: //localhost: 8080/person ,可以看到客户端的控制台输出如下:

初始化 person bean:Songrongliang

修改 SVN 中的配置文件并提交,然后向 http://localhost: 8080/refresh 端点发送 POST请求(运行 RefreshClient ),最后再次访问 http ://localhost: 8080/person ,可以看到客户端控制台输出如下:

初始化 person bean:xieyinan 输出person实例的名称:xieyinan

根据输出可知,属性值己经被刷新,使用该属性值创建的 Person 实例己经被重新创建。