基于Eureka搭建Springcloud微服务-11.使用Apollo配置中心管理配置

lingwh原创2020年6月9日大约 6 分钟约 1682 字

11.使用Apollo配置中心管理配置

11.1.章节内容概述

本章节涉及主要内容有:
 11.1.章节内容概述
 11.2.章节内容大纲
 11.3.Apollo配置中心简介
 11.4.搭建Apollo配置中心
 11.5.搭建服务消费者(Apollo)
具体每个小节中包含的内容可使通过下面的章节内容大纲进行查看。

11.2.章节内容大纲

11.3.Apollo配置中心简介

Apollo(阿波罗)是一款可靠的分布式配置管理中心,诞生于携程框架研发部,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。

官方网址

https://www.apolloconfig.com

官网网址(GITHUB)

https://github.com/apolloconfig

11.4.搭建Apollo配置中心

基于独立部署的Eureka搭建Apollo配置中心-单环境版(Windows版)

11.5.搭建服务消费者(Apollo)

11.5.1.章节内容简介

本章节会展示如何使用Apollo配置中心来管理配置

11.5.2.模块简介

使用了Apollo配置中心的服务消费者,启动端口: 80

11.5.3.模块目录结构

springcloud-config-apollo-loadbalance-openfeign-configuration-order80
|-- src
|   •-- main
|       |-- java
|       |   •-- org
|       |       •-- openatom
|       |           •-- springcloud
|       |               |-- config
|       |               |   •-- OpenFeignConfig.java
|       |               |-- controller
|       |               |   |-- ApolloConfigController.java
|       |               |   |-- OrderConsumerController.java
|       |               |   •-- RestartApplicationController.java
|       |               |-- listener
|       |               |   |-- ApolloPropertiesChangedListener.java
|       |               |   •-- AutomaticApolloWatcher.java
|       |               |-- service
|       |               |   •-- PaymentServiceOpenFeign.java
|       |               •-- OrderServiceConsumerApolloLoadBalanceOpenFeignConfiguration80.java
|       •-- resources
|           |-- apollo-env.properties
|           •-- application.yml
•-- pom.xml

11.5.4.创建模块

在父工程(springcloud-eureka)中创建一个名为springcloud-config-apollo-loadbalance-openfeign-configuration-order80的maven模块,注意:当前模块创建成功后,在父工程pom.xml中<modules></modules>中会自动生成有关当前模块的信息

11.5.5.编写模块pom.xml

<?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">
    <parent>
        <artifactId>springcloud-eureka</artifactId>
        <groupId>org.openatom</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-config-apollo-loadbalance-openfeign-configuration-order80</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--引入公共的工程-->
        <dependency>
            <groupId>org.openatom</groupId>
            <artifactId>springcloud-api-commons</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--Apollo客户端-->
        <dependency>
            <groupId>com.ctrip.framework.apollo</groupId>
            <artifactId>apollo-client</artifactId>
            <!--是否依赖传递:true,依赖不传递,false:依赖传递,这是maven的特性-->
            <optional>true</optional>
        </dependency>
    </dependencies>
    <!--热部署需要加这个-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
        <!--打包多环境-->
        <resources>
            <resource>
                <directory>src/main/resources/</directory>
                <includes>
                    <!--不区分环境:直接加载application.yml配置文件-->
                    <include>application.yml</include>
                    <!--不区分环境:直接加载*.properties配置文件-->
                    <include>*.properties</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>

11.5.6.编写模块application.yml

#Apollo配置中心相关,其他所有配置都去Apollo配置中心中去获取
app:
  id: springcloud-eureka
apollo:
  bootstrap:
    enabled: true
    namespaces: application #多个namespaces之间使用,隔开
spring:
  cloud:
    inetutils:
      timeout-seconds: 5 # 设置网络超时
      ignored-interfaces: ## 忽略网卡
        - VMware Virtual Ethernet Adapter for VMnet1
        - VMware Virtual Ethernet Adapter for VMnet8
  devtools:
    restart:
      enabled: false
server:
  tomcat:
    mbeanregistry:
      enabled: true

management:
  endpoint:
    restart:
      enabled: true

11.5.7.编写模块Apollo配置文件

dev.meta=http://localhost:8080
pro.meta=http://localhost:8081

11.5.8.编写模块config

package org.openatom.springcloud.config;

import feign.Logger;
import feign.Retryer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class OpenFeignConfig {

    /**
     * NONE:默认的,不显示任何日志;
     * BASIC:仅记录请求方法、URL、响应状态码及执行时间;
     * HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;
     * FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。
     * @return
     */
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

}

11.5.9.编写模块service

package org.openatom.springcloud.service;

import org.openatom.springcloud.entities.CommonResult;
import org.openatom.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

/**
 * OpenFeign硬编码实现远程调用
 */
@Component
@FeignClient(name="SPRINGCLOUD-PROVIDER-APOLLO-PAYMENT-SERVICE-CLUSTER")
public interface PaymentServiceOpenFeign {
    @PostMapping(value = "/provider/payment/create")
    CommonResult create(@RequestBody Payment payment);

    @GetMapping(value = "/provider/payment/get/{id}")
    CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);

    @GetMapping(value = "/provider/payment/openfeign/timeout")
    String getPaymentByIdTimeout();
}

11.5.10.编写模块listener

package org.openatom.springcloud.listener;

import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.cloud.context.restart.RestartEndpoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
@Slf4j
public class ApolloPropertiesChangedListener implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Autowired
    private RestartEndpoint restartEndpoint;

    @ApolloConfigChangeListener
    private void someChangeHandler(ConfigChangeEvent changeEvent) {
        for (String key : changeEvent.changedKeys()) {
            ConfigChange change = changeEvent.getChange(key);
//            log.info("Found change - {}", change.toString());
            //如果key符合特定情况,则重启应用程序
            isRestartApplication(change.getPropertyName());
        }
        // 更新相应的bean的属性值,主要是存在@ConfigurationProperties注解的bean
        this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 重启SpringBoot项目
     */
    public void isRestartApplication(String propertyName){
        List<String> propertyNames = new ArrayList<>();
        propertyNames.add("spring.application.name");
        if(propertyNames.contains(propertyName)){
            restartEndpoint.restart();
        }
    }
}

11.5.11.编写模块controller

ApolloConfigController.java
package org.openatom.springcloud.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ApolloConfigController {

    /**
     * 从Apollo中获取应应用名称
     * @param applicationName
     * @return
     */
    @GetMapping("/consumer/apollo/test")
    public String apolloTest(@Value("${spring.application.name}") String applicationName) {
        return applicationName;
    }

}

OrderConsumerController.java
package org.openatom.springcloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.openatom.springcloud.service.PaymentServiceOpenFeign;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.openatom.springcloud.entities.CommonResult;
import org.openatom.springcloud.entities.Payment;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderConsumerController {

    //单机版
//    public static final String PAYMENT_URL = "http://localhost:8001";
//    public static final String PAYMENT_URL = "http://localhost:8002";

    @Resource
    private PaymentServiceOpenFeign paymentServiceOpenFeign;

    @GetMapping("/consumer/payment/create")
    public CommonResult<Payment> create(Payment payment) {
        return paymentServiceOpenFeign.create(payment);
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Long id) {
        return paymentServiceOpenFeign.getPaymentById(id);
    }

    @GetMapping("/consumer/payment/openfeign/timeout")
    public String getPaymentByIdTimeout() {
        return paymentServiceOpenFeign.getPaymentByIdTimeout();
    }
}

11.5.12.编写模块主启动类

package org.openatom.springcloud;

import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

import java.util.Properties;

/**
 * 1.使用OpenFeign完成远程调用,如果要配置负载均衡策略,和Ribbon配置负载均衡策略方式相同
 *      本微服务主要测试OpenFeign的功能,所以采用YML文件配置Ribbon的负载均衡策略
 * 2.OpenFeign是对Ribbon和RestTemplate的封装,所以配置负载均衡方式同Ribbon配置负载均衡方式,而且不需要在容器中手动注入ResTemplate对象
 * 3.OpenFeign YML文件配置实现远程调用,但不是完全将服务信息配置在YML中,只是在YML中写一些增强的配置,相关的服务中仍然要写服务名,@FeignClient(name="SPRING-CLOUD-PROVIDER-CONSUL-PAYMENT-SERVICE")
 * 4.对每个微服务单独进行配置,如连接超时时间配置、读取超时时间配置,YML没有把OpenFegin的配置和对Ribbon的配置写在一起
 * 5.开启OpenFeign增强日志后可以看到Http调用的详细信息
 *      2022-06-01 03:51:37.176 DEBUG 16792 --- [p-nio-80-exec-1] o.o.s.services.PaymentServiceOpenFeign   : [PaymentServiceOpenFeign#getPaymentById] <--- HTTP/1.1 200 (59ms)
 *      2022-06-01 03:51:37.176 DEBUG 16792 --- [p-nio-80-exec-1] o.o.s.services.PaymentServiceOpenFeign   : [PaymentServiceOpenFeign#getPaymentById] connection: keep-alive
 *      2022-06-01 03:51:37.176 DEBUG 16792 --- [p-nio-80-exec-1] o.o.s.services.PaymentServiceOpenFeign   : [PaymentServiceOpenFeign#getPaymentById] content-type: application/json
 *      2022-06-01 03:51:37.176 DEBUG 16792 --- [p-nio-80-exec-1] o.o.s.services.PaymentServiceOpenFeign   : [PaymentServiceOpenFeign#getPaymentById] date: Tue, 31 May 2022 19:51:37 GMT
 *      2022-06-01 03:51:37.176 DEBUG 16792 --- [p-nio-80-exec-1] o.o.s.services.PaymentServiceOpenFeign   : [PaymentServiceOpenFeign#getPaymentById] keep-alive: timeout=60
 *      2022-06-01 03:51:37.176 DEBUG 16792 --- [p-nio-80-exec-1] o.o.s.services.PaymentServiceOpenFeign   : [PaymentServiceOpenFeign#getPaymentById] transfer-encoding: chunked
 *      2022-06-01 03:51:37.176 DEBUG 16792 --- [p-nio-80-exec-1] o.o.s.services.PaymentServiceOpenFeign   : [PaymentServiceOpenFeign#getPaymentById]
 *      2022-06-01 03:51:37.176 DEBUG 16792 --- [p-nio-80-exec-1] o.o.s.services.PaymentServiceOpenFeign   : [PaymentServiceOpenFeign#getPaymentById] {"code":200,"message":"查询成功,serverPort:  8006","data":{"id":1,"serial":"15646546546"}}
 *      2022-06-01 03:51:37.176 DEBUG 16792 --- [p-nio-80-exec-1] o.o.s.services.PaymentServiceOpenFeign   : [PaymentServiceOpenFeign#getPaymentById] <--- END HTTP (94-byte body)
 */
/**
 * 支付接口提供端
 * 使用Eureka作为注册中心+使用Apollo作为注册中心
 * 运行时要添加如下VM Options:
 *      获取DEV环境数据:
 *          -Denv=DEV -Dapollo.cacheDir=D:\repository\cache\apollo -Dapollo.cluster=DEFAULT
 *      获取PRO环境数据:
 *          -Denv=PRO -Dapollo.cacheDir=D:\repository\cache\apollo -Dapollo.cluster=DEFAULT
 */
@EnableApolloConfig
@EnableEurekaClient  //添加@EnableEurekaClient好像没什么用,但是还是加上
@SpringBootApplication
@EnableFeignClients
public class OrderServiceConsumerApolloLoadBalanceOpenFeignConfiguration80 {
    public static void main(String[] args) {
//        System.setProperty("env","dev");
//        System.setProperty("apollo.cacheDir","D:\\repository\\cache\\apollo");
//        System.setProperty("apollo.cluster","dafult");
        SpringApplication.run(OrderServiceConsumerApolloLoadBalanceOpenFeignConfiguration80.class, args);
    }
}

11.5.13.测试模块

11.5.13.1.访问Apollo主界面

启动相关服务
Eureka注册中心
http://localhost:7001/
看到如下界面代表apollo的adminservice服务和configservice服务启动成功
Apollo配置中心
http://localhost:8070/
登录用户名/密码: apollo/admin

11.5.13.2.配置Apollo系统参数

添加系统参数
添加新部门(可选)
key
organizations
value
[{"orgId":"TEST1","orgName":"样例部门1"},{"orgId":"TEST2","orgName":"样例部门2"},{"orgId":"micro_service","orgName":"微服务部门"}]

11.5.13.3.在Apollo中创建项目

创建项目
应用信息中AppId值和application.yml中保持一致
app:
  id: springcloud-eureka

新增配置
编辑配置
具体文本配置内容如下
server.port = 80
eureka.client.service-url.defaultZone = http://localhost:7001/eureka
eureka.client.fetchRegistry = true
eureka.instance.prefer-ip-address = false
feign.client.config.SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER.connectTimeout = 5000
feign.client.config.SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER.readTimeout = 5000
logging.level.org.openatom.springcloud.services.PaymentServiceOpenFeign = debug
spring.application.name = SPRINGCLOUD-APOLLO-LOADBALANCE-OPENFEIGN-CONFIGURATION-ORDER80
spring.devtools.restart.enabled = true
spring.logging.level = info
SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER.ribbon.NFLoadBalancerRuleClassName = com.netflix.loadbalancer.RandomRule
eureka.client.register-with-eureka = true
此时界面上显示出了刚才添加的配置

11.5.13.4.启动当前模块

在idea中给当前模块配置jvm启动参数
    -Denv=DEV -Dapollo.cacheDir=D:\repository\cache\apollo -Dapollo.cluster=DEFAULT
-Denv=DEV代表可以获取到Apollo中DEV环境的参数

配置好jvm参数后,在idea中启动当前模块,如果项目可以正常启动,说明已经从Apollo中读取到了配置

11.5.13.5.测试Apollo

在浏览器中访问
http://localhost/consumer/apollo/test
返回结果

SPRINGCLOUD-APOLLO-LOADBALANCE-OPENFEIGN-CONFIGURATION-ORDER80 这个结果是从Apollo中获取到的服务名

在Apollo中修改spring.application.name的值为
SPRINGCLOUD-APOLLO-LOADBALANCE-OPENFEIGN-CONFIGURATION-ORDER80-XXX
再次访问
http://localhost/consumer/apollo/test
返回结果
SPRINGCLOUD-APOLLO-LOADBALANCE-OPENFEIGN-CONFIGURATION-ORDER80-XXX
此时获取到的服务名已经变成了刚才修改后的服务名

到idea控制台中查看
a.输出了如下内容,这说明监听器也监听到了Apollo中配置文件的变化
ConfigChange{namespace='application', propertyName='spring.application.name', oldValue='SPRINGCLOUD-APOLLO-LOADBALANCE-OPENFEIGN-CONFIGURATION-ORDER80', newValue='SPRINGCLOUD-APOLLO-LOADBALANCE-OPENFEIGN-CONFIGURATION-ORDER80-XXX', changeType=MODIFIED}
b.服务自动重启了
这是因为在编写当前模块的监听器时,里面的逻辑是当监听到了Apollo中配置文件的变化,就自动重启服务

拓展:如何实现SpringCloud项目的自动重启
a.application.yml中添加配置
server:
  tomcat:
    mbeanregistry:
      enabled: true

management:
  endpoint:
    restart:
      enabled: true
b.编写重启方法
    @Autowired
    private RestartEndpoint restartEndpoint;

    /**
     * 访问这个地址可以重启SpringBoot项目
     */
    public String restartApplication(){
        restartEndpoint.restart();
        return "请稍后,应用程序正在重启...";
    }
这个方法可以直接调用触发(如当前模块中由在监听器中调用触发重启的方法),可以通过REST API触发,如
package org.openatom.springcloud.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.restart.RestartEndpoint;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RestartApplicationController {

    @Autowired
    private RestartEndpoint restartEndpoint;

    /**
     * 访问这个地址可以重启SpringBoot项目
     */
    @GetMapping("/consumer/apollo/restart")
    public String restartApplication(){
        restartEndpoint.restart();
        return "请稍后,应用程序正在重启...";
    }
}

上次编辑于: 2022/9/9 06:18:47
贡献者: lingwh
评论