再见了,Nacos。。。

张开发
2026/4/22 17:43:17 15 分钟阅读

分享文章

再见了,Nacos。。。
目前我们所有微服务的配置中心都没有采用Nacos而是选择了另一款携程开源的分布式配置中心Apollo今天就跟大家详细介绍一下这款神级配置中心1. 基本概念由于 Apollo 概念比较多刚开始使用比较复杂最好先过一遍概念再动手实践尝试使用。1.1、背景随着程序功能的日益复杂程序的配置日益增多各种功能的开关、参数的配置、服务器的地址……对程序配置的期望值也越来越高配置修改后实时生效灰度发布分环境、分集群管理配置完善的权限、审核机制…… 在这样的大环境下传统的通过配置文件、数据库等方式已经越来越无法满足开发人员对配置管理的需求。因此 Apollo 配置中心应运而生1.2、简介Apollo阿波罗是携程框架部门研发的开源配置管理中心能够集中化管理应用不同环境、不同集群的配置配置修改后能够实时推送到应用端并且具备规范的权限、流程治理等特性。1.3、特点部署简单灰度发布版本发布管理提供开放平台API客户端配置信息监控提供Java和.Net原生客户端配置修改实时生效热发布权限管理、发布审核、操作审计统一管理不同环境、不同集群的配置1.4、基础模型如下即是 Apollo 的基础模型(1)、用户在配置中心对配置进行修改并发布(2)、配置中心通知Apollo客户端有配置更新(3)、Apollo客户端从配置中心拉取最新的配置、更新本地配置并通知到应用1.5、Apollo 的四个维度Apollo支持4个维度管理Key-Value格式的配置application (应用)environment (环境)cluster (集群)namespace (命名空间)(1)、applicationApollo 客户端在运行时需要知道当前应用是谁从而可以根据不同的应用来获取对应应用的配置。每个应用都需要有唯一的身份标识可以在代码中配置app.id参数来标识当前应用Apollo 会根据此指来辨别当前应用。(2)、environment在实际开发中我们的应用经常要部署在不同的环境中一般情况下分为开发、测试、生产等等不同环境不同环境中的配置也是不同的在 Apollo 中默认提供了四种环境FATFeature Acceptance Test功能测试环境UATUser Acceptance Test集成测试环境DEVDevelop开发环境PROProduce生产环境在程序中如果想指定使用哪个环境可以配置变量env的值为对应环境名称即可。(3)、cluster一个应用下不同实例的分组比如典型的可以按照数据中心分把上海机房的应用实例分为一个集群把北京机房的应用实例分为另一个集群。对不同的集群同一个配置可以有不一样的值比如说上面所指的两个北京、上海两个机房设置两个集群两个集群中都有 mysql 配置参数其中参数中配置的地址是不一样的。(4)、namespace一个应用中不同配置的分组可以简单地把 namespace 类比为不同的配置文件不同类型的配置存放在不同的文件中如数据库配置文件RPC 配置文件应用自身的配置文件等。熟悉 SpringBoot 的都知道SpringBoot 项目都有一个默认配置文件application.yml如果还想用多个配置可以创建多个配置文件来存放不同的配置信息通过指定spring.profiles.active参数指定应用不同的配置文件。这里的namespace概念与其类似将不同的配置放到不同的配置namespace中。Namespace 分为两种权限分别为public公共的public权限的 Namespace能被任何应用获取。private私有的只能被所属的应用获取到。一个应用尝试获取其它应用 private 的 NamespaceApollo 会报 404 异常。Namespace 分为三种类型分别为私有类型私有类型的 Namespace 具有 private 权限。例如 application Namespace 为私有类型。公共类型公共类型的 Namespace 具有 public 权限。公共类型的N amespace 相当于游离于应用之外的配置且通过 Namespace 的名称去标识公共 Namespace所以公共的 Namespace 的名称必须全局唯一。关联类型继承类型关联类型又可称为继承类型关联类型具有 private 权限。关联类型的 Namespace 继承于公共类型的 Namespace将里面的配置全部继承并且可以用于覆盖公共 Namespace 的某些配置。1.6、本地缓存Apollo客户端会把从服务端获取到的配置在本地文件系统缓存一份用于在遇到服务不可用或网络不通的时候依然能从本地恢复配置不影响应用正常运行。本地缓存路径默认位于以下路径所以请确保/opt/data或C:\opt\data\目录存在且应用有读写权限。Mac/Linux/opt/data/{appId}/config-cacheWindowsC:\opt\data{appId}\config-cache本地配置文件会以下面的文件名格式放置于本地缓存路径下{appId}{cluster}{namespace}.properties1.7、客户端设计上图简要描述了Apollo客户端的实现原理客户端和服务端保持了一个长连接从而能第一时间获得配置更新的推送。客户端还会定时从 Apollo 配置中心服务端拉取应用的最新配置。这是一个 fallback 机制为了防止推送机制失效导致配置不更新客户端定时拉取会上报本地版本所以一般情况下对于定时拉取的操作服务端都会返回 304 - Not Modified定时频率默认为每 5 分钟拉取一次客户端也可以通过在运行时指定apollo.refreshInterval来覆盖单位为分钟。客户端从 Apollo 配置中心服务端获取到应用的最新配置后会保存在内存中。客户端会把从服务端获取到的配置在本地文件系统缓存一份 在遇到服务不可用或网络不通的时候依然能从本地恢复配置。应用程序从 Apollo 客户端获取最新的配置、订阅配置更新通知。配置更新推送实现前面提到了 Apollo 客户端和服务端保持了一个长连接从而能第一时间获得配置更新的推送。长连接实际上我们是通过 Http Long Polling 实现的具体而言客户端发起一个 Http 请求到服务端服务端会保持住这个连接 60 秒如果在 60 秒内有客户端关心的配置变化被保持住的客户端请求会立即返回并告知客户端有配置变化的 namespace 信息客户端会据此拉取对应 namespace 的最新配置如果在 60 秒内没有客户端关心的配置变化那么会返回 Http 状态码 304 给客户端客户端在收到服务端请求后会立即重新发起连接回到第一步考虑到会有数万客户端向服务端发起长连在服务端我们使用了 async servlet(Spring DeferredResult) 来服务 Http Long Polling 请求。1.8、总体设计上图简要描述了Apollo的总体设计我们可以从下往上看Config Service 提供配置的读取、推送等功能服务对象是 Apollo 客户端Admin Service 提供配置的修改、发布等功能服务对象是 Apollo Portal管理界面Config Service 和 Admin Service 都是多实例、无状态部署所以需要将自己注册到 Eureka 中并保持心跳在 Eureka 之上我们架了一层 Meta Server 用于封装Eureka的服务发现接口Client 通过域名访问 Meta Server 获取Config Service服务列表IPPort而后直接通过 IPPort 访问服务同时在 Client 侧会做 load balance 错误重试Portal 通过域名访问 Meta Server 获取 Admin Service 服务列表IPPort而后直接通过 IPPort 访问服务同时在 Portal 侧会做 load balance、错误重试为了简化部署我们实际上会把 Config Service、Eureka 和 Meta Server 三个逻辑角色部署在同一个 JVM 进程中1.9、可用性考虑配置中心作为基础服务可用性要求非常高下面的表格描述了不同场景下Apollo的可用性场景影响降级原因某台 config service 下线无影响Config service无状态客户端重连其它config service所有 config service 下线客户端无法读取最新配置Portal无影响客户端重启时,可以读取本地缓存配置文件某台 admin service 下线无影响Admin service无状态Portal重连其它 admin service所有 admin service 下线客户端无影响portal无法更新配置某台 portal 下线无影响Portal域名通过slb绑定多台服务器重试后指向可用的服务器全部 portal 下线客户端无影响portal无法更新配置某个数据中心下线无影响多数据中心部署数据完全同步Meta Server/Portal 域名通过 slb 自动切换到其它存活的数据中心2. Apollo 配置中心创建项目与配置接下来我们将创建一个 Apollo 的客户端项目引用 Apollo 来实现配置动态更新不过在此之前我们需要提前进入 Apollo Portal 界面在里面提前创建一个项目并在其配置一个参数方便后续客户端引入该配置参数测试是否能动态变化。2.1、登录 Apollo我这里是部署到 Kubernetes 中通过 NodePort 方式暴露出一个端口打开这个地址登录 Apollo用户名apollo密 码admin2.2、修改与增加部门数据在登录后创建项目时选择部门默认只能选择 Apollo 自带的 测试部门1与测试部门2两个选项。开始这真让人迷糊原来 Apoloo 没有修改或新增部门信息的管理节目只能通过修改数据库来新增或者修改数据这里打开Portal对月的数据库中的表ApolloPortalDB修改key为organizations的value的 json 数据改成自己对于的部门信息。2.3、创建一个项目修改完数据库部门信息后重新登录 Apollo Portal然后创建项目这时候选择部门可以看到已经变成我们自己修改后的部门信息了选择我们自定义部门然后设置应用 ID 为 apollo-test应用名为 apollo-demo 。创建完成后进入配置管理界面2.4、创建一个配置参数创建一个配置参数方便后续 Apollo 客户端项目引入该参数进行动态配置测试。设置 key 为testvalue 为123456然后设置一个备注保存。创建完成后可以看到配置管理节目新增了一条配置。接下来我们将此配置通过发布按钮进行发布。3. 创建 Apollo 客户端测试项目这里创建一个 SpringBoot 项目引入 Apollo 客户端来来实现与 Apollo 配置中心服务端交互。3.1、Mavne 添加 Apollo 依赖?xml version1.0 encodingUTF-8? projectxmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.1.8.RELEASE/version /parent groupIdclub.mydlq/groupId artifactIdapollo-demo/artifactId version0.0.1/version nameapollo-demo/name descriptionApollo Demo/description properties java.version1.8/java.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.ctrip.framework.apollo/groupId artifactIdapollo-client/artifactId version1.4.0/version /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /build /project3.2、配置文件添加参数在 application.yml 配置文件中添加下面参数这里简单介绍下 Apollo 参数作用apollo.metaApollo 配置中心地址。apollo.cluster指定使用某个集群下的配置。apollo.bootstrap.enabled是否开启 Apollo。apollo.bootstrap.namespaces 指定使用哪个 Namespace 的配置默认 application。apollo.cacheDir/opt/data/some-cache-dir为了防止配置中心无法连接等问题Apollo 会自动将配置本地缓存一份。apollo.autoUpdateInjectedSpringPropertiesSpring应用通常会使用 Placeholder 来注入配置如${someKey:someDefaultValue}冒号前面的是 key冒号后面的是默认值。如果想关闭 placeholder 在运行时自动更新功能可以设置为 false。apollo.bootstrap.eagerLoad.enabled 将 Apollo 加载提到初始化日志系统之前如果设置为 false那么将打印出 Apollo 的日志信息但是由于打印 Apollo 日志信息需要日志先启动启动后无法对日志配置进行修改所以 Apollo 不能管理应用的日志配置如果设置为 true那么 Apollo 可以管理日志的配置但是不能打印出 Apollo 的日志信息。#应用配置 server: port: 8080 spring: application: name: apollo-demo #Apollo 配置 app: id: apollo-test #应用ID apollo: cacheDir: /opt/data/ #配置本地配置缓存目录 cluster: default #指定使用哪个集群的配置 meta: http://192.168.2.11:30002 #DEV环境配置中心地址 autoUpdateInjectedSpringProperties: true #是否开启 Spring 参数自动更新 bootstrap: enabled: true #是否开启 Apollo namespaces: application #设置 Namespace eagerLoad: enabled: false #将 Apollo 加载提到初始化日志系统之前3.3、创建测试 Controller 类写一个 Controller 类来输出 test 变量的值使用了 Spring 的 Value 注解用于读取配置文件中的变量的值这里来测试该值项目启动后读取到的变量的值是设置在 application 配置文件中的默认值还是远程 Apollo 中的值如果是 Apollo 中配置的值那么再测试在 Apollo 配置中心中改变该变量的值后这里是否会产生变化。RestController publicclassTestController{ Value(${test:默认值}) private String test; GetMapping(/test) public String test(){ returntest的值为: test; } }3.4、创建启动类SpringBoot 项目启动类。SpringBootApplication publicclassApplication{ publicstaticvoidmain(String[] args){ SpringApplication.run(Application.class, args); } }3.5、JVM 启动参数添加启动参数由于本人的 Apollo 是部署在 Kubernetes 环境中的JVM 参数中必须添加两个变量env应用使用 Apollo 哪个环境例如设置为DEV就是指定使用开发环境如果设置为PRO就是制定使用生产环境。apollo.configService指定配置中心的地址跳过 meta 的配置在测试时指定 meta 地址无效果。如果 Apollo 是部署在 Kubernetes 中则必须设置该参数为配置中心地址如果 Apollo 不是在 Kubernetes 环境中可以不设置此参数只设置 meta 参数即可。一般情况下configService 和 meta 值一致。如果是在 Idea 中启动可以配置启动参数加上-Dapollo.configServicehttp://192.168.2.11:30002 -DenvDEV如果是 java 命令启动程序需要 JVM 加上:$ java -Dapollo.configServicehttp://192.168.2.11:30002 -DenvDEV -jar apollo-demo.jar“注意上面 env 指定的环境要和 apollo.meta 指定 Config 地址的环境一致例如 -DenvDEV 即使用开发环境那么 apollo.metahttp://xxx.xxx.xxx:8080 这个url 的 Config 也是开发环境下的配置中心服务而不能是 PRO 或者其它环境下的配置中心。4. 启动项目进行测试4.1、测试是否能够获取 Apollo 中设置的值启动上面的测试用例然后输入地址 http://localhost:8080/test 查看test的值为:123456可以看到使用的是 Apollo 中配置的test参数的值123456而不是默认的值。4.2、测试当 Apollo 中修改参数值后客户端是否能及时刷新修改 Apollo 配置中心参数test值为666666然后再次发布。发布完成后再次输入地址 http://localhost:8080/test 查看test的值为:666666可以看到示例应用中的值已经改变为最新的值。4.3、测试当 Apollo 执行配置回滚操作时客户端是否能及时改变回滚完成后状态将变为未发布状态则时候输入地址 http://localhost:8080/test 查看test的值为:123456可以看到已经回滚到之前的test配置的值了。4.4、测试当不能访问 Apollo 时客户端的变化这里我们将 JVM 参数中 Apollo 配置中心地址故意改错-Dapollo.configServicehttp://192.168.2.100:30002 -DenvDEV然后输入地址 http://localhost:8080/test 可以看到值为test的值为:123456可以看到显示的值并不是我们定义的默认值而还是 Apollo 配置中心配置的test参数的值。考虑到由于 Apollo 会在本地将配置缓存一份出现上面原因估计是缓存生效。当客户端不能连接到 Apollo 配置中心时候默认使用本地缓存文件中的配置。上面我们配置了本地缓存配置文件存放地址为 /opt/data/ 接下来进入缓存目录找到对应的缓存配置文件删除缓存配置文件后重启应用再次输入地址查看test的值为:默认值删除缓存配置文件后可以看到输出的值为自己定义的默认值。4.5、测试当 Apollo 中将参数删除后客户端的变化这里我们进入 Apollo 配置中心删除之前创建的test参数然后发布。然后再次打开地址 http://localhost:8080/test 查看test的值为:默认值可以看到显示的是应用程序中设置的默认值。5. 对 Apollo 的 Cluster、Namespace 进行探究在 Apollo 中配置可以根据不同的环境划分为 Dev开发、Prod生产 等环境又能根据区域划分为不同的 Cluster集群还能根据配置参数作用功能的不同划分为不同的 Namespace命名空间这里探究下如何使用上述能力。5.1、不同环境下的配置1、Apollo 配置中心 PRO 环境添加参数打开 Apollo 配置中心环境列表点击 PRO 环境然后新增一条配置和之前例子中参数保持一致都为test参数创建完成后发布。然后修改上面的示例项目将配置参数指定为 PRO 环境:2、示例项目修改 application.yml 配置文件把apollo.meta参数改成 RPO 的配置中心地址...... apollo: meta: http://192.168.2.11:30005 #RPO环境配置中心地址 ......3、示例项目修改 JVM 参数把apollo.configService参数改成 PRO 配置中心地址env参数的值改为PRO。-Dapollo.configServicehttp://192.168.2.11:30005 -DenvPRO4、启动示例项目观察结果启动示例项目然后接着输入地址 http://localhost:8080/test 查看信息test的值为:abcdefg可以看到已经改成生成环境配置所以在实际项目中如果要更换环境需要修改 JVM 参数env如果 Apollo 部署在 Kubernetes 环境中还需要修改apollo.configService参数和修改 application.yml 配置文件的参数apollo.meta值。5.2、不同集群下的配置1、创建两个集群例如在开发过程中经常要将应用部署到不同的机房这里分别创建beijing、shanghai两个集群。2、两个集群都配置同样的参数不同的值在两个集群beijing与shanghai中都统一配置参数test并且设置不同的值。3、示例项目 application.yml 修改集群配置参数,并启动项目观察结果指定集群为 beijing:...... apollo: cluster: beijing #指定使用 beijing 集群 ......启动示例项目然后接着输入地址 http://localhost:8080/test 查看信息test的值为:Cluster-BeiJing可以看到用的是 beijing 集群的配置指定集群为 shanghai:...... apollo: cluster: shanghai #指定使用 shanghai 集群 ......启动示例项目然后接着输入地址 http://localhost:8080/test 查看信息test的值为:Cluster-ShangHai可以看到用的是 shanghai 集群的配置5.3、不同命名空间下的配置1、创建两个命名空间命名空间有两种一种是 public公开一种是 private 私有公开命名空间所有项目都能读取配置信息而私有的只能app.id值属于该应用的才能读取配置。这里创建dev-1与dev-2两个私有的命名空间用于测试。2、两个集群都配置同样的参数不同的值在两个命名空间中都统一配置参数test并且设置不同的值设置完后发布。3、示例项目 application.yml 修改命名空间配置参数,并启动项目观察结果指定命名空间为 dev-1:...... apollo: bootstrap: namespaces: dev-1 #设置 dev-1 命名空间 ......启动示例项目然后接着输入地址 http://localhost:8080/test 查看信息test的值为:dev-1 Namespace可以看到用的是 dev-1 命名空间的配置指定命名空间为 dev-2:...... apollo: bootstrap: namespaces: dev-2 #设置 dev-1 命名空间 ......YAML启动示例项目然后接着输入地址 http://localhost:8080/test 查看信息test的值为:dev-2 NamespaceHTML可以看到用的是 dev-2 命名空间的配置6. Kubernetes 的 SpringBoot 应用使用 Apollo 配置中心本人的 Apollo 和 SpringBoot 应用一般都是基于 Kubernetes 部署的所以这里简单介绍下如何在 Kubernetes 环境下部署 SpringBoot 应用且使用 Apollo 作为配置中心。这里项目依旧使用上面的示例不过首先要将其编译成 Docker 镜像方便后续部署到 Kubernetes 环境下。6.1、构建 Docker 镜像1、执行 Maven 编译首先执行 Maven 命令将项目编译成一个可执行 JAR。$ mvn clean installBASH2、准备 Dockerfile创建构建 Docker 镜像需要的 Dockerfile 文件将 Maven 编译的 JAR 复制到镜像内部然后设置两个变量分别是JAVA_OPTSJava JVM 启动参数变量这里需要在这里加一个时区参数。APP_OPTSSpring 容器启动参数变量方便后续操作时能通过此变量配置 Spring 参数。DockerfileFROM openjdk:8u222-jre-slim VOLUME /tmp ADD target/*.jar app.jar RUN sh -c touch /app.jar ENV JAVA_OPTS-XX:MaxRAMPercentage80.0 -Duser.timezoneAsia/Shanghai ENV APP_OPTS ENTRYPOINT [ sh, -c, java $JAVA_OPTS -Djava.security.egdfile:/dev/./urandom -jar /app.jar $APP_OPTS ]3、构建 Docker 镜像执行 Docker Build 命令构建 Docker 镜像。$ docker build -t mydlqclub/springboot-apollo:0.0.1 .BASH6.2、Kubernetes 部署示例应用1、创建 SpringBoot 且使用 Apollo 配置中心的 Kubernetes 部署文件这里创建 Kubernetes 下的 SpringBoot 部署文件apollo-demo-example.yaml。在之前 Dockerfile 中设置了两个环境变量JAVA_OPTS与APP_OPTS。其中JAVA_OPTS变量的值将会作为 JVM 启动参数APP_OPTS变量的值将会作为应用的配置参数。所以这里我们将 Apollo 配置参数放置到变量中这样一来就可以方便修改与维护 Apollo 的配置信息。“在下面配置的环境变量参数中设置的配置中心地址为http://service-apollo-config-server-dev.mydlqclub:8080这是因为 Apollo 部署在 K8S 环境中且可以使用域名方式访问service-apollo-config-server-dev 是应用的 Service 名称mydlqcloud 是 K8S 下的 Namespace 名称。springboot-apollo.yamlapiVersion: v1 kind: Service metadata: name: springboot-apollo spec: type: NodePort ports: - name: server nodePort: 31080 port: 8080 targetPort: 8080 - name: management nodePort: 31081 port: 8081 targetPort: 8081 selector: app: springboot-apollo --- apiVersion: apps/v1 kind: Deployment metadata: name: springboot-apollo labels: app: springboot-apollo spec: replicas: 1 selector: matchLabels: app: springboot-apollo template: metadata: name: springboot-apollo labels: app: springboot-apollo spec: restartPolicy: Always containers: - name: springboot-apollo image: mydlqclub/springboot-apollo:0.0.1 imagePullPolicy: Always ports: - containerPort: 8080 name: server env: - name: JAVA_OPTS value: -DenvDEV ##注意修改此处的 mydlqcloud 为你自己的 Namespace 名称 - name: APP_OPTS value: --app.idapollo-demo --apollo.bootstrap.enabledtrue --apollo.bootstrap.eagerLoad.enabledfalse --apollo.cacheDir/opt/data/ --apollo.clusterdefault --apollo.bootstrap.namespacesapplication --apollo.autoUpdateInjectedSpringPropertiestrue --apollo.metahttp://service-apollo-config-server-dev.mydlqcloud:8080 resources: limits: memory: 1000Mi cpu: 1000m requests: memory: 500Mi cpu: 500m2、部署 SpringBoot 应用到 Kubernetes-n创建应用到指定的 Namespace 中。$ kubectl apply -f springboot-apollo.yaml -n mydlqcloudBASH6.3、测试部署的应用接口上面的应用配置了 NodePort 端口可以通过此端口访问 Kubernetes 集群内的应用接口本人 Kubernetes 集群地址为 192.168.2.11 且 NodePort 端口为 31081所以浏览器访问地址 http://192.168.2.11:31081/test 来测试接口显示test的值为:123456可以看到能通过 Apollo 获取参数值此文章到此结束。来源网络

更多文章