关于nacos:CICDNacos双云双活及Nacos数据横向同步原理

背景2022年,37手游实现了业务双云架构的部署2023年,通过故障注入,确认任一单云故障下,都能够逃生到另外一朵云此刻,尽管业务具备了双云双活能力,但也发现了一些边缘组件不具备,其中cicd依赖nacos就是其中之一 利用场景?那么咱们在nacos的应用场景是什么呢?其实次要用在CICD编译这个环节 面临的问题一旦腾讯云整体故障,那么咱们的cicd平台就无奈应用,即便业务逃生到另外一朵云,无论是利用公布、重启,都将受到影响,如下构图,cicd单云 于是,咱们做了cicd双云v1版本后期没有关注度到nacos的强依赖,在理论应用上,任然是单点(nacos单云) cicd v2版本一开始,将nacos当做无状态服务,间接双云部署,而后烟囱式拜访 cicd v2版本存在的问题:批改配置后,配置数据不统一在理论的故障注入演练中,咱们发现,在一朵云的nacos批改了配置后,尽管它们都是用的雷同的数据库,无奈同步到另外一朵云,如下架构 nacos双云不能同步的起因Nacos利用会将数据库的配置数据缓存在本地内存,而后每6小时去全量dump更新一次,所以比方咱们从腾讯云nacos集群入口批改配置,批改了nacos数据库,然而阿里云nacos容器集群因为不晓得数据库配置已被批改,因而就导致,腾讯云是新批改的配置,阿里云还是旧的缓存数据,须要6小时后才会同步更新 那么腾讯云nacos容器集群内的所有Pod为什么是都是新的呢nacos集群内,是有一个横向告诉机制的,要搞明确这个问题,要先搞清楚,nacos集群运作的以下几个原理 nacos数据一致性原理:raft算法1.raft算法选举Leader过程1.1 如一个nacos集群有3个节点,节点一开始启动时都是Follow跟随者状态,会进入一个选举超时工夫,期待Leader或者Candidate发送心跳包1.2 如果期待超时,如第一个节点期待超时就会变为Candidate候选者,向其余节点发动投票选举本人为Leader1.3 其余Follow节点收到Candidate发动的投票批准后,第一个节点节点成为Leader 2.raft算法的选举超时机制在raft算法中,选举超时工夫(Election Timeout)是用来定义Follower在主动转化为Candidate前的等待时间 默认的选举超时工夫是在固定值(1000ms)的根底上,减少150ms到300ms随机值的值。这个工夫设置是为了避免集群中的所有节点同时发动投票,导致选举无奈进行 例如,如果集群由3个节点组成,每个节点的选举超时工夫别离为168ms、210ms和200ms。在这个过程中,最先达到选举超时工夫的节点会最先成为Candidate,并向其余两个节点发动投票申请。如果其余节点收到投票申请并且返回了投票,那么最先成为Candidate的节点会因为取得了过半数节点的投票而从Candidate状态转变为Leader状态 每个节点的选举超时工夫是随机调配的,这个随机工夫范畴是150ms到300ms,能够无效地避免所有节点同时发动投票。如果一个节点在选举超时工夫内没有收到来自Leader的心跳音讯,它会从Follower状态主动转化为Candidate状态,并发动选举 nacos数据一致性原理:读写申请nacos集群为了保证数据一致性,无论读写申请都会通过Leader节点,架构如下 如上图,第一步:客户端申请nacos的入口LB,随机申请到Follower2,第二步:Follower2会将读写申请都转给Leader节点,第三步:如果是一个读申请,那么Leader节点会将读申请解决后再返回给Follower2,第四步:再由Follower2节点返回给客户端数据 通过下面4个步骤,能够保障所有的写申请都由Leader节点解决,从而保证数据的一致性 nacos数据一致性原理:Leader节点故障期间如果Leader节点挂掉了,那么集群会进行新的选举产生新的Leader,之前挂掉的Leader重启后作为Follower退出集群,并同步新选举的Leader上的数据。这样能够保障即便在Leader节点故障的状况下,也能保证数据的可靠性和一致性 那么Leader故障或者失联期间,Follower是否单独解决呢?答案是必定的,见架构图如上图,如果Leader故障,且又没有新的的Leader产生时,Follower接到申请后,还是依照原门路申请Leader,但此时是不通的,所以这个中央Follower就有个超时机制,毫秒级,如果拿不到Leader的返回,就将缓存的数据返回给客户端 nacos数据一致性原理:节点数据同步Nacos通过发送心跳的形式来放弃Leader和Follower之间的数据同步,Leader节点会定期向每个Follower节点发送心跳,这个心跳的默认工夫是30秒,也就是30秒同步一次,并带上所有的key和key对应的工夫戳。Follower节点收到心跳后,会查看其中的key是否存在或者工夫戳是否较小,如果不满足条件,则会向Leader发送申请拿取最新的数据。这样能够保障各个节点数据的实时性和一致性 基于Nacos的原理,37手游的nacos双云双活设计已知,Nacos能够在集群节点内,Follower节点都会从Leader节点同步数据,但问题是不同集群间,Nacos是无奈做到同步的,也就是说,Nacos是有肯定状态的,并非齐全无状态,看看新旧架构比照 老的架构做法从上图能够看到,老的架构因为是2个集群,尽管他们都读取了同一个数据库,但当任一一朵云的Nacos有写操作,都不会及时同步到另外一朵云,此时两朵云读取的配置信息是有差别的,起因是因为nacos有一层缓存存在,这个缓存工夫默认6小时,每隔6小时,Leader会全量dump数据库的信息到本地缓存 新的架构做法如上图,将2集群合并成一个集群,双云之间通过专线买通网络,拜访和读取还是烟囱式,只是数据库还是单边,这样做的益处是,在任一一朵云批改数据,都会将腾讯云和阿里云所有节点都同步到,因为他们是同一个集群,应用原生的nacos集群同步形式 总结在nacos集群的应用上,咱们的业务场景次要还是cicd公布时须要读取配置,是一个读多写少的场景,基本上能够认为,只有读胜利,咱们的cicd就能残缺运行,因而在设计上,咱们优先思考读场景的可用性,无论哪一朵云故障,咱们的cicd零碎都能失常运行,因为在个别故障状况下,咱们也很少会写nacos 因而从架构上看,在腾讯云故障下,写是无奈在阿里云进行的,因为阿里云只有2个节点,无奈投票成为Leader,也就无奈实现写入,因而失常状况下,是读写nacos双云双活,极其状况下,阿里云故障,腾讯云读写nacos无异样,腾讯云故障,阿里云能够读,但无奈写

September 21, 2023 · 1 min · jiezi

关于nacos:Nacos-核心原理解读高性能微服务系统实战

download:Nacos 外围原理解读+高性能微服务零碎实战《模仿人生4》(The Sims 4)是一款由Maxis和The Sims Studio开发,由Electronic Arts发行的模仿人生游戏。它被宽泛认为是模仿人生系列中最好玩的一部分。本文将向您介绍TS4的入门常识。 TS4的基本概念在TS4中,你能够创立本人的虚拟世界并管制其中的角色。每个角色都有本人的特点、情感和属性。你须要治理他们的日常事务,例如吃饭、睡觉、工作和社交活动等,以确保他们有一个高兴、衰弱、富有成效的生存。 为了开始玩TS4,你须要购买游戏并下载安装。当你进入游戏时,你能够抉择创立新的家庭或加载现有的家庭。在初始阶段,你须要创立本人的角色并为他们抉择外貌、性情和技能。而后,你须要为他们建造屋宇或购买曾经建好的屋宇,并为他们购买必要的设施、家具和装饰品。 游戏中的操作在游戏中,你能够应用鼠标和键盘来管制你的角色。通过单击菜单按钮,你能够查看可用的操作选项。例如,你能够抉择让你的角色去吃饭、睡觉、工作或社交活动等。你还能够应用鼠标拖动和调整物品的地位和大小。此外,你能够应用游戏中的相机性能来浏览你的家庭和城市。 在TS4中,每个角色都有本人的技能和事业。这些技能能够通过一直的练习和学习来进步。当你的角色把握了某项特定技能时,他们将有机会取得更好的工作和更高的支出。 游戏中的社交互动与其余角色进行社交互动是TS4中的重要局部。通过与其余角色互动,你能够建设深厚的关系,并增进彼此之间的友情。你能够通过向其余角色打招呼、聊天、分享感情和密切接触等形式来建立联系。此外,你还能够邀请其余角色来加入你的派对和社交活动。 TS4的扩大包除了根本游戏之外,还有许多扩大包可用于TS4,这些扩大包能够减少新的游戏选项和性能。例如,你能够购买“城市生存”扩大包,以减少城市环境和城市居民的元素。或者,你能够购买“节令”扩大包,以减少天气变动和节令循环的元素。 论断在TS4中,你能够创立本人的虚拟世界,并管制其中的角色。通过治理他们的日常事务并与其余角色进行社交互动,你能够建设深厚的关系并取得更好的工作和更高的支出。如果你心愿更多地理解TS4,能够思考购买扩大包或查看游戏手册。

May 19, 2023 · 1 min · jiezi

关于nacos:Nacos-配置管理最佳实践

Nacos 简介Nacos 是一个更易于构建云原生利用的微服务根底平台,外围蕴含动静服务发现,配置管理,服务治理平台。 残缺内容请点击下方链接查看: https://developer.aliyun.com/article/1155681?utm_content=g_10... 版权申明:本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。

May 18, 2023 · 1 min · jiezi

关于nacos:完结Nacos-核心原理解读高性能微服务系统实战无密

微服务架构曾经成为越来越多企业的首选计划,然而在理论利用中,微服务的数量往往会十分宏大。因而,服务发现和配置管理变得尤为要害。Nacos就是一个十分好的解决方案。本文将具体介绍 Nacos+微服务 的应用办法。download:https://www.97yrbl.com/t-1655.html 服务注册与发现在微服务架构中,每个服务都能够独立地部署在不同的机器上。因而,咱们须要一种机制来主动地将服务注册到注册核心,并在须要时进行服务发现。Nacos能够帮忙咱们实现这些性能。 首先,咱们能够通过Nacos提供的API将服务注册到注册核心。例如: curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=my-service&ip=192.168.0.100&port=8080'其中,serviceName是服务名,ip是服务所在的IP地址,port是服务监听的端口号。 而后,咱们能够通过Nacos提供的API来实现服务发现。例如: curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instances?serviceName=my-service'这个API会返回所有注册了 my-service 这个服务的实例列表,包含它们的IP地址、端口号等信息。咱们能够依据这些信息来调用服务。 对立配置管理在微服务架构中,服务的配置通常须要依据不同的环境进行调整。应用Nacos,咱们能够将所有的配置文件都放在一个中央,并能够随时进行批改。当某个服务须要更新配置时,它能够从Nacos中获取最新的配置信息。 首先,咱们须要在Nacos中创立一个配置集和一个配置项。例如,咱们能够创立一个名为 my-config 的配置集,并增加一个名为 timeout 的配置项: curl -X POST 'http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=my-config&group=my-group&content=timeout=5000'其中,dataId是配置项的ID,group是配置项所属的组。content是配置项的具体内容。 而后,咱们能够通过Nacos提供的API来获取配置信息。例如: curl -X GET 'http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=my-config&group=my-group'这个API会返回 my-config 这个配置集中的所有配置项。 健康检查Nacos还提供了健康检查性能。咱们能够通过Nacos来监控每个微服务的状态,并及时进行解决。如果某个服务呈现故障或者异样,咱们能够通过Nacos来实现主动切换到备用服务,从而确保整个零碎的稳定性和可用性。 首先,咱们须要在Nacos中创立一个健康检查配置。例如: curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/health/checker?serviceName=my-service&groupName=my-其中,serviceName是服务名,groupName是服务所属的组。healthCheckerType是健康检查类型,这里抉择了HTTP形式。healthCheckerProperties是健康检查相干的属性,例如HTTP申请门路。 而后,咱们能够通过Nacos提供的API来获取服务的衰弱状态。例如: curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=my-service&groupName=my-group&healthyOnly=true'这个API会返回 my-service 这个服务中所有衰弱的实例列表。 综上所述,Nacos是一个十分好的工具,能够帮忙咱们更加不便地构建和治理微服务架构。无论是对于开发人员还是运维人员来说,都是一个不可或缺的利器。

May 13, 2023 · 1 min · jiezi

关于nacos:Nacos-核心原理解读高性能微服务系统实战不若鼠横行

download:Nacos 外围原理解读+高性能微服务零碎实战运维部署:如何疾速无效地治理软件系统关键字:运维、部署、自动化、监控、平安古代软件系统由多个组件形成,这些组件须要一直地进行更新和保护。为了保证系统的稳定性和可靠性,咱们须要一个高效的运维部署流程。本文将介绍如何疾速无效地治理软件系统,进步运维效率和品质。 首先,自动化部署是进步运维效率和品质的要害。通过应用自动化工具和脚本,能够轻松地进行软件部署和配置,缩小手动操作带来的谬误和提早。同时,自动化部署还可能实现疾速回滚和版本控制,保证系统的稳定性和可靠性。 其次,监控是保障系统运行的重要伎俩。通过对系统各个组件的监控和告警,能够及时发现并解决问题,防止因故障而导致的业务中断。同时,监控数据也能够作为优化和改善零碎的根据,进步零碎的性能和效率。 另外,平安是运维部署的重要方面。在进行软件部署和保护时,须要留神爱护零碎的安全性,例如加密敏感信息、限度拜访权限等。同时,也须要定期进行破绽扫描和修复,确保零碎不受攻打和毁坏。 最初,团队合作和常识治理也是运维部署的重要方面。通过建设良好的团队合作机制和常识共享平台,能够增强团队成员之间的交换和单干,进步运维效率和品质。 综上所述,运维部署是软件系统治理中不可或缺的一环。通过自动化部署、监控、平安爱护和团队合作等伎俩,能够进步运维效率和品质,实现疾速无效地治理软件系统。

May 13, 2023 · 1 min · jiezi

关于nacos:nacos初探

我的项目和文档github国内镜像站https://kgithub.com/nacos我的项目地址https://kgithub.com/alibaba/nacos/releasesnacos-docker我的项目地址https://kgithub.com/nacos-group/nacos-docker/blob/master/READ...nacos中文文档https://nacos.io/zh-cn/docs/quick-start.html镜像和部署最新的docker镜像(nacos服务端)docker pull nacos/nacos-server:v2.2.0数据库初始化脚本下载https://raw.kgithub.com/alibaba/nacos/develop/distribution/co...将数据库初始化脚本mysql-schema.sql搁置到docker-compose.yml文件的同一个目录nacos docker-compose配置version: "3.8"services: nacos-server: image: nacos/nacos-server:v2.2.0 environment: # 系统启动形式: 集群/单机,cluster/standalone 默认 cluster MODE: standalone # 数据库类型 SPRING_DATASOURCE_PLATFORM: mysql # mysql地址:能够间接援用docker-compose内的服务名称,也能够援用主机ip MYSQL_SERVICE_HOST: nacos-mysql # 如果mysql地址用的是服务名,这里就用容器端口;如果mysql地址用的是主机ip,那么就用主机映射端口 MYSQL_SERVICE_PORT: "3306" MYSQL_SERVICE_DB_NAME: nacos_devtest MYSQL_SERVICE_USER: root MYSQL_SERVICE_PASSWORD: root MYSQL_SERVICE_DB_PARAM: "characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true" # 内存参数 JVM_XMS: "1g" JVM_XMX: "1g" # 是否开启近程调试 NACOS_DEBUG: "n" volumes: - "./logs:/home/nacos/logs" ports: # 主端口,web端口 - "8848:8848" # grpc端口,应该等于 映射主端口 + 1000 (Nacos2.0的gRPC端口均通过主端口的偏移量计算产生,因而端口转发也须要满足该偏移量) - "9848:9848" # grpc端口,应该等于 映射主端口 + 1001 - "9849:9849" depends_on: nacos-mysql: condition: service_healthy# restart: on-failure nacos-mysql: image: mysql:5.7 environment: # root用户明码 MYSQL_ROOT_PASSWORD: root # 运行时须要创立的数据库名称 MYSQL_DATABASE: nacos_devtest # 运行时须要创立的用户名 MYSQL_USER: nacos # 运行时须要创立的用户,对应的明码 MYSQL_PASSWORD: nacos volumes: - "./mysql-schema.sql:/docker-entrypoint-initdb.d/nacos-mysql.sql" - "nacos-mysql_data:/var/lib/mysql" ports: - "8806:3306" command: - "--character-set-server=utf8mb4" - "--collation-server=utf8mb4_unicode_ci" healthcheck: test: [ "CMD", "mysqladmin", "-uroot", "-proot" ,"ping", "-h", "localhost" ] interval: 5s timeout: 10s retries: 10volumes: nacos-mysql_data:目录构造如启动/进行命令docker-compose up &docker-compose stop ...

March 13, 2023 · 1 min · jiezi

关于nacos:Higress-Nacos-微服务网关最佳实践

在去年11月的云栖大会上,咱们开源了云原生网关 Higress,时隔 2 月,Higress 的 Github 我的项目曾经播种了 700+ star,以及大量社区小伙伴的关注。在社区的交换中咱们发现有不少微服务开发者在应用如 Spring Cloud Gateway/Zuul 等微服务网关对接 Nacos 注册核心实现微服务的路由,并且心愿理解迁徙到 Higress 网关能带来哪些益处。 Higress 的 Github 我的项目:https://github.com/alibaba/hi...本文将介绍 Higress 组合 Nacos 作为微服务网关能力,并介绍微服务网关倒退的两个趋势,为网关的选型指明路线: 趋势一:对立 API 规范,向云原生微服务架构演进趋势二:合并平安&流量网关,向 DevSecOps 演进Higress:Nacos 的最佳拍档 Higress 和 Nacos 其实是师出同门——阿里中间件团队。在 Higress 撑持阿里外部业务的阶段,Higress 就曾经搭配 Nacos 作为微服务网关应用,凭借高性能撑持了双十一的洪峰流量;到了云产品商业化阶段,Higress 和 Nacos 持续基于阿里云 MSE(Microservices Engine)产品,严密合作演进产品性能;Higress 开源之后,如果想要自建微服务网关,抉择 Higress 配合 Nacos 应用,具备以下劣势: 比照 Spring Cloud Gateway/Zuul 等传统 Java 微服务网关性能高出 2-4 倍,能够显著升高资源老本作为云原生网关,实现了 Ingress/Gateway API 规范,兼容 Nginx Ingress 大部分注解,反对业务渐进式向基于 K8s 的微服务架构演进与 Dubbo/OpenSergo/Sentinel 等开源微服务生态深度整合,提供最佳实际Higress 的装置部署请点击原文参考 Higress 官网文档,这里默认曾经装置好 Higress,搭配 Nacos 应用的具体形式如下: ...

February 15, 2023 · 2 min · jiezi

关于nacos:Nacos-配置管理最佳实践

Nacos 简介 Nacos 是一个更易于构建云原生利用的微服务根底平台,外围蕴含动静服务发现,配置管理,服务治理平台。 配置管理是 Nacos 的外围性能,它提供了运行期不重启利用的状况下动静批改配置值的性能。 Nacos 配置核心倒退历程 Nacos 配置核心是从阿里团体内配置核心 Diamond 孵化而来,其整体倒退分为三个阶段: 1.阿里团体外部孵化期 nacos 配置核心诞生于阿里巴巴团体外部的配置核心 Diamond,后期次要服务于团体外部对动静配置的需要。 2.开源&商业化摸索尝试 团体 Diamond 经验了从开源再到闭源的过程,公布了商业化产品 ACM,并在 2018 年以 Nacos 配置核心为载体再次开源,期间对配置核心的开源及商业化进行了摸索。 3.三位一体交融倒退 明确三位一体倒退策略,以开源 Nacos 为内核,插件化反对团体 Diamond &商业化 MSE 定制的配置核心,三位一体交融倒退。 开源:以开源 Nacos 2.0 为内核,重构通信协议,性能扩展性晋升10倍,反对 10w 级实例规模,晋升开放性,联结开源微服务生态独特倒退。 商业化:反对 Nacos2.0 和专业版,目前 20% 用户降级到 Nacos2.0,并且反对配置鉴权和加密能力,推送轨迹等高级性能。 团体:关注性能和高可用能力,反对大促 1 小时建站,10 分钟反对响应;实现 Diamond Over Nacos2.0 架构演进,扩展性晋升 1 倍,反对 500w 实例规模。 利用场景&双十一实际Nacos 配置管理利用场景 配置核心在业务域,根底技术域都有着宽泛的利用,包含业务利用的开关,微服务生态的服务路由及元数据,高可用生态的预案,切流规定及降级开关等,前端生态的各类文案布告,数据库生态的外围配置参数,动静切库等配置。 在每年阿里团体的双十一大促中,配置核心也是一个不可或缺的根底组件,包含后期热点商品推送,大促气氛流动标调整,大促期间数据库主备切换开关,外围性能降级,各类名单调整,预案限流调整,各种根底中间件的外围参数动静,大促完结后各类预案的复原,大促态到日常态的状态切换,都是配置核心所反对的场景。 配置核心应用指引1.配置核心原理 业务利用:nacos 的应用方,通过 nacos-client 实现配置的公布,查问,监听回调的等根底操作。负载平衡 SLB:与后端的 nacos 服务节点进行交互的地址,在用户自建或者调试的场景下,也能够采纳直连 IP 或者地址服务器 endpoint 的模式。Nacos Server:nacos 服务端存储以后集群全量配置的内存和磁盘缓存,集群节点之间进行程度告诉配置变更事件,和后端数据库进行对账保证数据一致性。Nacos 控制台:治理控制台,能够进行配置查看,配置公布,监听查问等运维性能。商业化 Nacos 反对推送轨迹,监控,事件核心等高级性能。数据库:配置长久化存储的数据库,个别是主备库架构进行容灾。用户在接入 nacos 次要有两种模式,一种是通过原生 nacos-client 的 ConfigService 组件的根底 API 接入,第二种是通过 Spring 框架或者其余相似框架组件接入,包含 SpringCloud 和 SpringBoot 等。 ...

February 14, 2023 · 2 min · jiezi

关于nacos:Nacos-中的配置文件如何实现加密传输

小伙伴们晓得,Spring Cloud Config 很早就提供了配置文件的加解密性能,并且反对对称加密和非对称加密两种不同的模式。Nacos 作为分布式配置核心+服务注册核心的合体,在配置文件加密这块始终差点意思,不过好在,如果你应用的 Nacos 版本大于 2.0.4 这个版本,那么当初也能够通过插件的形式来实现配置文件加密了。 1. 配置文件加密松哥在之前的微服务视频中讲过,Spring Cloud Config 的对称加密和非对称加密,加密后的文件格式相似上面这样: name={cipher}密文password={cipher}密文能够看到,在 Spring Cloud Config 中,对配置文件的加密是针对字段一个一个加密的。 而 Nacos 中的加密,则是对整个配置文件的内容进行加密,这点和 Spring Cloud Config 不同。 Nacos 中是通过 SPI 的机制形象出加密和解密的操作,Nacos 默认提供 AES 对称加密的实现,不过用户也能够自定义加解密的实现形式。 在 Nacos 服务端启动的时候就会加载所有依赖的加解密算法,而后通过公布配置的 dataId 的前缀来进行匹配是否须要加解密和应用的加解密算法。 客户端公布的配置会在客户端通过 filter 实现加解密,也就是配置在传输过程中都是密文的,而控制台公布的配置会在服务端进行解决。 换言之,用了 Nacos 的配置文件加密插件之后,咱们在 Nacos 治理页面上配置的配置文件,将会以加密的密文模式存储在数据库中,也会以密文的模式传输到客户端,而后在客户端主动实现解密操作。大抵上就是这样一个过程。接下来咱们就来看看具体的用法。 2. 实际首先咱们须要下载 nacos 源码进行编译,编译实现之后,须要将之装置到本地 Maven 仓库(因为编译加密插件须要用到 Nacos)。 首先 clone nacos 源码,如下: git clone https://github.com/alibaba/nacos.git下载之后,集体倡议用 IDEA 去编译,操作不便一些(因为后续还有其余操作)。 所以咱们先用 IDEA 关上我的项目,确认我的项目所需依赖均已下载结束,而后点击 install 按钮,将我的项目编译装置到本地仓库: 接下来 clone 配置文件加解密的插件,如下: ...

November 28, 2022 · 1 min · jiezi

关于nacos:Nacos设置配置数据源到Mysql

1. 首先要配置数据库源,个别都用MySQL:关上conf文件夹下的application.properties,批改数据库信息: 2,再应用MySQL创立数据库,数据库名为nacos(上图显示),可更改。创立好数据库后,执行conf文件夹下mysql-schema.sql文件即可。 3,重启Nacos杀掉Nacos: sudo lsof -i:8848kill 指定PID重启: 启动胜利:

November 24, 2022 · 1 min · jiezi

关于nacos:ubuntu部署Nacos

1,下载Nacos,并放到服务器 2,进度到Nacos所在目录并解压 3,进入到nacos目录并启动 http://192.168.xxx.xxx:8848/nacos/index.html默认登陆账号和明码账号:nacos明码:nacos4,进入我的项目配置信息(此处为spring-boot我的项目,文档和代码示例能够再次看:https://nacos.io/zh-cn/docs/q...) 5,测试能够失常应用:@RestController@NacosPropertySource(dataId = "test",autoRefreshed = true)public class test1 { @NacosValue(value = "${url:kason}", autoRefreshed = true) private String url = ""; @NacosValue(value = "${user:kason}", autoRefreshed = true) private String user = ""; @NacosValue(value = "${pass:pass}", autoRefreshed = true) private String pass = ""; @GetMapping(value="/testNacos") public Map<String,String> testNacos() { Map<String, String> map = new HashMap<>(); map.put("url", url); map.put("user", user); map.put("pass", pass); return map; } @GetMapping(value="/test01") public String test01() { return "how are you?"; } } ...

November 1, 2022 · 1 min · jiezi

关于nacos:注册中心高并发场景微服务实战九

你好,我是程序员Alan. 我在《文言服务治理—高并发场景微服务实战(八)》中,简略介绍了微服务常见组件性能,从本篇开始我将进一步解说各个组件的内容和利用。 服务调用的问题在《需要剖析—高并发常见微服务实战(二)》剖析业务需要时,其中有个简略的性能点:订票服务能够申请不同的航空公司查问机票信息,订购指定航空公司航班机票。这里就波及到两个或多个服务间的调用问题。 服务调用能够简略的分为单实例状况和多实例状况。 单实例状况能够采纳 IP + Port + 接口的模式,采纳点对点的HTTP间接调用。这种状况有个显著的毛病就是如果服务增多,将会造成蜘蛛网的模式,十分不利于开发保护。 多实例状况在理论生产场景中,咱们通常会采纳多实例集群部署,来应答服务的压力。但多实例部署后,间接面临一个问题,即调用方如何通晓调用哪个实例,当实例运行失败后,如何转移到别的实例下来解决申请?此时咱们可能会抉择Nginx负载平衡,但往往是动态的,在服务不可用时,如何动静的更新负载平衡列表,保障调用者的失常调用呢? 面对以上两种状况,咱们须要将所有的服务对立的、动静的治理起来,此时注册核心就应运而生。 服务注册核心服务注册核心作分布式服务框架的外围模块,要实现的性能是服务的注册、订 阅、登记、告诉 。 所有的服务都与注册核心产生连贯,由注册核心对立配置管理,不再由实例本身间接调用。服务治理过程大抵过程如下图所示: 从图中,能够看到一个残缺的,服务注册和发现的过程: 1.服务提供者启动时,将服务提供者的信息被动提交到服务注册核心进行服务注册。 2.服务调用者启动时,将服务提供者信息从注册核心下载到调用者本地,调用者从本地的 服务提供者列表中,基于某种负载平衡策略抉择一台服务实例发动近程调用,这是一个点到点调用的形式。 3.服务注册核心可能感知服务提供者某个实例下线,同时将该实例服务提供者信息从注册核心革除,并告诉服务调用者集群中的每一个实例,告知服务调用者不再调用本实例,免得调用失败。 从这个过程中能够看出,有了注册核心之后,服务节点的减少和缩小对于客户端就是通明的。这样,除了能够实现不重启客户端,就能动静地变更服务节点以外,还能够实现优雅敞开的性能。 Nacos利用目前业界有很多可供你来抉择的注册核心组件,例如 ZooKeeper,阿里的微服务注册核心 Nacos,Spring Cloud 的 Eureka 等等。 我集体比拟罕用的组件是Nacos,它的定位是一个更易于构建云原生利用的动静服务发现、配置管理和服务治理平台。 装置NacosNacos官网地址:https://nacos.io/en-us/,本文采纳Nacos 1.1.4 版本。 下载后解压,应用对应命令启动。 下载后解压,应用对应命令启动。 startup.cmd -m standalone (standalone代表着单机模式运行,非集群模式) 启动日志如下: 启动胜利后,关上http://127.0.0.1:8848/nacos,输出默认的用户名 nacos、明码 nacos,就能够看到如下界面。 能够看到Nacos左侧菜单栏看到Nacos提供的次要性能,本次咱们只用到Nacos服务治理性能,其余性能前面章节再讲。 服务中利用Nacos本文演示如何在Sping Boot我的项目中启动Nacos的服务发现性能。 1.增加依赖 <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>nacos-discovery-spring-boot-starter</artifactId> <version>0.2.7</version></dependency>留神:版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。 \2. 在application.yml中配置Nacos server的地址 nacos: discovery: server-addr: 127.0.0.1:8848\3. 应用 @NacosInjected 注入 Nacos 的 NamingServerice 实例 ...

November 1, 2022 · 1 min · jiezi

关于nacos:聊聊使用RefreshScope与nacos2整合踩到的坑

前言本文的素材来源于敌人整合nacos2作为配置核心进行动静刷新时,踩到的坑。他过后遇到的问题,如下截图因为那段时间比较忙,于是我在没看敌人我的项目代码的根底上,就找个了看似解决方案的答案,扔了过来前面敌人加了这个配置,问题果然没有解决。前面就抽了一点工夫,要了他的我的项目代码来看下。 代码示例因为他这个我的项目次要是他自学nacos的我的项目,也没波及啥敏感信息。本文就间接拿他的我的项目示例演示 1、我的项目pom依赖<properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <java.version>1.8</java.version> <spring-boot.version>2.3.12.RELEASE</spring-boot.version> <spring-cloud.version>Hoxton.SR12</spring-cloud.version> <spring-cloud-alibaba.version>2.2.8.RELEASE</spring-cloud-alibaba.version> </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-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <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> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> </dependencies>注: nacos服务端版本为2.1.1 2、nacos配置核心地址,配置在bootstrap.yml外面spring: cloud: nacos: server-addr: localhost:88483、我的项目的根本信息配置在application.yml外面#spring: application: name: nacos-configserver: port: 80304、编写一个须要动静刷新获取值的controller@RestController@RequestMapping("/config")@RefreshScopepublic class ConfigController { @Value("${user.userName:123}") private String userName; @RequestMapping("/get") private String get(){ return userName; }}5、业务我的项目在nacos服务端上配置如下 ...

September 8, 2022 · 1 min · jiezi

关于nacos:Nacos集成Spring-Cloud-Gateway实现动态路由

Nacos集成Spring Cloud Gateway实现动静路由后面咱们曾经介绍了Nacos 的装置与配置,Spring Cloud 集成Nacos 作为服务的注册核心和配置核心,集成Nacos 实现服务的负载平衡和一些常见的负载平衡策略、应用Dubbo、OpenFegin进行RPC调用以及整合Spring Cloud Gateway作为服务的网关和Gateway的过滤器配置接下来就让咱们一起来看看Spring Cloud Gateway的动静路由以及负载平衡关联服务名的动静路由之前咱们都是这样来配置的路由 service-url: user-service: http://localhost:...spring: cloud:   gateway:     routes:       - id: path_route         uri: ${service-url.user-service}/user/get/{id}         predicates:           - Path=/user/get/{id}复制代码置信同学们都发现了一个问题,在微服务集群部署中一个服务可能会有多台主机,咱们这样配置路由不够灵便,每更改一次服务的主机信息都要从新编写一次配制文件,而后还须要重启Gateway服务器。咱们要晓得,在真正的我的项目中重启服务是很耗时的,咱们应该尽量避免这种状况Spring Cloud Gateway提供了lb//服务名的形式来动静的配置路由,网关会依据注册核心的服务名动静的获取服务的URL,这样即使该服务的某一台主机地址扭转或者挂掉,网关都不用再跟着扭转因而,咱们能够将路由更改成这种形式 spring: cloud:   gateway:     routes:       - id: path_route          # uri: ${service-url.user-service}/user/get/{id}         uri: lb://user-service         predicates:           - Path=/user/get/{id}复制代码留神,引入依赖时须要排除Nacos中ribbon的依赖,而后再导入loadbalancer的依赖 ...

September 3, 2022 · 3 min · jiezi

关于nacos:Nacos的注册和使用

1.服务注册导入Nacos依赖<!-- SpringCloudAlibaba无关依赖 --><dependency> <groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.2.5.RELEASE</version><type>pom</type><scope>import</scope></dependency> 复制代码客户端导入依赖,如果有eureka的依赖得先注掉<!-- nacos客户端起步依赖 --><dependency> <groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency> 复制代码 1.1 批改配置文件 注册cloudcloud: nacos: server-addr: localhost:8848 # nacos 服务地址复制代码 到后盾在配置文件中查看 2.NacosRule负载平衡2.1.配置集群属性 在配置文件cloud的属性下中增加cluster-name 设置集群昵称,就会主动调配 2.2 依据集群配置负载平衡 NFLoadBalancerRuLeClassName 同个集群下有多个服务时会采纳随机的形式来负载平衡。增加以下配置文件spring:userservice: #要做配置的微服务名称 ribbon: NFLoadBalancerRuLeClassName: 配置负载平衡的规定复制代码 呈现跨集群拜访时,控制台会输入正告信息,提醒运维人员。 2.3 依据权重负载平衡 3.环境隔离 在没有设置空间下节点默认都是放在命名空间的public默认空间 3.1 新建空间 命名空间后会发现多进去了一个空间ID,在会到服务列表会看到咱们新增的一个命名空间 3.2 代码对接命名空间 在配置文件中增加namespace:前面跟着命名空间的id 4.和Eureka的区别 Eureka通过被动询问来判断服务是否还存活,Nacos是通过心跳的来判断。 5.Nacos配置管理5.1 创立配置文件实现热更新新增配置 配置文件昵称必须是要惟一的用于对立治理, 配置内容是把有热更新需要的放进来,如固定格局的配置不须要更新的就不须要填写。 ...

August 24, 2022 · 1 min · jiezi

关于nacos:记一次使用nacos2踩到的坑

前言本文素材起源敌人学习nacos2.1.1踩到的坑。间接上正菜 坑点一:呈现端口被占用因为是学习应用,敌人就在物理机搭建了搭建了nacos伪集群,即ip都一样,端口别离为8848,8847,8849。然而启动nacos服务器后,一台失常启动,其余两台都报了端口被占用 呈现这种状况的起因,官网有做了解释通过官网咱们能够很容易得悉,这个端口被占用次要是因为grpc引起的,因为他端口的生成形式,是由主端口+1000、主端口+1001生成。 解决办法集群的端口不要采纳相邻数字,步长尽量搞大点。比方设置为7777、8888、9999之类的 坑二:微服务项目启动呈现com.alibaba.nacos.api.exception.NacosException: Client not connected, current status:STARTING异样这个问题呈现在敌人在我的项目中配置的nacos地址为nginx地址,配置示例如下 spring.cloud.nacos.discovery.server-addr=nginx ip一开始敌人nginx的配置示例如下 upstream nacos-cluster { server 127.0.0.1:7777; server 127.0.0.1:8888; server 127.0.0.1:9999; } server { listen 80; server_name localhost; location / { proxy_pass http://nacos-cluster; } }浏览器通过nginx拜访没问题,然而我的项目中把nacos服务地址配置为nginx ip就报了 com.alibaba.nacos.api.exception.NacosException: Client not connected, current status:STARTING这个异样信息,前面敌人查资料,官网上有写 于是他就将转发形式改为TCP,他的nginx版本是1.9+以上版本,默认就反对TCP代理了,不必额定装置stream模块。nginx配置TCP的示例形如下 stream { upstream nacos-cluster-grpc{ # nacos2版本,grpc端口与要比主端口多1000,主端口为7777、8888、9999 server 127.0.0.1:8777; server 127.0.0.1:9888; server 127.0.0.1:10999; } server{ listen 9848; proxy_pass nacos-cluster-grpc; }}当敌人配置好nginx tcp代理转发后,通过telnet命令 telnet 127.0.0.1 9848来看是否能失常转发给nacos服务端,通过验证,网络能够连通。接着敌人在微服务项目的nacos配置填写如下地址 spring.cloud.nacos.discovery.server-addr=127.0.0.1:9848 #nginx代理tcp的地址原本认为高枕无忧,后果我的项目一启动,依然报 com.alibaba.nacos.api.exception.NacosException: Client not connected, current status:STARTING于是敌人懵了,啥状况?就来找我交换一下其实在nacos官网的FAQ就有提到相应的解题思路了 ...

August 19, 2022 · 1 min · jiezi

关于nacos:On-NacosSpringCloud-方式使用-Nacos

如果大家想要理解更多的 Nacos 教程,欢送 star 《on-nacos》开源我的项目。基于 Nacos 2.x 的入门、原理、源码、实战介绍,帮忙开发者疾速上手 Nacos。本文介绍下如何在 Spring Cloud 我的项目中应用 Nacos,Nacos 次要分为两个局部,配置核心和服务注册与发现。在应用 Spring Cloud 我的项目中应用 Nacos ,首先要保障启动一个 Nacos 服务,具体能够参考《疾速上手 Nacos》来搭建一个单机的 Nacos 服务。 Nacos 接入 Spring Cloud 的源代码能够参考 spring-cloud-alibaba 这个我的项目,感兴趣的敌人能够查看源代码。Spring Cloud Alibaba 的版本对应关系能够参考:版本阐明 Wiki 本篇文章的具体的代码示例点击【nacos-spring-cloud】查看 配置核心创立配置关上控制台 http://127.0.0.1:8848/nacos ,进入 配置管理-配置列表 点击+号新建配置,这里创立个数据源配置例子: nacos-datasource.yaml spring: datasource: name: datasource url: jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&useDynamicCharsetInfo=false&useSSL=false username: root password: root driverClassName: com.mysql.jdbc.Driver 增加依赖配置创立好就能够在控制台 配置管理-配置列表中查看到。接下来演示下怎么在 Spring Cloud 我的项目中获取到 Nacos 中的配置信息。 须要在我的项目中增加以下依赖: <!--配置核心--><dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>${latest.version}</version></dependency>如果我的项目的 Spring Boot 版本 小于 2.4.0 须要在我的项目中创立 bootstrap.properties 而后在我的项目中的 bootstrap.properties 文件中增加 nacos 的一些配置: ...

June 29, 2022 · 2 min · jiezi

关于nacos:服务管理与通信基础原理分析

波及轻微的源码展现,可释怀参考;一、根底简介服务注册发现是微服务架构中最根底的能力,上面将从源码层面剖析实现逻辑和原理,在这之前要先来看下依赖工程的根底构造,波及如下几个外围组件: commons:服务组件的形象申明,本文只剖析注册发现与负载平衡;nacos:当下罕用的注册核心组件,用来进行服务治理;feign:服务间通信交互组件,在服务申请时波及负载平衡的策略;ribbon:在服务间通信申请时,提供多种负载平衡的策略实现;在相熟工程依赖之间的构造时,还要明确服务间交互的流程和原理,这样在剖析源码设计时,有一个清晰的思路与轮廓;如何实现上面的服务交互模式,在浏览源码工程时,围绕如下两个外围逻辑: 注册发现:注册时如何上报服务的信息数据,这些数据以怎么的形式治理;负载平衡:当申请的服务同时存在多个时,以什么样的策略抉择执行申请的服务;在这里先简略的聊一下集体在浏览源码工程时的基本思路,比方微服务组件:通常从配置参数作为切入口,察看基于参数构建的外围对象,再重点剖析对象的管理模式,以及适配的扩大能力,最初联合我的项目的利用场景即可: 浏览源码最重要的是耐着情绪缓缓看,并顺手画下外围流程,实际上如果有肯定的编程教训,不论是浏览什么工程的源码,只有用心去剖析单点的实现原理,都算不上适度简单,然而组件通常为了复用能力,会去适配多种简单的场景,这样势必要采纳形象的封装和设计模式,源码工程的复杂度天然就会相应进步,这个话题后续会细聊。 二、服务注册1、服务配置首先从Nacos配置参数开始,这里只设置服务发现的两个参数:1Nacos注册核心的服务端地址,2在服务的元数据中加载分支号;而后来具体的看源码流程: 在配置参数加载的过程中,有很多缺省的默认值,所以须要关注最终会提供的参数信息,来判断是否须要自定义设置,另外AutoConfig配置要重点看实例化的对象;断点的流程能够依照如下的形式做设置,这里排列的是在配置加载阶段的几个外围节点: 参数:NacosDiscoveryProperties#getNacosProperties配置:NacosServiceAutoConfiguration#nacosServiceManager构建:NacosServiceManager#buildNamingService NamingService是Nacos服务治理接口,波及注册、查问、撤销、查看等多个办法,即对应的是Nacos服务端的相应API申请,在注册执行的阶段会细说用法。 2、注册构建看完服务配置之后再看注册配置,对于配置中简单的设计,须要重点关注两个信息:ConditionalOn和matchIfMissing,这样很容易发现默认加载: 配置:NacosServiceRegistryAutoConfiguration#nacosServiceRegistry注册:NacosServiceRegistry#register实例:NacosServiceRegistry#getNacosInstanceFromRegistration 在构建服务注册的外围类NacosServiceRegistry时,通过服务的注销信息转换为注册的实例化对象,而后通过NamingService接口办法,上报实例化对象;须要留神的是,尽管这里只看了Nacos中的相干API,但实际上API实现了诸多spring-cloud-commons包中申明的接口,比方Registration、ServiceInstance等。 3、执行上报通常微服务的注册核心组件,都是基于server-client架构和部署形式,客户端须要依据本身启动状态去上报或者撤销注册,服务端负责对立保护注册数据: 实现:NacosNamingService#registerInstance执行:NamingProxy#registerService接口:InstanceController#register 在最终执行服务注册时,其动作实质就是申请Nacos服务端的一个Post办法,并将配置数据上报,例如:IP地址、端口、元数据、权重等;这样客户端注册逻辑执行实现,而后再看服务端数据可视化界面,就能够看到注册的客户端服务。 至于Nacos服务端是如何治理这些注册数据的,参考部署版本的nacos-naming模块源码,浏览上报接口和页面中的列表加载的实现即可;留神在初始的配置文件中,退出的branch分支参数也在元数据结构中。 在NamingService接口中,波及多个服务治理的办法,在执行原理上基本相同就不在赘述,这样注册核心的Client端和Server端就造成了通信机制,接下来再看Client端之间的通信。 三、服务通信1、根底配置Feign在配置方面比较复杂,提供了多个场景下的适配能力,这里只以两个常见的参数作为切入点:1通信超时工夫,2Http选型(采纳默认值); 参数:FeignClientProperties#getConfig注解:FeignClientsRegistrar#registerFeignClients配置:FeignAutoConfiguration#feignContext构建:FeignClientFactoryBean#getTarget 这里要重点关注的是注解的扫描和注册以及容器治理,要了解Feign的上下文环境须要明确上文中形容的服务间交互原理,而后参考FeignClientFactoryBean工厂类中构建逻辑。 2、通信逻辑尽管Feign注解的形式能够简化开发,然而在具体执行的时候还是Http的申请响应模式,这里能够参考LoadBalancerFeignClient类中的execute办法: 配置:FeignRibbonClientAutoConfiguration通信构建:LoadBalancerFeignClient#execute负载平衡:AbstractLoadBalancerAwareClient#executeWithLoadBalancer 不论是Feign组件还是Spring框架,默认的负载平衡策略都是采纳Ribbon的实现形式,在上述流程中配置和负载平衡命令都依赖Ribbon组件,接下来看服务抉择策略。 四、负载平衡1、命令构建这里构建了调用负载平衡接口的命令,ILoadBalancer接口中提供服务治理的相干办法,其中最外围的就是chooseServer办法,而后联合具体的策略规定实现服务的抉择的性能: 命令构建:LoadBalancerCommand.Builder#build负载容器:LoadBalancerContext#getServerFromLoadBalancer抉择接口:ILoadBalancer#chooseServer 2、策略规定Ribbon组件中负载平衡的策略有好几种规定,比方随机抉择、Key匹配、权重歪斜等;在工作中罕用的就是默认规定即RoundRobinRule,以及基于Key设计的灰度模式,简略做法就是服务启动时在元数据中增加的分支号作为匹配的标识; 规定设置:BaseLoadBalancer#setRule随机策略:RoundRobinRule#choose过滤策略:PredicateBasedRule#choose 当初回到流程的开始看,通过Nacos组件进行服务注册和治理,通过Feign组件基于Ribbon负载平衡策略做服务通信,如果单看各节点组件的逻辑还比拟容易了解,然而通过Spring框架做组件之间的合作调度时,复杂程度明显提高; 如果是刚开始浏览源码的阶段,能够只关注相应流程的外围逻辑,选择性疏忽细节的实现原理,当然重点还是要多读读Spring的设计,这样工夫久了天然会有很多播种。 五、参考源码编程文档:https://gitee.com/cicadasmile/butte-java-note利用仓库:https://gitee.com/cicadasmile/butte-flyer-parent

June 8, 2022 · 1 min · jiezi

关于nacos:Nacos配置中心介绍与应用微服务开发标配组件

配置核心作为散布式微服务开发的标配组件,业界已有很多胜利的典型利用,如:携程 Apollo 分布式配置核心、百度 Disconf 分布式配置核心等。盘古开发框架配置核心基于阿里的 Nacos 提供动静配置服务。 鉴于文档可读性,盘古教程和参考范例都应用的本地配置的形式。本文将介绍如何基于配置核心让盘古利用取得动静配置服务的能力。在理论利用中,如无非凡需要咱们个别都倡议采纳配置核心的形式来开发。配置核心介绍Nacos 动静配置服务能够让你以中心化、内部化和动态化的形式治理所有环境的利用配置和服务配置。动静配置打消了配置变更时重新部署利用和服务的须要,让配置管理变得更加高效和麻利。配置中心化治理让实现无状态服务变得更简略,让服务按需弹性扩大变得更容易。它还提供了一个简洁易用的 UI 帮忙你治理所有的服务和利用的配置,包含配置版本跟踪、金丝雀公布、一键回滚配置等一系列开箱即用的配置管理个性,帮忙你更平安地在生产环境中治理配置变更和升高配置变更带来的危险。 疾速 QA:前文中 Nacos 用于服务注册,为什么配置核心也是它? Nacos 是构建以“服务”为核心的古代利用架构 (例如微服务范式、云原生范式) 的服务基础设施。致力于发现、配置和治理微服务,完满的整合了配置核心和服务注册核心。因而,Nacos 不仅是服务注册核心也是功能完善的分布式配置核心。 疾速 QA:单体分层架构的开发模式也能够应用配置核心进行配置吗? 配置核心是散布式微服务架构开发环境下强烈建议的必选标配组件。但如果你是基于单体分层架构开发,配置核心也是一样能够应用的。对于这些根底能力,无论是微服务还是单体,盘古框架都做了完满适配,只须要依赖 pangu-spring-boot-starter 就能够实现开箱即用。 相干名词解释命名空间用于进行租户粒度的配置隔离。不同的命名空间下,能够存在雷同的 Group 或 Data ID 的配置。Namespace 的罕用场景之一是不同环境的配置的辨别隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。 配置管理系统配置的编辑、存储、散发、变更治理、历史版本治理、变更审计等所有与配置相干的流动。 配置项一个具体的可配置的参数与其值域,通常以 param-key=param-value 的模式存在。例如咱们常配置零碎的日志输入级别( logLevel=INFO|WARN|ERROR )就是一个配置项。 配置集一组相干或者不相干的配置项的汇合称为配置集。在零碎中,一个配置文件通常就是一个配置集,蕴含了零碎各个方面的配置。例如,一个配置集可能蕴含了数据源、线程池、日志级别等配置项。 配置集IDNacos 中的某个配置集的 ID。配置集 ID 是组织划分配置的维度之一。一个零碎或者利用能够蕴含多个配置集,每个配置集都能够被一个有意义的名称标识。(比方:应用利用名称作为 Data ID) 配置快照Nacos 的客户端 SDK 会在本地生成配置的快照。当客户端无奈连贯到 Nacos Server 时,能够应用配置快照显示零碎的整体容灾能力。配置快照相似于 Git 中的本地 commit,也相似于缓存,会在适当的机会更新,然而并没有缓存过期( expiration )的概念。 本地配置与配置核心比照本地配置(配置文件) 配置扩散、与利用耦合、动态配置无环境隔离无版本反对,容易引发生产事变无平安审计配置核心 配置集中、内部化、动态化实时失效多环境隔离多版本反对,较平安配置权限管制、操作变更审计配置核心实战上面介绍一个应用配置核心的例子。其它基于本地配置的范例都能够参考这个例子改为基于配置核心的动静配置。 装置相干盘古模块盘古 Parent<parent> <groupId>com.gitee.pulanos.pangu</groupId> <artifactId>pangu-parent</artifactId> <version>latest.version.xxx</version> <relativePath/></parent>根底模块<dependency> <groupId>com.gitee.pulanos.pangu</groupId> <artifactId>pangu-spring-boot-starter</artifactId></dependency>本地配置基于配置核心的配置也是须要一个本地配置文件的,但这个配置文件是固定的模版格局。用于配置一些与配置核心进行数据通信相干的根底类参数。如下所示。 ...

June 6, 2022 · 1 min · jiezi

关于nacos:Nacos源码学习系列第2篇服务搭建集群模式

明天咱们搭建一个3个node的nacos 集群我的项目配置把我的项目的源码整体复制2份到新的文件夹【naco2】和【nacos3】 参照【Nacos源码学习系列第1篇服务搭建-单机模式】导入我的项目并设置启动端口别离为【8060】和【8070】 数据库连贯配置3个服务能够共用一套, 不用反复创立数据库我的项目启动区别于单机模式的vm启动参数配置, VM参数须要设置为 -Dnacos.standalone=false 为每个服务创立3个独立的运行时文件目录 同时在该目录下创立一个conf子目录并创立 cluster.conf文件 文件目录构造: 【起始目录 -> nacos1 -> nacos -> conf -> cluster.conf】 【起始目录 -> nacos2 -> nacos -> conf -> cluster.conf】 【起始目录 -> nacos3 -> nacos -> conf -> cluster.conf】 文件内容对立如下: 你的ip:8860你的ip:8850你的ip:8870留神: 每个我的项目肯定要配置独立的运行目录, 共用的话服务启动会失败别离为每个我的项目设置VM 参数  -Duser.home=起始目录/nacos1 -Duser.home=起始目录/nacos2 -Duser.home=起始目录/nacos3 debug 模式运行 【cosole】模块下的 【Nacos.java】 看到上面的日志阐明启动胜利,启动过程有点慢,大略要2分钟左右: 2022-05-21 21:51:16.486 INFO 266344 --- [acos-starting.0] c.a.n.c.l.StartingApplicationListener : Nacos is starting...2022-05-21 21:51:17.507 INFO 266344 --- [acos-starting.0] c.a.n.c.l.StartingApplicationListener : Nacos is starting...2022-05-21 21:51:18.566 INFO 266344 --- [acos-starting.0] c.a.n.c.l.StartingApplicationListener : Nacos is starting...2022-05-21 21:51:19.585 INFO 266344 --- [acos-starting.0] c.a.n.c.l.StartingApplicationListener : Nacos is starting...2022-05-21 21:51:20.590 INFO 266344 --- [acos-starting.0] c.a.n.c.l.StartingApplicationListener : Nacos is starting...2022-05-21 21:51:21.595 INFO 266344 --- [acos-starting.0] c.a.n.c.l.StartingApplicationListener : Nacos is starting...2022-05-21 21:51:22.345 INFO 266344 --- [ main] c.a.n.c.l.StartingApplicationListener : Nacos started successfully in cluster mode. use external storage如果启动过程中碰到上面的谬误,起因是没有找到运行目录下的集群配置文件【nacos -> conf -> cluster.conf】:Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serverMemberManager' defined in file [D:\code\nacos2\core\target\classes\com\alibaba\nacos\core\cluster\ServerMemberManager.class]: Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.alibaba.nacos.core.cluster.ServerMemberManager]: Constructor threw exception; nested exception is ErrCode:500, ErrMsg:jmenv.tbsite.net at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:304) at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:285) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1338) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:554) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:514) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:321) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:319) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1276) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1196) at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857) at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760) ... 90 common frames omittedCaused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.alibaba.nacos.core.cluster.ServerMemberManager]: Constructor threw exception; nested exception is ErrCode:500, ErrMsg:jmenv.tbsite.net at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:187) at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:117) at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:300) ... 104 common frames omittedCaused by: com.alibaba.nacos.api.exception.NacosException: java.net.UnknownHostException: jmenv.tbsite.net at com.alibaba.nacos.core.cluster.lookup.AddressServerMemberLookup.run(AddressServerMemberLookup.java:152) at com.alibaba.nacos.core.cluster.lookup.AddressServerMemberLookup.doStart(AddressServerMemberLookup.java:100) at com.alibaba.nacos.core.cluster.AbstractMemberLookup.start(AbstractMemberLookup.java:55) at com.alibaba.nacos.core.cluster.ServerMemberManager.initAndStartLookup(ServerMemberManager.java:185) at com.alibaba.nacos.core.cluster.ServerMemberManager.init(ServerMemberManager.java:165) at com.alibaba.nacos.core.cluster.ServerMemberManager.<init>(ServerMemberManager.java:146) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:175) ... 106 common frames omittedCaused by: java.net.UnknownHostException: jmenv.tbsite.net at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184) at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) at java.net.Socket.connect(Socket.java:606) at sun.net.NetworkClient.doConnect(NetworkClient.java:175) at sun.net.www.http.HttpClient.openServer(HttpClient.java:463) at sun.net.www.http.HttpClient.openServer(HttpClient.java:558) at sun.net.www.http.HttpClient.<init>(HttpClient.java:242) at sun.net.www.http.HttpClient.New(HttpClient.java:339) at sun.net.www.http.HttpClient.New(HttpClient.java:357) at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1226) at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1162) at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1056) at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:990) at com.alibaba.nacos.common.http.client.request.JdkHttpClientRequest.execute(JdkHttpClientRequest.java:114) at com.alibaba.nacos.common.http.client.NacosRestTemplate.execute(NacosRestTemplate.java:482) at com.alibaba.nacos.common.http.client.NacosRestTemplate.get(NacosRestTemplate.java:72) at com.alibaba.nacos.core.cluster.lookup.AddressServerMemberLookup.syncFromAddressUrl(AddressServerMemberLookup.java:175) at com.alibaba.nacos.core.cluster.lookup.AddressServerMemberLookup.run(AddressServerMemberLookup.java:143) ... 116 common frames omitted查看控制台浏览器关上控制台 ...

May 31, 2022 · 2 min · jiezi

关于nacos:Nacos源码学习系列第1篇服务搭建之单机模式

服务搭建咱们分成3个局部服务端单机部署、服务端集群部署、客户端集成别离解说 本篇学习指标:能把Nacos服务端2.1.0版本的源码以Debug模式运行再IDE中 源码下载进入nacos我的项目的的github地址,咱们抉择版本2.1.0(在分支抉择框外面点击tags上面的2.1.0)  把我的项目代码下载到本地目录。https://github.com/alibaba/nacos 我的项目导入   把我的项目导入 idea, 在导入之前倡议把我的项目名由【nacos-2.1.0】 改成【nacos1】, 因为前面咱们在搭建服务端集群的时候会同时启动多个nacos server。    批改console模块下的 application.properties 文件 找到端口配置项【server.port】如果么有找到则增加设置启动端口(这里咱们选用8850 默认端口是8848) server.port=8850导入数据库须要大家提前装置好数据环境,创立数据库【nacos】, 并导入执行 【console】我的项目目录下 \src\main\resources\META-INF\schema.sql 数据库脚本 批改【console】我的项目下的 application.properties 文件, 增加数据库连贯配置 db.num=1 db.url.0=jdbc:mysql://数据库地址:数据库端口/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTCdb.user.0=数据库用户名db.password.0=数据库明码 留神: db.num=1  这个属性不能够少 单机模式下大家还能够应用内置存储的形式启动而不依赖内部数据, 只须要在vm启动配置加上 【-DembeddedStorage=true】 留神集群模式不能用内置存储否则无奈启动 编译启动找到console模块下的服务启动类【Nacos.java】 启动之前 设置 VM 参数: -Dnacos.standalone=true 而后以debug模式启动【Nacos.java】 此时你肯定会在控制台遇到相似上面的编译错误信息: Error:(19, 44) java: 程序包com.alibaba.nacos.consistency.entity不存在Error:(20, 44) java: 程序包com.alibaba.nacos.consistency.entity不存在Error:(21, 44) java: 程序包com.alibaba.nacos.consistency.entity不存在Error:(72, 22) java: 找不到符号 符号: 类 ReadRequest 地位: 接口 com.alibaba.nacos.consistency.ConsistencyProtocol<T,P>Error:(72, 5) java: 找不到符号 符号: 类 Response 地位: 接口 com.alibaba.nacos.consistency.ConsistencyProtocol<T,P>Error:(80, 42) java: 找不到符号 符号: 类 ReadRequest 地位: 接口 com.alibaba.nacos.consistency.ConsistencyProtocol<T,P>Error:(80, 23) java: 找不到符号 符号: 类 Response 地位: 接口 com.alibaba.nacos.consistency.ConsistencyProtocol<T,P>Error:(90, 20) java: 找不到符号 符号: 类 WriteRequest 地位: 接口 com.alibaba.nacos.consistency.ConsistencyProtocol<T,P>Error:(90, 5) java: 找不到符号 符号: 类 Response 地位: 接口 com.alibaba.nacos.consistency.ConsistencyProtocol<T,P>Error:(100, 44) java: 找不到符号 符号: 类 WriteRequest 地位: 接口 com.alibaba.nacos.consistency.ConsistencyProtocol<T,P>Error:(100, 23) java: 找不到符号 符号: 类 Response 地位: 接口 com.alibaba.nacos.consistency.ConsistencyProtocol<T,P>D:\code\nacos3\consistency\src\main\java\com\alibaba\nacos\consistency\RequestProcessor.javaError:(19, 44) java: 程序包com.alibaba.nacos.consistency.entity不存在Error:(20, 44) java: 程序包com.alibaba.nacos.consistency.entity不存在Error:(21, 44) java: 程序包com.alibaba.nacos.consistency.entity不存在Error:(38, 40) java: 找不到符号 符号: 类 ReadRequest 地位: 类 com.alibaba.nacos.consistency.RequestProcessorError:(38, 21) java: 找不到符号 符号: 类 Response 地位: 类 com.alibaba.nacos.consistency.RequestProcessorError:(46, 38) java: 找不到符号 符号: 类 WriteRequest 地位: 类 com.alibaba.nacos.consistency.RequestProcessorError:(46, 21) java: 找不到符号 符号: 类 Response 地位: 类 com.alibaba.nacos.consistency.RequestProcessorD:\code\nacos3\consistency\src\main\java\com\alibaba\nacos\consistency\ProtoMessageUtil.javaError:(19, 44) java: 程序包com.alibaba.nacos.consistency.entity不存在Error:(20, 44) java: 程序包com.alibaba.nacos.consistency.entity不存在Error:(21, 44) java: 程序包com.alibaba.nacos.consistency.entity不存在Error:(22, 44) java: 程序包com.alibaba.nacos.consistency.entity不存在Error:(85, 54) java: 找不到符号 符号: 类 Log 地位: 类 com.alibaba.nacos.consistency.ProtoMessageUtilError:(85, 19) java: 找不到符号 符号: 类 WriteRequest 地位: 类 com.alibaba.nacos.consistency.ProtoMessageUtilError:(100, 52) java: 找不到符号 符号: 类 GetRequest 地位: 类 com.alibaba.nacos.consistency.ProtoMessageUtilError:(100, 19) java: 找不到符号 符号: 类 ReadRequest 地位: 类 com.alibaba.nacos.consistency.ProtoMessageUtilError:(54, 30) java: 找不到符号 符号: 变量 ReadRequest 地位: 类 com.alibaba.nacos.consistency.ProtoMessageUtilError:(56, 30) java: 找不到符号 符号: 变量 WriteRequest 地位: 类 com.alibaba.nacos.consistency.ProtoMessageUtilError:(65, 13) java: 找不到符号 符号: 类 GetRequest 地位: 类 com.alibaba.nacos.consistency.ProtoMessageUtilError:(65, 34) java: 找不到符号 符号: 变量 GetRequest 地位: 类 com.alibaba.nacos.consistency.ProtoMessageUtilError:(71, 13) java: 找不到符号 符号: 类 Log 地位: 类 com.alibaba.nacos.consistency.ProtoMessageUtilError:(71, 23) java: 找不到符号 符号: 变量 Log 地位: 类 com.alibaba.nacos.consistency.ProtoMessageUtilError:(86, 16) java: 找不到符号 符号: 变量 WriteRequest 地位: 类 com.alibaba.nacos.consistency.ProtoMessageUtilError:(101, 16) java: 找不到符号 符号: 变量 ReadRequest 地位: 类 com.alibaba.nacos.consistency.ProtoMessageUtil问题起因: 因为nacos 2.x 应用了grpc 作为底层通信协定,这些对象都是以proto文件模式定义在proto文件夹中,须要咱们应用maven插件手动编译成java class. ...

May 30, 2022 · 4 min · jiezi

关于nacos:Spring-Cloud-Alibaba入门实践一-概述

Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此我的项目蕴含开发分布式应用服务的必须组件,不便开发者通过 Spring Cloud 编程模型轻松应用这些组件来开发分布式应用服务。 依靠 Spring Cloud Alibaba,您只须要增加一些注解和大量配置,就能够将 Spring Cloud 利用接入阿里分布式应用解决方案,通过阿里中间件来迅速搭建分布式应用零碎。目前 Spring Cloud Alibaba 提供了如下性能: 服务限流降级:反对 WebServlet、WebFlux, OpenFeign、RestTemplate、Dubbo 限流降级性能的接入,能够在运行时通过控制台实时批改限流降级规定,还反对查看限流降级 Metrics 监控。服务注册与发现:适配 Spring Cloud 服务注册与发现规范,默认集成了 Ribbon 的反对。分布式配置管理:反对分布式系统中的内部化配置,配置更改时主动刷新。Rpc服务:扩大 Spring Cloud 客户端 RestTemplate 和 OpenFeign,反对调用 Dubbo RPC 服务音讯驱动能力:基于 Spring Cloud Stream 为微服务利用构建音讯驱动能力。分布式事务:应用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。阿里云对象存储:阿里云提供的海量、平安、低成本、高牢靠的云存储服务。反对在任何利用、任何工夫、任何地点存储和拜访任意类型的数据。分布式任务调度:提供秒级、精准、高牢靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的工作执行模型,如网格工作。网格工作反对海量子工作平均调配到所有 Worker(schedulerx-client)上执行。阿里云短信服务:笼罩寰球的短信服务,敌对、高效、智能的互联化通信能力,帮忙企业迅速搭建客户触达通道。涵盖其次要组件 NacosSentinelOauth2ZuulRocketMQSeataSkywalking

May 27, 2022 · 1 min · jiezi

关于nacos:无法连接读取-nacos-配置中心及文件能踩的坑都踩了

之前做我的项目不相熟 nacos 导致一个配置核心弄了整整一天,第二天尽管弄好了,然而又因为开发中代码出错回滚了一下,这下回滚不要紧,间接把我之前配置好的 nacos 文件也一起回滚了,因为遗记了上次哪里出错,又从新配了一天,能够说是网上有的没的我都踩了。。。。。 先说一下运行环境,或者因为你我的版本不一样,解决的办法也不一样 SpringBoot版本SpringCloud版本cloud Alibaba版本2.1.8.RELEASEGreenwich.SR62.1.4.RELEASEnamespace 填写的是命名空间的ID不是名称本地配置文件名称是 application.yml(properties) 和 bootstrap.yam(properties) 不要因为 nacos 下面有个 yaml 选项就把后缀写的一样SpringBoot 不辨认 bootstrap 文件,须要增加依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId><version>3.0.1</version></dependency>若要应用 @Value 读取本地配置文件,记得导入正确的包以及 @Value(${"key"}) key为你要读取的配置名称,这能力读取到重点!!! 在 nacos 上新建配置文件的 Data Id 不能随便乱写,依据官网文档是由这几局部组成 ${prefix}-${spring.profiles.active}.${file-extension}prefix :注册在 nacos 上的服务名 spring.application.name 的值 spring.profiles.active :配置的开发环境是开发还是测试,比方 dev,test,prod 这个最好写上 prefix 和 spring.profiles.active 之间有一个 - 连接符,如果不写 spring.profiles.active 的话那么间接 prefix 和 file-extension 拼接,没有连接符 file-extension :这个和你抉择的扩展名以及本地扩展名这三个值统一 yml 文件中留神这几个层级关系以及缩进 file-extension的层级关系为 spring.cloud.nacos.config.file-extension 写错了会导致申请被回绝 本地配置文件的写错会导致无法访问网站申请被拒连贯配置核心的参数须要写在 bootstrap 外面,写在 application 外面无奈连贯配置核心bootstrap 比 application 先执行,且内容无奈笼罩(具体差别网上说的挺具体)留神连贯 nacos 的地址,如果是虚拟机启动 nacos,而在本地连接的话,地址为虚拟机地址而不是本地 localhost(127.0.0.1)先写到这,有坑再填 ...

April 14, 2022 · 1 min · jiezi

关于nacos:微服务Nacos的使用

如何应用Nacos作为配置核心对立治理配置首先,批改 pom.xml 文件,引入 Nacos Config Starter。 <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>在微服务resources文件夹下,创立bootstrap.properties 并写入。 spring.application.name=mznzmall-product spring.cloud.nacos.config.server-addr=127.0.0.1:8848启动nacos并拜访http://127.0.0.1:8848/nacos/#...在配置列表写增加 数据集( Data Id) 利用名.properties利用名.properties增加任何配置动静获取配置@RefreshScope:动静刷新并获取配置 @Value("${配置项的名字}"):获取到配置如果配置核心和以后利用的配置文件中都配置了雷同的项,优先应用配置核心的配置 细节命名空间:配置隔离;默认:public(保留空间);默认新增的所有配置都在public(保留空间)。 开发,测试,生产:利用命名空间来做环境隔离。 留神:在bootstrap.properties配置上,须要应用哪个命名空间下的配置 spring.cloud.nacos.config.namespace=943865ec-f45b-4baa-84a2-d4abc6d205b0每一个微服务之间相互隔离配置,每一个微服务都创立本人的命名空间,只加载本人命名空间下的所有配置配置集:所有配置的汇合配置集ID:相似于文件名。Data ID:类型于文件名 配置分组默认所有的配置集都属于:DEFAULT_GROUP 能够应用dev,test,prod,618,11-11,12-12来辨别 每个微服务创立本人的命名空间,应用配置分组辨别环境,dev,test,prod同时加载多个配置集微服务任何配置信息,任何配置文件都能够放在配置文件中只须要在bootstrap.properties阐明加载配置核心中哪些配置文件以前SpringBoot任何办法从配置文件中获取值,都能应用。 @Value,@ConfigurationProperties等等配置核心有的优先应用配置核心的。

March 1, 2022 · 1 min · jiezi

关于nacos:功能回顾Apache-APISIX-基于-Nacos-实现服务发现

本文为您介绍 Apache APISIX、Nacos 基本概念以及注册核心的作用,并为您展现了 Apache APISIX 基于 Nacos 实现服务发现的具体操作。 背景信息对于 Apache APISIXApache APISIX 是一个动静、实时、高性能的 API 网关,提供负载平衡、动静上游、灰度公布、服务熔断、身份认证、可观测性等丰盛的流量治理性能。Apache APISIX 不仅领有泛滥实用的插件,而且反对插件动静变更和热插拔。 对于 NacosNacos 是阿里巴巴开源的一个易于应用的动静服务发现、配置和服务治理平台。它提供了一组简略易用的个性集,能够帮忙您疾速实现动静服务发现,服务配置,服务元数据及流量治理,让您更麻利和容易地构建,交付和治理微服务平台。Nacos 是构建以“服务”为核心的古代利用架构(例如微服务范式、云原生范式)的服务基础设施。 注册核心什么是注册核心服务注册核心是服务要实现服务化治理的外围组件,相似于目录服务的作用,也是微服务架构中最根底的设施之一,次要用来存储服务信息,譬如服务提供者 URL 、路由信息等。注册核心的实现是通过一种映射的形式,将简单的服务端信息映射为简略易懂的信息提供给客户端。 注册核心的外围性能为以下三点: 服务注册:服务提供方向注册核心进行注册。服务发现:服务生产方能够通过注册核心寻找到服务提供方的调用路由信息。衰弱检测:确保注册到注册核心的服务节点是能够被失常调用的,防止有效节点导致的调用资源节约等问题。为什么须要注册核心?注册核心实质上是为了解耦服务提供者和服务消费者,在微服务体系中,各个业务服务之间会频繁相互调用,并且须要对各个服务的 IP、port 等路由信息进行对立的治理。然而要如何进行治理呢?咱们能够通过注册核心的 服务注册 性能将已有服务的相干信息提供到对立的注册核心进行治理。 通过上述形容,您能够理解到注册核心能够帮忙用户通过映射疾速找到服务和服务地址。随着业务更新迭代,服务会频繁发生变化,在服务端中注册了新的服务或者服务宕机后,客户端依然能够通过注册核心的 服务发现 性能拉取服务列表,如果注册核心的服务节点产生变更,注册核心会发送申请告诉客户端从新拉取。 如果服务端的服务忽然宕机,并且没有向注册核心反馈,客户端能够通过注册核心的健康检查性能,进行固定工夫距离的被动上报心跳形式向服务端表明本人的服务状态。如果服务状态异样,则会告诉注册核心,注册核心能够及时把曾经宕机的服务节点进行剔除,防止资源的节约。 Apache APISIX + Nacos 为用户提供了什么利用场景?Apache APISIX + Nacos 能够将各个微服务节点中与业务无关的各项管制,集中在 Apache APISIX 中进行对立治理,即通过Apache APISIX 实现接口服务的代理和路由转发的能力。各个微服务在 Nacos 上注册后,Apache APISIX 能够通过 Nacos 的服务发现性能获取服务列表,查找对应的服务地址从而实现动静代理。 [外链图片转存失败,源站可能有防盗链机制,倡议将图片保留下来间接上传(img-Sk6XaUbU-1645607132156)(https://tfzcfxawmk.feishu.cn/...)] Apache APISIX 基于 Nacos 实现服务发现前提条件本文操作基于以下环境进行。 操作系统 Centos 7.9。已装置 Apache APISIX 2.12.1,详情请参考:Apache APISIX how-to-bulid。已装置 Nacos 2.0.4,详情请参考:quick start。已装置 Node.js,详情请参考:node.js Installation。步骤一:服务注册应用 Node.js 的 Koa 框架在 3005 端口启动一个简略的测试服务作为上游(Upstream)。const Koa = require('koa');const app = new Koa();app.use(async ctx => { ctx.body = 'Hello World';});app.listen(3005);在命令行中通过申请 Nacos Open API 的形式进行服务注册。curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=APISIX-NACOS&ip=127.0.0.1&port=3005&ephemeral=false'执行服务注册后应用以下命令查问以后服务状况。curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=APISIX-NACOS'正确返回后果示例如下: ...

February 23, 2022 · 2 min · jiezi

关于nacos:nacos和gateway服务路由缓存刷新

gateway的服务注册路由默认是第一次启动的时候就加载,有个监听器RouteRefreshListener类,外面定义了一些事件,比方ContextRefreshEvent,HeartbeatEvent等事件,而后都会调用ApplicationEventPubilsher.publishEvent(new RereshRoutesEvent(this));这里应该就是程序启动时进行的路由刷新操作了。 而后gateway有一个刷新的endpoint,/actuator/gateway/refresh,能够找到这个控制器,外面也调用的ApplicationEventPubilsher.publishEvent(new RereshRoutesEvent(this));办法,这是spring的事件驱动,能够看出gateway都是通过这个事件来触发服务路由设置的。 顺着找到了监听事件的实现类,CachingRouteLocator,这同时也是gateway的缓存路由解决类,这是个包装类(路由配置起源有yaml配置,服务注册核心的配置,这里就蕴含了PropertiesRouteDefinitionLocator,DiscoveryClientRouteDefinitionLocator类),咱们是应用nacos依据服务发现主动配置的,所以咱们关注DiscoveryClientRouteDefinitionLocator这个类。 能够看到外面的getRouteDefinitions()办法,次要就是serviceInstances字段转换成routeDefinition的,而这个serviceInstances是通过DiscoveryClient.getServices()得来的,(这个DiscoveryClient和NacosReactiveDiscoveryClient能够去看我的对于spring和nacos服务注册相干的文章)。 这就是gateway加载nacos注册服务路由的流程。 这里就有个问题了,当nacos新注册一个服务的时候,gateway不晓得,其实nacos有定义一个NacosWatch的bean,这个bean的会应用NamingService.subscribe(serviceName, groupName,clusterName,eventListener)向nacos注册一个监听器,NamingEvent(蕴含实例列表等信息)事件,而后向spring发送一个HeartbeatEvent事件,第一段说了这个事件,触发这个事件是能够刷新路由配置的,然而实际上并不是如此。 因为咱们向nacos注册监听器的时候,有个参数是serviceName,这里默认取的就是以后服务名spring.application.name,只有当这个服务的实例发生变化时才告诉,如果是一个新的服务,则不告诉(这里我本人写了一个监听器,并监听了一个其余服务的名称,那个服务启动时,这边是会触发的)。 我用的nacos版本是2.x的,看了网上的一些文章,如同0.9版本的能够获取到所有服务的变更事件,难道nacos只能启动的时候获取服务列表,前面都不进行动静更新了?目前我还在找这个办法.... https://github.com/theonefx/s...

November 3, 2021 · 1 min · jiezi

关于nacos:Nacos配置文件的实用知识

1、入门在spring cloud生态下应用nacos config很容易,引入pom依赖: <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>创立bootstrap.properties文件,并增加nacos server的连贯地址即可: spring.application.name=demo-apispring.cloud.nacos.config.server-addr=nacos服务器地址spring.cloud.nacos.config.prefix=nacos匹配配置前缀,默认为${spring.application.name}2、配置文件2.1. 规定配置nacos上的dataId残缺格局如下: ${prefix}-${spring.profile.active}.${file-extension}prefix:取自bootstrap配置文件外面spring.cloud.nacos.config.prefix的值,而该配置的默认值又为${spring.application.name}spring.profile.active:取以后环境的profile。如果我的项目spring.profile.active为空,dataId格局变成 ${prefix}.${file-extension}file-exetension:为配置内容的数据格式,能够通过配置项 spring.cloud.nacos.config.file-extension来配置。目前只反对 properties和yaml类型,默认properties。理论测试,如果配置了spring.profile.active,我的项目会被动获取nacos服务器上的配置文件包含:${prefix}-${spring.profile.active}.${file-extension}、${prefix}.${file-extension} 2.2. 共享配置大一点的我的项目,通常须要将配置信息拆分成多个配置文件,如:数据库连贯信息、多语言配置等。因而就存在了两种状况: 一个我的项目会加载多个配置文件。多个我的项目会共享同一个配置文件。此时能够应用 shared-configs 或 extension-configs ,始终没发现二者之间有啥区别,作用和用法根本一样。例如: spring.cloud.nacos.config.shared-configs[0].data-id=mysql.propertiesspring.cloud.nacos.config.shared-configs[0].group=DATABASE_GROUPspring.cloud.nacos.config.shared-configs[1].data-id=redis.propertiesspring.cloud.nacos.config.shared-configs[1].group=DATABASE_GROUPspring.cloud.nacos.config.shared-configs[2].data-id=common-i18n.propertiesspring.cloud.nacos.config.shared-configs[2].group=I18N_GROUPspring.cloud.nacos.config.shared-configs[2].refresh=true上述配置中,将 shared-configs[n] 替换成 extension-configs[n],并没有任何不同。 3. 优先级1.配置文件优先级依照后面介绍的,在应用nacos后,我的项目的配置文件起源多了起来,依照从高到低的优先级程序别离为: 通过外部相干规定 ${prefix}-${spring.profile.active}.${file-extension} 主动生成相干的Data Id配置。通过 extension-configs 形式反对的Data Id配置。通过 shared-configs 形式反对的Data Id配置。2. 共享配置外部优先级共享配置 shared-configs[n]、extension-configs[n]外部,n的值越大,优先级越高。 后面的例子中优先级程序:common-i18n.properties > redis.properties > mysql.properties 3. 本地优先级如果在配置文件中开启了nacos config,在nacos服务器和我的项目本地,都创立了同样的配置文件。nacos服务器上的配置项,优先级比本地高。 4. 通过日志查看优先级当你切实搞不清naco配置文件加载的优先级时,不要慌,间接看启动我的项目日志即可。 在spring我的项目中开启nacos config后,启动我的项目会在控制台打印出nacos加载的所有配置文件,而且依照优先级程序从前往后排序。 如依照后面的配置,打印进去的日志为: [PropertySourceBootstrapConfiguration.java:112] [] [ ] - Located property source: [BootstrapPropertySource {name='bootstrapProperties-demo-api-native.properties,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-demo-api.properties,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-demo-api,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-common-i18n.properties,I18N_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-redis.properties,DATABASE_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-mysql.properties,DATABASE_GROUP'}] ...

September 3, 2021 · 1 min · jiezi

关于nacos:06篇-Nacos-Client本地缓存及故障转移

学习不必那么功利,二师兄带你从更高维度轻松浏览源码~本篇文章咱们来通过源码剖析一下Nacos的本地缓存及故障转移性能,波及到外围类为ServiceInfoHolder和FailoverReactor。 ServiceInfoHolder性能概述ServiceInfoHolder类,顾名思义,服务信息的持有者。后面文章曾经屡次波及到ServiceInfoHolder类,比方每次客户端从注册核心获取新的服务信息时都会调用该类的processServiceInfo办法来进行本地化的解决,包含更新缓存服务、公布事件、更新本地文件等。 除了上述性能,该类在实例化时,还做了蕴含本地缓存目录初始化、故障转移初始化等操作。上面咱们就逐个剖析一下。 ServiceInfo的本地内存缓存ServiceInfo,注册服务的信息,其中蕴含了服务名称、分组名称、集群信息、实例列表信息、上次更新工夫等。也就是说,客户端从注册核心获取到的信息在本地都以ServiceInfo作为承载着。 而ServiceInfoHolder类又持有了ServiceInfo,通过一个ConcurrentMap来存储: public class ServiceInfoHolder implements Closeable { private final ConcurrentMap<String, ServiceInfo> serviceInfoMap;}这就是Nacos客户端对服务注册信息的第一层缓存。后面剖析processServiceInfo办法时,咱们曾经看到,当服务信息变更时会第一工夫更新serviceInfoMap中的信息。 public ServiceInfo processServiceInfo(ServiceInfo serviceInfo) {// .... // 缓存服务信息 serviceInfoMap.put(serviceInfo.getKey(), serviceInfo); // 判断注册的实例信息是否已变更 boolean changed = isChangedServiceInfo(oldService, serviceInfo); if (StringUtils.isBlank(serviceInfo.getJsonFromServer())) { serviceInfo.setJsonFromServer(JacksonUtils.toJson(serviceInfo)); } // ....}对于serviceInfoMap的应用就这么简略,当变动实例向其中put最新数据即可。当应用实例,依据key进行get操作即可。 而serviceInfoMap在ServiceInfoHolder的构造方法中进行初始化,默认创立一个空的ConcurrentMap。但当配置了启动时从缓存文件读取信息时,则会从本地缓存进行加载。 // 启动时是否从缓存目录读取信息,默认false。设置为true会读取缓存文件if (isLoadCacheAtStart(properties)) { this.serviceInfoMap = new ConcurrentHashMap<String, ServiceInfo>(DiskCache.read(this.cacheDir));} else { this.serviceInfoMap = new ConcurrentHashMap<String, ServiceInfo>(16);}这里波及到了本地缓存目录,在processServiceInfo办法中,当服务实例变更时,会看到通过DiskCache#write办法向该目录写入ServiceInfo信息。 // 服务实例已变更if (changed) { NAMING_LOGGER.info("current ips:(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getKey() + " -> " + JacksonUtils.toJson(serviceInfo.getHosts())); // 增加实例变更事件,会被推动到订阅者执行 NotifyCenter.publishEvent(new InstancesChangeEvent(serviceInfo.getName(), serviceInfo.getGroupName(), serviceInfo.getClusters(), serviceInfo.getHosts())); // 记录Service本地文件 DiskCache.write(serviceInfo, cacheDir);}上面就来聊聊本地缓存目录。 ...

August 18, 2021 · 3 min · jiezi

关于nacos:跟二师兄学Nacos02篇-Nacos的临时与持久化实例傻傻分不清

学习不必那么功利,二师兄带你从更高维度轻松浏览源码~本篇文章Nacos外围逻辑篇,给大家解说一下「长期实例」与「长久化实例」的区别及使用场景。 Nacos的长期实例与长久化实例在Nacos Client进行实例注册时,咱们晓得是通过Instance对象来携带实例的根本信息的。在Instance中有一个ephemeral字段,用来示意该实例是长期实例,还是长久化实例。 public class Instance implements Serializable { /** * If instance is ephemeral. * * @since 1.0.0 */ private boolean ephemeral = true; // 省略其余}从源码能够看出ephemeral字段是1.0.0版本新增的,用来示意注册的实例是否是长期实例还是长久化实例。 目前,无论是Nacos 1.x版本,还是2.x版本,ephemeral的默认值都是true。在1.x版本中服务注册默认采纳http协定,2.x版本默认采纳grpc协定,但这都未影响到ephemeral字段的默认值。 也就是说,始终以来,Nacos实例默认都是以长期实例的模式进行注册的。 当然,也是能够通过application的配置来扭转这里默认值的。比方: # false为永恒实例,true示意长期实例spring.cloud.nacos.discovery.ephemeral=false下面是基于Spring Cloud进行配置,false为永恒实例,true示意长期实例,默认为true。 长期实例与长久化实例的区别长期实例与长久化实例的区别次要体现在服务器对该实例的解决上。 长期实例向Nacos注册,Nacos不会对其进行长久化存储,只能通过心跳形式保活。默认模式是:客户端心跳上报Nacos实例衰弱状态,默认距离5秒,Nacos在15秒内未收到该实例的心跳,则会设置为不衰弱状态,超过30秒则将实例删除。 长久化实例向Nacos注册,Nacos会对其进行长久化解决。当该实例不存在时,Nacos只会将其衰弱状态设置为不衰弱,但并不会对将其从服务端删除。 另外,能够应用实例的ephemeral来判断健康检查模式,ephemeral为true对应的是client模式(客户端心跳),为false对应的是server模式(服务端查看)。 为什么要设计两种模式?下面说了两种模式的不同和解决上的区别,那么Nacos为什么设计两种模式,它们是为了应答什么样的场景而存在呢? 对于长期实例,健康检查失败,则间接能够从列表中删除。这种个性就比拟适宜那些须要应答流量突增的场景,服务能够进行弹性扩容。当流量过来之后,服务停掉即可主动登记了。 对于长久化实例,健康检查失败,会被标记成不衰弱状态。它的益处是运维能够实时看到实例的衰弱状态,便于后续的正告、扩容等一些列措施。 除了上述场景之外,长久化实例还有另外一个场景用的到,那就是爱护阈值。 Nacos的爱护阈值对于爱护阈值,在后面的文章中专门写到过。 Nacos中能够针对具体的实例设置一个爱护阈值,值为0-1之间的浮点类型。实质上,爱护阈值是⼀个⽐例值(以后服务衰弱实例数/以后服务总实例数)。 ⼀般状况下,服务消费者要从Nacos获取可⽤实例有衰弱/不衰弱状态之分。Nacos在返回实例时,只会返回衰弱实例。 但在⾼并发、⼤流量场景会存在⼀定的问题。比方,服务A有100个实例,98个实例都处于不衰弱状态,如果Nacos只返回这两个衰弱实例的话。流量洪峰的到来可能会间接打垮这两个服务,进一步产生雪崩效应。 爱护阈值存在的意义在于当服务A衰弱实例数/总实例数 < 爱护阈值时,阐明衰弱的实例不多了,爱护阈值会被触发(状态true)。 Nacos会把该服务所有的实例信息(衰弱的+不衰弱的)全副提供给消费者,消费者可能拜访到不衰弱的实例,申请失败,但这样也⽐造成雪崩要好。就义了⼀些申请,保障了整个零碎的可⽤。 这里咱们看到了不衰弱实例的另外一个作用:避免产生雪崩。 那么,如果所有的实例都是长期实例,当雪崩场景产生时,Nacos的阈值爱护机制是不是就没有足够的(蕴含不衰弱实例)实例返回了?如果有一部分实例是长久化实例,即使它们曾经挂掉,状态为不衰弱的,但当触发阈值爱护时,还是能够起到分流的作用。 小结对于Nacos长期实例与长久化实例就聊这么多了。如果想更深刻理解,其实能够读一下源码。因为基于gRPC的实现过于简单,可读性不够强,如果想浏览,倡议浏览基于Http的实现。 如果文章内容有问题或想技术探讨请分割我(微信:zhuan2quan,备注Nacos),如果感觉写的还不错,值得一起学习,那就关注一下吧。 博主简介:《SpringBoot技术底细》技术图书作者,热爱钻研技术,写技术干货文章。 公众号:「程序新视界」,博主的公众号,欢送关注~ 技术交换:请分割博主微信号:zhuan2quan

August 5, 2021 · 1 min · jiezi

关于nacos:Nacosnacos启动报orgspringUnable-to-start-embedded-Tomcat错

nacos启动报org.springframework.context.ApplicationContextException: Unable to start web server; nested错

July 29, 2021 · 1 min · jiezi

关于nacos:nacos学习

摘要nacos作为微服务项目的注册核心、配置核心;同时也作为大数据我的项目文件配置。 内容1.nacos源码nacos源码分析:https://www.cnblogs.com/dyg08... 2.nacos源码编译部署参考官网:https://nacos.io/zh-cn/docs/q...

July 23, 2021 · 1 min · jiezi

关于nacos:跟二师兄学Nacos吧EXT03篇-Nacos中此处为什么采用反射机制

学习不必那么功利,二师兄带你从更高维度轻松浏览源码~大家可能看到过很多写Java反射机制的文章,但如果在浏览源码的过程中,遇到反射机制的应用,你是否想过为什么要这么用吗? 这篇文章就带大家来看看Nacos中对Java反射机制的一处实际案例。这篇文章既属于知识点的剖析,也属于Nacos设计层面的剖析。 Nacos中反射机制实际先来介绍一下Nacos反射机制应用的背景。 nacos-client我的项目中,能够通过NacosFactory取得NamingService,而后基于NamingService来进行服务实例的注册性能: NamingService namingService = NacosFactory.createNamingService(properties);namingService.registerInstance("nacos.test.1", instance);而在NacosFactory中又是基于NamingFactory来实现NamingService的创立的: public static NamingService createNamingService(Properties properties) throws NacosException { return NamingFactory.createNamingService(properties);}NamingFactory具体创立局部代码如下: public static NamingService createNamingService(Properties properties) throws NacosException { try { Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService"); Constructor constructor = driverImplClass.getConstructor(Properties.class); return (NamingService) constructor.newInstance(properties); } catch (Throwable e) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e); }}到这里,终于看到了反射机制的应用了,通过Class#forName办法获取Class对象,而后获取构造方法,创立实例。 如果你浏览源码时只看到这些,可能你会错过一些有意思的设计和事件。你是否思考过,为什么这里要采纳反射机制呢?间接new一个对象不行吗? 在解答上述问题之前,咱们先来简略科普一下Java发反射机制。 Java反射机制这里从基本概念、原理、简略实际说起。 Java反射简介Java是预编的语言,对象的类型在编译期曾经确定。在程序运行时可能须要动静加载某些类,这些类之前用不到,所以就没有被加载到JVM中。须要时,可通过反射在运行时动静地创建对象并调用其属性或办法,而不须要在编译期就晓得运行的对象是谁。 Java反射机制的外围是在程序运行时动静加载类并获取类的详细信息,从而可能操作类或对象的属性和办法。 Java反射的优缺点Java反射的长处: 减少程序的灵活性,防止将程序写死到代码里;代码简洁,进步代码的复用率,内部调用不便;对于任意一个类,都可能晓得这个类的所有属性和办法;对于任意一个对象,都可能调用它的任意一个办法;反射的原理在理解反射的基本原理之前,咱们须要晓得在Java程序编译实现之后,会把所有class文件中所蕴含的类的根本元信息装载到JVM内存中,以Class类的模式保留。Class类可了解为形容类的类,每一个Class类对象代表一个具体类的根本元信息。反射就是在Class类的根底上进行的,Class类对象存储着类的所有相干信息。 对于JVM外部的操作步骤,咱们这里不做拓展。须要理解的就是Class对象是JVM加载.class文件之后生成的对象,而反射机制提供了获取该对象,能够基于此进行属性拜访或对象结构。而这一步是产生在运行时期间。 反射的根本应用通常应用反射有三种形式: // 形式一:应用Class.forName静态方法Class clz = Class.forName("java.lang.String");// 形式二:应用.class办法Class clz = String.class;// 形式三:应用类对象的getClass()办法String str = new String("Hello");Class clz = str.getClass();上述三种形式,个别罕用第一种,字符串参数能够传入也能够写在配置文件中。第二种须要导入类包,依赖太强,不导包就抛编译谬误。第三种对象都有了还要反射干什么。 ...

July 23, 2021 · 1 min · jiezi

关于nacos:nacos第二章

Nacos 反对服务注册与发现,能够说这个性能时每一个服务治理的根底性能,其余模块都是基于服务注册与发现实现的。开始实操练习:在Nacos源码中有个model是example[nacos-example],这个模块在develop分支中有三个demo类, App.java、ConfigExample.java、NamingExample.java这一节次要介绍下这个NamingExample.java类,代码如下: public class NamingExample { public static void main(String[] args) throws NacosException { /* 设置属性, serverAddr:连贯nacos的ip与port namespace:命名空间如果不设置则默认是public 设置的话须要在nacos console上也创立好,否则找不到相应服务的 */ Properties properties = new Properties(); properties.setProperty("serverAddr", System.getProperty("serverAddr")); properties.setProperty("namespace", System.getProperty("namespace")); /* * 这是一个工厂办法,用来创立NamingService的实现类 */ NamingService naming = NamingFactory.createNamingService(properties); /* * 向nacos注册服务 */ naming.registerInstance("nacos.test.3", "11.11.11.11", 8888, "TEST1"); naming.registerInstance("nacos.test.3", "2.2.2.2", 9999, "DEFAULT"); /* * 获取服务名称为nacos.test.3的所有实例信息 */ System.out.println(naming.getAllInstances("nacos.test.3")); /* * 与 registerInstance 相同,这个是登记服务实例的 */ naming.deregisterInstance("nacos.test.3", "2.2.2.2", 9999, "DEFAULT"); System.out.println(naming.getAllInstances("nacos.test.3")); /* * 订阅服务 */ naming.subscribe("nacos.test.3", new EventListener() { @Override public void onEvent(Event event) { System.out.println(((NamingEvent) event).getServiceName()); System.out.println(((NamingEvent) event).getInstances()); } }); }}当初一一办法来看一边 ...

July 10, 2021 · 2 min · jiezi

关于nacos:解决Nacos-单机模式下报错server-is-DOWN-nowplease-try-again-later

原文地址:Nacos 单机模式下报错:server is DOWN now,please try again later! 问题形容Spring Boot利用启动时连贯 Nacos 失败,报如下谬误: com.alibaba.nacos.api.exception.NacosException: failed to req API:/nacos/v1/ns/instance after all servers([127.0.0.1:8848]) tried: ErrCode:503, ErrMsg:server is DOWN now, please try again later! at com.alibaba.nacos.client.naming.net.NamingProxy.reqApi(NamingProxy.java:552) ~[nacos-client-1.3.3.jar:na] at com.alibaba.nacos.client.naming.net.NamingProxy.reqApi(NamingProxy.java:491) ~[nacos-client-1.3.3.jar:na] at com.alibaba.nacos.client.naming.net.NamingProxy.reqApi(NamingProxy.java:486) ~[nacos-client-1.3.3.jar:na]解决删除 nacos 目录下 data/protocol 目录,重新启动 nacos 服务。 cd nacos/data# 删除 protocol 目录rm -rf protocol/# 重启sh startup.sh -m standalone

July 8, 2021 · 1 min · jiezi

关于nacos:深入浅出讲解MSE-Nacos-20新特性

简介:随着云原生时代的到来,微服务曾经成为利用架构的支流,Nacos也凭借简略易用、稳固牢靠、性能卓越的外围竞争力成为国内微服务畛域首选的注册核心和配置核心;Nacos2.0更是把性能做到极致,让业务疾速倒退的用户再也不必放心性能问题;同时阿里云MSE也提供Nacos2.0托管服务,一键开明享受阿里十年积淀微服务所有能力! 作者|风卿 前言MSE从2020年1月公布Nacos1.1.3版本引擎,反对在私有云环境全托管的形式应用Nacos作为注册核心。2020年7月公布Nacos1.2.1版本反对元配置数据管理,反对微服务利用在运行时动静批改配置信息和路由规定等。随着用户的深刻应用,Nacos1.X版本的性能问题也慢慢裸露进去。通过对1.X版本的内核革新,Nacos2.0专业版性能晋升10倍,根本能满足用户对微服务场景的性能要求。 除了性能的晋升,专业版具备更高的SLA保障,并且在配置数据上具备更高的安全性,同时通过MCP协定与Istio生态买通,作为Istio的注册核心。 MSE Nacos1.X根底版架构整体1.X架构能够粗略分为五层,别离是接入层、通信层、性能层、同步层和长久化层。 • 用户通过接入层拜访Nacos,比方SDK、SCA、Dubbo、Console,Nacos也提供了HTTP协定的open API拜访形式。 • 通信层蕴含HTTP和UDP,Nacos次要通过HTTP进行通信,少部分服务推送性能会用到UDP。 • 性能层目前有Naming和Config两大部分,别离提供服务发现和配置管理能力。 • 同步层蕴含AP模式的Distro协定(服务注册)和CP模式的Raft协定(服务元信息),以及配置告诉的Notify同步形式 • Nacos的数据长久化有用到Mysql、Derby和本地文件,配置数据、用户信息、权限数据存储在Mysql或者Derby中,长久化的服务数据则寄存在本地文件 MSE Nacos1.X根底版架构问题目前1.X的架构存在几个问题: • 每个服务实例都通过心跳续约,在Dubbo场景每个接口对应一个服务,当Dubbo的利用接口数较多时须要心跳续约TPS会很高。 • 心跳续约感知时缩短,须要达到续约超时工夫能力删除实例,个别须要15S,时效性较差 • 通过UDP推送变更数据不牢靠,须要客户端定时进行数据全量对账保证数据的正确性,大量有效查问,整体服务的QPS很高 • 通信形式基于HTTP短链接的形式,Nacos侧开释连贯会进入TIME_WAIT状态,当QPS较高时会有连贯耗尽导致报错的危险,当然这里通过SDK引入HTTP连接池能缓解,但不能根治 • 配置的长轮询形式会导致相干数据进入JVM Old区申请和开释内存,引起频繁的CMS GC MSE Nacos2.0专业版架构及新模型1.X架构的问题外围点在于连贯模型上,2.0架构降级为长连贯模型,在通信层通过gRPC和RSocket实现长连贯数据传输和推送能力,在连贯层新减少申请处理器、流控和负载平衡等性能 2.0架构解决的问题: • 利用POD依照长连贯维度进行心跳续约,不须要依照实例级,大大降低反复申请 • 长连贯断开时能够疾速感知到,不必期待续约超时时长就能够移除实例 • NIO流式推送机制绝对于UDP更牢靠,并且能够升高利用对账数据频率 • 没有连贯重复创立的开销,大幅升高TIME_WAIT连贯多问题 • 长连贯也解决了配置模块长轮询CMS GC问题 2.0架构带来的问题: • 绝对于Tomcat HTTP短连贯模型,长连贯模型须要本人治理连贯状态,减少了复杂性• 长连贯gRPC基于HTTP2.0 Stream,绝对于HTTP的open API可观测性和易用性升高了 2.0架构整体来说升高了资源开销,进步了零碎吞吐量,在性能上有大幅晋升,但同时也减少了复杂度 MSE Nacos2.0专业版性能Nacos分为服务发现模块和配置管理模块,这里先对服务发现场景进行性能测试。 应用200台施压机,每个施压机模仿500个客户端,每个客户端注册5个服务,订阅5个服务,最高能够提供10W个长连贯、50W个服务实例和订阅者压测场景 服务发现压测次要压变更态和稳固态两种场景: • 变更态:施压机施压阶段会大量连贯Nacos注册和订阅服务,这个阶段服务端的压力绝对会比拟大,须要看整体注册和订阅是否最终齐全胜利。 • 稳固态:当施压机申请都胜利之后就会进入稳固状态,客户端和服务端之间只须要维持长连贯心跳即可,这个阶段服务端的压力会比拟小。如果在变更态服务端的压力过大会产生申请超时、连贯断开等问题,不能进入稳固态 服务发现也会在MSE上对低版本做降级,比照降级前后的性能变动曲线,这样的性能比照更直观 配置管理模块在理论应用中是写少读多的场景,次要瓶颈点在单台机器性能上,压测场景次要基于单台机器的读性能和连贯撑持数 ...

July 7, 2021 · 1 min · jiezi

关于nacos:Nacos-20-升级前后性能对比压测

简介: Nacos 2.0 通过降级通信协议和框架、数据模型的形式将性能晋升了约 10 倍,解决继 Nacos 1.0 公布逐渐裸露的性能问题。本文通过压测 Nacos 1.0,Nacos 1.0 降级 Nacos 2.0 过程中,Nacos 2.0 进行全面性能比照,直观的展现 Nacos 2.0 所带来的性能晋升。作者|席翁 Nacos 2.0 通过降级通信协议和框架、数据模型的形式将性能晋升了约 10 倍,解决继 Nacos 1.0 公布逐渐裸露的性能问题。本文通过压测 Nacos 1.0,Nacos 1.0 降级 Nacos 2.0 过程中,Nacos 2.0 进行全面性能比照,直观的展现 Nacos 2.0 所带来的性能晋升。 压测筹备 环境筹备为了不便 Nacos 部署降级和展现外围性能指标,咱们是从阿里云微服务引擎 MSE(_https://cn.aliyun.com/product...)中购买的一个 2 核 CPU+4G 内存的三节点 Nacos 集群。 压测模型为了展现不同规模下的零碎体现,咱们采纳逐渐增压的形式进行压测,将压力分为 3个批次进行逐渐启动,并察看每个批次下集群的运行体现。同时会在压力集群之外,再减少一个 Dubbo 服务的 Demo ,并应用 Jmeter 以 100 TPS 的压力不停的调用,以模仿不同压力下,对理论业务调用存在的可能影响。 压测过程中,会在适当的时候对服务端和客户端进行降级;服务端的降级将间接应用 MSE 提供的一键降级性能,客户端的降级会应用分批次轮流重启的形式进行。 压测过程 Nacos1.X Server + Nacos1.X Client ...

July 7, 2021 · 1 min · jiezi

关于nacos:Docker环境下Nacos使用MySQL存储

1、Docker下载镜像docker pull nacos/nacos-server笔者下载时曾经到1.3.2 其中mysqljar曾经更新至8.0.16 2、创立数据库create database nacos_confighttps://github.com/alibaba/na... 复制sql并执行 3、创立Nacos容器docker run -d \-e PREFER_HOST_MODE=hostname \-e MODE=standalone \-e SPRING_DATASOURCE_PLATFORM=mysql \-e MYSQL_MASTER_SERVICE_HOST=数据库ip \-e MYSQL_MASTER_SERVICE_PORT=数据库端口 \-e MYSQL_MASTER_SERVICE_USER=用户名 \-e MYSQL_MASTER_SERVICE_PASSWORD=明码 \-e MYSQL_MASTER_SERVICE_DB_NAME=对应的数据库名 \-e MYSQL_SLAVE_SERVICE_HOST=从数据库ip \-p 8848:8848 \--name nacos-sa-mysql \--restart=always \nacos/nacos-server4、批改容器docker exec -it nacos bash#进入nacos容器:docker exec -it nacos bash #进入conf文件夹cd conf#批改配置参数vim application.properties# springserver.servlet.contextPath=${SERVER_SERVLET_CONTEXTPATH:/nacos}server.contextPath=/nacosserver.port=${NACOS_APPLICATION_PORT:8848}spring.datasource.platform=${SPRING_DATASOURCE_PLATFORM:mysql}nacos.cmdb.dumpTaskInterval=3600nacos.cmdb.eventTaskInterval=10nacos.cmdb.labelTaskInterval=300nacos.cmdb.loadDataAtStart=falsedb.num=${MYSQL_DATABASE_NUM:1}db.url.0=jdbc:mysql://${MYSQL_SERVICE_HOST:mysql地址}:${MYSQL_SERVICE_PORT:端口}/${MYSQL_SERVICE_DB_NAME:数据库名}?serverTimezone=GMT%2B8&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true#db.url.1=jdbc:mysql://${MYSQL_SERVICE_HOST}:${MYSQL_SERVICE_PORT:3306}/${MYSQL_SERVICE_DB_NAME}?serverTimezone=GMT%2B8&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=truedb.user=${MYSQL_SERVICE_USER:oam}db.password=${MYSQL_SERVICE_PASSWORD:Ssl_qjy&^986_}### The auth system to use, currently only 'nacos' is supported:nacos.core.auth.system.type=${NACOS_AUTH_SYSTEM_TYPE:nacos}### The token expiration in seconds:nacos.core.auth.default.token.expire.seconds=${NACOS_AUTH_TOKEN_EXPIRE_SECONDS:18000}### The default token:nacos.core.auth.default.token.secret.key=${NACOS_AUTH_TOKEN:SecretKey012345678901234567890123456789012345678901234567890123456789}### Turn on/off caching of auth information. By turning on this switch, the update of auth information would have a 15 seconds delay.nacos.core.auth.caching.enabled=${NACOS_AUTH_CACHE_ENABLE:false}server.tomcat.accesslog.enabled=${TOMCAT_ACCESSLOG_ENABLED:false}server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D# default current work dirserver.tomcat.basedir=## spring security config### turn off securitynacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/**# metrics for elastic searchmanagement.metrics.export.elastic.enabled=falsemanagement.metrics.export.influx.enabled=falsenacos.naming.distro.taskDispatchThreadCount=10nacos.naming.distro.taskDispatchPeriod=200nacos.naming.distro.batchSyncKeyCount=1000nacos.naming.distro.initDataRatio=0.9nacos.naming.distro.syncRetryDelay=5000nacos.naming.data.warmup=true相干文章:DOCKER内部署单机NACOS应用MYSQL8.0存储SpringCloud Alibaba之Nacos集群、长久化 ...

May 10, 2021 · 1 min · jiezi

关于nacos:小白也能懂的-Nacos-服务模型介绍

简介:了解了 Nacos 的服务模型,也有利于咱们理解 Nacos 背地的工作原理,从而确保咱们正确地应用 Nacos。作者:岛风前言依照目前市场上的支流应用场景,Nacos 被分成了两块性能:服务注册发现(Naming)和配置核心(Config)。在之前的文章中我介绍了 Nacos 配置核心的实现原理,明天这篇文章所介绍的内容则是与 Nacos 服务注册发现性能相干,来聊一聊 Nacos 的服务模型。说到服务模型,其实须要辨别视角,一是用户视角,一个内核视角。即 Nacos 用户视角看到的服务模型和 Nacos 开发者设计的内核模型可能是齐全不一样的,而明天的文章,是站在用户视角察看的,旨在探讨 Nacos 服务发现的最佳实际。服务模型介绍个别我在聊注册核心时,都会以 Zookeeper 为引子,这也是很多人最相熟的注册核心。但如果你真的写过或看过应用 Zookeeper 作为注册核心的适配代码,会发现并不是那么容易,再加上注册核心波及到的一致性原理,这就导致很多人对注册核心的第一印象是:这个货色好难! 但归根到底是因为 Zookeeper 基本不是专门为注册核心而设计的,其提供的 API 以及内核设计,并没有预留出「服务模型」的概念,这就使得开发者须要自行设计一个模型,去填补 Zookeeper 和服务发现之间的鸿沟。微服务架构逐步深入人心后,Nacos、Consul、Eureka 等注册核心组件进入公众的眼帘。能够发现,这些”真正“的注册核心都有各自的「服务模型」,在应用上也更加的不便。为什么要有「服务模型」?实践上,一个根底组件能够被塑造成任意的模样,如果你违心,一个数据库也能够被设计成注册核心,这并不是”夸大“的修辞手法,在阿里还真有人这么干过。那么代价是什么呢?肯定会在业务倒退到肯定体量后遇到瓶颈,肯定会遇到某些极其 case 导致其无奈失常工作,肯定会导致其扩展性低下。正如刚学习数据结构时,同学们常见的一个疑难一样:为什么栈只能先进后出。不是所有开发都是中间件专家,所以 Nacos 设计了本人的「服务模型」,这尽管限度了使用者的”想象力“,但保障了使用者在正确地应用 Nacos。花了肯定的篇幅介绍 Nacos 为什么须要设计「服务模型」,再来看看理论的 Nacos 模型是个啥,其实没那么玄乎,一张图就能表白分明: 与 Consul、Eureka 设计有别,Nacos 服务发现应用的畛域模型是命名空间-分组-服务-集群-实例这样的多层构造。服务 Service 和实例 Instance 是外围模型,命名空间 Namespace 、分组 Group、集群 Cluster 则是在不同粒度实现了服务的隔离。为了更好的了解两个外围模型:Service 和 Instance,咱们以 Dubbo 和 SpringCloud 这两个曾经适配了 Nacos 注册核心的微服务框架为例,介绍下二者是如何映射对应模型的。• Dubbo。将接口三元组(接口名+分组名+版本号)映射为 Service,将实例 IP 和端口号定义为 Instance。一个典型的注册在 Nacos 中的 Dubbo 服务:providers:com.alibaba.mse.EchoService:1.0.0:DUBBO• Spring Cloud。将利用名映射为 Service,将实例 IP 和端口号定义为 Instance。一个典型的注册在 Nacos 中的 Spring Cloud 服务:helloApp上面咱们将会更加具体地阐释 Nacos 提供的 API 和服务模型之间的关系。环境筹备须要部署一个 Nacos Server 用于测试,我这里抉择间接在 https://mse.console.aliyun.com/ 购买一个 MSE 托管的 Nacos,读者们能够抉择购买 MSE Nacos 或者自行搭建一个 Nacos Server。MSE Nacos 提供的可视化控制台,也能够帮忙咱们更好的了解 Nacos 的服务模型。下文的一些截图,均来自 MSE Nacos 的商业化控制台。疾速开始先来实现一个最简略的服务注册与发现 demo。Nacos 反对从客户端注册服务实例和订阅服务,具体代码如下:Properties properties = new Properties();properties.setProperty(PropertyKeyConst.SERVER_ADDR, "mse-xxxx-p.nacos-ans.mse.aliyuncs.com:8848");String serviceName = "nacos.test.service.1";String instanceIp = InetAddress.getLocalHost().getHostAddress();int instancePort = 8080;namingService.registerInstance(serviceName, instanceIp, instancePort);System.out.println(namingService.getAllInstances(serviceName));上述代码定义了一个 service:nacos.test.service.1;定义了一个 instance,以本机 host 为 IP 和 8080 为端口号,察看理论的注册状况:并且控制台也打印出了服务的详情。至此一个最简略的 Nacos 服务发现 demo 就曾经实现了。对一些细节稍作解释:• 属性 PropertyKeyConst.SERVER_ADDR 示意的是 Nacos 服务端的地址。• 创立一个 NamingService 实例,客户端将为该实例创立独自的资源空间,包含缓存、线程池以及配置等。Nacos 客户端没有对该实例做单例的限度,请小心保护这个实例,以防新建多于预期的实例。• 注册服务 registerInstance 应用了最简略的重载办法,只须要传入服务名、IP、端口就能够。上述的例子中,并没有呈现 Namespace、Group、Cluster 等前文提及的服务模型,我会在上面一节具体介绍,这个例子次要是为了演示 Nacos 反对的一些缺省配置,其中 Service 和 Instance 是必不可少的,这也验证了前文提到的服务和实例是 Nacos 的一等公民。通过截图咱们能够发现缺省配置的默认值:• Namespace:默认值是 public 或者空字符串,都能够代表默认命名空间。• Group:默认值是 DEFAULT_GROUP。• Cluster:默认值是 DEFAULT。构建自定义实例为了展现出 Nacos 服务模型的全貌,还须要介绍下实例相干的 API。例如咱们心愿注册的实例中,有一些可能被调配更多的流量;或者可能传入一些实例的元信息存储到 Nacos 服务端,例如 IP 所属的利用或者所在的机房,这样在客户端能够依据服务下挂载的实例元信息,来自定义负载平衡模式。Nacos 也提供了另外的注册实例接口,使得用户在注册实例时能够指定实例的属性:/** ...

April 14, 2021 · 3 min · jiezi

关于nacos:重磅官宣Nacos20-发布性能提升-10-倍

简介: 继 Nacos 1.0 公布以来,Nacos 迅速被成千上万家企业采纳,并构建起弱小的生态。然而随着用户深刻应用,逐步裸露一些性能问题,因而咱们启动了 Nacos 2.0 的隔代产品设计,时隔半年咱们终于将其全副实现,实测性能晋升 10 倍,置信能满足所有用户的性能需求。上面由我代表社区为大家介绍一下这款跨代产品。 作者 | 席翁起源 | 阿里巴巴云原生公众号 继 Nacos 1.0 公布以来,Nacos 迅速被成千上万家企业采纳,并构建起弱小的生态。然而随着用户深刻应用,逐步裸露一些性能问题,因而咱们启动了 Nacos 2.0 的隔代产品设计,时隔半年咱们终于将其全副实现,实测性能晋升 10 倍,置信能满足所有用户的性能需求。上面由我代表社区为大家介绍一下这款跨代产品。 Nacos 简介Nacos 是一个更易于构建云原生利用的动静服务发现、配置管理和服务治理平台。它孵化于阿里巴巴,成长于十年双十一的洪峰考验,积淀了简略易用、稳固牢靠、性能卓越的外围竞争力。 Nacos 2.0 架构全新 2.0 架构不仅将性能大幅晋升 10 倍,而且内核进行了分层形象,并且实现插件扩大机制。 Nacos 2.0 架构档次如下图,它相比Nacos1.X的最次要变动是: 通信层对立到 gRPC 协定,同时欠缺了客户端和服务端的流量管制和负载平衡能力,晋升的整体吞吐。将存储和一致性模型做了充沛形象分层,架构更简略清晰,代码更加强壮,性能更加强悍。设计了可拓展的接口,晋升了集成能力,如让用户扩大实现各自的平安机制。 Nacos2.0 服务发现降级一致性模型Nacos2.0 架构下的服务发现,客户端通过 gRPC,发动注册服务或订阅服务的申请。服务端应用 Client 对象来记录该客户端应用 gRPC 连贯公布了哪些服务,又订阅了哪些服务,并将该 Client 进行服务间同步。因为理论的应用习惯是服务到客户端的映射,即服务下有哪些客户端实例;因而 2.0 的服务端会通过构建索引和元数据,疾速生成相似 1.X 中的 Service 信息,并将 Service 的数据通过  gRPC Stream 进行推送。 Nacos2.0 配置管理降级通信机制配置管理之前用 Http1.1 的 Keep Alive 模式 30s 发一个心跳模仿长链接,协定难以了解,内存耗费大,推送性能弱,因而 2.0 通过 gRPC 彻底解决这些问题,内存耗费大量升高。 ...

April 6, 2021 · 2 min · jiezi

关于nacos:重磅官宣Nacos20发布性能提升10倍

简介: Nacos2.0 作为一个跨代版本,彻底解决了 Nacos1.X 的性能问题,将性能晋升了 10 倍。 作者:席翁 继 Nacos 1.0 公布以来,Nacos 迅速被成千上万家企业采纳,并构建起弱小的生态。 然而随着用户深刻应用,逐步裸露一些性能问题,因而咱们启动了 Nacos 2.0 的隔代产品设计,时隔半年咱们终于将其全副实现,实测性能晋升10倍,置信能满足所有用户的性能需求。上面由我代表社区为大家介绍一下这款跨代产品。 Nacos 简介Nacos 是一个更易于构建云原生利用的动静服务发现、配置管理和服务治理平台。它 孵化于 阿里巴巴,成长于十年双十一的洪峰考验,积淀了简略易用、稳固牢靠、性能卓越的外围竞争力。 Nacos 2.0 架构全新2.0 架构不仅将性能大幅晋升10倍,而且内核进行了分层形象,并且实现插件扩大机制。 Nacos 2.0 架构档次如下图,它相比Nacos1.X的最次要变动是: 通信层对立到gRPC协定,同时欠缺了客户端和服务端的流量管制和负载平衡能力,晋升的整体吞吐。将存储和一致性模型做了充沛形象分层,架构更简略清晰,代码更加强壮,性能更加强悍。设计了可拓展的接口,晋升了集成能力,如让用户扩大实现各自的平安机制。 Nacos2.0 服务发现降级一致性模型Nacos2架构下的服务发现,客户端通过Grpc,发动注册服务或订阅服务的申请。服务端应用Client对象来记录该客户端应用Grpc连贯公布了哪些服务,又订阅了哪些服务,并将该Client进行服务间同步。因为理论的应用习惯是服务到客户端的映射,即服务下有哪些客户端实例;因而2.0的服务端会通过构建索引和元数据,疾速生成相似1.X中的Service信息,并将Service的数据通过Grpc Stream进行推送。 Nacos2.0 配置管理降级通信机制配置管理之前用Http1.1的Keep Alive模式30s发一个心跳模仿长链接,协定难以了解,内存耗费大,推送性能弱,因而2.0通过gRPC彻底解决这些问题,内存耗费大量升高。 Nacos2.0 架构劣势Nacos2.0大幅升高了资源耗费,晋升吞吐性能,优化客户端和服务端交互,对用户更加敌对;尽管可观测性稍微降落,然而整体性价比十分高。 Nacos2.0 性能晋升因为Nacos由服务发现和配置管理两大模块形成,业务模型略有差别,因而咱们上面别离介绍一下具体压测指标。 Nacos2.0 服务发现的性能晋升服务发现场景咱们次要关注客户端数,服务数实例数,及服务订阅者数在大规模场景下,服务端在推送及稳固状态时的性能体现。同时还关注在有大量服务在进行高低线时,零碎的性能体现。 容量及稳固状态测试该场景次要关注随着服务规模和客户端实例规模上涨,零碎性能体现。 能够看到2.0.0版本在10W级客户端规模下,可能稳固的撑持,在达到稳固状态后,CPU的损耗非常低。尽管在最后的大量注册阶段,因为存在刹时的大量注册和推送,因而有肯定的推送超时,然而会在重试后推送胜利,不会影响数据一致性。 反观1.X版本,在10W、5W级客户端下,服务端齐全处于Full GC状态,推送齐全失败,集群不可用;在2W客户端规模下,尽管服务端运行状态失常,但因为心跳解决不及时,大量服务在摘除和注册阶段重复进行,因而达不到稳固状态,CPU始终很高。1.2W客户端规模下,能够稳固运行,但稳态时CPU耗费是更大规模下2.0的3倍以上。 频繁变更测试该场景次要关注业务大规模公布,服务频繁推送条件下,不同版本的吞吐和失败率。 频繁变更时,2.0和1.X在达到稳固状态后,均能稳固撑持,其中2.0因为不再有刹时的推送风暴,因而推送失败率归0,而1.X的UDP推送的不稳定性导致了有极小局部推送呈现了超时,须要重试推送。 Nacos2.0 配置管理的性能晋升因为配置是少写多读场景,所以瓶颈次要在单台监听的客户端数量以及配置的推送获取上,因而配置管理的压测性能次要集中于单台服务端的连贯数量以及大量推送的比拟。 Nacos2.0 连贯容量测试该场景次要关注不同客户端规模下的零碎压力。 Nacos2.0 最高单机可能撑持4.2w个配置客户端连贯,在连贯建设的阶段,有大量订阅申请须要解决,因而CPU耗费较高,但达到稳态后,CPU的耗费会变得很低。简直没有耗费。 反观Nacos1.X, 在客户端6000时,稳固状态的CPU始终很高,且GC频繁,次要起因是长轮训是通过hold申请来放弃连贯,每30s须要回一次 Response并且从新发动连贯和申请。须要做大量的上下文切换,同时还须要持有所有Request 和 Response。当规模达到1.2w客户端时,曾经无奈达到稳态,所以无奈撑持这个量级的客户端数。 Nacos2.0 频繁推送测试该场景关注不同推送规模下的零碎体现。 ...

March 30, 2021 · 1 min · jiezi

关于nacos:Rancher2x上部署单机版Nacos-140

Nacos 参考Nacos官网:https://nacos.io/en-us/参考Nacos官网应用k8s治理nacos:https://nacos.io/zh-cn/docs/u...1  筹备数据库Mysql数据库建表脚本 https://github.com/alibaba/na...筹备数据库:mysql.db.name: “nacos”mysql.port: “3306”2  Rancher上部署nacos镜像地址:nacos/nacos-server:1.4.02.1  配置映射名称:nacos配置映射键:custom.properties值: management.endpoints.web.exposure.include=*management.endpoint.health.show-details=alwaysspring.datasource.platform=mysqldb.num=1db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTCdb.user.0=rootdb.password.0=root2.2 部署服务 2.2.1 配置环境变量 2.2.2 配置数据卷抉择配置好的nacos映射 点击 启动 3 配置负载平衡 OK,拜访域名 nacos.com 高兴的游玩吧! 4 Nacos作为配置及注册核心应用4.1 依赖<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>环境隔离配置参考如下,本地调试切换环境批改spring.profiles.active值即可,若测试环境以namespace隔离增加对应的namespace,rancher部署服务时增加环境变量spring.profiles.active,值为对应的环境变量如dev即可spring: application: name: cart-service profiles: active: dev---spring: profiles: dev cloud: nacos: config: server-addr: http://nacos.di.com file-extension: yaml discovery: server-addr: http://nacos.di.com---spring: profiles: fat cloud: nacos: config: server-addr: http://nacos.fi.com file-extension: yaml discovery: server-addr: http://nacos.fi.com---spring: profiles: prod cloud: nacos: config: server-addr: http://nacos.prod.com file-extension: yaml discovery: server-addr: http://nacos.prod.com

March 11, 2021 · 1 min · jiezi

关于nacos:Nacos-Feign调用研究

FeignClient调用原理要启用FeignClient首先必须在启动类上加上注解@EnableFeignClients,EnableFeignClients代码如下 @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(FeignClientsRegistrar.class)public @interface EnableFeignClients { ....留神到注解@Import(FeignClientsRegistrar.class),FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar,在启动时会执行registerBeanDefinitions动静注册 class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerDefaultConfiguration(metadata, registry); registerFeignClients(metadata, registry); } ...}registerDefaultConfiguration咱们首先看registerDefaultConfiguration,代码不多,间接贴代码 private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { Map<String, Object> defaultAttrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName(), true); if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) { String name; if (metadata.hasEnclosingClass()) { name = "default." + metadata.getEnclosingClassName(); } else { name = "default." + metadata.getClassName(); } registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration")); } }获取配置信息defaultAttrs注册默认配置类信息,配置类从defaultConfiguration中获取并且名称为 "default." + metadata.getClassName() 比方启动类为TestApplication,那么名称为default.com.test.TestApplicationregisterClientConfiguration将配置信息注册为FeignClientSpecification ...

March 5, 2021 · 3 min · jiezi

关于nacos:nacos-配置中心-V141

nacos 配置核心 V1.4.1参考文档:nacos官网文档 nacos服务端分为单机和集群两者,下文次要讲述单机版 1. 启动篇&部署篇nacos本地服务 git clone https://github.com/alibaba/nacos.gitcd nacos/mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U打包胜利后会呈现zip,解压后进入distribution/target下,应该会有个nacos-sever-$version.zip,只提取这个至某目录下,解压后进入/nacos/bin- startup.cmd 开启服务- shutdown.cmd 敞开服务如果startup.cmd启动报错,可能是配置文件中默认注明以集群的形式启动的,此时须要编辑startup.cmd,<26>行批改为set MODE = "standalone",即以单机模式启动;docker部署nacos docker pull nacos/nacos-server:1.4.1首先,咱们须要去初始化nacos的数据库在上文中nacos-sever-$version.zip解压后,进入/nacos/conf目录下,咱们会发现nacos-mysql.sql脚本,咱们能够应用该脚本进行初始化;另外,临时我只发现须要关注两个配置文件,logs和application.properties在服务器中咱们建设nacos文件夹,并在其中创立logs和config两个文件夹,目标是映射容器内两个文件夹;在config目录下创立application.properties,该文件可参考解压后的文件中/nacos/conf目录下的application.properties,留神开启mysql及以下属性- spring.datasource.platform=mysql- db.url.0- db.user.0- db.password.0配置对应的docker-compose,暂不介绍配置完后即可UP2. nacos关键点介绍认识:nacos能够看作一个配置管理核心,通常来说,咱们会把我的项目配置文件写在config下,然而须要批改时咱们须要拜访服务器对应目录下的配置文件进行批改,如果配置文件的变更须要设计多个我的项目,反复操作过多。nacos帮忙咱们解决的就是此类问题,它提供web模式的图形化界面,咱们可在界面中进行批改,配合@RefreshScope注解达到动静更新的目标。 nacos服务端登陆后,在配置管理-配置列表中可创立配置文件配置文件的命名规定为${prefix}-${spring.profiles.active}.${file-extension}通常来说,prefix默认为spring.application.name, 而file-extension为咱们在nacos外面创立配置文件的类型** 这里须要留神: spring.cloud.nacos.config.file-extension可指定file-extension,但必须与咱们nacos中创立的配置文件应用类型雷同 **nacos共有几个关键词需注意- data-id - group- namespace其中data-id为${prefix}-${spring.profiles.active}.${file-extension}group可指定,下文形容;** namespace是齐全隔离的,即不能在一个我的项目中逾越多个namespace **** @Value关键字可读取配置文件中的对应属性 ** 3. nacos解决方案:违反初衷的解决方案我感觉,namespace可作为我的项目名,group可作为版本号,每个我的项目通过namespace做到隔离性,通过group可用作版本隔离,且可用作profiles的隔离; 通常来说,一个我的项目能够分为5个配置文件 comment.properties 通用配置文件,多个我的项目可克隆复用我的项目名.properties 该我的项目配置文件,次要用来配置该我的项目中在环境切换时不变的属性,如mybatis扫描的相干配置,我的项目公有的配置参数等;我的项目名-profiles(dev,test,pro).properties 不同环境的公有配置,如ip的切换4. 根本应用如下spring: application: name: example cloud: nacos: config: prefix: 我的项目名up 优先级大于spring.application.name server-addr: 127.0.0.1:8848 file-extension: yml group: V1_GROUP namespace: 我的项目名 作为命名空间 (以下是依赖于其余配置文件,优先级下方大于上方) extension-configs: - data-id: comment.properties 通用配置 group: COMMENT_V_GROUP refresh: true - data-id: 我的项目名+profikes.properties group: V1_GROUP refresh: true

February 8, 2021 · 1 min · jiezi

关于nacos:用了3年Apollo这次我选择了Nacos原因不多说了

老板都闭口了,我能说不么? 本文探讨一下如何实现不同环境(开发、测试、灰度、正式)的配置管理问题。 就像Maven用groupId、artifactId、version三者来定位jar包在仓库中的地位一样,Nacos也提供了 Namespace (命名空间) 、Data ID (配置集ID)、 Group (组) 来确定一个配置文件(或者叫配置集)。 由此,实现多环境配置的计划也有三种: 1、用命名空间(namespace)来辨别不同的环境,一个命名空间对应一个环境; 2、用配置组(group)来辨别不同的环境,命名空间用默认的public即可,一个组对应一种环境; 3、用配置集ID(Data ID)名称来辨别不同的环境,命名空间和组用默认的即可,通过文件命名来辨别; 接下来,一一来看 http://{host}:{port}/nacos http://{host}:{port}/nacos/index.html 默认用户名明码都是nacos为了不便演示,这里建了一个名为example的Spring Boot我的项目pom.xml 1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <parent> 6 <groupId>org.springframework.boot</groupId> 7 <artifactId>spring-boot-starter-parent</artifactId> 8 <version>2.3.6.RELEASE</version> 9 <relativePath/> <!-- lookup parent from repository -->10 </parent>11 <groupId>com.example</groupId>12 <artifactId>example</artifactId>13 <version>0.0.1-SNAPSHOT</version>14 <name>example</name>15 16 <properties>17 <java.version>1.8</java.version>18 <spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version>19 </properties>20 21 <dependencies>22 <dependency>23 <groupId>org.springframework.boot</groupId>24 <artifactId>spring-boot-starter-web</artifactId>25 </dependency>26 <dependency>27 <groupId>com.alibaba.cloud</groupId>28 <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>29 </dependency>30 </dependencies>31 32 <dependencyManagement>33 <dependencies>34 <dependency>35 <groupId>com.alibaba.cloud</groupId>36 <artifactId>spring-cloud-alibaba-dependencies</artifactId>37 <version>${spring-cloud-alibaba.version}</version>38 <type>pom</type>39 <scope>import</scope>40 </dependency>41 </dependencies>42 </dependencyManagement>43 44 <build>45 <plugins>46 <plugin>47 <groupId>org.springframework.boot</groupId>48 <artifactId>spring-boot-maven-plugin</artifactId>49 </plugin>50 </plugins>51 </build>52 53 </project> bootstrap.yml ...

January 18, 2021 · 2 min · jiezi

关于nacos:nacos系列

Nacos分为服务发现(Naming)和配置核心(Config)。 注册核心Nacos - 启动Nacos - NacosNamingService初始化Nacos - 事件的注册、勾销与监听(EventDispatcher)Nacos - HostReactor的创立Nacos - 客户端实例列表获取Nacos - 客户端注册Nacos - 客户端心跳续约及客户端总结Nacos - 服务端解决注册申请Nacos - 服务端解决心跳申请Nacos - 服务端解决实例列表申请 配置核心暂无

January 5, 2021 · 1 min · jiezi

关于nacos:Nacos-服务端处理实例列表请求

服务端解决实例列表申请的入口是InstanceController#list。 InstanceController#listpublic ObjectNode list(HttpServletRequest request) throws Exception { // 其余略 // 获取服务列表 return doSrvIpxt(namespaceId, serviceName, agent, clusters, clientIP, udpPort, env, isCheck, app, tenant, healthyOnly);}InstanceController#doSrvIpxt这里次要是推送UDP申请,退出Client,Nacos - 服务端解决心跳申请的clients就是这里退出的。另外就是判断爱护阈值,如果比例低于阈值,则把衰弱和不衰弱的都返回,如果高于阈值,就只返回衰弱的实例。 public ObjectNode doSrvIpxt(String namespaceId, String serviceName, String agent, String clusters, String clientIP, int udpPort, String env, boolean isCheck, String app, String tid, boolean healthyOnly) throws Exception { ClientInfo clientInfo = new ClientInfo(agent); ObjectNode result = JacksonUtils.createEmptyJsonNode(); // 从serviceMap缓存获取Service Service service = serviceManager.getService(namespaceId, serviceName); long cacheMillis = switchDomain.getDefaultCacheMillis(); // now try to enable the push try { // 端口大于0,且客户端agent信息合乎则发表 if (udpPort > 0 && pushService.canEnablePush(agent)) { // 心跳那个章节的client就是这里加的 pushService .addClient(namespaceId, serviceName, clusters, agent, new InetSocketAddress(clientIP, udpPort), pushDataSource, tid, app); cacheMillis = switchDomain.getPushCacheMillis(serviceName); } } catch (Exception e) { Loggers.SRV_LOG .error("[NACOS-API] failed to added push client {}, {}:{}", clientInfo, clientIP, udpPort, e); cacheMillis = switchDomain.getDefaultCacheMillis(); } // 为空封装返回 if (service == null) { if (Loggers.SRV_LOG.isDebugEnabled()) { Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName); } result.put("name", serviceName); result.put("clusters", clusters); result.put("cacheMillis", cacheMillis); result.replace("hosts", JacksonUtils.createEmptyArrayNode()); return result; } // 查看service状态 checkIfDisabled(service); List<Instance> srvedIPs; // 获取相干实例的ip srvedIPs = service.srvIPs(Arrays.asList(StringUtils.split(clusters, ","))); // filter ips using selector: // 对ip过滤 if (service.getSelector() != null && StringUtils.isNotBlank(clientIP)) { srvedIPs = service.getSelector().select(clientIP, srvedIPs); } // id为空封装返回 if (CollectionUtils.isEmpty(srvedIPs)) { if (Loggers.SRV_LOG.isDebugEnabled()) { Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName); } if (clientInfo.type == ClientInfo.ClientType.JAVA && clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) { result.put("dom", serviceName); } else { result.put("dom", NamingUtils.getServiceName(serviceName)); } result.put("name", serviceName); result.put("cacheMillis", cacheMillis); result.put("lastRefTime", System.currentTimeMillis()); result.put("checksum", service.getChecksum()); result.put("useSpecifiedURL", false); result.put("clusters", clusters); result.put("env", env); result.set("hosts", JacksonUtils.createEmptyArrayNode()); result.set("metadata", JacksonUtils.transferToJsonNode(service.getMetadata())); return result; } // 次要是把衰弱和不衰弱的辨别进去 Map<Boolean, List<Instance>> ipMap = new HashMap<>(2); ipMap.put(Boolean.TRUE, new ArrayList<>()); ipMap.put(Boolean.FALSE, new ArrayList<>()); for (Instance ip : srvedIPs) { ipMap.get(ip.isHealthy()).add(ip); } if (isCheck) { result.put("reachProtectThreshold", false); } double threshold = service.getProtectThreshold(); // 阈值爱护,衰弱实例和吧衰弱实例的比例小于阈值,则把衰弱和不衰弱的都返回 if ((float) ipMap.get(Boolean.TRUE).size() / srvedIPs.size() <= threshold) { Loggers.SRV_LOG.warn("protect threshold reached, return all ips, service: {}", serviceName); if (isCheck) { result.put("reachProtectThreshold", true); } ipMap.get(Boolean.TRUE).addAll(ipMap.get(Boolean.FALSE)); ipMap.get(Boolean.FALSE).clear(); } // 其余略,就是解决ipMap的信息返回值 return result;}总结流程图如下: ...

January 5, 2021 · 2 min · jiezi

关于nacos:Nacos-服务端处理心跳请求

服务端用InstanceController#beat办法接管心跳申请。 InstanceController#beat这里会判断是否曾经有实例,如果没有就创立实例,而后再开始查看心跳。 public ObjectNode beat(HttpServletRequest request) throws Exception { ObjectNode result = JacksonUtils.createEmptyJsonNode(); // 设置心跳工夫,会间接改客户端的心跳工夫 result.put(SwitchEntry.CLIENT_BEAT_INTERVAL, switchDomain.getClientBeatInterval()); String beat = WebUtils.optional(request, "beat", StringUtils.EMPTY); // 其余略 // 通过namespaceId, serviceName, clusterName, ip, port获取Instance Instance instance = serviceManager.getInstance(namespaceId, serviceName, clusterName, ip, port); // 如果没有,则注册 if (instance == null) { // 这个是通过beat判断的,如果是第一次,则beat有信息,就会创立clientBeat // 如果不是第一次,失常instance不为空的,所以此时为空阐明可能被移除了 if (clientBeat == null) { result.put(CommonParams.CODE, NamingResponseCode.RESOURCE_NOT_FOUND); return result; } // 其余略 // 注册 serviceManager.registerInstance(namespaceId, serviceName, instance); } // 从serviceMap缓存获取Service Service service = serviceManager.getService(namespaceId, serviceName); if (service == null) { throw new NacosException(NacosException.SERVER_ERROR, "service not found: " + serviceName + "@" + namespaceId); } // 不是第一次,组装clientBeat if (clientBeat == null) { clientBeat = new RsInfo(); clientBeat.setIp(ip); clientBeat.setPort(port); clientBeat.setCluster(clusterName); } // 解决心跳 service.processClientBeat(clientBeat); result.put(CommonParams.CODE, NamingResponseCode.OK); if (instance.containsMetadata(PreservedMetadataKeys.HEART_BEAT_INTERVAL)) { result.put(SwitchEntry.CLIENT_BEAT_INTERVAL, instance.getInstanceHeartBeatInterval()); } result.put(SwitchEntry.LIGHT_BEAT_ENABLED, switchDomain.isLightBeatEnabled()); return result;}ServiceManager#getInstance通过ip和端口获取实例 ...

January 5, 2021 · 4 min · jiezi

关于nacos:Nacos-服务端处理注册请求

Nacos - 客户端注册曾经讲过了,那这里讲一下服务端是怎么解决申请的。解决客户的申请在InstanceController里,咱们看看register办法。 InstanceController#register这里次要是封装Instance,并调用serviceManager的registerInstance办法进行服务注册。 public String register(HttpServletRequest request) throws Exception { // 获取namespaceId final String namespaceId = WebUtils .optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID); // 获取serviceName final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME); // 验证serviceName的合法性 NamingUtils.checkServiceNameFormat(serviceName); // 封装并验证Instance的合法性 final Instance instance = parseInstance(request); // 服务注册 serviceManager.registerInstance(namespaceId, serviceName, instance); return "ok";}ServiceManager#registerInstance判断是否曾经注册过,如果没有注册,则创立一个Service并注册,而后再增加实例。 public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException { // 是否曾经注册过,如果没有注册,则创立一个Service并注册 createEmptyService(namespaceId, serviceName, instance.isEphemeral()); // 从注册的服务中取Service Service service = getService(namespaceId, serviceName); if (service == null) { throw new NacosException(NacosException.INVALID_PARAM, "service not found, namespace: " + namespaceId + ", service: " + serviceName); } // 增加实例 addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);}ServiceManager#createEmptyService间接调用createServiceIfAbsent办法。 ...

January 5, 2021 · 4 min · jiezi

关于nacos:Nacos-客户端心跳续约及客户端总结

Nacos - 客户端注册中提到了心跳续约。 public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName); if (instance.isEphemeral()) { // 封装心跳信息 BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance); // 开启定时工作续约 beatReactor.addBeatInfo(groupedServiceName, beatInfo); } serverProxy.registerService(groupedServiceName, groupName, instance);}BeatReactor#addBeatInfo把心跳工作放入线程池。 public void addBeatInfo(String serviceName, BeatInfo beatInfo) { NAMING_LOGGER.info("[BEAT] adding beat: {} to beat map.", beatInfo); String key = buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort()); BeatInfo existBeat = null; //fix #1733 // 移除旧的BeatInfo if ((existBeat = dom2Beat.remove(key)) != null) { existBeat.setStopped(true); } //存入dom2Beat dom2Beat.put(key, beatInfo); // 开启定时工作 executorService.schedule(new BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS); MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());}BeatReactor.BeatTask#run向服务器发送心跳,如果没有心跳信息,则从新注册。 ...

January 4, 2021 · 1 min · jiezi

关于nacos:Nacos-客户端注册

Nacos - 启动中提到了注册的入口,这里就讲一下注册的细节。Tomcat启动胜利后,会调用AbstractAutoServiceRegistration的onApplicationEvent办法,他会持续调用AbstractAutoServiceRegistration#bind。 AbstractAutoServiceRegistration#bindpublic void bind(WebServerInitializedEvent event) { ApplicationContext context = event.getApplicationContext(); if (context instanceof ConfigurableWebServerApplicationContext) { if ("management".equals(((ConfigurableWebServerApplicationContext) context) .getServerNamespace())) { return; } } // 设置端口号 this.port.compareAndSet(0, event.getWebServer().getPort()); this.start();}AbstractAutoServiceRegistration#startpublic void start() { if (!isEnabled()) { if (logger.isDebugEnabled()) { logger.debug("Discovery Lifecycle disabled. Not starting"); } return; } // only initialize if nonSecurePort is greater than 0 and it isn't already running // because of containerPortInitializer below // 没启动过才注册 if (!this.running.get()) { // 公布InstancePreRegisteredEvent事件 this.context.publishEvent( new InstancePreRegisteredEvent(this, getRegistration())); // 注册 register(); if (shouldRegisterManagement()) { // 注册registerManagement registerManagement(); } // 公布InstanceRegisteredEvent事件 this.context.publishEvent( new InstanceRegisteredEvent<>(this, getConfiguration())); // 设置状态为启动 this.running.compareAndSet(false, true); }}NacosAutoServiceRegistration#registerprotected void register() { if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) { log.debug("Registration disabled."); return; } // 小于0从新设置端口 if (this.registration.getPort() < 0) { this.registration.setPort(getPort().get()); } super.register();}AbstractAutoServiceRegistration#registerregistration的注入在启动的时候曾经说过了。 ...

January 4, 2021 · 2 min · jiezi

关于nacos:Nacos-实例列表获取

实例列表获取次要是HostReactor#getServiceInfo办法。Nacos - 启动中namingService.subscribe注册监听的时候,也会调用这个办法。 getServiceInfopublic ServiceInfo getServiceInfo(final String serviceName, final String clusters) { // 如果产生故障转移,就从文件缓存里取 NAMING_LOGGER.debug("failover-mode: " + failoverReactor.isFailoverSwitch()); String key = ServiceInfo.getKey(serviceName, clusters); if (failoverReactor.isFailoverSwitch()) { return failoverReactor.getService(key); } // 从serviceInfoMap里取 ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters); // serviceInfoMap没有 if (null == serviceObj) { serviceObj = new ServiceInfo(serviceName, clusters); serviceInfoMap.put(serviceObj.getKey(), serviceObj); updatingMap.put(serviceName, new Object()); // 内存没有,从服务器取 updateServiceNow(serviceName, clusters); updatingMap.remove(serviceName); } else if (updatingMap.containsKey(serviceName)) { // 如果正在更新,则wait,防止多线程同时调用服务器 if (UPDATE_HOLD_INTERVAL > 0) { // hold a moment waiting for update finish synchronized (serviceObj) { try { serviceObj.wait(UPDATE_HOLD_INTERVAL); } catch (InterruptedException e) { NAMING_LOGGER .error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, e); } } } } // 开启定时工作更新服务列表 scheduleUpdateIfAbsent(serviceName, clusters); // 从内存里取 return serviceInfoMap.get(serviceObj.getKey());}updateServiceNow从服务器获取,NamingProxy会调用NamingProxy#reqApi,他会随机取一个server,调用NamingProxy#callServer。NamingProxy的代码就略了。 ...

January 4, 2021 · 4 min · jiezi

关于nacos:Nacos-HostReactor的创建

Nacos - NacosNamingService初始化中提到NacosNamingService初始化会初始化EventDispatcher、NamingProxy、BeatReactor、HostReactor。其中EventDispatcher曾经说了,NamingProxy的定时工作次要是默认每30毫秒更新服务器地址、默认每5毫秒登陆获取token等信息,这里过了。BeatReactor初始化的时候并没有开启定时工作,前面来说,那只剩下HostReactor了。咱们看看他的构造函数,会创立一个FailoverReactor和PushReceiver对象。 public HostReactor(EventDispatcher eventDispatcher, NamingProxy serverProxy, BeatReactor beatReactor, String cacheDir, boolean loadCacheAtStart, int pollingThreadCount) { // 其余略 this.failoverReactor = new FailoverReactor(this, cacheDir); this.pushReceiver = new PushReceiver(this);}FailoverReactorFailoverReactor的构造函数,会调用他的init办法: public FailoverReactor(HostReactor hostReactor, String cacheDir) { //其余略 this.init();}在init里会有三个工作: FailoverReactor.SwitchRefresher,默认每5秒检测是否开启故障转移,如果开启,则把文件数据读入serviceMap。FailoverReactor.DiskFileWriter,默认每天把服务信息写入本地。创立10秒后调用DiskFileWriter#run,检测本地缓存文件,如果没有则创立缓存文件。public void init() { executorService.scheduleWithFixedDelay(new SwitchRefresher(), 0L, 5000L, TimeUnit.MILLISECONDS); executorService.scheduleWithFixedDelay(new DiskFileWriter(), 30, DAY_PERIOD_MINUTES, TimeUnit.MINUTES); // backup file on startup if failover directory is empty. executorService.schedule(new Runnable() { //其余略 }, 10000L, TimeUnit.MILLISECONDS);}FailoverReactor.SwitchRefresher,默认每5秒检测是否开启故障转移,如果开启,则把文件数据读入serviceMap。 ...

January 4, 2021 · 2 min · jiezi

关于nacos:Nacos-事件的注册取消与监听EventDispatcher

咱们在Nacos - NacosNamingService初始化提过,NacosNamingService对象创立的时候,会创立一个EventDispatcher对象。EventDispatcher的构造方法如下,创立一个线程池,而后放入Notifier工作。 public EventDispatcher() { this.executor = Executors.newSingleThreadExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r, "com.alibaba.nacos.naming.client.listener"); thread.setDaemon(true); return thread; } }); this.executor.execute(new Notifier());}Notifier的Runnable的类,所以放入线程池的时候,会执行run办法。他次要是从阻塞队列changedServices取出ServiceInfo,而后依据ServiceInfo的key取出他对应的EventListener汇合,再执行EventListener的onEvent办法。 @Overridepublic void run() { while (!closed) { ServiceInfo serviceInfo = null; try { // changedServices是LinkedBlockingQueue,从阻塞队列取值 serviceInfo = changedServices.poll(5, TimeUnit.MINUTES); } catch (Exception ignore) { } // 没取值,从新从阻塞队列取 if (serviceInfo == null) { continue; } try { // 从observerMap取到EventListener汇合 List<EventListener> listeners = observerMap.get(serviceInfo.getKey()); if (!CollectionUtils.isEmpty(listeners)) { for (EventListener listener : listeners) { // 执行onEvent办法 List<Instance> hosts = Collections.unmodifiableList(serviceInfo.getHosts()); listener.onEvent(new NamingEvent(serviceInfo.getName(), serviceInfo.getGroupName(), serviceInfo.getClusters(), hosts)); } } } catch (Exception e) { NAMING_LOGGER.error("[NA] notify error for service: " + serviceInfo.getName() + ", clusters: " + serviceInfo.getClusters(), e); } }}在Nacos - 启动中,提到NacosWatch实例化的时候,就会调用namingService.subscribe,他会调用EventDispatcher#addListener办法,在这里会把监听放入observerMap的map里,而后调用serviceChanged办法。 ...

January 4, 2021 · 1 min · jiezi

关于nacos:Nacos-NacosNamingService初始化

Nacos - 启动提到了NacosWatch#start会获取NamingService,他托管NacosServiceManager来实现这件事。 NacosServiceManager#getNamingService为空的时候,去创立一个NamingService public NamingService getNamingService(Properties properties) { // 为空的时候,去创立一个NamingService if (Objects.isNull(this.namingService)) { buildNamingService(properties); } // 返回namingService return namingService;}NacosServiceManager#buildNamingService加锁保障只能有一个namingService private NamingService buildNamingService(Properties properties) { if (Objects.isNull(namingService)) { // 加锁保障只能有一个namingService synchronized (NacosServiceManager.class) { if (Objects.isNull(namingService)) { namingService = createNewNamingService(properties); } } } return namingService;}最终调用NamingFactory#createNamingService来创立NamingService对象。 private NamingService createNewNamingService(Properties properties) { try { return createNamingService(properties); } catch (NacosException e) { throw new RuntimeException(e); }}// NacosFactory中的办法public static NamingService createNamingService(Properties properties) throws NacosException { return NamingFactory.createNamingService(properties);}NamingFactory#createNamingService通过反射的形式创立了com.alibaba.nacos.client.naming.NacosNamingService对象。 ...

December 31, 2020 · 1 min · jiezi

关于nacos:Nacos-启动

看了Eureka系列、Ribbon系列、Feign系列、Zuul系列,我置信大家应该晓得怎么找到看源码的入口了,一个是须要用的注解,一个是spring.factories。咱们还是从注解先来。 @EnableDiscoveryClient这个注解做了两件事,一个是import了EnableDiscoveryClientImportSelector,另外一个就是默认autoRegister为true,开启主动注册。 @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Import(EnableDiscoveryClientImportSelector.class)public @interface EnableDiscoveryClient { boolean autoRegister() default true;}EnableDiscoveryClientImportSelector的selectImports办法,通过下面的autoRegister来判断,如果为true,则import了AutoServiceRegistrationConfiguration,如果为false,pring.cloud.service-registry.auto-registration.enabled设置为false。这个参数决定了是否引入主动注册。 @Overridepublic String[] selectImports(AnnotationMetadata metadata) { // 其余略 boolean autoRegister = attributes.getBoolean("autoRegister"); if (autoRegister) { List<String> importsList = new ArrayList<>(Arrays.asList(imports)); importsList.add( "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration"); imports = importsList.toArray(new String[0]); } else { // 其余略 // spring.cloud.service-registry.auto-registration.enabled设置为false map.put("spring.cloud.service-registry.auto-registration.enabled", false); // 其余略 } return imports;}NacosServiceAutoConfiguration注解的局部看完了,咱们开始看spring.factories。首先是NacosServiceAutoConfiguration。这里只加载了NacosServiceManager,是service外围治理类,NamingService就是他治理的。 NacosDiscoveryAutoConfiguration这个是用于服务发现的,他加载了两个类,NacosDiscoveryProperties和NacosServiceDiscovery。NacosDiscoveryProperties是配置类,咱们配置注册核心的信息就是在这里配置。NacosServiceDiscovery次要是用于服务发现。 NacosServiceRegistryAutoConfigurationNacosServiceRegistryAutoConfiguration,这个是用于主动注册的。咱们看到他下面的注解信息,@AutoConfigureAfter确保AutoServiceRegistrationConfiguration先加载,而后判断spring.cloud.service-registry.auto-registration.enabled是否为true,为true才能够加载。所以下面@EnableDiscoveryClient的autoRegister如果设置为false,则不加载。因为spring.cloud.service-registry.auto-registration.enabled默认是true的,所以@EnableDiscoveryClient其实不引入,也是能够加载NacosServiceRegistryAutoConfiguration。 @Configuration(proxyBeanMethods = false)@EnableConfigurationProperties@ConditionalOnNacosDiscoveryEnabled@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class, AutoServiceRegistrationAutoConfiguration.class, NacosDiscoveryAutoConfiguration.class })public class NacosServiceRegistryAutoConfiguration这个类会加载NacosServiceRegistry、NacosRegistration、NacosAutoServiceRegistration。NacosServiceRegistry、NacosRegistration会注入到NacosAutoServiceRegistration。NacosServiceRegistry次要用于注册、勾销注册。NacosRegistration是以后实例的信息,比方实例名称、实例id、端口、ip等。NacosAutoServiceRegistration继承了NacosAutoServiceRegistration,NacosAutoServiceRegistration又继承了AbstractAutoServiceRegistration。AbstractAutoServiceRegistration实现了ApplicationListener<WebServerInitializedEvent>接口,通过WebServerInitializedEvent就晓得了,当tomcat启动胜利后,会调用onApplicationEvent(WebServerInitializedEvent event)办法。这个办法会实现注册操作,前面再来解说。 ...

December 31, 2020 · 1 min · jiezi

关于nacos:Nacos-下载及配置

下载: 查看官网 准备:JAVA_HOME变量正确配置(集群)配置mysql,建数据库 运行: bin目录下start.cmd,命令行进入启动命令:D:\dev\nacos\bin>startup.cmd -m standalone 默认明码均为nacos

September 30, 2020 · 1 min · jiezi

聊聊nacossdkgo的ConfigProxy

序本文主要研究一下nacos-sdk-go的ConfigProxy ConfigProxynacos-sdk-go-v0.3.2/clients/config_client/config_proxy.go type ConfigProxy struct { nacosServer nacos_server.NacosServer clientConfig constant.ClientConfig}ConfigProxy定义了nacosServer、clientConfig属性NewConfigProxynacos-sdk-go-v0.3.2/clients/config_client/config_proxy.go func NewConfigProxy(serverConfig []constant.ServerConfig, clientConfig constant.ClientConfig, httpAgent http_agent.IHttpAgent) (ConfigProxy, error) { proxy := ConfigProxy{} var err error proxy.nacosServer, err = nacos_server.NewNacosServer(serverConfig, clientConfig, httpAgent, clientConfig.TimeoutMs, clientConfig.Endpoint) proxy.clientConfig = clientConfig return proxy, err}NewConfigProxy方法创建ConfigProxy,并设置其nacosServer、clientConfig属性GetServerListnacos-sdk-go-v0.3.2/clients/config_client/config_proxy.go func (cp *ConfigProxy) GetServerList() []constant.ServerConfig { return cp.nacosServer.GetServerList()}GetServerList方法委托nacosServer.GetServerList()来获取serverListGetConfigProxynacos-sdk-go-v0.3.2/clients/config_client/config_proxy.go func (cp *ConfigProxy) GetConfigProxy(param vo.ConfigParam, tenant, accessKey, secretKey string) (string, error) { params := util.TransformObject2Param(param) if len(tenant) > 0 { params["tenant"] = tenant } var headers = map[string]string{} headers["accessKey"] = accessKey headers["secretKey"] = secretKey result, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_PATH, params, headers, http.MethodGet, cp.clientConfig.TimeoutMs) return result, err}GetConfigProxy方法通过nacosServer.ReqConfigApi获取configProxySearchConfigProxynacos-sdk-go-v0.3.2/clients/config_client/config_proxy.go ...

July 1, 2020 · 2 min · jiezi

聊聊nacossdkgo的NacosServer

序本文主要研究一下nacos-sdk-go的NacosServer NacosServernacos-sdk-go-v0.3.2/common/nacos_server/nacos_server.go type NacosServer struct { sync.RWMutex securityLogin security.AuthClient serverList []constant.ServerConfig httpAgent http_agent.IHttpAgent timeoutMs uint64 endpoint string lastSrvRefTime int64 vipSrvRefInterMills int64}NacosServer定义了securityLogin、serverList、httpAgent、timeoutMs、endpoint、lastSrvRefTime、vipSrvRefInterMills属性NewNacosServernacos-sdk-go-v0.3.2/common/nacos_server/nacos_server.go func NewNacosServer(serverList []constant.ServerConfig, clientCfg constant.ClientConfig, httpAgent http_agent.IHttpAgent, timeoutMs uint64, endpoint string) (NacosServer, error) { if len(serverList) == 0 && endpoint == "" { return NacosServer{}, errors.New("both serverlist and endpoint are empty") } securityLogin := security.NewAuthClient(clientCfg, serverList, httpAgent) ns := NacosServer{ serverList: serverList, securityLogin: securityLogin, httpAgent: httpAgent, timeoutMs: timeoutMs, endpoint: endpoint, vipSrvRefInterMills: 10000, } ns.initRefreshSrvIfNeed() _, err := securityLogin.Login() if err != nil { return ns, err } securityLogin.AutoRefresh() return ns, nil}NewNacosServer创建NacosServer及securityLogin,然后执行securityLogin.AutoRefresh()callConfigServernacos-sdk-go-v0.3.2/common/nacos_server/nacos_server.go ...

June 28, 2020 · 5 min · jiezi

聊聊nacossdkgo的NamingProxy

序本文主要研究一下nacos-sdk-go的NamingProxy NamingProxynacos-sdk-go-v0.3.2/clients/naming_client/naming_proxy.go type NamingProxy struct { clientConfig constant.ClientConfig nacosServer nacos_server.NacosServer}NamingProxy定义了clientConfig、nacosServer属性NewNamingProxynacos-sdk-go-v0.3.2/clients/naming_client/naming_proxy.go func NewNamingProxy(clientCfg constant.ClientConfig, serverCfgs []constant.ServerConfig, httpAgent http_agent.IHttpAgent) (NamingProxy, error) { srvProxy := NamingProxy{} srvProxy.clientConfig = clientCfg var err error srvProxy.nacosServer, err = nacos_server.NewNacosServer(serverCfgs, clientCfg, httpAgent, clientCfg.TimeoutMs, clientCfg.Endpoint) if err != nil { return srvProxy, err } return srvProxy, nil}NewNamingProxy通过nacos_server.NewNacosServer创建srvProxy.nacosServerRegisterInstancenacos-sdk-go-v0.3.2/clients/naming_client/naming_proxy.go func (proxy *NamingProxy) RegisterInstance(serviceName string, groupName string, instance model.Instance) (string, error) { log.Printf("[INFO] register instance namespaceId:<%s>,serviceName:<%s> with instance:<%s> \n", proxy.clientConfig.NamespaceId, serviceName, utils.ToJsonString(instance)) params := map[string]string{} params["namespaceId"] = proxy.clientConfig.NamespaceId params["serviceName"] = serviceName params["groupName"] = groupName params["clusterName"] = instance.ClusterName params["ip"] = instance.Ip params["port"] = strconv.Itoa(int(instance.Port)) params["weight"] = strconv.FormatFloat(instance.Weight, 'f', -1, 64) params["enable"] = strconv.FormatBool(instance.Enable) params["healthy"] = strconv.FormatBool(instance.Healthy) params["metadata"] = utils.ToJsonString(instance.Metadata) params["ephemeral"] = strconv.FormatBool(instance.Ephemeral) return proxy.nacosServer.ReqApi(constant.SERVICE_PATH, params, http.MethodPost)}RegisterInstance构造params,然后通过proxy.nacosServer.ReqApi(constant.SERVICE_PATH, params, http.MethodPost)发送POST请求DeregisterInstancenacos-sdk-go-v0.3.2/clients/naming_client/naming_proxy.go ...

June 27, 2020 · 3 min · jiezi

聊聊nacossdkgo的BeatReactor

序本文主要研究一下nacos-sdk-go的BeatReactor BeatReactornacos-sdk-go-v0.3.2/clients/naming_client/beat_reactor.go type BeatReactor struct { beatMap cache.ConcurrentMap serviceProxy NamingProxy clientBeatInterval int64 beatThreadCount int beatThreadSemaphore *nsema.Semaphore beatRecordMap cache.ConcurrentMap}BeatReactor定义了beatMap、serviceProxy、clientBeatInterval、beatThreadCount、beatThreadSemaphore、beatRecordMap属性NewBeatReactornacos-sdk-go-v0.3.2/clients/naming_client/beat_reactor.go func NewBeatReactor(serviceProxy NamingProxy, clientBeatInterval int64) BeatReactor { br := BeatReactor{} if clientBeatInterval <= 0 { clientBeatInterval = 5 * 1000 } br.beatMap = cache.NewConcurrentMap() br.serviceProxy = serviceProxy br.clientBeatInterval = clientBeatInterval br.beatThreadCount = Default_Beat_Thread_Num br.beatRecordMap = cache.NewConcurrentMap() br.beatThreadSemaphore = nsema.NewSemaphore(br.beatThreadCount) return br}NewBeatReactor方法创建了BeatReactor,并初始化其属性AddBeatInfonacos-sdk-go-v0.3.2/clients/naming_client/beat_reactor.go func (br *BeatReactor) AddBeatInfo(serviceName string, beatInfo model.BeatInfo) { log.Printf("[INFO] adding beat: <%s> to beat map.\n", utils.ToJsonString(beatInfo)) k := buildKey(serviceName, beatInfo.Ip, beatInfo.Port) br.beatMap.Set(k, &beatInfo) go br.sendInstanceBeat(k, &beatInfo)}AddBeatInfo方法通过buildKey构建key,然后将其放进去beatMap,之后异步执行br.sendInstanceBeatRemoveBeatInfonacos-sdk-go-v0.3.2/clients/naming_client/beat_reactor.go ...

June 26, 2020 · 1 min · jiezi

聊聊nacossdkgo的HostReactor

序本文主要研究一下nacos-sdk-go的HostReactor HostReactornacos-sdk-go-v0.3.2/clients/naming_client/host_reator.go type HostReactor struct { serviceInfoMap cache.ConcurrentMap cacheDir string updateThreadNum int serviceProxy NamingProxy pushReceiver PushReceiver subCallback SubscribeCallback updateTimeMap cache.ConcurrentMap updateCacheWhenEmpty bool}HostReactor定义了serviceInfoMap、cacheDir、updateThreadNum、serviceProxy、pushReceiver、subCallback、updateTimeMap、updateCacheWhenEmpty属性NewHostReactornacos-sdk-go-v0.3.2/clients/naming_client/host_reator.go func NewHostReactor(serviceProxy NamingProxy, cacheDir string, updateThreadNum int, notLoadCacheAtStart bool, subCallback SubscribeCallback, updateCacheWhenEmpty bool) HostReactor { if updateThreadNum <= 0 { updateThreadNum = Default_Update_Thread_Num } hr := HostReactor{ serviceProxy: serviceProxy, cacheDir: cacheDir, updateThreadNum: updateThreadNum, serviceInfoMap: cache.NewConcurrentMap(), subCallback: subCallback, updateTimeMap: cache.NewConcurrentMap(), updateCacheWhenEmpty: updateCacheWhenEmpty, } pr := NewPushRecevier(&hr) hr.pushReceiver = *pr if !notLoadCacheAtStart { hr.loadCacheFromDisk() } go hr.asyncUpdateService() return hr}NewHostReactor方法创建HostReactor,然后通过NewPushRecevier创建pushReceiver,对于notLoadCacheAtStart为false的则执行loadCacheFromDisk,之后异步执行asyncUpdateServiceloadCacheFromDisknacos-sdk-go-v0.3.2/clients/naming_client/host_reator.go ...

June 25, 2020 · 2 min · jiezi

聊聊nacossdkgo的NamingClient

序本文主要研究一下nacos-sdk-go的NamingClient NamingClientnacos-sdk-go-v0.3.2/clients/naming_client/naming_client.go type NamingClient struct { nacos_client.INacosClient hostReactor HostReactor serviceProxy NamingProxy subCallback SubscribeCallback beatReactor BeatReactor indexMap cache.ConcurrentMap}NamingClient定义了hostReactor、serviceProxy、subCallback、beatReactor、indexMap属性NewNamingClientnacos-sdk-go-v0.3.2/clients/naming_client/naming_client.go func NewNamingClient(nc nacos_client.INacosClient) (NamingClient, error) { naming := NamingClient{} clientConfig, err := nc.GetClientConfig() if err != nil { return naming, err } serverConfig, err := nc.GetServerConfig() if err != nil { return naming, err } httpAgent, err := nc.GetHttpAgent() if err != nil { return naming, err } err = logger.InitLog(clientConfig.LogDir) if err != nil { return naming, err } naming.subCallback = NewSubscribeCallback() naming.serviceProxy, err = NewNamingProxy(clientConfig, serverConfig, httpAgent) if err != nil { return naming, err } naming.hostReactor = NewHostReactor(naming.serviceProxy, clientConfig.CacheDir+string(os.PathSeparator)+"naming", clientConfig.UpdateThreadNum, clientConfig.NotLoadCacheAtStart, naming.subCallback, clientConfig.UpdateCacheWhenEmpty) naming.beatReactor = NewBeatReactor(naming.serviceProxy, clientConfig.BeatInterval) naming.indexMap = cache.NewConcurrentMap() return naming, nil}NewNamingClient方法创建NamingClient,并设置其subCallback、serviceProxy、hostReactor、beatReactor、indexMap属性RegisterInstancenacos-sdk-go-v0.3.2/clients/naming_client/naming_client.go ...

June 24, 2020 · 4 min · jiezi

Nacos集群搭建

一、Nacos简介Nacos(Naming and Configuration Service)致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。 详情查看Nacos官方文档二、Nacos安装1、Nacos依赖Nacos基于java开发的,运行依赖于java环境。 依赖64 bit JDK 1.8+,前往官网下载JDK2、Nacos安装下载编译后压缩包,最新稳定版本unzip nacos-server-$version.zip 或者 tar -xvf nacos-server-$version.tar.gz cd nacos/bin三、Nacos部署1、单实例部署单实例部署不适合生产环境,单点故障是致命的。 Linux单实例非集群模式启动命令startup.sh -m standaloneLinux单实例非集群模式关闭命令shutdown.sh访问nacos管理页面,初始化用户名密码均为nacos 2、集群部署1、集群架构 高可用Nginx集群Nacos集群(至少三个实例)高可用数据库集群(取代Nacos内嵌数据库)2、本地虚拟机模拟集群部署本地环境准备系统版本机器IP部署应用应用版本CentOS 7.6192.168.15.146Nginx1.18.0CentOS 7.6192.168.15.145Nacos1.2.1CentOS 7.6192.168.15.147Nacos1.2.1CentOS 7.6192.168.15.148Nacos1.2.1CentOS 7.6192.168.15.141MySQL5.7.24在本地PC机上利用VMware workstation虚拟出如上表所示的几台机器,其中Nginx和MySQL都是采用的单实例,仅做练习使用。 搭建步骤初始化nacos必须的数据库表并配置找到Nacos安装目录下提供的数据库脚本文件 在MySQL实例创建nacos_config库并导入脚本 修改修改Nacos配置文件,指向MySQL实例,替换其内嵌数据库 #*************** 切换Nacos内嵌数据库平台为MySQL ***************#spring.datasource.platform=mysqldb.num=1db.url.0=jdbc:mysql://192.168.15.141:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTCdb.user=rootdb.password=123456说明:三台nacos实例都需要切换MySQL平台,均需执行以上操作 nacos集群配置复制cluster.conf文件 Nacos集群配置,修改cluster.conf文件[root@localhost conf]# vim ./cluster.conf#it is ip#example192.168.15.145192.168.15.147192.168.15.148说明:三台nacos实例都需要做以上集群配置,至此关于nacos的配置结束了,可以尝试以集群模式启动三个nacos实例了 以集群模式分别启动三个nacos实例 尝试访问nacos管理页,测试三个实例是否正常 说明:如果三个实例以集群模式正常启动,那么分别访问三个实例的管理页就是展示以上登录页了。如果不能访问,则可能防火墙未开放nacos服务的端口,可执行如下命令。 [root@localhost bin]# firewall-cmd --add-port=8848/tcp --permanentsuccess[root@localhost bin]# firewall-cmd --reloadsuccess[root@localhost bin]# firewall-cmd --list-allpublic (active) target: default icmp-block-inversion: no interfaces: ens33 sources: services: ssh dhcpv6-client ports: 27017/tcp 8848/tcp protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules: [root@localhost bin]# Nginx配置Nginx安装参考,Nginx源码安装修改Nginx配置文件nginx.confworker_processes 1;events { worker_connections 1024;}http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; #nacos集群负载均衡 upstream nacos-cluster { server 192.168.15.145:8848; server 192.168.15.147:8848; server 192.168.15.148:8848; } server { listen 80; server_name 192.168.15.146; location / { #root html; #index index.html index.htm; proxy_pass http://nacos-cluster; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } }}启动Nginx/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf微服务配置微服务父pom配置<?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.atguigu.springcloud</groupId> <artifactId>cloud2020</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <!-- 模块 --> <modules> <module>cloud-alibaba-nacos-config-client-3377</module> </modules> <!-- 统一管理jar版本 --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <junit.version>4.12</junit.version> <log4j.version>1.2.17</log4j.version> <lombok.version>1.16.18</lombok.version> <mysql.version>5.1.47</mysql.version> <druid.version>1.1.16</druid.version> <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version> </properties> <!-- 统一依赖管理 --> <dependencyManagement> <dependencies> <!-- spring boot 2.2.2 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot</artifactId> <version>2.2.2.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <!-- spring cloud Hoxton.SR1 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR1</version> <type>pom</type> <scope>import</scope> </dependency> <!-- spring cloud alibaba 2.1.0.RELEASE --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <!-- mysql连接器 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <!--druid 数据源--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <!-- mybatis 整合 spring --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.spring.boot.version}</version> </dependency> <!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> </dependency> <!-- log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> <addResources>true</addResources> </configuration> </plugin> </plugins> </build></project>微服务pom依赖<?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>cloud2020</artifactId> <groupId>com.atguigu.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-alibaba-nacos-config-client-3377</artifactId> <dependencies> <!-- nacos config --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!-- nacos discovery --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- actuator --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- devtools --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies></project>微服务bootstrap.yml配置server: port: 3377spring: application: name: nacos-config-client cloud: nacos: discovery: #server-addr: my.nacos.com:8848 #nacos集群配置(Nginx) server-addr: 192.168.15.146:80 config: #server-addr: my.nacos.com:8848 #nacos集群配置(Nginx) server-addr: 192.168.15.146:80 #指定yaml格式的配置 file-extension: yaml #指定分组 group: DEV_GROUP #指定命名空间ID namespace: my_nacos_namespace微服务启动类配置package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class NacosConfigClientMain3377 { public static void main(String[] args) { SpringApplication.run(NacosConfigClientMain3377.class, args); }}微服务Controller读取nacos配置package com.atguigu.springcloud.controller;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.cloud.context.config.annotation.RefreshScope;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestController@Slf4j@RefreshScope //支持Nacos的动态刷新功能public class ConfigClientController { @Value("${config.info}") private String configInfo; @GetMapping("/config/info") public String getConfigInfo() { return configInfo; }}在nacos管理页上维护一个配置 ...

June 7, 2020 · 2 min · jiezi

搭建SpringCloud微服务框架三读取Nacos的配置信息

搭建微服务框架(读取Nacos的配置信息)本篇文章来记录下使用Nacos进行远程配置文件读取的操作,类似于 SpringCloud-Config 组件的功能本文源地址:读取Nacos的配置信息 Github地址:SQuid 介绍Nacos不仅仅只具备服务注册发现功能,它同时也具备远程动态读取配置文件的功能。 如果你认为这个功能没什么用,那么就真的大错特错了,举例: 一些关键性的配置项拿我当前公司的项目上来举例,一些服务的调用时间,我们还是写在项目上的 properties 文件中,像企业级应用,我们设置的服务调用时间在一部分对外的接口上会出现超时的情况, 这个时候,如果可以直接在 Nacos Config 上进行修改,效率也会提升不少。 数据库配置信息以Mysql的连接池配置来说,如果配置文件全部都写在项目的 resource 目录下,万一代码泄露或者被某些想要报复社会的人拿到,后果的话,大家都懂的, 绝对的 Welcome to 51Job。 使用我们这次直接在上篇文章中搭建的 squid-example-provider 中来实现,一如既往的开始第一步,引入依赖文件: <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>在 squid-example-provider 中引入 Nacos Config 依赖后,我们开始来进行下一步的操作。 Nacos新建配置:登入Nacos控制台,进入配置管理,配置列表,新增一个配置: KeyValueData ID由项目中的 bootstrap.properties 指定。Group分组信息,可以自己填写。标签N/A归属应用归属的应用的信息,可以自己填写。描述本次配置的描述。配置格式根据自己项目需求来选择。配置内容对应配置格式的配置文件。 项目resource下新建 bootstrap.properties完成Nacos的新建配置后,我们这个时候可以来到项目中新建一个 bootstrap.properties 文件,之前的 application.yaml 文件可以删除掉了,之所以命名为 bootstrap.properties,是因为SpringCloud的加载配置顺序优先级properties文件大于yaml。 KeyValuespring.profiles.active配置文件的属性,比如上面Nacos里的Data ID是以-test结尾,这里我们就写 test。spring.application.name应用名称,写项目名就好了spring.cloud.nacos.config.file-extension加载的配置文件格式。spring.cloud.nacos.config.server-addrNacos的地址。 spring.profiles.active=test spring.application.name=squid-example-provider spring.cloud.nacos.config.file-extension=yaml spring.cloud.nacos.config.server-addr=yanzhenyidai.com:8848启动项目配置完成后,我们可以启动 Application 类,来检验是否可以成功读取到配置文件信息。 总结Nacos的Config配置功能真的很方便,而且支持热加载形式,感兴趣的朋友可以更深层次的了解。 参考资料: Nacos(GITHUB) Nacos(WIKI)

May 29, 2020 · 1 min · jiezi

聊聊nacos-client的ConfigFilterChainManager

序本文主要研究一下nacos client的ConfigFilterChainManager IConfigFilterChainnacos-1.1.3/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilterChain.java public interface IConfigFilterChain { /** * Filter aciton * * @param request request * @param response response * @throws NacosException NacosException */ void doFilter(IConfigRequest request, IConfigResponse response) throws NacosException;}IConfigFilterChain接口定义了doFilter方法ConfigFilterChainManagernacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigFilterChainManager.java public class ConfigFilterChainManager implements IConfigFilterChain { private List<IConfigFilter> filters = Lists.newArrayList(); public synchronized ConfigFilterChainManager addFilter(IConfigFilter filter) { // 根据order大小顺序插入 int i = 0; while (i < this.filters.size()) { IConfigFilter currentValue = this.filters.get(i); if (currentValue.getFilterName().equals(filter.getFilterName())) { break; } if (filter.getOrder() >= currentValue.getOrder() && i < this.filters.size()) { i++; } else { this.filters.add(i, filter); break; } } if (i == this.filters.size()) { this.filters.add(i, filter); } return this; } @Override public void doFilter(IConfigRequest request, IConfigResponse response) throws NacosException { new VirtualFilterChain(this.filters).doFilter(request, response); } //......}ConfigFilterChainManager实现了IConfigFilterChain接口,其doFilter方法使用filters创建VirtualFilterChain,然后执行其doFilter方法;它提供了addFilter方法,可以根据filter的order顺序添加到filters中VirtualFilterChainnacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigFilterChainManager.java ...

October 18, 2019 · 2 min · jiezi

Spring-Cloud-Alibaba-Nacos-Config-实战

Nacos 提供用于存储配置和其他元数据的 key/value 存储,为分布式系统中的外部化配置提供服务器端和客户端支持。使用 Spring Cloud Alibaba Nacos Config,您可以在 Nacos Server 集中管理你 Spring Cloud 应用的外部属性配置 一、安装 Nacos1、下载 Nacos最新稳定版下载:https://github.com/alibaba/nacos/releases 2、启动 Nacos启动 Nacos (单机模式) sh startup.sh -m standalone关闭 Nacos sh shutdown.sh二、配置 Nacos1、打开 Nacos默认地址:http://127.0.0.1:8848/nacos/#/login 默认账号:账号密码相同,都为nacos 2、添加配置配置数据: Data ID: nacos-dev.propertiesGroup : DEFAULT_GROUP配置格式: Properties配置内容: useLocalCache=true Data ID 的格式说明: ${prefix}-${spring.profile.active}.${file-extension}prefix: 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix 来配置 spring.profile.active: 即为当前环境对应的 profile, 注意:当 spring.profile.active 为空时,对应的连接符 - 也将不存在,dataId 的拼接格式变成 ${prefix}.${file-extension} file-exetension: 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置。目前只支持 properties 和 yaml 类型 ...

October 17, 2019 · 1 min · jiezi

聊聊nacos-client的ServerHttpAgent

序本文主要研究一下nacos client的ServerHttpAgent HttpAgentnacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/http/HttpAgent.java public interface HttpAgent { /** * start to get nacos ip list * @return Nothing. * @throws NacosException on get ip list error. */ void start() throws NacosException; /** * invoke http get method * @param path http path * @param headers http headers * @param paramValues http paramValues http * @param encoding http encode * @param readTimeoutMs http timeout * @return HttpResult http response * @throws IOException If an input or output exception occurred */ HttpResult httpGet(String path, List<String> headers, List<String> paramValues, String encoding, long readTimeoutMs) throws IOException; /** * invoke http post method * @param path http path * @param headers http headers * @param paramValues http paramValues http * @param encoding http encode * @param readTimeoutMs http timeout * @return HttpResult http response * @throws IOException If an input or output exception occurred */ HttpResult httpPost(String path, List<String> headers, List<String> paramValues, String encoding, long readTimeoutMs) throws IOException; /** * invoke http delete method * @param path http path * @param headers http headers * @param paramValues http paramValues http * @param encoding http encode * @param readTimeoutMs http timeout * @return HttpResult http response * @throws IOException If an input or output exception occurred */ HttpResult httpDelete(String path, List<String> headers, List<String> paramValues, String encoding, long readTimeoutMs) throws IOException; /** * get name * @return String */ String getName(); /** * get namespace * @return String */ String getNamespace(); /** * get tenant * @return String */ String getTenant(); /** * get encode * @return String */ String getEncode();}HttpAgent定义了start、httpGet、httpPost、httpDelete、getName、getNamespace、getTenant、getEncode方法ServerHttpAgentnacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/http/ServerHttpAgent.java ...

October 17, 2019 · 4 min · jiezi

聊聊nacos-client的ServerListManager的start

序本文主要研究一下nacos client的ServerListManager的start ServerListManagernacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/impl/ServerListManager.java public class ServerListManager { private static final Logger LOGGER = LogUtils.logger(ServerListManager.class); private static final String HTTPS = "https://"; private static final String HTTP = "http://"; //...... public synchronized void start() throws NacosException { if (isStarted || isFixed) { return; } GetServerListTask getServersTask = new GetServerListTask(addressServerUrl); for (int i = 0; i < initServerlistRetryTimes && serverUrls.isEmpty(); ++i) { getServersTask.run(); try { this.wait((i + 1) * 100L); } catch (Exception e) { LOGGER.warn("get serverlist fail,url: {}", addressServerUrl); } } if (serverUrls.isEmpty()) { LOGGER.error("[init-serverlist] fail to get NACOS-server serverlist! env: {}, url: {}", name, addressServerUrl); throw new NacosException(NacosException.SERVER_ERROR, "fail to get NACOS-server serverlist! env:" + name + ", not connnect url:" + addressServerUrl); } TimerService.scheduleWithFixedDelay(getServersTask, 0L, 30L, TimeUnit.SECONDS); isStarted = true; } //......}ServerListManager的start方法在非isStarted且非isFixed的条件下会执行GetServerListTask,失败重试次数为initServerlistRetryTimes,如果serverUrls为空则抛出NacosException;如果不为空则注册该getServersTask每隔30秒执行一次来刷新server listGetServerListTasknacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/impl/ServerListManager.java ...

October 16, 2019 · 2 min · jiezi

聊聊nacos的LocalConfigInfoProcessor

序本文主要研究一下nacos的LocalConfigInfoProcessor LocalConfigInfoProcessornacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/impl/LocalConfigInfoProcessor.java public class LocalConfigInfoProcessor { private static final Logger LOGGER = LogUtils.logger(LocalConfigInfoProcessor.class); static public String getFailover(String serverName, String dataId, String group, String tenant) { File localPath = getFailoverFile(serverName, dataId, group, tenant); if (!localPath.exists() || !localPath.isFile()) { return null; } try { return readFile(localPath); } catch (IOException ioe) { LOGGER.error("[" + serverName + "] get failover error, " + localPath, ioe); return null; } } /** * 获取本地缓存文件内容。NULL表示没有本地文件或抛出异常。 */ static public String getSnapshot(String name, String dataId, String group, String tenant) { if (!SnapShotSwitch.getIsSnapShot()) { return null; } File file = getSnapshotFile(name, dataId, group, tenant); if (!file.exists() || !file.isFile()) { return null; } try { return readFile(file); } catch (IOException ioe) { LOGGER.error("[" + name + "]+get snapshot error, " + file, ioe); return null; } } static private String readFile(File file) throws IOException { if (!file.exists() || !file.isFile()) { return null; } if (JVMUtil.isMultiInstance()) { return ConcurrentDiskUtil.getFileContent(file, Constants.ENCODE); } else { InputStream is = null; try { is = new FileInputStream(file); return IOUtils.toString(is, Constants.ENCODE); } finally { try { if (null != is) { is.close(); } } catch (IOException ioe) { } } } } static public void saveSnapshot(String envName, String dataId, String group, String tenant, String config) { if (!SnapShotSwitch.getIsSnapShot()) { return; } File file = getSnapshotFile(envName, dataId, group, tenant); if (null == config) { try { IOUtils.delete(file); } catch (IOException ioe) { LOGGER.error("[" + envName + "] delete snapshot error, " + file, ioe); } } else { try { File parentFile = file.getParentFile(); if (!parentFile.exists()) { boolean isMdOk = parentFile.mkdirs(); if (!isMdOk) { LOGGER.error("[{}] save snapshot error", envName); } } if (JVMUtil.isMultiInstance()) { ConcurrentDiskUtil.writeFileContent(file, config, Constants.ENCODE); } else { IOUtils.writeStringToFile(file, config, Constants.ENCODE); } } catch (IOException ioe) { LOGGER.error("[" + envName + "] save snapshot error, " + file, ioe); } } } /** * 清除snapshot目录下所有缓存文件。 */ static public void cleanAllSnapshot() { try { File rootFile = new File(LOCAL_SNAPSHOT_PATH); File[] files = rootFile.listFiles(); if (files == null || files.length == 0) { return; } for (File file : files) { if (file.getName().endsWith("_nacos")) { IOUtils.cleanDirectory(file); } } } catch (IOException ioe) { LOGGER.error("clean all snapshot error, " + ioe.toString(), ioe); } } static public void cleanEnvSnapshot(String envName) { File tmp = new File(LOCAL_SNAPSHOT_PATH, envName + "_nacos"); tmp = new File(tmp, "snapshot"); try { IOUtils.cleanDirectory(tmp); LOGGER.info("success delete " + envName + "-snapshot"); } catch (IOException e) { LOGGER.info("fail delete " + envName + "-snapshot, " + e.toString()); e.printStackTrace(); } } static File getFailoverFile(String serverName, String dataId, String group, String tenant) { File tmp = new File(LOCAL_SNAPSHOT_PATH, serverName + "_nacos"); tmp = new File(tmp, "data"); if (StringUtils.isBlank(tenant)) { tmp = new File(tmp, "config-data"); } else { tmp = new File(tmp, "config-data-tenant"); tmp = new File(tmp, tenant); } return new File(new File(tmp, group), dataId); } static File getSnapshotFile(String envName, String dataId, String group, String tenant) { File tmp = new File(LOCAL_SNAPSHOT_PATH, envName + "_nacos"); if (StringUtils.isBlank(tenant)) { tmp = new File(tmp, "snapshot"); } else { tmp = new File(tmp, "snapshot-tenant"); tmp = new File(tmp, tenant); } return new File(new File(tmp, group), dataId); } public static final String LOCAL_FILEROOT_PATH; public static final String LOCAL_SNAPSHOT_PATH; static { LOCAL_FILEROOT_PATH = System.getProperty("JM.LOG.PATH", System.getProperty("user.home")) + File.separator + "nacos" + File.separator + "config"; LOCAL_SNAPSHOT_PATH = System.getProperty("JM.SNAPSHOT.PATH", System.getProperty("user.home")) + File.separator + "nacos" + File.separator + "config"; LOGGER.info("LOCAL_SNAPSHOT_PATH:{}", LOCAL_SNAPSHOT_PATH); }}getFailover方法首先会通过getFailoverFile获取本地配置文件,然后通过readFile读取;getSnapshot方法首先通过getSnapshotFile获取snapshot文件,然后通过readFile读取;saveSnapshot方法会存储新的config;cleanAllSnapshot方法会清除snapshot目录下所有缓存文件CacheDatanacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/impl/CacheData.java ...

October 15, 2019 · 4 min · jiezi

聊聊NacosNamingService的selectInstances

序本文主要研究一下NacosNamingService的selectInstances NacosNamingServicenacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java public class NacosNamingService implements NamingService { private static final String DEFAULT_PORT = "8080"; private static final long DEFAULT_HEART_BEAT_INTERVAL = TimeUnit.SECONDS.toMillis(5); /** * Each Naming instance should have different namespace. */ private String namespace; private String endpoint; private String serverList; private String cacheDir; private String logName; private HostReactor hostReactor; private BeatReactor beatReactor; private EventDispatcher eventDispatcher; private NamingProxy serverProxy; //...... @Override public List<Instance> selectInstances(String serviceName, boolean healthy) throws NacosException { return selectInstances(serviceName, new ArrayList<String>(), healthy); } @Override public List<Instance> selectInstances(String serviceName, String groupName, boolean healthy) throws NacosException { return selectInstances(serviceName, groupName, healthy, true); } @Override public List<Instance> selectInstances(String serviceName, boolean healthy, boolean subscribe) throws NacosException { return selectInstances(serviceName, new ArrayList<String>(), healthy, subscribe); } @Override public List<Instance> selectInstances(String serviceName, String groupName, boolean healthy, boolean subscribe) throws NacosException { return selectInstances(serviceName, groupName, new ArrayList<String>(), healthy, subscribe); } @Override public List<Instance> selectInstances(String serviceName, List<String> clusters, boolean healthy) throws NacosException { return selectInstances(serviceName, clusters, healthy, true); } @Override public List<Instance> selectInstances(String serviceName, String groupName, List<String> clusters, boolean healthy) throws NacosException { return selectInstances(serviceName, groupName, clusters, healthy, true); } @Override public List<Instance> selectInstances(String serviceName, List<String> clusters, boolean healthy, boolean subscribe) throws NacosException { return selectInstances(serviceName, Constants.DEFAULT_GROUP, clusters, healthy, subscribe); } @Override public List<Instance> selectInstances(String serviceName, String groupName, List<String> clusters, boolean healthy, boolean subscribe) throws NacosException { ServiceInfo serviceInfo; if (subscribe) { serviceInfo = hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ",")); } else { serviceInfo = hostReactor.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ",")); } return selectInstances(serviceInfo, healthy); } private List<Instance> selectInstances(ServiceInfo serviceInfo, boolean healthy) { List<Instance> list; if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) { return new ArrayList<Instance>(); } Iterator<Instance> iterator = list.iterator(); while (iterator.hasNext()) { Instance instance = iterator.next(); if (healthy != instance.isHealthy() || !instance.isEnabled() || instance.getWeight() <= 0) { iterator.remove(); } } return list; } //......}selectInstances首先从hostReactor获取serviceInfo,然后再从serviceInfo.getHosts()剔除非healty、非enabled、weight小于等于0的instance再返回;如果subscribe为true,则执行hostReactor.getServiceInfo获取serviceInfo,否则执行hostReactor.getServiceInfoDirectlyFromServer获取serviceInfoHostReactornacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/naming/core/HostReactor.java ...

October 8, 2019 · 4 min · jiezi

聊聊NacosNamingService的deregisterInstance

序本文主要研究一下NacosNamingService的deregisterInstance NacosNamingServicenacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java public class NacosNamingService implements NamingService { private static final String DEFAULT_PORT = "8080"; private static final long DEFAULT_HEART_BEAT_INTERVAL = TimeUnit.SECONDS.toMillis(5); /** * Each Naming instance should have different namespace. */ private String namespace; private String endpoint; private String serverList; private String cacheDir; private String logName; private HostReactor hostReactor; private BeatReactor beatReactor; private EventDispatcher eventDispatcher; private NamingProxy serverProxy; //...... @Override public void deregisterInstance(String serviceName, String ip, int port) throws NacosException { deregisterInstance(serviceName, ip, port, Constants.DEFAULT_CLUSTER_NAME); } @Override public void deregisterInstance(String serviceName, String groupName, String ip, int port) throws NacosException { deregisterInstance(serviceName, groupName, ip, port, Constants.DEFAULT_CLUSTER_NAME); } @Override public void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException { deregisterInstance(serviceName, Constants.DEFAULT_GROUP, ip, port, clusterName); } @Override public void deregisterInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException { Instance instance = new Instance(); instance.setIp(ip); instance.setPort(port); instance.setClusterName(clusterName); deregisterInstance(serviceName, groupName, instance); } @Override public void deregisterInstance(String serviceName, Instance instance) throws NacosException { deregisterInstance(serviceName, Constants.DEFAULT_GROUP, instance); } @Override public void deregisterInstance(String serviceName, String groupName, Instance instance) throws NacosException { if (instance.isEphemeral()) { beatReactor.removeBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), instance.getIp(), instance.getPort()); } serverProxy.deregisterService(NamingUtils.getGroupedName(serviceName, groupName), instance); } //......}deregisterInstance方法对于ephemeral的instance(默认是ephemeral)会将其BeatInfo从beatReactor中移除,然后通过serverProxy.deregisterService进行服务注销BeatReactornacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/naming/beat/BeatReactor.java ...

October 7, 2019 · 2 min · jiezi

聊聊NacosNamingService的registerInstance

序本文主要研究一下NacosNamingService的registerInstance NacosNamingServicenacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java public class NacosNamingService implements NamingService { private static final String DEFAULT_PORT = "8080"; private static final long DEFAULT_HEART_BEAT_INTERVAL = TimeUnit.SECONDS.toMillis(5); //...... @Override public void registerInstance(String serviceName, String ip, int port) throws NacosException { registerInstance(serviceName, ip, port, Constants.DEFAULT_CLUSTER_NAME); } @Override public void registerInstance(String serviceName, String groupName, String ip, int port) throws NacosException { registerInstance(serviceName, groupName, ip, port, Constants.DEFAULT_CLUSTER_NAME); } @Override public void registerInstance(String serviceName, String ip, int port, String clusterName) throws NacosException { registerInstance(serviceName, Constants.DEFAULT_GROUP, ip, port, clusterName); } @Override public void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException { Instance instance = new Instance(); instance.setIp(ip); instance.setPort(port); instance.setWeight(1.0); instance.setClusterName(clusterName); registerInstance(serviceName, groupName, instance); } @Override public void registerInstance(String serviceName, Instance instance) throws NacosException { registerInstance(serviceName, Constants.DEFAULT_GROUP, instance); } @Override public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { if (instance.isEphemeral()) { BeatInfo beatInfo = new BeatInfo(); beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName)); beatInfo.setIp(instance.getIp()); beatInfo.setPort(instance.getPort()); beatInfo.setCluster(instance.getClusterName()); beatInfo.setWeight(instance.getWeight()); beatInfo.setMetadata(instance.getMetadata()); beatInfo.setScheduled(false); long instanceInterval = instance.getInstanceHeartBeatInterval(); beatInfo.setPeriod(instanceInterval == 0 ? DEFAULT_HEART_BEAT_INTERVAL : instanceInterval); beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo); } serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance); } //......}registerInstance方法对于ephemeral的instance(默认是ephemeral)会创建BeatInfo,通过beatReactor.addBeatInfo方法添加到调度任务中,然后通过serverProxy.registerService进行服务注册BeatReactornacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/naming/beat/BeatReactor.java ...

October 6, 2019 · 3 min · jiezi

聊聊nacos-NamingProxy的getServiceList

序本文主要研究一下nacos NamingProxy的getServiceList NamingProxy.initRefreshSrvIfNeednacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/naming/net/NamingProxy.java public class NamingProxy { private static final int DEFAULT_SERVER_PORT = 8848; private int serverPort = DEFAULT_SERVER_PORT; private String namespaceId; private String endpoint; private String nacosDomain; private List<String> serverList; private List<String> serversFromEndpoint = new ArrayList<String>(); private long lastSrvRefTime = 0L; private long vipSrvRefInterMillis = TimeUnit.SECONDS.toMillis(30); private Properties properties; public NamingProxy(String namespaceId, String endpoint, String serverList) { this.namespaceId = namespaceId; this.endpoint = endpoint; if (StringUtils.isNotEmpty(serverList)) { this.serverList = Arrays.asList(serverList.split(",")); if (this.serverList.size() == 1) { this.nacosDomain = serverList; } } initRefreshSrvIfNeed(); } private void initRefreshSrvIfNeed() { if (StringUtils.isEmpty(endpoint)) { return; } ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setName("com.alibaba.nacos.client.naming.serverlist.updater"); t.setDaemon(true); return t; } }); executorService.scheduleWithFixedDelay(new Runnable() { @Override public void run() { refreshSrvIfNeed(); } }, 0, vipSrvRefInterMillis, TimeUnit.MILLISECONDS); refreshSrvIfNeed(); } //...... private void refreshSrvIfNeed() { try { if (!CollectionUtils.isEmpty(serverList)) { NAMING_LOGGER.debug("server list provided by user: " + serverList); return; } if (System.currentTimeMillis() - lastSrvRefTime < vipSrvRefInterMillis) { return; } List<String> list = getServerListFromEndpoint(); if (CollectionUtils.isEmpty(list)) { throw new Exception("Can not acquire Nacos list"); } if (!CollectionUtils.isEqualCollection(list, serversFromEndpoint)) { NAMING_LOGGER.info("[SERVER-LIST] server list is updated: " + list); } serversFromEndpoint = list; lastSrvRefTime = System.currentTimeMillis(); } catch (Throwable e) { NAMING_LOGGER.warn("failed to update server list", e); } } public List<String> getServerListFromEndpoint() { try { String urlString = "http://" + endpoint + "/nacos/serverlist"; List<String> headers = builderHeaders(); HttpClient.HttpResult result = HttpClient.httpGet(urlString, headers, null, UtilAndComs.ENCODING); if (HttpURLConnection.HTTP_OK != result.code) { throw new IOException("Error while requesting: " + urlString + "'. Server returned: " + result.code); } String content = result.content; List<String> list = new ArrayList<String>(); for (String line : IoUtils.readLines(new StringReader(content))) { if (!line.trim().isEmpty()) { list.add(line.trim()); } } return list; } catch (Exception e) { e.printStackTrace(); } return null; } //......}NamingProxy的构造器执行了initRefreshSrvIfNeed方法,该方法在endpoint不为空的时候,会注册一个定时任务,每隔vipSrvRefInterMillis时间执行一次refreshSrvIfNeed方法,同时立马调用了refreshSrvIfNeed方法refreshSrvIfNeed方法在serverList为空,且距离lastSrvRefTime大于等于vipSrvRefInterMillis时会通过getServerListFromEndpoint()方法获取serverList更新serversFromEndpoint及lastSrvRefTimegetServerListFromEndpoint方法会向endpoint请求/serverlist接口,获取server端返回的serverListNamingProxy.getServiceListnacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/naming/net/NamingProxy.java ...

October 5, 2019 · 4 min · jiezi

聊聊nacos的RaftProxy

序本文主要研究一下nacos的RaftProxy RaftProxynacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftProxy.java @Componentpublic class RaftProxy { public void proxyGET(String server, String api, Map<String, String> params) throws Exception { // do proxy if (!server.contains(UtilsAndCommons.IP_PORT_SPLITER)) { server = server + UtilsAndCommons.IP_PORT_SPLITER + RunningConfig.getServerPort(); } String url = "http://" + server + RunningConfig.getContextPath() + api; HttpClient.HttpResult result = HttpClient.httpGet(url, null, params); if (result.code != HttpURLConnection.HTTP_OK) { throw new IllegalStateException("leader failed, caused by: " + result.content); } } public void proxy(String server, String api, Map<String, String> params, HttpMethod method) throws Exception { // do proxy if (!server.contains(UtilsAndCommons.IP_PORT_SPLITER)) { server = server + UtilsAndCommons.IP_PORT_SPLITER + RunningConfig.getServerPort(); } String url = "http://" + server + RunningConfig.getContextPath() + api; HttpClient.HttpResult result; switch (method) { case GET: result = HttpClient.httpGet(url, null, params); break; case POST: result = HttpClient.httpPost(url, null, params); break; case DELETE: result = HttpClient.httpDelete(url, null, params); break; default: throw new RuntimeException("unsupported method:" + method); } if (result.code != HttpURLConnection.HTTP_OK) { throw new IllegalStateException("leader failed, caused by: " + result.content); } } public void proxyPostLarge(String server, String api, String content, Map<String, String> headers) throws Exception { // do proxy if (!server.contains(UtilsAndCommons.IP_PORT_SPLITER)) { server = server + UtilsAndCommons.IP_PORT_SPLITER + RunningConfig.getServerPort(); } String url = "http://" + server + RunningConfig.getContextPath() + api; HttpClient.HttpResult result = HttpClient.httpPostLarge(url, headers, content); if (result.code != HttpURLConnection.HTTP_OK) { throw new IllegalStateException("leader failed, caused by: " + result.content); } }}RaftProxy提供了proxyGET、proxy、proxyPostLarge三个方法,其中proxy接收了HttpMethod,可以处理GET、POST、DELETEHttpClientnacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/misc/HttpClient.java ...

October 4, 2019 · 3 min · jiezi

聊聊nacos-RaftCore的MasterElection

序本文主要研究一下nacos RaftCore的MasterElection RaftCorenacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftCore.java @Componentpublic class RaftCore { //...... @PostConstruct public void init() throws Exception { Loggers.RAFT.info("initializing Raft sub-system"); executor.submit(notifier); long start = System.currentTimeMillis(); raftStore.loadDatums(notifier, datums); setTerm(NumberUtils.toLong(raftStore.loadMeta().getProperty("term"), 0L)); Loggers.RAFT.info("cache loaded, datum count: {}, current term: {}", datums.size(), peers.getTerm()); while (true) { if (notifier.tasks.size() <= 0) { break; } Thread.sleep(1000L); } initialized = true; Loggers.RAFT.info("finish to load data from disk, cost: {} ms.", (System.currentTimeMillis() - start)); GlobalExecutor.registerMasterElection(new MasterElection()); GlobalExecutor.registerHeartbeat(new HeartBeat()); Loggers.RAFT.info("timer started: leader timeout ms: {}, heart-beat timeout ms: {}", GlobalExecutor.LEADER_TIMEOUT_MS, GlobalExecutor.HEARTBEAT_INTERVAL_MS); } //......}RaftCore的init方法通过GlobalExecutor.registerMasterElection(new MasterElection())注册了MasterElectionGlobalExecutornacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalExecutor.java ...

October 2, 2019 · 2 min · jiezi

聊聊nacos的ServerChangeListener

序本文主要研究一下nacos的ServerChangeListener ServerChangeListenernacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/cluster/servers/ServerChangeListener.java public interface ServerChangeListener { /** * If member list changed, this method is invoked. * * @param servers servers after change */ void onChangeServerList(List<Server> servers); /** * If reachable member list changed, this method is invoked. * * @param healthyServer reachable servers after change */ void onChangeHealthyServerList(List<Server> healthyServer);}ServerChangeListener定义了onChangeServerList、onChangeHealthyServerList方法DistroMappernacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/core/DistroMapper.java @Component("distroMapper")public class DistroMapper implements ServerChangeListener { private List<String> healthyList = new ArrayList<>(); //...... @Override public void onChangeServerList(List<Server> latestMembers) { } @Override public void onChangeHealthyServerList(List<Server> latestReachableMembers) { List<String> newHealthyList = new ArrayList<>(); for (Server server : latestReachableMembers) { newHealthyList.add(server.getKey()); } healthyList = newHealthyList; } //......}DistroMapper实现了ServerChangeListener接口,其onChangeHealthyServerList会更新自己的healthyListRaftPeerSetnacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/consistency/persistent/raft/RaftPeerSet.java ...

October 1, 2019 · 2 min · jiezi

聊聊nacos的ServerListManager

序本文主要研究一下nacos的ServerListManager ServerListManagernacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/cluster/ServerListManager.java @Component("serverListManager")public class ServerListManager { private static final int STABLE_PERIOD = 60 * 1000; @Autowired private SwitchDomain switchDomain; private List<ServerChangeListener> listeners = new ArrayList<>(); private List<Server> servers = new ArrayList<>(); private List<Server> healthyServers = new ArrayList<>(); private Map<String, List<Server>> distroConfig = new ConcurrentHashMap<>(); private Map<String, Long> distroBeats = new ConcurrentHashMap<>(16); private Set<String> liveSites = new HashSet<>(); private final static String LOCALHOST_SITE = UtilsAndCommons.UNKNOWN_SITE; private long lastHealthServerMillis = 0L; private boolean autoDisabledHealthCheck = false; private Synchronizer synchronizer = new ServerStatusSynchronizer(); public void listen(ServerChangeListener listener) { listeners.add(listener); } @PostConstruct public void init() { GlobalExecutor.registerServerListUpdater(new ServerListUpdater()); GlobalExecutor.registerServerStatusReporter(new ServerStatusReporter(), 5000); } //......}ServerListManager的init方法注册了ServerListUpdater、ServerStatusReporter两个定时任务GlobalExecutornacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalExecutor.java ...

September 30, 2019 · 4 min · jiezi

聊聊nacos的HttpHealthCheckProcessor

序本文主要研究一下nacos的HttpHealthCheckProcessor HealthCheckProcessornacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HealthCheckProcessor.java public interface HealthCheckProcessor { /** * Run check task for service * * @param task check task */ void process(HealthCheckTask task); /** * Get check task type, refer to enum HealthCheckType * * @return check type */ String getType();}HealthCheckProcessor接口定义了process、getType方法HttpHealthCheckProcessornacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HttpHealthCheckProcessor.java @Componentpublic class HttpHealthCheckProcessor implements HealthCheckProcessor { @Autowired private SwitchDomain switchDomain; @Autowired private HealthCheckCommon healthCheckCommon; private static AsyncHttpClient asyncHttpClient; private static final int CONNECT_TIMEOUT_MS = 500; static { try { AsyncHttpClientConfig.Builder builder = new AsyncHttpClientConfig.Builder(); builder.setMaximumConnectionsTotal(-1); builder.setMaximumConnectionsPerHost(-1); builder.setAllowPoolingConnection(false); builder.setFollowRedirects(false); builder.setIdleConnectionTimeoutInMs(CONNECT_TIMEOUT_MS); builder.setConnectionTimeoutInMs(CONNECT_TIMEOUT_MS); builder.setCompressionEnabled(false); builder.setIOThreadMultiplier(1); builder.setMaxRequestRetry(0); builder.setUserAgent("VIPServer"); asyncHttpClient = new AsyncHttpClient(builder.build()); } catch (Throwable e) { SRV_LOG.error("[HEALTH-CHECK] Error while constructing HTTP asynchronous client", e); } } @Override public String getType() { return "HTTP"; } @Override public void process(HealthCheckTask task) { List<Instance> ips = task.getCluster().allIPs(false); if (CollectionUtils.isEmpty(ips)) { return; } if (!switchDomain.isHealthCheckEnabled()) { return; } Cluster cluster = task.getCluster(); for (Instance ip : ips) { try { if (ip.isMarked()) { if (SRV_LOG.isDebugEnabled()) { SRV_LOG.debug("http check, ip is marked as to skip health check, ip: {}" + ip.getIp()); } continue; } if (!ip.markChecking()) { SRV_LOG.warn("http check started before last one finished, service: {}:{}:{}", task.getCluster().getService().getName(), task.getCluster().getName(), ip.getIp()); healthCheckCommon.reEvaluateCheckRT(task.getCheckRTNormalized() * 2, task, switchDomain.getHttpHealthParams()); continue; } AbstractHealthChecker.Http healthChecker = (AbstractHealthChecker.Http) cluster.getHealthChecker(); int ckPort = cluster.isUseIPPort4Check() ? ip.getPort() : cluster.getDefCkport(); URL host = new URL("http://" + ip.getIp() + ":" + ckPort); URL target = new URL(host, healthChecker.getPath()); AsyncHttpClient.BoundRequestBuilder builder = asyncHttpClient.prepareGet(target.toString()); Map<String, String> customHeaders = healthChecker.getCustomHeaders(); for (Map.Entry<String, String> entry : customHeaders.entrySet()) { if ("Host".equals(entry.getKey())) { builder.setVirtualHost(entry.getValue()); continue; } builder.setHeader(entry.getKey(), entry.getValue()); } builder.execute(new HttpHealthCheckCallback(ip, task)); MetricsMonitor.getHttpHealthCheckMonitor().incrementAndGet(); } catch (Throwable e) { ip.setCheckRT(switchDomain.getHttpHealthParams().getMax()); healthCheckCommon.checkFail(ip, task, "http:error:" + e.getMessage()); healthCheckCommon.reEvaluateCheckRT(switchDomain.getHttpHealthParams().getMax(), task, switchDomain.getHttpHealthParams()); } } } //......}HttpHealthCheckProcessor实现了HealthCheckProcessor接口,其static方法初始化了AsyncHttpClient;其getType返回的是HTTP;其process方法会遍历instances,然后对于非marked及markChecking的执行health check,并注册HttpHealthCheckCallback;对于非markChecking的或者出现异常的则执行healthCheckCommon.reEvaluateCheckRTHttpHealthCheckCallbacknacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/HttpHealthCheckProcessor.java ...

September 20, 2019 · 3 min · jiezi

聊聊nacos的DistroConsistencyServiceImpl

序本文主要研究一下nacos的DistroConsistencyServiceImpl ConsistencyServicenacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/consistency/ConsistencyService.java public interface ConsistencyService { /** * Put a data related to a key to Nacos cluster * * @param key key of data, this key should be globally unique * @param value value of data * @throws NacosException * @see */ void put(String key, Record value) throws NacosException; /** * Remove a data from Nacos cluster * * @param key key of data * @throws NacosException */ void remove(String key) throws NacosException; /** * Get a data from Nacos cluster * * @param key key of data * @return data related to the key * @throws NacosException */ Datum get(String key) throws NacosException; /** * Listen for changes of a data * * @param key key of data * @param listener callback of data change * @throws NacosException */ void listen(String key, RecordListener listener) throws NacosException; /** * Cancel listening of a data * * @param key key of data * @param listener callback of data change * @throws NacosException */ void unlisten(String key, RecordListener listener) throws NacosException; /** * Tell the status of this consistency service * * @return true if available */ boolean isAvailable();}ConsistencyService定义了put、remove、get、listen、unlisten、isAvailable方法EphemeralConsistencyServicenacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/EphemeralConsistencyService.java ...

September 11, 2019 · 5 min · jiezi

聊聊nacos的DistroMapper

序本文主要研究一下nacos的DistroMapper ServerChangeListenernacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/cluster/servers/ServerChangeListener.java public interface ServerChangeListener { /** * If member list changed, this method is invoked. * * @param servers servers after change */ void onChangeServerList(List<Server> servers); /** * If reachable member list changed, this method is invoked. * * @param healthyServer reachable servers after change */ void onChangeHealthyServerList(List<Server> healthyServer);}ServerChangeListener定义了onChangeServerList、onChangeHealthyServerList方法DistroMappernacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/core/DistroMapper.java @Component("distroMapper")public class DistroMapper implements ServerChangeListener { private List<String> healthyList = new ArrayList<>(); public List<String> getHealthyList() { return healthyList; } @Autowired private SwitchDomain switchDomain; @Autowired private ServerListManager serverListManager; /** * init server list */ @PostConstruct public void init() { serverListManager.listen(this); } public boolean responsible(Cluster cluster, Instance instance) { return switchDomain.isHealthCheckEnabled(cluster.getServiceName()) && !cluster.getHealthCheckTask().isCancelled() && responsible(cluster.getServiceName()) && cluster.contains(instance); } public boolean responsible(String serviceName) { if (!switchDomain.isDistroEnabled() || SystemUtils.STANDALONE_MODE) { return true; } if (CollectionUtils.isEmpty(healthyList)) { // means distro config is not ready yet return false; } int index = healthyList.indexOf(NetUtils.localServer()); int lastIndex = healthyList.lastIndexOf(NetUtils.localServer()); if (lastIndex < 0 || index < 0) { return true; } int target = distroHash(serviceName) % healthyList.size(); return target >= index && target <= lastIndex; } public String mapSrv(String serviceName) { if (CollectionUtils.isEmpty(healthyList) || !switchDomain.isDistroEnabled()) { return NetUtils.localServer(); } try { return healthyList.get(distroHash(serviceName) % healthyList.size()); } catch (Exception e) { Loggers.SRV_LOG.warn("distro mapper failed, return localhost: " + NetUtils.localServer(), e); return NetUtils.localServer(); } } public int distroHash(String serviceName) { return Math.abs(serviceName.hashCode() % Integer.MAX_VALUE); } @Override public void onChangeServerList(List<Server> latestMembers) { } @Override public void onChangeHealthyServerList(List<Server> latestReachableMembers) { List<String> newHealthyList = new ArrayList<>(); for (Server server : latestReachableMembers) { newHealthyList.add(server.getKey()); } healthyList = newHealthyList; }}DistroMapper实现了ServerChangeListener接口,其onChangeHealthyServerList方法会更新healthyList它还提供了responsible方法,该方法在switchDomain.isHealthCheckEnabled以及cluster.getHealthCheckTask()不是cancelled的情况下会执行responsible,该方法会调用distroHash来计算hash值它还提供了mapSrv方法,也是通过distroHash计算hash然后与healthyList的大小取余,最后返回server的key小结ServerChangeListener定义了onChangeServerList、onChangeHealthyServerList方法;DistroMapper实现了ServerChangeListener接口,其onChangeHealthyServerList方法会更新healthyList;DistroMapper还提供了responsible方法及mapSrv方法 ...

September 10, 2019 · 2 min · jiezi

聊聊nacos的DelegateConsistencyServiceImpl

序本文主要研究一下nacos的DelegateConsistencyServiceImpl ConsistencyServicenacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/consistency/ConsistencyService.java public interface ConsistencyService { /** * Put a data related to a key to Nacos cluster * * @param key key of data, this key should be globally unique * @param value value of data * @throws NacosException * @see */ void put(String key, Record value) throws NacosException; /** * Remove a data from Nacos cluster * * @param key key of data * @throws NacosException */ void remove(String key) throws NacosException; /** * Get a data from Nacos cluster * * @param key key of data * @return data related to the key * @throws NacosException */ Datum get(String key) throws NacosException; /** * Listen for changes of a data * * @param key key of data * @param listener callback of data change * @throws NacosException */ void listen(String key, RecordListener listener) throws NacosException; /** * Cancel listening of a data * * @param key key of data * @param listener callback of data change * @throws NacosException */ void unlisten(String key, RecordListener listener) throws NacosException; /** * Tell the status of this consistency service * * @return true if available */ boolean isAvailable();}ConsistencyService定义了put、remove、get、listen、unlisten、isAvailable方法DelegateConsistencyServiceImplnacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/consistency/DelegateConsistencyServiceImpl.java ...

September 8, 2019 · 2 min · jiezi

聊聊nacos的DataSyncer

序本文主要研究一下nacos的DataSyncer DataSyncernacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DataSyncer.java @Component@DependsOn("serverListManager")public class DataSyncer { @Autowired private DataStore dataStore; @Autowired private GlobalConfig partitionConfig; @Autowired private Serializer serializer; @Autowired private DistroMapper distroMapper; @Autowired private ServerListManager serverListManager; private Map<String, String> taskMap = new ConcurrentHashMap<>(); @PostConstruct public void init() { startTimedSync(); } public void submit(SyncTask task, long delay) { // If it's a new task: if (task.getRetryCount() == 0) { Iterator<String> iterator = task.getKeys().iterator(); while (iterator.hasNext()) { String key = iterator.next(); if (StringUtils.isNotBlank(taskMap.putIfAbsent(buildKey(key, task.getTargetServer()), key))) { // associated key already exist: if (Loggers.DISTRO.isDebugEnabled()) { Loggers.DISTRO.debug("sync already in process, key: {}", key); } iterator.remove(); } } } if (task.getKeys().isEmpty()) { // all keys are removed: return; } GlobalExecutor.submitDataSync(new Runnable() { @Override public void run() { try { if (getServers() == null || getServers().isEmpty()) { Loggers.SRV_LOG.warn("try to sync data but server list is empty."); return; } List<String> keys = task.getKeys(); if (Loggers.DISTRO.isDebugEnabled()) { Loggers.DISTRO.debug("sync keys: {}", keys); } Map<String, Datum> datumMap = dataStore.batchGet(keys); if (datumMap == null || datumMap.isEmpty()) { // clear all flags of this task: for (String key : task.getKeys()) { taskMap.remove(buildKey(key, task.getTargetServer())); } return; } byte[] data = serializer.serialize(datumMap); long timestamp = System.currentTimeMillis(); boolean success = NamingProxy.syncData(data, task.getTargetServer()); if (!success) { SyncTask syncTask = new SyncTask(); syncTask.setKeys(task.getKeys()); syncTask.setRetryCount(task.getRetryCount() + 1); syncTask.setLastExecuteTime(timestamp); syncTask.setTargetServer(task.getTargetServer()); retrySync(syncTask); } else { // clear all flags of this task: for (String key : task.getKeys()) { taskMap.remove(buildKey(key, task.getTargetServer())); } } } catch (Exception e) { Loggers.DISTRO.error("sync data failed.", e); } } }, delay); } public void retrySync(SyncTask syncTask) { Server server = new Server(); server.setIp(syncTask.getTargetServer().split(":")[0]); server.setServePort(Integer.parseInt(syncTask.getTargetServer().split(":")[1])); if (!getServers().contains(server)) { // if server is no longer in healthy server list, ignore this task: return; } // TODO may choose other retry policy. submit(syncTask, partitionConfig.getSyncRetryDelay()); } public void startTimedSync() { GlobalExecutor.schedulePartitionDataTimedSync(new TimedSync()); } //...... public List<Server> getServers() { return serverListManager.getHealthyServers(); } public String buildKey(String key, String targetServer) { return key + UtilsAndCommons.CACHE_KEY_SPLITER + targetServer; }}DataSyncer定义了submit、retrySync、startTimedSync、getServers等方法,其init方法会执行startTimedSyncsubmit方法对于retryCount为0的任务会判断taskMap是否存在该任务如果存在则移除其taskKey,之后使用GlobalExecutor.submitDataSync提交一个sync任务,它主要是通过amingProxy.syncData来同步,成功则移除,不成功则使用retrySync重试retrySync则重新构建server调用submit执行;startTimedSync方法则是使用GlobalExecutor.schedulePartitionDataTimedSync提交TimedSync任务;getServers则通过serverListManager.getHealthyServers()返回健康的实例TimedSyncnacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/DataSyncer.java ...

September 7, 2019 · 2 min · jiezi

聊聊nacos-config的EventDispatcher

序本文主要研究一下nacos config的EventDispatcher EventDispatchernacos-1.1.3/config/src/main/java/com/alibaba/nacos/config/server/utils/event/EventDispatcher.java public class EventDispatcher { /** * add event listener */ static public void addEventListener(AbstractEventListener listener) { for (Class<? extends Event> type : listener.interest()) { getEntry(type).listeners.addIfAbsent(listener); } } /** * fire event, notify listeners. */ static public void fireEvent(Event event) { if (null == event) { throw new IllegalArgumentException(); } for (AbstractEventListener listener : getEntry(event.getClass()).listeners) { try { listener.onEvent(event); } catch (Exception e) { log.error(e.toString(), e); } } } /** * For only test purpose */ static public void clear() { LISTENER_HUB.clear(); } /** * get event listener for eventType. Add Entry if not exist. */ static Entry getEntry(Class<? extends Event> eventType) { for (; ; ) { for (Entry entry : LISTENER_HUB) { if (entry.eventType == eventType) { return entry; } } Entry tmp = new Entry(eventType); /** * false means already exists */ if (LISTENER_HUB.addIfAbsent(tmp)) { return tmp; } } } //...... static private final Logger log = LoggerFactory.getLogger(EventDispatcher.class); static final CopyOnWriteArrayList<Entry> LISTENER_HUB = new CopyOnWriteArrayList<Entry>(); public interface Event { } //......}EventDispatcher定义了addEventListener、fireEvent、clear方法;addEventListener会添加listener到Entry;fireEvent会遍历指定event的listener然后回调其onEvent方法;clear会清空整个LISTENER_HUBEntrynacos-1.1.3/config/src/main/java/com/alibaba/nacos/config/server/utils/event/EventDispatcher.java ...

September 7, 2019 · 2 min · jiezi

Spring-Cloud-Alibaba-Nacos心跳与选举

通过阅读NACOS的源码,了解其心跳与选举机制。开始阅读此篇文章之前,建议先阅读如下两篇文章: Spring Cloud Alibaba Nacos(功能篇) Spring Cloud Alibaba Nacos(源码篇) 一、心跳机制只有NACOS服务与所注册的Instance之间才会有直接的心跳维持机制,换言之,这是一种典型的集中式管理机制。 在client这一侧是心跳的发起源,进入NacosNamingService,可以发现,只有注册服务实例的时候才会构造心跳包: @Override public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { if (instance.isEphemeral()) { BeatInfo beatInfo = new BeatInfo(); beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName)); beatInfo.setIp(instance.getIp()); beatInfo.setPort(instance.getPort()); beatInfo.setCluster(instance.getClusterName()); beatInfo.setWeight(instance.getWeight()); beatInfo.setMetadata(instance.getMetadata()); beatInfo.setScheduled(false); beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo); } serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance); }没有特殊情况,目前ephemeral都是true。BeatReactor维护了一个Map对象,记录了需要发送心跳的BeatInfo,构造了一个心跳包后,BeatReactor.addBeatInfo方法将BeatInfo放入Map中。然后,内部有一个定时器,每隔5秒发送一次心跳。 class BeatProcessor implements Runnable { @Override public void run() { try { for (Map.Entry<String, BeatInfo> entry : dom2Beat.entrySet()) { BeatInfo beatInfo = entry.getValue(); if (beatInfo.isScheduled()) { continue; } beatInfo.setScheduled(true); executorService.schedule(new BeatTask(beatInfo), 0, TimeUnit.MILLISECONDS); } } catch (Exception e) { NAMING_LOGGER.error("[CLIENT-BEAT] Exception while scheduling beat.", e); } finally { executorService.schedule(this, clientBeatInterval, TimeUnit.MILLISECONDS); } } }通过设置scheduled的值来控制是否已经下发了心跳任务,具体的心跳任务逻辑放在了BeatTask。 ...

July 8, 2019 · 4 min · jiezi

Spring-Cloud-Alibaba-Nacos源码篇

在看这篇文章之前,最好对NACOS相关功能有所了解,推荐看完Spring Cloud Alibaba Nacos(功能篇)。 针对功能,有目的的去找相对应的源代码,进一步了解功能是如何被实现出来的。 本文针对有一定源代码阅读经验的人群,不会深入太多的细节,还需要读者打开源码跟踪,自行领会。 一、引子进入GitHub对应的页面,将NACOS工程clone下来。目录和文件看起来很冗长,但是对于看源代码真正有帮助的部分并不多。 有了这三张图,就能顺利找到突破口了,核心内容就集中在nacos-console,nacos-naming,nacos-config,顺藤摸瓜,就能看到不少内容了。 如果还是感觉无从下手的话,那就移步nacos-example,里面有主要业务的调用入口,一看便知。 二、配置服务首先从一个工厂类说起:com.alibaba.nacos.api.NacosFactory。 里面的静态方法用于创建ConfigService和NamingService,代码类似,以创建ConfigService为例: public static ConfigService createConfigService(Properties properties) throws NacosException { try { Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.config.NacosConfigService"); Constructor constructor = driverImplClass.getConstructor(Properties.class); ConfigService vendorImpl = (ConfigService) constructor.newInstance(properties); return vendorImpl; } catch (Throwable e) { throw new NacosException(-400, e.getMessage()); }}没有什么复杂的逻辑,使用的是基本的反射原理。构造参数传入了properties,这些属性可以通过bootstrap.yml中指定,对应的是NacosConfigProperties。 需要细看的是构造函数中对于namespace初始化的那部分内容。 private void initNamespace(Properties properties) { String namespaceTmp = null; String isUseCloudNamespaceParsing = properties.getProperty(PropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, System.getProperty(SystemPropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, String.valueOf(Constants.DEFAULT_USE_CLOUD_NAMESPACE_PARSING))); if (Boolean.valueOf(isUseCloudNamespaceParsing)) { namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable<String>() { @Override public String call() { return TenantUtil.getUserTenantForAcm(); } }); namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable<String>() { @Override public String call() { String namespace = System.getenv(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_NAMESPACE); return StringUtils.isNotBlank(namespace) ? namespace : EMPTY; } }); } if (StringUtils.isBlank(namespaceTmp)) { namespaceTmp = properties.getProperty(PropertyKeyConst.NAMESPACE); } namespace = StringUtils.isNotBlank(namespaceTmp) ? namespaceTmp.trim() : EMPTY; properties.put(PropertyKeyConst.NAMESPACE, namespace);}传入的properties会指定是否解析云环境中的namespace参数,如果是的,就是去读取阿里云环境的系统变量;如果不是,那么就读取properties中指定的namespace,没有指定的话,最终解析出来的是空字符串。从代码上看出来,获取云环境的namespace做成了异步化的形式,但是目前版本还是使用的同步调用。 ...

July 3, 2019 · 4 min · jiezi

Spring-Cloud-Alibaba-Nacos

越来越多的读者在和我交流关于Spring Cloud Alibaba的种种事宜,甚至于在一次面试中,一半时间都在聊这个话题。所以,本着对技术钻研的热情,对Spring Cloud Alibaba进行了一番研究。 在这里,并不想高谈阔论,也不想预言未来,只是挑选了几个从阿里巴巴中间件团队内脱胎出来的开源组件,对于解决实际业务问题有所裨益。 一、背景先来说说大背景。 现在,很明显的一个趋势就是:微服务。 这个趋势的底层驱动力就来源于分布式系统的普及,而微服务的各个特性是如今大大小小的企业无法拒绝的诱惑。 然后,用上了微服务的架构风格,用Spring Cloud,或者Dubbo搭了一套脚手架,就开始干起来了。 接下来,一众小公司画完了大饼之后,发现自己根本吃不下。这就是典型的落后劳动力与先进生产力的尖锐矛盾。这个时候,返璞归真的想法是不能有了,重构代价太大。 当然,哪里有问题,哪里就有商机。各大XX云厂商经过一系列包装之后,用“云原生(Cloud Native)”的新概念粉墨登场。 Spring Cloud Alibaba就是其中之一。 这个概念的一个核心价值就是:平滑上云,赋能运维。最明显的业务表象就是,会提供一套Open API,甚至是贴心的提供一个可视化控制台,傻瓜式的那种。 二、从NACOS说起这是一颗耀眼的掌上明珠,迅速引起了我的注意。 按照套路,分为两讲。其一讲述NACOS的功能特性,及其使用,再者就是更深入一步,看看大厂的攻城狮们写的代码。 本文所使用的版本是NACOS 1.0.0,由于此版本还是第一个NACOS正式版,NACOS正处在飞速发展阶段,本文的一些内容可能会不适用于以后的版本,请读者自行辨别。 NACOS解决两个核心问题:动态配置管理,服务注册发现。 兼容性方面,除了支持自家的Dubbo,还对Spring Cloud,Kubernetes,Istio有所兼容。 对照以上的全景图,现在的NACOS还有一段距离,但是并不遥远。 至此,不说道说道Eureka,都有点过意不去了。 我用下来的体验是:NACOS完全可以替代Eureka了。 江山代有才人出,这是必然的结果。 在“云原生”的大背景之下,NACOS顺利成章的推出了Console,将触角进一步延伸至服务的精细化管理。 当然,不排除Eureka也在憋大招。 再说说动态配置的特性。 当然,NACOS略胜一筹,可替代Spring Cloud Config了。 原先在Git/SVN上托管的配置项,都可以在Console上统一管理了。 如果想先睹为快,可以接下着往下读。如果想再多了解一些,可以直接跳过这部分,阅读下一个小节。 可以把NACOS理解成是一个中心化的服务,这在阿里系的架构中屡见不鲜。所以,必须得先启动这个服务。 有两个办法:其一是直接clone源码,使用maven打包。第二个办法是直接下载GitHub release出来的压缩包。 推荐后者。 方法1:主要运行以下命令: git clone https://github.com/alibaba/nacos.git cd nacos/ mvn -Prelease-nacos clean install -U经过一段时间的构建过程,在./distribution/target目录下有我们想要的压缩包。 方法2:进入https://github.com/alibaba/nacos/releases,找到压缩包,下载。 为了演示,我们先用单机模式启动。 Windows环境下: startup.cmd -m standalone一切就绪的话,访问http://127.0.0.1:8848/nacos/index.html,使用nacos/nacos登录。 接下来,随便逛逛。 三、重要的概念为了避免在Console中迷失自我,有必要先阐述几个重要的概念。 这张图很重要。表述了namespace、group和service/dataId的包含关系。 NACOS给的最佳实践表明,最外层的namespace是可以用于区分部署环境的,比如test,uat,product等。同时,也有一个商业利用价值:多租户。以namespace为单位,给用户开辟使用空间。 其它两个领域模型不用多解释了,见名知意。其目的也非常明显,就是为了能够逻辑上区分两个目标对象。 默认情况下,namespace=public,group=DEFAULT_GROUP。 明白了这个数据模型后,可以稍微玩转一下Console了,比如新建若干个namespace: ...

June 23, 2019 · 2 min · jiezi

Nacos-Namespace-和-Endpoint-在生产环境下的最佳实践

随着使用 Nacos 的企业越来越多,遇到的最频繁的两个问题就是:如何在我的生产环境正确的来使用 namespace 以及 endpoint。这篇文章主要就是针对这两个问题来聊聊使用 nacos 过程中关于这两个参数配置的最佳实践方式。 namespce关于 namespace ,以下主要从 namespace 的设计背景 和 namespace 的最佳实践 两个方面来讨论。 namespace 的设计背景namespace 的设计是 nacos 基于此做多环境以及多租户数据(配置和服务)隔离的。即: 从一个租户(用户)的角度来看,如果有多套不同的环境,那么这个时候可以根据指定的环境来创建不同的 namespce,以此来实现多环境的隔离。例如,你可能有日常,预发和生产三个不同的环境,那么使用一套 nacos 集群可以分别建以下三个不同的 namespace。如下图所示: 从多个租户(用户)的角度来看,每个租户(用户)可能会有自己的 namespace,每个租户(用户)的配置数据以及注册的服务数据都会归属到自己的 namespace 下,以此来实现多租户间的数据隔离。例如超级管理员分配了三个租户,分别为张三、李四和王五。分配好了之后,各租户用自己的账户名和密码登录后,创建自己的命名空间。如下图所示。 注意: 该功能还在规划中。 namespace 的最佳实践关于 namespace 的最佳实践 ,这部分主要包含有两个 Action: 如何来获取 namespace 的值namespace 参数初始化方式如何来获取 namespace 的值无论您是基于 Spring Cloud 或者 Dubbo 来使用 nacos,都会涉及到 namespace 的参数输入,那么这个时候 namespace 的值从哪里可以获取呢? 如果您在使用过程中没有感知到这个参数的输入,那么 nacos 统一会使用一个默认的 namespace 作为输入,nacos naming 会使用 public 作为默认的参数来初始化,nacos config 会使用一个空字符串作为默认的参数来初始化。。如果您需要自定义自己的 namespace,那么这个值该怎么来产生?可以在 nacos 的控制台左边功能侧看到有一个 命名空间 的功能,点击就可以看到 新建命名空间 的按钮,那么这个时候就可以创建自己的命名空间了。创建成功之后,会生成一个命名空间ID,主要是用来避免命名空间名称有可能会出现重名的情况。因此当您在应用中需要配置指定的 namespace 时,填入的是命名空间ID。重要的事情说三遍, 当您在应用中需要配置指定的 namespace 时,填入的是命名空间 ID当您在应用中需要配置指定的 namespace 时,填入的是命名空间 ID当您在应用中需要配置指定的 namespace 时,填入的是命名空间 ID说明: namesace 为 public 是 nacos 的一个保留控件,如果您需要创建自己的 namespace,最好不要和 public 重名,以一个实际业务场景有具体语义的名字来命名,以免带来字面上不容易区分自己是哪一个 namespace。 ...

May 28, 2019 · 1 min · jiezi

Spring-Cloud-Alibaba基础教程Sentinel-Dashboard中修改规则同步到Nacos

上一篇我们介绍了如何通过改造Sentinel Dashboard来实现修改规则之后自动同步到Apollo。下面通过这篇,详细介绍当使用Nacos作为配置中心之后,如何实现Sentinel Dashboard中修改规则同步到Nacos。关于下面改造的原理和分析可以见上一篇《Sentinel Dashboard中修改规则同步到Apollo》的头两节内容,这里不重复介绍了。 代码实现下面直接来看看如何实现的具体改造步骤,这里参考了Sentinel Dashboard源码中关于Nacos实现的测试用例。但是由于考虑到与Spring Cloud Alibaba的结合使用,略作修改。 第一步:修改pom.xml中的sentinel-datasource-nacos的依赖,将<scope>test</scope>注释掉,这样才能在主程序中使用。 <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <!--<scope>test</scope>--></dependency>第二步:找到resources/app/scripts/directives/sidebar/sidebar.html中的这段代码: <li ui-sref-active="active"> <a ui-sref="dashboard.flowV1({app: entry.app})"> <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则 </a></li>修改为: <li ui-sref-active="active"> <a ui-sref="dashboard.flow({app: entry.app})"> <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则 </a></li>第三步:在com.alibaba.csp.sentinel.dashboard.rule包下新建一个nacos包,用来编写针对Nacos的扩展实现。 第四步:创建Nacos的配置类,具体代码如下: @Configurationpublic class NacosConfig { @Bean public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() { return JSON::toJSONString; } @Bean public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() { return s -> JSON.parseArray(s, FlowRuleEntity.class); } @Bean public ConfigService nacosConfigService() throws Exception { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "localhost"); return ConfigFactory.createConfigService(properties); }}如果用到了namespace隔离环境,可以在nacosConfigService方法中再加入配置,比如:properties.put(PropertyKeyConst.NAMESPACE, "130e71fa-97fe-467d-ad77-967456f2c16d"); ...

May 22, 2019 · 2 min · jiezi

Nacos部署中的一些常见问题汇总

开个帖子,汇总一下读者经常提到的一些问题问题一:Ubuntu下启动Nacos报错问题描述 使用命令sh startup.sh -m standalone启动报错: ./startup.sh: 78: ./startup.sh: [[: not found./startup.sh: 88: ./startup.sh: [[: not found./startup.sh: 90: ./startup.sh: [[: not found./startup.sh: 96: ./startup.sh: [[: not found/usr/lib/jvm/java-8-openjdk-amd64/bin/java -server -Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/soft/nacos/logs/java_heapdump.hprof -XX:-UseLargePages -Djava.ext.dirs=/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext:/usr/lib/jvm/java-8-openjdk-amd64/lib/ext:/data/soft/nacos/plugins/cmdb:/data/soft/nacos/plugins/mysql -Xloggc:/data/soft/nacos/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -Dnacos.home=/data/soft/nacos -jar /data/soft/nacos/target/nacos-server.jar --spring.config.location=classpath:/,classpath:/config/,file:./,file:./config/,file:/data/soft/nacos/conf/ --logging.config=/data/soft/nacos/conf/nacos-logback.xml./startup.sh: 116: ./startup.sh: [[: not foundnacos is starting,you can check the /data/nacos/logs/start.out解决方法 改用命令bash -f ./startup.sh -m standalone启动 ...

May 10, 2019 · 1 min · jiezi

初识 Nacos(上) 学习《Spring Cloud 服务发现新选择》

本文来自于我的个人主页:初识 Nacos(上) 学习《Spring Cloud 服务发现新选择》,转载请保留链接 ;)最近在从零接触Alibaba 开源项目Nacos,学习的是小马哥(mercyblitz)的技术周报,之前看了后忘记总结,导致也没有什么印象。所以现在决定学习一章,写一篇学习感悟,并且持续更新下去。首先这一章节主要讲得是服务发现(Service Discovery),作为 Spring Cloud 最核心功能特性之一,受到业界的广泛关注。Spring Cloud 整体架构在现行的 Spring Cloud 服务发现技术体系中,以 Spring Cloud Eureka 为典型代表,它作为官方推荐解决方案,被业 界广泛运用,然而其设计缺陷也非常之明显。还有Spring Cloud Zookeeper和Spring Cloud Consul。那么先介绍这三种的特点吧。Spring Cloud Eureka 特点优点:Spring Cloud 背书 - 推荐服务发现方案CAP 理论 - AP模型,数据最终一致简单易用 - 开箱即用,控制台管理缺点:内存限制 - 客户端上报完整注册信息,造成服务端内存浪费单一调度更新 - 客户端简单轮训更新,增加服务器压力集群伸缩性限制 - 广播式复制模型,增加服务器压力Spring Cloud Zookeeper 特点优点:成熟协调系统 - Dubbo、Spring Cloud等适配方案CAP理论 - CP模型,ZAB算法,强数据一致性缺点:维护成本 - 客户端、Session状态、网络故障伸缩性限制 - 内存、GC、连接Spring Cloud Consul 特点优点:通用方案 - 适用于 Service Mesh、 Java 生态CAP理论 - AP 模型,Raft+Gossip 算法,数据最终一致缺点:可靠性无法保证 - 未经过大规模验证非 Java 生态 - 维护和问题排查困难综上所述,让我得出了Spring Cloud服务发现方案对比结果:那么这三种服务发现的基本模式是怎样的呢?现在来谈谈Spring cloud 服务器发现模式。首先都是服务器启动 - 启动注册中心然后增加客户端依赖 - sping-cloud-start-*最后就是客户端注册 - 记得在XXApplication.java文件中添加@EnableDiscoveryClient,注解开启服务注册与发现功能。以下我以Eureka发现模式为例:首先去Spring Initializr快速创建Eureka服务端和客户端应用程序,然后导入自己的IDE。当然你如果嫌麻烦,也可以直接导入已经写好的工程。然后在resources-application.properties中分别配置好两者的端口号,像客户端这块还需要写好应用名称、以及Eureka 服务器地址。最后我们就直接可以runXXApplication.java了,像我的服务端端口是12345,就访问localhost:12345。页面跳转如下图所示,恭喜你的Eureka服务已经起来了。Eureka-client亦如此,成功run起来后,在之前的服务端页面,也就是localhost:12345,刷新下会在Instances currently registered with Eureka出现EUREKA-CLIENT的状态信息。spring-cloud-alibaba-nacos-discovery 作为 Spring Cloud Alibaba 服务发现的核心模块,其架构基础与 Spring Cloud 现行方案相同,均构建在 Spring Cloud Commons 抽象。因此,它在 Spring Cloud 服务发现的使用上,开发人员将不会心存任何的违和感。Alibaba Nacos 生态介绍从功能特性而言,spring-cloud-alibaba-nacos-discovery 仅是 Nacos 在 Spring Cloud 服务发现的解决方案,Nacos 在 Spring Cloud 中还支持分布式配置的特性。与开源产品不同的是,Nacos 曾经历过中国特色的超大流量考验,以及巨型规模的集群实施,无论从经验积累还是技术沉淀,现行 Spring Cloud 解决方案 都是无法比拟的。然而这并非说明它完美无缺,在内部的评估和讨论中,也发现其中差距和文化差异。为了解决这些问题,讨论将从整体架构和设计思考两个方面,介绍 Nacos 与 Spring 技术栈整合情况,以及与其他开源方案的适配思考,整体上,降低 Nacos 使用门槛,使迁移成本接近为零,达到“一次开发,到处运行”的目的。那么接下来我们通过Github上,Spring Cloud Alibaba项目中官方给出的指导文档来配置启动 Nacos吧。下载注册中心首先需要获取 Nacos Server,支持直接下载和源码构建两种方式。直接下载:Nacos Server 下载页源码构建:进入 Nacos Github 项目页面,将代码 git clone 到本地自行编译打包,参考此文档。推荐使用源码构建方式以获取最新版本启动注册中心启动 Server,进入解压后文件夹或编译打包好的文件夹,找到如下相对文件夹 nacos/bin,并对照操作系统实际情况之下如下命令。Linux/Unix/Mac 操作系统,执行命令 sh startup.sh -m standaloneWindows 操作系统,执行命令 cmd startup.cmd增加第三方依赖首先,修改 pom.xml 文件,引入 Nacos Discovery Starter。 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>外部化配置在应用的 /src/main/resources/application.properties 配置文件中配置 Nacos Server 地址 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848激活服务发现使用 @EnableDiscoveryClient 注解开启服务注册与发现功能(SpringApplication.run) @SpringBootApplication @EnableDiscoveryClient public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @RestController class EchoController { @RequestMapping(value = “/echo/{string}”, method = RequestMethod.GET) public String echo(@PathVariable String string) { return string; } } }应用启动增加配置,在 nacos-discovery-provider-example 项目的 /src/main/resources/application.properties 中添加基本配置信息 spring.application.name=service-provider server.port=18082启动应用,支持 IDE 直接启动和编译打包后启动。IDE直接启动:找到 nacos-discovery-provider-example 项目的主类 ProviderApplication,执行 main 方法启动应用。打包编译后启动:在 nacos-discovery-provider-example 项目中执行 mvn clean package 将工程编译打包,然后执行 java -jar nacos-discovery-provider-example.jar启动应用。验证检验服务发现在浏览器输入此地址http://127.0.0.1:8848/nacos/v1/ns/instances?serviceName=service-provider 并点击跳转,可以看到服务节点已经成功注册到 Nacos Server。 ...

April 20, 2019 · 2 min · jiezi

阿里巴巴基于 Nacos 实现环境隔离的实践

随着Nacos 0.9版本的发布,Nacos 离正式生产版本(GA)又近了一步,其实已经有不少企业已经上了生产,例如虎牙直播。本周三(今天),晚上 19:00~21:00 将会在 Nacos 钉钉群(群号:21708933)直播 Nacos 1.0.0 所有发布特性的预览以及升级和使用上的指导。Nacos环境隔离通常,企业研发的流程是这样的:先在测试环境开发和测试功能,然后灰度,最后发布到生产环境。并且,为了生产环境的稳定,需要将测试环境和生产环境进行隔离,此时,必然会遇到问题是多环境问题,即:多个环境的数据如何隔离?如何优雅的隔离?(不需要用户做任何改动)本文将就 Nacos 环境隔离,向大家介绍阿里在这方面的实践经验。什么是环境?说到环境隔离,首先应该定义好什么是环境。环境这个词目前还没有一个比较统一的定义,有些公司叫环境,在阿里云上叫 region,在 Kubernetes 架构中叫 namespace。本文认为,环境是逻辑上或物理上独立的一整套系统,这套系统中包含了处理用户请求的全部组件,例如网关、服务框架、微服务注册中心、配置中心、消息系统、缓存、数据库等,可以处理指定类别的请求。举个例子,很多网站都会有用户 ID 的概念,可以按照用户 ID 划分,用户 ID 以偶数结尾的请求全部由一套系统处理,而奇数结尾的请求由另一套系统处理。如下图所示。 我们这里说的环境隔离是指物理隔离,即不同环境是指不同的机器集群。环境隔离有什么用上一节定义了环境的概念,即一套包含了处理用户请求全部必要组件的系统,用来处理指定类别的请求。本节跟大家讨论一下环境隔离有哪些好处。从概念的定义可以看出,环境隔离至少有三个方面的好处:故障隔离、故障恢复、灰度测试;故障隔离首先,因为环境是能够处理用户请求的独立组件单元,也就是说用户请求的处理链路有多长,都不会跳出指定的机器集群。即使这部分机器故障了,也只是会影响部分用户,从而把故障隔离在指定的范围内。如果我们按照用户id把全部机器分为十个环境,那么一个环境出问题,对用户的影响会降低为十分之一,大大提高系统可用性。故障恢复环境隔离的另一个重要优势是可以快速恢复故障。当某个环境的服务出现问题之后,可以快速通过下发配置,改变用户请求的路由方向,把请求路由到另一套环境,实现秒级故障恢复。当然,这需要一个强大的分布式系统支持,尤其是一个强大的配置中心(如Nacos),需要快速把路由规则配置数据推送到全网的应用进程。灰度测试灰度测试是研发流程中不可或缺的一个环节。传统的研发流程中,测试和灰度环节,需要测试同学做各种各样的配置,如绑定host、配置jvm参数、环境变量等等,比较麻烦。经过多年的实践,阿里巴巴内部的测试和灰度对开发和测试非常友好,通过环境隔离功能来保证请求在指定的机器集群处理,开发和测试不需要做任何做任何配置,大大提高了研发效率。Nacos如何做环境隔离前两节讲到了环境的概念和环境隔离的作用,本节介绍如何基于 Nacos,实现环境的隔离。Nacos 脱胎于阿里巴巴中间件部门的软负载小组,在环境隔离的实践过程中,我们是基于 Nacos 去隔离多个物理集群的,同时,在 Nacos 客户端不需要做任何代码改动的情况下,就可以实现环境的自动路由。开始前,我们先做一些约束:一台机器上部署的应用都在一个环境内;一个应用进程内默认情况下只连一个环境的 Nacos;通过某种手段可以拿到客户端所在机器 IP;用户对机器的网段有规划;基本原理是:网络中 32 位的 IPV4 可以划分为很多网段,如192.168.1.0/24,并且一般中大型的企业都会有网段规划,按照一定的用途划分网段。我们可以利用这个原理做环境隔离,即不同网段的 IP 属于不同的环境,如192.168.1.0/24属于环境A, 192.168.2.0/24属于环境B等。Nacos 有两种方式初始化客户端实例,一种是直接告诉客户端 Nacos 服务端的IP;另一种是告诉客户端一个 Endpoint,客户端通过 HTTP 请求到 Endpoint,查询 Nacos 服务端的 IP 列表。这里,我们利用第二种方式进行初始化。增强 Endpoint 的功能。在 Endpoint 端配置网段和环境的映射关系,Endpoint 在接收到客户端的请求后,根据客户端的来源 IP 所属网段,计算出该客户端的所属环境,然后找到对应环境的 IP 列表返回给客户端。如下图一个环境隔离server的示例上面讲了基于IP段做环境隔离的约束和基本原理,那么如何实现一个地址服务器呢。最简单的方法是基于nginx实现,利用nginx的geo模块,做IP端和环境的映射,然后利用nginx返回静态文件内容。安装nginx http://nginx.org/en/docs/install.html在nginx-proxy.conf中配置geo映射,参考这里geo $env { default “”; 192.168.1.0/24 -env-a; 192.168.2.0/24 -env-b;}配置nginx根路径及转发规则,这里只需要简单的返回静态文件的内容;# 在http模块中配置根路径root /tmp/htdocs;# 在server模块中配置location / { rewrite ^(.*)$ /$1$env break;}配置Nacos服务端IP列表配置文件,在/tmp/hotdocs/nacos目录下配置以环境名结尾的文件,文件内容为IP,一行一个$ll /tmp/hotdocs/nacos/total 0-rw-r–r– 1 user1 users 0 Mar 5 08:53 serverlist-rw-r–r– 1 user1 users 0 Mar 5 08:53 serverlist-env-a-rw-r–r– 1 user1 users 0 Mar 5 08:53 serverlist-env-b$cat /tmp/hotdocs/nacos/serverlist192.168.1.2192.168.1.3验证curl ’localhost:8080/nacos/serverlist'192.168.1.2192.168.1.3至此, 一个简单的根据IP网段做环境隔离的示例已经可以工作了,不同网段的nacos客户端会自动获取到不同的Nacos服务端IP列表,实现环境隔离。这种方法的好处是用户不需要配置任何参数,各个环境的代码和配置是一样的,但需要提供底层服务的同学做好网络规划和相关配置。总结本文简单介绍了环境隔离的概念,环境隔离的三个好处以及 Nacos 如何基于网段做环境隔离。最后,给出了一个基于 Nginx 做 Endpoint 服务端的环境隔离配置示例。需要注意的是,本文只是列出了一种可行的方法,不排除有更优雅的实现方法,如果大家有更好的方法,欢迎到Nacos 社区或官网贡献方案。本文作者:正己,GitHub ID @jianweiwang,负责 Nacos 的开发和社区维护,阿里巴巴高级开发工程师。本文作者:中间件小哥阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

March 14, 2019 · 1 min · jiezi

Nacos系列:Nacos的三种部署模式

三种部署模式Nacos支持三种部署模式1、单机模式:可用于测试和单机使用,生产环境切忌使用单机模式(满足不了高可用)2、集群模式:可用于生产环境,确保高可用3、多集群模式:可用于多数据中心场景单机模式启动 Nacos ServerLinux:sh startup.sh -m standaloneWindows:cmd startup.cmd -m standalone 或 双击 startup.cmd 启动关闭 Nacos ServerLinux:sh shutdown.shWindows:cmd shutdown.cmd 或 双击 shutdown.cmd 启动在0.7版本之前,Nacos使用的是嵌入式数据库Derby (Apache Derby)来存储数据;0.7版本,增加了对mysql数据源的支持。Derby数据源内嵌的数据库,通过命令直接启动即可,无需额外安装。startup.cmd -m standaloneMySQL数据源步骤一:安装MySQL数据,版本要求:5.6.5+步骤二:初始化数据库创建数据库create database if not exists nacos default charset utf8 collate utf8_general_ci;初始化数据库在nacos server解压目录conf下,找到 nacos-mysql.sql 文件,直接执行,执行完成后,用Navicat客户端查看步骤三:修改conf/application.properties文件,添加如下信息## mysql datasourcespring.datasource.platform=mysql db.num=1db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=truedb.user=rootdb.password=123456注意:spring.datasource.platform=mysql别漏了,要不然还是使用Derby数据库。步骤四:启动 Nacos Server启动成功后,我们使用上一篇博客:《Nacos系列:Nacos的Java SDK使用》中的案例来验证,运行NacosConfig,观察数据表的内容变化,示例知行后,config_info表和his_config_info表都会有和配置相关的数据,如下图所示:似乎Nacos的MySQL数据源只存储了配置数据,服务列表和注册的服务实例信息并不会出现在数据表中(通过运行示例中的NacosDiscovery类main()方法,就可以观察到该现象)集群模式资源有限,我直接在Windows上模拟部署搭建“伪集群”, 新建一个文件目录NacosCluster,将Nacos Server解压三份到该目录下,分别命名为nasosSlave0、nasosSlave1、nasosSlave2,分配端口:8845、8846、8847修改nasosSlave0/conf/application.properties,server.port=8845,并添加## mysql datasourcespring.datasource.platform=mysql db.num=1db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=truedb.user=rootdb.password=123456这里先使用一个MySQL库演示功能,实际生产上至少要使用主备模式,例如:db.num=2db.url.0=jdbc:mysql://127.0.0.1:3306/nacos1?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=truedb.url.1=jdbc:mysql://127.0.0.1:3306/nacos2?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=truedb.user=rootdb.password=123456拷贝nasosSlave0/conf/cluster.conf.example为cluster.conf,修改内容如下:127.0.0.1:8845127.0.0.1:8846127.0.0.1:8847nasosSlave1、nasosSlave2 和 nasosSlave0 除了端口不同外,其它配置保持一致。分别启动每台 Nacos Serverstartup.cmd -m cluster注意:在Windows下,这个时候不能再双击startup.cmd启动了,如果这样启动仍然是以单机模式运行,因为在bin/startup.cmd中有下面这段代码:if not “%2” == “cluster” ( set “JAVA_OPT=%JAVA_OPT% -Xms512m -Xmx512m -Xmn256m” set “JAVA_OPT=%JAVA_OPT% -Dnacos.standalone=true” ) else ( set “JAVA_OPT=%JAVA_OPT% -server -Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m” set “JAVA_OPT=%JAVA_OPT% -XX:-OmitStackTraceInFastThrow XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=%BASE_DIR%\logs\java_heapdump.hprof” set “JAVA_OPT=%JAVA_OPT% -XX:-UseLargePages” )观察控制台,启动成功后,在控制台可以看到如下信息:E:\Software\Nacos\nacosCluster\nacosSlave0\bin>startup.cmd -m cluster ,–. ,–.’| ,–,: : | Nacos 0.8.0,--.'| ’ : ,—. Running in cluster mode| : : | | ’ ,’\ .–.–. Port: 8845: | \ | : ,–.–. ,—. / / | / / ’ Pid: 6568| : ’ ‘; | / \ / . ; ,. :| : /./ Console: http://192.168.1.102:8845/nacos/index.html' ' ;. ;.--. .-. | / / '' | |: :| : ;_| | | \ | \__\/: . .. ' / ' | .; : \ \ . https://nacos.io’ : | ; .’ ," .–.; |’ ; :__| : | ----. \| | '–’ / / ,. |’ | ‘.’|\ \ / / /--' /' : | ; : .' \ : : —-’ ‘–’. /; |.’ | , .-./\ \ / --'---''---' –---' —-‘2019-02-20 23:06:13,059 INFO The server IP list of Nacos is [127.0.0.1:8845, 127.0.0.1:8846, 127.0.0.1:8847]2019-02-20 23:06:14,185 INFO Nacos is starting…2019-02-20 23:06:15,409 INFO Nacos is starting…2019-02-20 23:06:16,512 INFO Nacos is starting…2019-02-20 23:06:17,605 INFO Nacos is starting…2019-02-20 23:06:18,736 INFO Nacos is starting…2019-02-20 23:06:19,860 INFO Nacos is starting…2019-02-20 23:06:21,021 INFO Nacos is starting…2019-02-20 23:06:22,230 INFO Nacos is starting…2019-02-20 23:06:23,390 INFO Nacos is starting…2019-02-20 23:06:24,605 INFO Nacos is starting…2019-02-20 23:06:25,991 INFO Nacos is starting…2019-02-20 23:06:26,993 INFO Nacos is starting…2019-02-20 23:06:28,197 INFO Nacos is starting…2019-02-20 23:06:29,264 INFO Nacos is starting…2019-02-20 23:06:30,515 INFO Nacos is starting…2019-02-20 23:06:31,810 INFO Nacos is starting…2019-02-20 23:06:32,934 INFO Nacos is starting…2019-02-20 23:06:33,976 INFO Nacos is starting…2019-02-20 23:06:35,044 INFO Nacos is starting…2019-02-20 23:06:36,153 INFO Nacos is starting…2019-02-20 23:06:37,290 INFO Nacos is starting…2019-02-20 23:06:38,616 INFO Nacos is starting…2019-02-20 23:06:39,736 INFO Nacos is starting…2019-02-20 23:06:40,824 INFO Nacos is starting…2019-02-20 23:06:41,757 INFO Nacos Log files: E:\Software\Nacos\nacosCluster\nacosSlave0/logs/2019-02-20 23:06:41,768 INFO Nacos Conf files: E:\Software\Nacos\nacosCluster\nacosSlave0/conf/2019-02-20 23:06:41,771 INFO Nacos Data files: E:\Software\Nacos\nacosCluster\nacosSlave0/data/2019-02-20 23:06:41,774 INFO Nacos started successfully in cluster mode.在浏览器分别访问如下路径http://localhost:8845/nacoshttp://localhost:8846/nacoshttp://localhost:8847/nacos如果都能访问成功,证明集群模式部署成功。多集群模式Nacos支持NameServer路由请求模式,通过它您可以设计一个有用的映射规则来控制请求转发到相应的集群,在映射规则中您可以按命名空间或租户等分片请求(From Nacos官网)其他说明据Nacos官方宣称,Nacos v0.8.0 Pre-GA版本已经可以用于生产环境,在此之前的版本,请勿在生产上使用。参考资料部署手册集群部署说明推荐阅读Nacos系列:欢迎来到Nacos的世界!Nacos系列:基于Nacos的注册中心Nacos系列:基于Nacos的配置中心Nacos系列:Nacos的Java SDK使用 ...

February 23, 2019 · 2 min · jiezi

Nacos系列:Nacos的Java SDK使用

Maven依赖Nacos提供完整的Java SDK,便于配置管理和服务发现及管理,以 Nacos-0.8.0 版本为例添加Maven依赖:<dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>0.8.0</version></dependency>仅仅引入nacos-client是不够的,否则启动时会出现如下错误:sun.misc.Launcher$AppClassLoader@18b4aac2 JM.Log:WARN Init JM logger with NopLoggerFactory, pay attention. sun.misc.Launcher$AppClassLoader@18b4aac2java.lang.ClassNotFoundException: org.apache.logging.log4j.core.Logger at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:264) at com.alibaba.nacos.client.logger.log4j2.Log4j2LoggerFactory.<init>(Log4j2LoggerFactory.java:33) at com.alibaba.nacos.client.logger.LoggerFactory.<clinit>(LoggerFactory.java:59) at com.alibaba.nacos.client.config.utils.LogUtils.<clinit>(LogUtils.java:49) at com.alibaba.nacos.client.config.NacosConfigService.<clinit>(NacosConfigService.java:55) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:264) at com.alibaba.nacos.api.config.ConfigFactory.createConfigService(ConfigFactory.java:40) at com.alibaba.nacos.api.config.ConfigFactory.createConfigService(ConfigFactory.java:59) at com.alibaba.nacos.api.NacosFactory.createConfigService(NacosFactory.java:52) at com.learn.nacos.config.NacosConfig.main(NacosConfig.java:12)根据错误提示,应该还需要添加log4j相关依赖,官网的文档并没有对此说明,我在pom.xml添加了下面这些依赖才不报错<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.11</version></dependency><dependency> <groupId>org.logback-extensions</groupId> <artifactId>logback-ext-spring</artifactId> <version>0.1.4</version></dependency><dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.25</version></dependency><dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version></dependency><dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version></dependency>配置管理创建ConfigService,可以通过 NacosFactory.createConfigService() 或 ConfigFactory.createConfigService() 来创建,后者是前者的底层实现方式,这两种方式都包含如下两个方法:createConfigService(serverAddr)createConfigService(properties)创建示例:// 方式一String serverAddr = “127.0.0.1:8848”;ConfigService configService = ConfigFactory.createConfigService(serverAddr);// 方式二ConfigService configService = ConfigFactory.createConfigService(properties)Properties properties = new Properties();properties.put(“serverAddr”, serverAddr);查看ConfigService源码,它提供了如下方法:获取 Nacos Server 当前状态:String getServerStatus()底层源码:public String getServerStatus() { if (worker.isHealthServer()) { return “UP”; } else { return “DOWN”; }}根据源码注释,该状态应该是指 Nacos Server 的状态,我把 Nacos Server 关闭之后,再次运行示例,得到的结果仍然是UP,不知道这是不是一个BUG。发布配置:boolean publishConfig(String dataId, String group, String content) throws NacosException支持程序自动发布Nacos配置,创建和修改配置使用同一个方法,配置不存在则创建;配置已存在则更新。底层源码:try { result = agent.httpPost(url, headers, params, encode, POST_TIMEOUT);} catch (IOException ioe) { log.warn(“NACOS-0006”, LoggerHelper.getErrorCodeStr(“NACOS”, “NACOS-0006”, “环境问题”, “[publish-single] exception”)); log.warn(agent.getName(), “[publish-single] exception, dataId={}, group={}, msg={}”, dataId, group, ioe.toString()); return false;}发布配置后,如果马上用getConfig()读取配置,有时候会读不到,设置了足够的等待时长后才可保证每次正常读取,看了源码才知道Nacos的配置管理(发布、读取、移除)都是通过HTTP接口完成的,但发布配置的时延是多少,官网似乎没有说明?几秒钟的时延在一些对实时性要求很高的场景会不会存在影响呢?读取配置:String getConfig(String dataId, String group, long timeoutMs) throws NacosExceptiontimeoutMs指读取配置超时时间,官网推荐设置为3000ms底层源码:// 优先使用本地配置String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);if (content != null) { log.warn(agent.getName(), “[get-config] get failover ok, dataId={}, group={}, tenant={}, config={}”, dataId, group, tenant, ContentUtils.truncateContent(content)); cr.setContent(content); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); return content;}try { content = worker.getServerConfig(dataId, group, tenant, timeoutMs); cr.setContent(content); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); return content;} catch (NacosException ioe) { if (NacosException.NO_RIGHT == ioe.getErrCode()) { throw ioe; } log.warn(“NACOS-0003”, LoggerHelper.getErrorCodeStr(“NACOS”, “NACOS-0003”, “环境问题”, “get from server error”)); log.warn(agent.getName(), “[get-config] get from server error, dataId={}, group={}, tenant={}, msg={}”, dataId, group, tenant, ioe.toString());}从源码上看,配置会先从本地缓存文件读取,如果没读取到,才会去请求Nacos Server的配置,这个缓存文件在哪呢?就在当前用户的nacos目录下生成的缓存文件:nacos/config/fixed-127.0.0.1_8848_nacos/snapshot/DEFAULT_GROUP/nacos-sdk-java-config,配置内容和发布到Nacos Server的配置内容是一致的。移除配置:boolean removeConfig(String dataId, String group) throws NacosException支持程序自动发布Nacos配置,配置不存在时会直接返回成功,移除配置后,本地的缓存文件也会被删除底层源码:try { result = agent.httpDelete(url, null, params, encode, POST_TIMEOUT);} catch (IOException ioe) { log.warn("[remove] error, " + dataId + “, " + group + “, " + tenant + “, msg: " + ioe.toString()); return false;}移除配置同发布配置一样,如果移除后马上查询,有可能还能将刚移除的配置查出来,也存在一定的时延,需要设置等待时间读取。添加配置监听:void addListener(String dataId, String group, Listener listener) throws NacosException支持动态监听配置的变化,运行示例源码,在Nacos控制台把配置内容修改为sdk-java-config:change from nacos console,此时观看IDE控制台,你会看到如下打印信息:当前线程:com.alibaba.nacos.client.Worker.longPollingfixed-127.0.0.1_8848 ,监听到配置内容变化:sdk-java-config:change from nacos console移除配置监听:void removeListener(String dataId, String group, Listener listener)移除监听后,配置的变化不会再监听启动完整示例,运行结果如下,请注意配置监听线程和配置管理线程不是同一个线程当前线程:main ,服务状态:UP添加监听添加监听成功发布配置发布配置成功当前线程:com.alibaba.nacos.client.Worker.longPollingfixed-127.0.0.1_8848 ,监听到配置内容变化:nacos-sdk-java-config:init当前线程:main ,发布配置后获取配置内容:nacos-sdk-java-config:init重新发布配置重新发布配置成功当前线程:main ,重新发布配置后获取配置内容:sdk-java-config:update当前线程:com.alibaba.nacos.client.Worker.longPollingfixed-127.0.0.1_8848 ,监听到配置内容变化:sdk-java-config:update当前线程:com.alibaba.nacos.client.Worker.longPollingfixed-127.0.0.1_8848 ,监听到配置内容变化:sdk-java-config:change from nacos console移除配置移除配置成功当前线程:main ,移除配置后获取配置内容:null取消监听取消监听成功服务管理创建NamingService,可以通过 NacosFactory.createNamingService() 或 NamingFactory.createNamingService() 来创建,后者是前者的底层实现方式,这两种方式都包含如下两个方法:createNamingService(serverAddr)createNamingService(properties)创建示例:// 方式一String serverAddr = “127.0.0.1:8848”;NamingService namingService = NamingFactory.createNamingService(serverAddr);// 方式二NamingService namingService = NamingFactory.createNamingService(properties)Properties properties = new Properties();properties.put(“serverAddr”, serverAddr);查看NamingService类源码,它提供了如下方法:获取 Nacos Server 当前状态:String getServerStatus()注册服务实例:void registerInstance(多个参数)方式一:String serverIp = “127.0.0.1”;int serverPort = 8848;String serverAddr = serverIp + “:” + serverPort;String serviceName = “nacos-sdk-java-discovery”;NamingService namingService = NamingFactory.createNamingService(serverAddr);namingService.registerInstance(serviceName, serverIp, serverPort);方式二:Instance instance = new Instance();instance.setIp(serverIp);//IPinstance.setPort(serverPort);//端口instance.setServiceName(serviceName);//服务名instance.setEnabled(true);//true: 上线 false: 下线instance.setHealthy(healthy);//健康状态instance.setWeight(1.0);//权重instance.addMetadata(“nacos-sdk-java-discovery”, “true”);//元数据NamingService namingService = NamingFactory.createNamingService(serverAddr);namingService.registerInstance(serviceName, instance);注册后,本地会生成缓存文件1、在Nacos安装目录data目录下:data/naming/data/public/com.alibaba.nacos.naming.domains.meta.public##nacos-sdk-java-discovery2、当前用户的nacos目录下:/nacos/naming/public/failover/nacos-sdk-java-discovery3、当前用户的nacos目录下:/nacos/naming/public/nacos-sdk-java-discovery即使删除服务实例,上面三个缓存文件也不会被删除,Nacos控制台服务列表中该服务也还存在着(但服务实例数会变成0);删除Nacos控制台的该服务,安全目录data目录下的缓存文件会被删除,但当前用户的nacos目录下的文件不会被删除,这里面是什么机制,我暂时还没整明白,等后面整明白了再来补充。删除服务实例:void deregisterInstance(多个参数)获取所有服务实例:List<Instance> getAllInstances(多个参数)获取所有健康或不健康的服务实例:List<Instance> selectInstances(多个参数)随机获取一个健康实例(根据负载均衡算法):Instance selectOneHealthyInstance(多个参数)添加服务实例监听:void subscribe(多个参数)添加服务实例监听:void unsubscribe(多个参数)分页获取所有服务实例:ListView<String> getServicesOfServer(多个参数)获取所有监听的服务实例:List<ServiceInfo> getSubscribeServices()启动完整示例,运行结果如下,请注意服务实例监听线程和服务实例管理线程不是同一个线程当前线程:main ,服务状态:UP注册实例注册实例成功添加监听添加监听成功当前线程:main ,注册实例后获取所有实例:[{“clusterName”:“DEFAULT”,“enabled”:true,“instanceId”:“127.0.0.1#8848#DEFAULT#nacos-sdk-java-discovery”,“ip”:“127.0.0.1”,“metadata”:{},“port”:8848,“serviceName”:“nacos-sdk-java-discovery”,“valid”:true,“weight”:1.0}]当前线程:main ,注册实例后获取所有健康实例:[{“clusterName”:“DEFAULT”,“enabled”:true,“instanceId”:“127.0.0.1#8848#DEFAULT#nacos-sdk-java-discovery”,“ip”:“127.0.0.1”,“metadata”:{},“port”:8848,“serviceName”:“nacos-sdk-java-discovery”,“valid”:true,“weight”:1.0}]当前线程:com.alibaba.nacos.naming.client.listener ,监听到实例名称:nacos-sdk-java-discovery当前线程:com.alibaba.nacos.naming.client.listener ,监听到实例内容:[{“clusterName”:“DEFAULT”,“enabled”:true,“instanceId”:“127.0.0.1#8848#DEFAULT#nacos-sdk-java-discovery”,“ip”:“127.0.0.1”,“metadata”:{},“port”:8848,“serviceName”:“nacos-sdk-java-discovery”,“valid”:true,“weight”:1.0}]当前线程:main ,注册实例后获取一个健康实例:{“clusterName”:“DEFAULT”,“enabled”:true,“instanceId”:“127.0.0.1#8848#DEFAULT#nacos-sdk-java-discovery”,“ip”:“127.0.0.1”,“metadata”:{},“port”:8848,“serviceName”:“nacos-sdk-java-discovery”,“valid”:true,“weight”:1.0}当前线程:com.alibaba.nacos.naming.client.listener ,监听到实例名称:nacos-sdk-java-discovery当前线程:com.alibaba.nacos.naming.client.listener ,监听到实例内容:[{“clusterName”:“DEFAULT”,“enabled”:true,“instanceId”:“127.0.0.1#8848#DEFAULT#nacos-sdk-java-discovery”,“ip”:“127.0.0.1”,“metadata”:{“change”:“true;”},“port”:8848,“serviceName”:“nacos-sdk-java-discovery”,“valid”:true,“weight”:2.0}]取消监听取消监听成功删除实例删除实例成功Exception in thread “main” java.lang.IllegalStateException: no host to srv for serviceInfo: nacos-sdk-java-discovery at com.alibaba.nacos.client.naming.core.Balancer$RandomByWeight.selectAll(Balancer.java:45) at com.alibaba.nacos.client.naming.core.Balancer$RandomByWeight.selectHost(Balancer.java:53) at com.alibaba.nacos.client.naming.NacosNamingService.selectOneHealthyInstance(NacosNamingService.java:270) at com.alibaba.nacos.client.naming.NacosNamingService.selectOneHealthyInstance(NacosNamingService.java:263) at com.alibaba.nacos.client.naming.NacosNamingService.selectOneHealthyInstance(NacosNamingService.java:253) at com.learn.nacos.discovery.NacosDiscovery.main(NacosDiscovery.java:121)当前线程:main ,删除实例后获取所有实例:[]当前线程:main ,删除实例后获取所有健康实例:[]以上就是 Nacos Java SDK 配置管理和服务管理功能的介绍,请参考示例源码学习。示例源码项目:learn-nacos-sdk-java代码已上传至码云和Github上,欢迎下载学习GiteeGithub参考资料Nacos用户指南:Java的SDK推荐阅读Nacos系列:欢迎来到Nacos的世界!Nacos系列:基于Nacos的注册中心Nacos系列:基于Nacos的配置中心 ...

February 22, 2019 · 2 min · jiezi

Nacos系列:基于Nacos的配置中心

前言在看正文之前,我想请你回顾一下自己待过的公司都是怎么管理配置的,我想应该会有以下几种方式:1、硬编码没有什么配置不配置的,直接写在代码里面,比如使用常量类优势:对开发友好,开发清楚地知道代码需要用到什么配置劣势:涉及秘钥等敏感配置直接暴露给开发人员,不安全;如果想修改配置必须重新发版,比较麻烦2、外部化配置文件Spring项目经常会在resoures目录下放很多配置文件,各个环境对应不同的配置文件,通过SVN管理优势:配置文件外部化,支持多环境配置管理,修改配置只需重启服务,无需发版劣势:系统庞大时,配置文件很多,多人开发,配置格式不统一,维护麻烦;敏感配置不需要暴露给开发人员,降低风险,但开发经常要和运维沟通怎么修改配置,沟通不恰当容易引发生产事故;而且,如果应用部署在多台机器,对运维来说,修改配置也是非常头疼的事情(当然也可以引入NFS系统来解决一部分问题)3、数据库配置信息存储在数据库中,灵活修改优势:可以灵活管理配置,无需重启服务劣势:界面不友好,配置没有版本管理,一旦出现问题,回滚或定位问题都比较麻烦;此外,数据库必须要保证高可用,避免因此而造成生产故障4、配置中心微服务基础架构体系中的一个不可或缺的基础组件优势:集中化管理,敏感配置可控;多版本存储,方便追溯;界面友好,修改配置一键发布;即使面对多集群也能从容应对,十分淡定劣势:引入组件,增加系统风险;如果是中途切换成配置中心,也会增加研发接入成本;配置中心也需要保证高可用,否则容易造成大面积影响以上几种管理配置文件的方式,我想都会有公司在用,不要因为配置中心有诸多优点,就盲目引进项目中,我觉得应该遵守以下两个原则:做人做事,要知道自己几斤几两释义:没深入研究过的技术,就不要随便拿到公司项目中来试水啦,恐怕到时候坑够你填的,要不然就是你有信心玩得转它。杀只鸡而已,你拿牛刀来做甚?释义:小团队小项目选择简单的配置管理方式就好了,要什么配置中心,纯属没事找事。总而言之,我们必须从实际出发,实事求是,选择适合自己的技术栈。关于为什么需要有配置中心,我推荐一篇文章给你看,讲得比较透彻:《微服务架构为什么需要配置中心?》另外,我觉得对开发本身来说,是宁愿自己管理自己代码的配置的,交给运维总是会有各种各样的问题,至于敏感配置,说实话,开发人员要真想做点“坏事”,那拦得住吗?但是,从公司的角度来讲,把服务器的配置管理交给运维同事是符合常理的,系统需要稳定且安全地运行,这是对客户的负责,从这一方面去思考,这么做是合情合理的。Okay,我就啰嗦到这里吧,下面正式介绍Nacos作为配置中心是怎么使用的。Nacos 结合 Spring添加 maven 依赖:<dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-spring-context</artifactId> <version>${nacos-spring-context.version}</version></dependency>使用 @EnableNacosConfig 开启 Nacos Spring 的配置管理功能@Configuration@EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = “127.0.0.1:8848”))@NacosPropertySource(dataId = “nacos.spring.config”, autoRefreshed = true)public class NacosConfig {}其中:@Configuration:Spring的注解,配置应用上下文@EnableNacosConfig:Nacos的注册,启用 Nacos Spring 的配置管理服务@NacosProperties:全局和自定义Nacos属性的统一注解@NacosPropertySource:加载数据源globalProperties:全局 Nacos 属性serverAddr:Nacos Server服务器地址dataId:配置的数据集IDautoRefreshed:是否开启配置动态更新再写一个Controller类,来验证Nacos的配置管理功能,代码如下:package com.learn.nacos;import com.alibaba.nacos.api.annotation.NacosInjected;import com.alibaba.nacos.api.config.ConfigService;import com.alibaba.nacos.api.config.annotation.NacosValue;import com.alibaba.nacos.api.exception.NacosException;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;@Controller@RequestMapping(value = “config”)public class NacosConfigController { @NacosInjected private ConfigService configService; @NacosValue(value = “${useLocalCache:false}”, autoRefreshed = true) private boolean useLocalCache; @RequestMapping(value = “/get”, method = RequestMethod.GET) @ResponseBody public boolean get() { return useLocalCache; } @RequestMapping(method = RequestMethod.GET) @ResponseBody public ResponseEntity<String> publish(@RequestParam String dataId, @RequestParam(defaultValue = “DEFAULT_GROUP”) String group, @RequestParam String content) throws NacosException { boolean result = configService.publishConfig(dataId, group, content); if (result) { return new ResponseEntity<String>(“Success”, HttpStatus.OK); } return new ResponseEntity<String>(“Fail”, HttpStatus.INTERNAL_SERVER_ERROR); }}该Controller类提供了两个HTTP接口读取配置:http://127.0.0.1:8080/config/get发布配置:http://127.0.0.1:8080/config?dataId=XXX&content=XXX发布配置还可以通过 Nacos Open API:curl -X POST “http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=XXX&group=XXX&content=XXX 发布配置,你也可以用Postman工具模拟POST请求进行配置发布,我这里主要是为了方便验证问题,采用了这种方式。在验证之前,请先确保 Nacos Server 已经启动,Nacos Server 的安全及启动方式详见:《Nacos系列:欢迎来到Nacos的世界!》启动Tomcat,观察Console控制台20:50:13.646 [RMI TCP Connection(5)-127.0.0.1] WARN com.alibaba.nacos.spring.core.env.AnnotationNacosPropertySourceBuilder - There is no content for NacosPropertySource from dataId[nacos.spring.config] , groupId[DEFAULT_GROUP] , properties[{encode=${nacos.encode:UTF-8}, namespace=${nacos.namespace:}, contextPath=${nacos.context-path:}, endpoint=${nacos.endpoint:}, serverAddr=${nacos.server-addr:}, secretKey=${nacos.secret-key:}, accessKey=${nacos.access-key:}, clusterName=${nacos.cluster-name:}}].20:50:17.825 [RMI TCP Connection(5)-127.0.0.1] INFO com.alibaba.nacos.spring.context.event.LoggingNacosConfigMetadataEventListener - Nacos Config Metadata : dataId=‘nacos.spring.config’, groupId=‘DEFAULT_GROUP’, beanName=‘nacosConfig’, bean=‘null’, beanType=‘class com.learn.nacos.NacosConfig’, annotatedElement=‘null’, xmlResource=‘null’, nacosProperties=’{serverAddr=127.0.0.1:8848, encode=UTF-8}’, nacosPropertiesAttributes=’{encode=${nacos.encode:UTF-8}, namespace=${nacos.namespace:}, contextPath=${nacos.context-path:}, endpoint=${nacos.endpoint:}, serverAddr=${nacos.server-addr:}, secretKey=${nacos.secret-key:}, accessKey=${nacos.access-key:}, clusterName=${nacos.cluster-name:}}’, source=‘org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor@66e4d430’, timestamp=‘1550753413647’我们先通过http://127.0.0.1:8080/config?dataId=nacos.spring.config&content=useLocalCache=true发布一个dataId为nacos.spring.config且配置内容为useLocalCache=true的配置集,观察Nacos控制台的变化再通过http://127.0.0.1:8080/config/get读取配置然后在Nacos控制台将useLocalCache的值改为false,并发布配置再次访问http://127.0.0.1:8080/config/getNacos 结合 Spring Boot添加 Starter 依赖:<dependency> <groupId>com.alibaba.boot</groupId> <artifactId>nacos-config-spring-boot-starter</artifactId> <version>0.2.1</version></dependency>注意:版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。在application.properties中添加如下配置信息:nacos.config.server-addr=127.0.0.1:8848添加NacosConfigApplication启动类@SpringBootApplication@NacosPropertySource(dataId = “nacos.springboot.config”, autoRefreshed = true)public class NacosConfigApplication { public static void main(String[] args) { SpringApplication.run(NacosConfigApplication.class, args); }}如果你看过我的上一篇文章:《Nacos系列:基于Nacos的注册中心》,那么你应该知道 Spring Boot 实现方式和 Spring 的没太大差别,所以我就不再细说了,请参考我的源码示例或者官网资料学习。这里说下我在学习过程中遇到的一个问题,在application.properties添加配置文件的时候,不小心将nacos.config.server-addr写成了nacos.discovery.server-addr,结果启动项目时,一直报错:ERROR 9028 — [ main] o.s.b.d.LoggingFailureAnalysisReporter : ——APPLICATION FAILED TO START——Description:client error: invalid param. nullAction:please check your client configuration刚开始一直找不到原因,后面跟着官网代码示例复核,才发现是配置问题导致的,呵呵哒,自己给自己挖坑。后语我挺喜欢Nacos的,既然做服务发现和管理,又能做配置管理,这两者本质没多大区别,Nacos把这两者统一起来,一举两得,我觉得没什么不好,要不然你引入了Zookeeper作为注册中心,还要引入Apollo作为配置中心,无端增加学习成本。就像之前听音乐,我一般用网易云音乐就好,后面因为搞了版权的事,不得不下载了虾米和QQ音乐,我就听个歌而已,手机里装了三个APP,你说,这叫什么事儿?示例源码Nacos + Spring :learn-nacos-spring-configNacos + Spring Boot : learn-nacos-springboot-config代码已上传至码云和Github上,欢迎下载学习GiteeGithub参考资料微服务架构为什么需要配置中心?Nacos Spring 快速开始Nacos Spring Boot 快速开始SpringBoot使用Nacos配置中心Spring Cloud Alibaba基础教程:使用Nacos作为配置中心 ...

February 21, 2019 · 2 min · jiezi

Spring Cloud Alibaba基础教程:Nacos的集群部署

前情回顾:《Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现》《Spring Cloud Alibaba基础教程:支持的几种服务消费方式》《Spring Cloud Alibaba基础教程:使用Nacos作为配置中心》《Spring Cloud Alibaba基础教程:Nacos配置的加载规则详解》《Spring Cloud Alibaba基础教程:Nacos配置的多环境管理》《Spring Cloud Alibaba基础教程:Nacos配置的多文件加载与共享配置》《Spring Cloud Alibaba基础教程:Nacos的数据持久化》继续说说生产环境的Nacos搭建,通过上一篇《Nacos的数据持久化》的介绍,我们已经知道Nacos对配置信息的存储原理,在集群搭建的时候,必须要使用集中化存储,比如:MySQL存储。下面顺着上一篇的内容,继续下一去。通过本文,我们将完成Nacos生产环境的搭建。集群搭建根据官方文档的介绍,Nacos的集群架构大致如下图所示(省略了集中化存储信息的MySQL):下面我们就来一步步的介绍,我们每一步的搭建细节。MySQL数据源配置对于数据源的修改,在上一篇《Nacos的数据持久》中已经说明缘由,如果还不了解的话,可以先读一下这篇再回来看这里。在进行集群配置之前,先完成对MySQL数据源的初始化和配置。主要分以下两步:第一步:初始化MySQL数据库,数据库初始化文件:nacos-mysql.sql,该文件可以在Nacos程序包下的conf目录下获得。第二步:修改conf/application.properties文件,增加支持MySQL数据源配置,添加(目前只支持mysql)数据源的url、用户名和密码。配置样例如下:spring.datasource.platform=mysqldb.num=1db.url.0=jdbc:mysql://localhost:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=truedb.user=rootdb.password=更多介绍与思考,可见查看上一篇《Nacos的数据持久化》。集群配置在Nacos的conf目录下有一个cluster.conf.example,可以直接把example扩展名去掉来使用,也可以单独创建一个cluster.conf文件,然后打开将后续要部署的Nacos实例地址配置在这里。本文以在本地不同端点启动3个Nacos服务端为例,可以如下配置:127.0.0.1:8841127.0.0.1:8842127.0.0.1:8843注意:这里的例子仅用于本地学习测试使用,实际生产环境必须部署在不同的节点上,才能起到高可用的效果。另外,Nacos的集群需要3个或3个以上的节点,并且确保这三个节点之间是可以互相访问的。启动实例在完成了上面的配置之后,我们就可以开始在各个节点上启动Nacos实例,以组建Nacos集群来使用了。由于本文中我们测试学习采用了本地启动多实例的情况,与真正生产部署会有一些差异,所以下面分两种情况说一下,如何启动各个Nacos实例。本地测试本文中,在集群配置的时候,我们设定了3个Nacos的实例都在本地,只是以不同的端口区分,所以我们在启动Nacos的时候,需要修改不同的端口号。下面介绍一种方法来方便地启动Nacos的三个本地实例,我们可以将bin目录下的startup.sh脚本复制三份,分别用来启动三个不同端口的Nacos实例,为了可以方便区分不同实例的启动脚本,我们可以把端口号加入到脚本的命名中,比如:startup-8841.shstartup-8842.shstartup-8843.sh然后,分别修改这三个脚本中的参数,具体如下图的红色部分(端口号根据上面脚本命名分配):这里我们通过-Dserver.port的方式,在启动命令中,为Nacos指定具体的端口号,以实现在本机上启动三个不同的Nacos实例来组成集群。修改完3个脚本配置之后,分别执行下面的命令就可以在本地启动Nacos集群了:sh startup-8841.shsh startup-8842.shsh startup-8843.sh生产环境在实际生产环境部署的时候,由于每个实例分布在不同的节点上,我们可以直接使用默认的启动脚本(除非要调整一些JVM参数等才需要修改)。只需要在各个节点的Nacos的bin目录下执行sh startup.sh命令即可。Proxy配置在Nacos的集群启动完毕之后,根据架构图所示,我们还需要提供一个统一的入口给我们用来维护以及给Spring Cloud应用访问。简单地说,就是我们需要为上面启动的的三个Nacos实例做一个可以为它们实现负载均衡的访问点。这个实现的方式非常多,这里就举个用Nginx来实现的简单例子吧。在Nginx配置文件的http段中,我们可以加入下面的配置内容:这样,当我们访问:http://localhost:8080/nacos/的时候,就会被负载均衡的代理到之前我们启动的三个Nacos实例上了。这里我们没有配置upstream的具体策略,默认会使用线性轮训的方式,如果有需要,也可以配置上更为复杂的分发策略。这部分是Nginx的使用内容,这里就不作具体介绍了。这里提一下我在尝试搭建时候碰到的一个问题,如果您也遇到了,希望下面的说明可以帮您解决问题。错误信息如下:2019-02-20 16:20:53,216 INFO The host [nacos_server] is not valid Note: further occurrences of request parsing errors will be logged at DEBUG level.java.lang.IllegalArgumentException: The character [_] is never valid in a domain name. at org.apache.tomcat.util.http.parser.HttpParser$DomainParseState.next(HttpParser.java:926) at org.apache.tomcat.util.http.parser.HttpParser.readHostDomainName(HttpParser.java:822) at org.apache.tomcat.util.http.parser.Host.parse(Host.java:71) at org.apache.tomcat.util.http.parser.Host.parse(Host.java:45) at org.apache.coyote.AbstractProcessor.parseHost(AbstractProcessor.java:288) at org.apache.coyote.http11.Http11Processor.prepareRequest(Http11Processor.java:809) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:384) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:791) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748)主要原因是,一开始在配置upstream的时候,用了nacos_server作为名称,而在Nacos使用的Tomcat版本中不支持_符号出现在域名位置,所以上面截图给出的upstream的名称是nacosserver,去掉了_符号。到这里,Nacos的集群搭建就完成了!我们可以通过Nginx配置的代理地址:http://localhost:8080/nacos/来访问Nacos,在Spring Cloud应用中也可以用这个地址来作为注册中心和配置中心的访问地址来配置。读者可以使用文末的代码示例来修改原来的Nacos地址来启动,看是否可以获取配置信息来验证集群的搭建是否成功。也可以故意的关闭某个实例,来验证Nacos集群是否还能正常服务。深入思考在Nacos官方文档的指引下,Nacos的集群搭建总体上还是非常顺畅的,没有什么太大的难度。但是值得思考的一个问题跟在上一篇中讲数据持久化的思考类似,作为一个注册中心和配置中心,Nacos的架构是否显得太过于臃肿?除了Nacos自身之外,还需要依赖更多的中间件来完成整套生产环境的搭建,相较于其他的可以用于服务发现与配置的中间件来说,就不那么有优势了。尤其对于小团队来说,这样的复杂度与成本投入,也是在选型的时候需要去考虑的。代码示例本文介绍内容的客户端代码,示例读者可以通过查看下面仓库中的alibaba-nacos-config-client项目:Github:https://github.com/dyc87112/SpringCloud-Learning/Gitee:https://gitee.com/didispace/SpringCloud-Learning/如果您对这些感兴趣,欢迎star、follow、收藏、转发给予支持!以下专题教程也许您会有兴趣Spring Boot基础教程【新版】Spring Cloud从入门到精通 ...

February 21, 2019 · 1 min · jiezi

Nacos系列:基于Nacos的注册中心

前言所谓注册中心,其实是分布式架构演进过程中的产物,在系统中充当一个协调者的角色。但是,为什么需要这样一个协调者的角色呢?我们先来看一个例子,以便理解为什么分布式架构中需要有注册中心。案例小明和小新住在同一家沃尔玛超市附近,他俩都办了会员,经常关注超市的一些优惠活动,元宵节快到了,沃尔玛准备搞一个元宵节特惠活动,需要通知到附近的住户。对于沃尔玛来说,可以安排工作人员电话通知到小明和小新;而对于小明和小新来说,可以去超市咨询相关信息。那么问题来了,住在超市附近的,不只有小明和小新两个消费者,如果每个人都打电话去通知就显得太麻烦了,小明和小新提前在超市了解了相关信息,可是不巧的是,由于各种原因,沃尔玛元宵特惠活动要从上午改到下午才开始,他们又该从何得知呢?其实,沃尔玛关心的是通知能不能传达到附近的住户,小明和小新关心的是沃尔玛优惠活动的详情动态。沃尔玛不必给每个住户挨个电话通知,它只需要在它的微信公众号上推送一条消息即可,小明和小新也不用去超市咨询,只要随时关注沃尔玛公众号的推送消息即可。在上面这个例子中,沃尔玛就是服务提供者,小明和小新是服务消费者,微信公众号类似于注册中心,通过微信公众号,沃尔玛(服务方)和小明、小新(消费方)就“解耦”了。用这个例子来解释注册中心未必恰当,毕竟系统中的服务既可以是服务提供者(Provider),也可以是服务消费者(Consumer),但我想的是以一种更加通俗的方式来解释它,技术日新月异,各种技术、术语层出不穷,容易让人头晕眼花,但万变不离其宗,技术源于现实世界,亦服务于现实世界,在现实世界中,我们思考如何解决问题,技术也必然以同样的思路去解决问题。关于注册中心,更技术层面的解释,大家可以看一下这篇文章:《服务注册中心架构演进》在现有的技术方案中,注册中心主要分为两类,一类是CP类注册中心,另一类是AP类注册中心,Nacos属于后者,为什么会有 CP 和 AP 两种不同类型的注册中心呢?这就不得不提到分布式的一个理论:CAP理论。它是由加州大学的计算机科学家 Eric Brewer 提出,在一个分布式系统中,Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性)无法同时满足,正所谓“鱼和熊掌与虾不可兼得也”。CP类注册中心更强调一致性,而AP类注册中心更强调可用性,它们之间的区别,推荐阅读阿里中间件博客的文章:《阿里巴巴为什么不用 ZooKeeper 做服务发现?》, 这篇文章我看了好几遍,虽然不能完全理解,但也能明白十之八九。如果你看完文章后,得到的结论是以后再也不把 Dubbo 和 Zookeeper 结合起来使用了,那么你便错了。因为,对于绝大多数公司的绝大多数系统,无论是 Dubbo + Zookeeper,还是 Dubbo + Nacos,都能够满足需求,有的公司甚至都不需要使用Dubbo,所以,一定要结合实际的业务场景来分析判断。不过,我们作为技术开发人员,了解技术原理是很重要的,唯有了解其底层逻辑,才知道如何做技术选型以及解决疑难杂症。好了,让我们回到Nacos本身,下面将从代码层面分别介绍 Nacos + Spring 和 Nacos + Spring Boot 的使用,我的案例都是基于 Nacos 官网的示例(毕竟官网是最好的学习资料嘛)。Nacos 结合 Spring先来看 Nacos + Spring 的使用:添加 maven 依赖:<dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-spring-context</artifactId> <version>0.2.2-RC1</version></dependency>使用 @EnableNacosDiscovery 开启 Nacos Spring 的服务发现功能@Configuration@EnableNacosDiscovery(globalProperties = @NacosProperties(serverAddr = “127.0.0.1:8848”))public class NacosDiscovery {}使用 @NacosInjected 注入 Nacos 的NamingService实例,通过NamingService的registerInstance() 向 Nacos Server 注册一个名称为applicationName的服务,当然,你也可以通过 Nacos Open API 方式注册:curl -X PUT ‘http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=XXX&ip=XXX&port=XXX’,这里我们介绍使用代码的方式。@Configuration@EnableNacosDiscovery(globalProperties = @NacosProperties(serverAddr = “127.0.0.1:8848”))public class NacosDiscovery { @NacosInjected private NamingService namingService; @Value("${server.port}") private int serverPort; @Value("${spring.application.name}") private String applicationName; @PostConstruct public void registerInstance() throws NacosException { namingService.registerInstance(applicationName, “127.0.0.1”, serverPort); }}再写一个Controller来验证服务是否再 Nacos Server 上注册了,代码如下:@RestController@RequestMapping(value = “discovery”)public class NacosDiscoveryController { @NacosInjected private NamingService namingService; @RequestMapping(value = “/get”, method = GET) @ResponseBody public List<Instance> getInstance(@RequestParam String serviceName) throws NacosException { return namingService.getAllInstances(serviceName); }}启动 Nacos Server,安装及启动方式请参考:《欢迎来到Nacos的世界!》然后启动Tomcat,我们先来看看Nacos控制台有什么变化在控制台上,我们可以看到名为nacos-spring-discovery服务实例,点击详情按钮查看实例的详细信息:在浏览器上访问:http://127.0.0.1:8080/discovery/get?serviceName=nacos-spring-discovery,返回结果如下:[{ “instanceId”: “127.0.0.1#8080#{"defaultCheckPort":80,"defaultPort":80,"healthChecker":{"type":"TCP"},"metadata":{},"name":"","useIPPort4Check":true}#nacos-spring-discovery”, “ip”: “127.0.0.1”, “port”: 8080, “weight”: 1.0, “healthy”: true, “cluster”: { “serviceName”: null, “name”: “”, “healthChecker”: { “type”: “TCP” }, “defaultPort”: 80, “defaultCheckPort”: 80, “useIPPort4Check”: true, “metadata”: {} }, “service”: null, “metadata”: {}}]和我们刚才在控制台看到的数据是一致的。以上就是 Nacos 结合 Spring 的实现方式,那么 Nacos 结合 Spring Boot 呢?其实没什么太大区别。Nacos 结合 Spring Boot添加 Starter 依赖:<dependency> <groupId>com.alibaba.boot</groupId> <artifactId>nacos-discovery-spring-boot-starter</artifactId> <version>0.2.1</version></dependency>注意:版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。在application.properties中添加如下配置信息:server.port=8080spring.application.name=nacos-springboot-discoverynacos.discovery.server-addr=127.0.0.1:8848添加NacosDiscoveryApplication启动类,使用@NacosInjected注入 Nacos 的 NamingService实例,通过NamingService的registerInstance()向 Nacos Server 注册一个名称为applicationName的服务:@SpringBootApplicationpublic class NacosDiscoveryApplication { @NacosInjected private NamingService namingService; @Value("${server.port}") private int serverPort; @Value("${spring.application.name}") private String applicationName; @PostConstruct public void registerInstance() throws NacosException { namingService.registerInstance(applicationName, “127.0.0.1”, serverPort); } public static void main(String[] args) { SpringApplication.run(NacosDiscoveryApplication.class, args); }}添加NacosDiscoveryController类:@RestController@RequestMapping(value = “discovery”)public class NacosDiscoveryController { @NacosInjected private NamingService namingService; @RequestMapping(value = “/get”, method = GET) @ResponseBody public List<Instance> getInstance(@RequestParam String serviceName) throws NacosException { return namingService.getAllInstances(serviceName); }}启动NacosDiscoveryApplication,观察Nacos控制台在浏览器上访问:http://127.0.0.1:8080/discovery/get?serviceName=nacos-springboot-discovery,返回结果如下:[{ “instanceId”: “127.0.0.1#8080#{"defaultCheckPort":80,"defaultPort":80,"healthChecker":{"type":"TCP"},"metadata":{},"name":"","useIPPort4Check":true}#nacos-springboot-discovery”, “ip”: “127.0.0.1”, “port”: 8080, “weight”: 1.0, “healthy”: true, “cluster”: { “serviceName”: null, “name”: “”, “healthChecker”: { “type”: “TCP” }, “defaultPort”: 80, “defaultCheckPort”: 80, “useIPPort4Check”: true, “metadata”: {} }, “service”: null, “metadata”: {}}]以上两个示例的源码请从这里下载learn-nacos-spring-discoverylearn-nacos-springboot-discovery好了,关于 Nacos 作为注册中心的话题先聊到这里,下一期将介绍 Nacos 作为配置中心的使用,敬请期待!参考资料1、服务注册中心架构演进2、阿里巴巴为什么不用 ZooKeeper 做服务发现?3、Nacos Spring 快速开始4、Nacos Spring Boot 快速开始5、SpringBoot使用Nacos服务发现6、Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现 ...

February 18, 2019 · 2 min · jiezi

Spring Cloud Alibaba基础教程:Nacos的数据持久化

前情回顾:《Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现》《Spring Cloud Alibaba基础教程:支持的几种服务消费方式》《Spring Cloud Alibaba基础教程:使用Nacos作为配置中心》《Spring Cloud Alibaba基础教程:Nacos配置的加载规则详解》《Spring Cloud Alibaba基础教程:Nacos配置的多环境管理》《Spring Cloud Alibaba基础教程:Nacos配置的多文件加载与共享配置》通过之前几篇关于Nacos的博文,对于Nacos分别作为服务注册中心以及配置中心时,与Spring Cloud体系结合的基础使用方法已经介绍完毕了。下面我们再用几篇博文从生产部署的角度,介绍Nacos的相关内容。本文我们将具体说说Nacos的数据存储以及生产配置的推荐。数据持久化在之前的教程中,我们对于Nacos服务端自身并没有做过什么特殊的配置,一切均以默认的单机模式运行,完成了上述所有功能的学习。但是,Nacos的单机运行模式仅适用于学习与测试环境,对于有高可用要求的生产环境显然是不合适的。那么,我们是否可以直接启动多个单机模式的Nacos,然后客户端指定多个Nacos节点就可以实现高可用吗?答案是否定的。在搭建Nacos集群之前,我们需要先修改Nacos的数据持久化配置为MySQL存储。默认情况下,Nacos使用嵌入式数据库实现数据的存储。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只要支持MySQL的存储。配置Nacos的MySQL存储只需要下面三步:第一步:安装数据库,版本要求:5.6.5+第二步:初始化MySQL数据库,数据库初始化文件:nacos-mysql.sql,该文件可以在Nacos程序包下的conf目录下获得。执行完成后可以得到如下图所示的表结构:第三步:修改conf/application.properties文件,增加支持MySQL数据源配置,添加(目前只支持mysql)数据源的url、用户名和密码。配置样例如下:spring.datasource.platform=mysqldb.num=1db.url.0=jdbc:mysql://localhost:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=truedb.user=rootdb.password=到这里,Nacos数据存储到MySQL的配置就完成了,可以尝试继续用单机模式启动Nacos。然后再根据之前学习的Nacos配置中心的用法来做一些操作,配合MySQL工具就可以看到数据已经写入到数据库中了。下一篇,我们将继续深入思考关于Nacos数据的持久化实现,与其他的中间件相比,在实现上并没有采用分布式算法来解决一致性问题,而是采用了比较常规的集中化存储来实现。由于采用单一数据源的方式,直接解决了分布式一致性问题,所以从学习成本的角度上来说,Nacos的实现原理会更容易被理解和接受。但是,从部署的负责度和硬件投入成本上来说,与etcd、consul、zookeeper这些通过算法方式解决一致性问题的中间件相比,就显得不足了。同时,在引入MySQL的存储时,由于多了一个中间件的存在,整个Nacos系统的整体可用性一定是会所有下降的。所以为了弥补可用性的下降,在生产上MySQL的高可用部署也是必须的,成本再次提高。不论如何提高,可用性都难以达到100%,所以这种方式,不论如何提升存储的可用性,理论上都会对Nacos集群的自身可用性造成微小的下降。以上思考主要从理论上,粗略讨论的,并没有经过详细的成本评估与可用性计算。所以,对于实际应用场景下,可能这些成本的增加和可用性的降低并没有那么多大的影响。同时,Spring Cloud Alibaba下使用的各开源组件都有对应的商业产品,在没有足够运维人力的团队下,使用对应的商业产品可能从各方面都会更加划算。参考资料Nacos官方文档代码示例本文介绍内容的客户端代码,示例读者可以通过查看下面仓库中的alibaba-nacos-config-client项目:Github:https://github.com/dyc87112/SpringCloud-Learning/Gitee:https://gitee.com/didispace/SpringCloud-Learning/如果您对这些感兴趣,欢迎star、follow、收藏、转发给予支持!以下专题教程也许您会有兴趣Spring Boot基础教程【新版】Spring Cloud从入门到精通

February 18, 2019 · 1 min · jiezi

Spring Cloud Alibaba基础教程:Nacos配置的多环境管理

前情回顾:《Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现》《Spring Cloud Alibaba基础教程:支持的几种服务消费方式》《Spring Cloud Alibaba基础教程:使用Nacos作为配置中心》《Spring Cloud Alibaba基础教程:Nacos配置的加载规则详解》通过之前两篇对Nacos配置管理功能的介绍,已经学会了在Nacos中如何加入配置以及Spring Cloud应用如何通过配置来加载到对应的内容。接下来,我们讨论一个在使用配置中心时,都需要关注的一个问题:多环境的配置如何实现与管理?多环境管理在Nacos中,本身有多个不同管理级别的概念,包括:Data ID、Group、Namespace。只要利用好这些层级概念的关系,就可以根据自己的需要来实现多环境的管理。下面,我就来介绍一下,可以使用的几种实现方式:使用Data ID与profiles实现Data ID在Nacos中,我们可以理解为就是一个Spring Cloud应用的配置文件名。通过上一篇《Spring Cloud Alibaba基础教程:Nacos配置的加载规则详解》,我们知道默认情况下Data ID的名称格式是这样的:${spring.application.name}.properties,即:以Spring Cloud应用命名的properties文件。实际上,Data ID的规则中,还包含了环境逻辑,这一点与Spring Cloud Config的设计类似。我们在应用启动时,可以通过spring.profiles.active来指定具体的环境名称,此时客户端就会把要获取配置的Data ID组织为:${spring.application.name}-${spring.profiles.active}.properties。实际上,更原始且最通用的匹配规则,是这样的:${spring.cloud.nacos.config.prefix}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}。而上面的结果是因为${spring.cloud.nacos.config.prefix}和${spring.cloud.nacos.config.file-extension}都使用了默认值。动手试一试我们可以用《Spring Cloud Alibaba基础教程:使用Nacos作为配置中心》一文中的列子(可在文末仓库中获取)为基础,体验一下这种区分环境的配置方式。第一步:先在Nacos中,根据这个规则,创建两个不同环境的配置内容。比如:如上图,我们为alibaba-nacos-config-client应用,定义了DEV和TEST的两个独立的环境配置。我们可以在里面定义不同的内容值,以便后续验证是否真实加载到了正确的配置。第二步:在alibaba-nacos-config-client应用的配置文件中,增加环境配置:spring.profiles.active=DEV第三步:启动应用,我们可以看到日志中打印了,加载的配置文件:2019-01-30 15:25:18.216 INFO 96958 — [ main] o.s.c.a.n.c.NacosPropertySourceBuilder : Loading nacos data, dataId: ‘alibaba-nacos-config-client-DEV.properties’, group: ‘DEFAULT_GROUP’使用Group实现Group在Nacos中是用来对Data ID做集合管理的重要概念。所以,如果我们把一个环境的配置视为一个集合,那么也就可以实现不同环境的配置管理。对于Group的用法并没有固定的规定,所以我们在实际使用的时候,需要根据我们的具体需求,可以是架构运维上对多环境的管理,也可以是业务上对不同模块的参数管理。为了避免冲突,我们需要在架构设计之初,做好一定的规划。这里,我们先来说说如何用Group来实现多环境配置管理的具体实现方式。动手试一试第一步:先在Nacos中,通过区分Group来创建两个不同环境的配置内容。比如:如上图,我们为alibaba-nacos-config-client应用,定义了DEV环境和TEST环境的两个独立的配置,这两个匹配与上一种方法不同,它们的Data ID是完全相同的,只是GROUP不同。第二步:在alibaba-nacos-config-client应用的配置文件中,增加Group的指定配置:spring.cloud.nacos.config.group=DEV_GROUP第三步:启动应用,我们可以看到日志中打印了,加载的配置文件:2019-01-30 15:55:23.718 INFO 3216 — [main] o.s.c.a.n.c.NacosPropertySourceBuilder : Loading nacos data, dataId: ‘alibaba-nacos-config-client.properties’, group: ‘DEV_GROUP’使用Namespace实现Namespace在本系列教程中,应该还是第一次出现。先来看看官方的概念说明:用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的Group或Data ID的配置。Namespace的常用场景之一是不同环境的配置的区分隔离,例如:开发测试环境和生产环境的资源(如配置、服务)隔离等。在官方的介绍中,就介绍了利用其可以作为环境的隔离使用,下面我们就来试一下吧!动手试一试第一步:先在Nacos中,根据环境名称来创建多个Namespace。比如:第二步:在配置列表的最上方,可以看到除了Public之外,多了几个刚才创建的Namepsace。分别在DEV和TEST空间下为alibaba-nacos-config-client应用创建配置内容:第三步:在alibaba-nacos-config-client应用的配置文件中,增加Namespace的指定配置,比如:spring.cloud.nacos.config.namespace=83eed625-d166-4619-b923-93df2088883a。这里需要注意namespace的配置不是使用名称,而是使用Namespace的ID。第四步:启动应用,通过访问localhost:8001/test接口,验证一下返回内容是否正确。这种方式下,目前版本的日志并不会输出与Namespace相关的信息,所以还无法以此作为加载内容的判断依据。深入思考上面我们分别利用Nacos配置管理功能中的几个不同纬度来实现多环境的配置管理。从结果上而言,不论用哪一种方式,都能够胜任需求,但是哪一种最好呢?第一种:通过Data ID与profile实现。优点:这种方式与Spring Cloud Config的实现非常像,用过Spring Cloud Config的用户,可以毫无违和感的过渡过来,由于命名规则类似,所以要从Spring Cloud Config中做迁移也非常简单。缺点:这种方式在项目与环境多的时候,配置内容就会显得非常混乱。配置列表中会看到各种不同应用,不同环境的配置交织在一起,非常不利于管理。建议:项目不多时使用,或者可以结合Group对项目根据业务或者组织架构做一些拆分规划。第二种:通过Group实现。优点:通过Group按环境讲各个应用的配置隔离开。可以非常方便的利用Data ID和Group的搜索功能,分别从应用纬度和环境纬度来查看配置。缺点:由于会占用Group纬度,所以需要对Group的使用做好规划,毕竟与业务上的一些配置分组起冲突等问题。建议:这种方式虽然结构上比上一种更好一些,但是依然可能会有一些混乱,主要是在Group的管理上要做好规划和控制。第三种:通过Namespace实现。优点:官方建议的方式,通过Namespace来区分不同的环境,释放了Group的自由度,这样可以让Group的使用专注于做业务层面的分组管理。同时,Nacos控制页面上对于Namespace也做了分组展示,不需要搜索,就可以隔离开不同的环境配置,非常易用。缺点:没有啥缺点,可能就是多引入一个概念,需要用户去理解吧。建议:直接用这种方式长远上来说会比较省心。虽然可能对小团队而言,项目不多,第一第二方式也够了,但是万一后面做大了呢?注意:不论用哪一种方式实现。对于指定环境的配置(spring.profiles.active=DEV、spring.cloud.nacos.config.group=DEV_GROUP、spring.cloud.nacos.config.namespace=83eed625-d166-4619-b923-93df2088883a),都不要配置在应用的bootstrap.properties中。而是在发布脚本的启动命令中,用-Dspring.profiles.active=DEV的方式来动态指定,会更加灵活!。参考资料Nacos官方文档代码示例本文示例读者可以通过查看下面仓库的中的alibaba-nacos-config-client项目:Github:https://github.com/dyc87112/SpringCloud-Learning/Gitee:https://gitee.com/didispace/SpringCloud-Learning/如果您对这些感兴趣,欢迎star、follow、收藏、转发给予支持!以下专题教程也许您会有兴趣Spring Boot基础教程Spring Cloud基础教程

February 1, 2019 · 1 min · jiezi

Spring Cloud Alibaba基础教程:Nacos配置的加载规则详解

前情回顾:《Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现》《Spring Cloud Alibaba基础教程:支持的几种服务消费方式(RestTemplate、WebClient、Feign)》《Spring Cloud Alibaba基础教程:使用Nacos作为配置中心》上一篇,我们学习了如何在Nacos中创建配置,以及如何使用Spring Cloud Alibaba的Nacos客户端模块来加载配置。在入门例子中,我们只配置了Nacos的地址信息,没有配置任何其他与配置加载相关的其他内容。所以,接下来准备分几篇说说大家问的比较多的一些实际使用的问题或疑问。加载规则在《Spring Cloud Alibaba基础教程:使用Nacos作为配置中心》一文中,我们的例子完全采用了默认配置完成。所以,一起来看看Spring Cloud Alibaba Nacos模块默认情况下是如何加载配置信息的。首先,回顾一下,我们在入门例子中,Nacos中创建的配置内容是这样的:Data ID:alibaba-nacos-config-client.propertiesGroup:DEFAULT_GROUP拆解一下,主要有三个元素,它们与具体应用的配置内容对应关系如下:Data ID中的alibaba-nacos-config-client:对应客户端的配置spring.cloud.nacos.config.prefix,默认值为${spring.application.name},即:服务名Data ID中的properties:对应客户端的配置spring.cloud.nacos.config.file-extension,默认值为propertiesGroup的值DEFAULT_GROUP:对应客户端的配置spring.cloud.nacos.config.group,默认值为DEFAULT_GROUP在采用默认值的应用要加载的配置规则就是:Data ID=${spring.application.name}.properties,Group=DEFAULT_GROUP。下面,我们做一些假设例子,方便大家理解这些配置之间的关系:例子一:如果我们不想通过服务名来加载,那么可以增加如下配置,就会加载到Data ID=example.properties,Group=DEFAULT_GROUP的配置内容了:spring.cloud.nacos.config.prefix=example例子二:如果我们想要加载yaml格式的内容,而不是Properties格式的内容,那么可以通过如下配置,实现加载Data ID=example.yaml,Group=DEFAULT_GROUP的配置内容了:spring.cloud.nacos.config.prefix=examplespring.cloud.nacos.config.file-extension=yaml例子三:如果我们对配置做了分组管理,那么可以通过如下配置,实现加载Data ID=example.yaml,Group=DEV_GROUP的配置内容了:spring.cloud.nacos.config.prefix=examplespring.cloud.nacos.config.file-extension=yamlspring.cloud.nacos.config.group=DEV_GROUP深入思考上面,我们具体介绍了在Nacos中添加的各种配置与Spring Cloud应用中客户端配置的对照关系。对于spring.cloud.nacos.config.prefix和spring.cloud.nacos.config.file-extension来说,没有太多的花样可以去揣摩,大部分用户默认配置就可以使用,或者通过spring.cloud.nacos.config.file-extension修改下配置格式的后缀。但是对于spring.cloud.nacos.config.group的配置来说,还是可以派一些特殊的作用,比如:用它来区分不同的产品组下各个应用的配置内容(解决可能应用名冲突的问题)、或者用它来区分不同用途的配置内容、再或者用它来区分不同环境的配置(Nacos下的配置纬度很多,我们可以通过不同的手段来实现多环境的配置,后面会专门写一篇如何实现多环境的配置)等。如果您对spring.cloud.nacos.config.group还有什么其他妙用,欢迎留言分享您的使用方案。参考资料Nacos官方文档代码示例本系列教程的代码案例,都可以通过下面的仓库查看:Github:https://github.com/dyc87112/SpringCloud-Learning/Gitee:https://gitee.com/didispace/SpringCloud-Learning/如果您对这些感兴趣,欢迎star、follow、收藏、转发给予支持!以下专题教程也许您会有兴趣Spring Boot基础教程Spring Cloud基础教程

January 31, 2019 · 1 min · jiezi

Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现

自Spring Cloud Alibaba发布第一个Release以来,就备受国内开发者的高度关注。虽然Spring Cloud Alibaba还没能纳入Spring Cloud的主版本管理中,但是凭借阿里中间件团队的背景,还是得到不少团队的支持;同时,由于Spring Cloud Alibaba中的几项主要功能都直指Netflix OSS中的重要组件,而后者最近频繁宣布各组件不在更新新特性,这使得Spring Cloud Alibaba关注度不断飙升,不少开发者或团队也开始小范围试水。笔者对此也进行了一段时间的调研与试水,接下来计划以《Spring Cloud Alibaba基础教程》为主题,为大家完成一套快速入门的免费内容,以支持开源社区的发展! ^_^更多关于Spring Cloud Alibaba的介绍可见:《Spring Cloud 加盟重量级成员Spring Cloud Alibaba,打造更符合中国国情的微服务体系》什么是NacosNacos致力于帮助您发现、配置和管理微服务。Nacos提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。Nacos帮助您更敏捷和容易地构建、交付和管理微服务平台。Nacos是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。在接下里的教程中,将使用Nacos作为微服务架构中的注册中心(替代:eurekba、consul等传统方案)以及配置中心(spring cloud config)来使用。安装Nacos下载地址:https://github.com/alibaba/na…本文版本:0.7.0下载完成之后,解压。根据不同平台,执行不同命令,启动单机版Nacos服务:Linux/Unix/Mac:sh startup.sh -m standaloneWindows:cmd startup.cmd -m standalonestartup.sh脚本位于Nacos解压后的bin目录下。这里主要介绍Spring Cloud与Nacos的集成使用,对于Nacos的高级配置,后续再补充。所以,持续关注我的博客或者公众号吧!启动完成之后,访问:http://127.0.0.1:8848/nacos/,可以进入Nacos的服务管理页面,具体如下;构建应用接入Nacos注册中心在完成了Nacos服务的安装和启动之后,下面我们就可以编写两个应用(服务提供者与服务消费者)来验证服务的注册与发现了。服务提供者第一步:创建一个Spring Boot应用,可以命名为:alibaba-nacos-discovery-server。如果您还不会或者不了解Spring Boot应用,建议先学习《Spring Boot基础教程》。第二步:编辑pom.xml,加入必要的依赖配置,比如:<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> <!– lookup parent from repository –></parent><dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.SR1</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>0.2.1.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies></dependencyManagement><dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.2</version> <optional>true</optional> </dependency></dependencies>上述内容主要三部分:parent:定义spring boot的版本dependencyManagement:spring cloud的版本以及spring cloud alibaba的版本,由于spring cloud alibaba还未纳入spring cloud的主版本管理中,所以需要自己加入dependencies:当前应用要使用的依赖内容。这里主要新加入了Nacos的服务注册与发现模块:spring-cloud-starter-alibaba-nacos-discovery。由于在dependencyManagement中已经引入了版本,所以这里就不用指定具体版本了。第三步:创建应用主类,并实现一个HTTP接口:@EnableDiscoveryClient@SpringBootApplicationpublic class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Slf4j @RestController static class TestController { @GetMapping("/hello") public String hello(@RequestParam String name) { log.info(“invoked name = " + name); return “hello " + name; } }}内容非常简单,@SpringBootApplication定义是个Spring Boot应用;@EnableDiscoveryClient开启Spring Cloud的服务注册与发现,由于这里引入了spring-cloud-starter-alibaba-nacos-discovery模块,所以Spring Cloud Common中定义的那些与服务治理相关的接口将使用Nacos的实现。这点不论我们使用Eureka、Consul还是其他Spring Cloud整合的注册中心都一样,这也是Spring Cloud做了封装的好处所在,如果对Eureka、Consul这些注册中心的整合还不熟悉的可以看看以前的这篇:Spring Cloud构建微服务架构:服务注册与发现(Eureka、Consul)。第四步:配置服务名称和Nacos地址spring.application.name=alibaba-nacos-discovery-serverserver.port=8001spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848第五步:启动上面创建的应用。可以在启动时候增加-Dserver.port=8001的形式在本机的不同端口上启动多个实例。在应用启动好之后,我们可以在控制台或日志中看到如下内容,代表已经注册成功:INFO 10476 — [ main] o.s.c.a.n.registry.NacosServiceRegistry : nacos registry, alibaba-nacos-discovery-server 10.123.18.216:8001 register finished在启动都ok之后,我们可以访问Nacos的管理页面http://127.0.0.1:8848/nacos/来查看服务列表,此时可以看到如下内容:这里会显示当前注册的所有服务,以及每个服务的集群数目、实例数、健康实例数。点击详情,我们还能看到每个服务具体的实例信息,如下图所示:服务消费者接下来,实现一个应用来消费上面已经注册到Nacos的服务。第一步:创建一个Spring Boot应用,命名为:alibaba-nacos-discovery-client-common。第二步:编辑pom.xml中的依赖内容,与上面服务提供者的一样即可。第三步:创建应用主类,并实现一个HTTP接口,在该接口中调用服务提供方的接口。@EnableDiscoveryClient@SpringBootApplicationpublic class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Slf4j @RestController static class TestController { @Autowired LoadBalancerClient loadBalancerClient; @GetMapping("/test”) public String test() { // 通过spring cloud common中的负载均衡接口选取服务提供节点实现接口调用 ServiceInstance serviceInstance = loadBalancerClient.choose(“alibaba-nacos-discovery-server”); String url = serviceInstance.getUri() + “/hello?name=” + “didi”; RestTemplate restTemplate = new RestTemplate(); String result = restTemplate.getForObject(url, String.class); return “Invoke : " + url + “, return : " + result; } }}这里使用了Spring Cloud Common中的LoadBalancerClient接口来挑选服务实例信息。然后从挑选出的实例信息中获取可访问的URI,拼接上服务提供方的接口规则来发起调用。第四步:配置服务名称和Nacos地址,让服务消费者可以发现上面已经注册到Nacos的服务。spring.application.name=alibaba-nacos-discovery-client-commonserver.port=9000spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848第五步:启动服务消费者,然后通过curl或者postman等工具发起访问,下面以curl为例:$ curl localhost:9000/testInvoke : http://10.123.18.216:8001/hello?name=didi, return : hello didi$ curl localhost:9000/testInvoke : http://10.123.18.216:8002/hello?name=didi, return : hello didi可以看到,两次不同请求的时候,真正实际调用的服务提供者实例是不同的,也就是说,通过LoadBalancerClient接口在获取服务实例的时候,已经实现了对服务提供方实例的负载均衡。但是很明显,这样的实现还是比较繁琐,预告下后面的几篇,关于服务消费的几种不同姿势。参考资料Nacos官方文档Nacos源码分析代码示例本文示例读者可以通过查看下面仓库的中的alibaba-nacos-discovery-server和alibaba-nacos-discovery-client-common项目:GithubGitee如果您对这些感兴趣,欢迎star、follow、收藏、转发给予支持!以下专题教程也许您会有兴趣Spring Boot基础教程Spring Cloud基础教程 ...

January 16, 2019 · 2 min · jiezi