微服务系列四:热更新措施与配置共享

目录

前言

一、基于Nacos的管理中心整体方案

二、配置共享动态维护

2.1 分析哪些配置可拆,需要动态提供哪些参数

2.2 在nacos 分别创建共享配置

创建jdbc相关配置文件

 创建日志相关配置文件

创建接口文档配置文件

2.3 拉取本地合并配置文件

2.3.1 拉取出现的先后顺序问题——SpringCloud 和 SpringBoot 读取顺序问题

2.3.2 解决措施——添加引导配置文件,放在Springcloud初始化时读取

.2.3.3 引入依赖

2.3.4 新增bootstrap.yaml文件

2.3.5 重启测试购物车功能

2.3.6 同理修改其他微服务的配置文件

三、配置热更新(不停机更新)

3.1 前提条件

3.2 业务实践——热更新购物车容量

3.3 测试热更新效果

四、网关动态路由监听

监听Nacos配置变更

第一步:注入Nacos依赖

第二步:配置bootstrap.yaml文件

第三步:修改application.yaml文件,删除路由

第四步:编写动态路由加载器框架

第五步:更新路由方法拆解

【问题】如何获取RouteDefinition对象?如何更好的转换RouteDefinition对象?

第六步:动态路由加载器完整实现

第七步:在Nacos配置初始化的JSON格式路由

第八步:无需重启,测试网关生效

五、Nacos配置相关知识追问巩固


实验环境说明

本文有部分地方需要实验进行。首先对于看过黑马微服务的同学应该会比较熟悉。如果没有你也可以参考我实验的环境搭个简单的测试环境,用于实验。实验的目的也是为了更好的理解业务、理解知识点。

本地环境部分:

服务名端口号备注
nginx18080 / 18081前端运行环境
hm-gateway8080后端项目网关模块
item-service8081后端商品服务模块
cart-service8082 后端购物车服务模块
item-service28083后端商品服务模块
item-service38084后端商品服务模块
user-service8085后端用户管理模块
trade-service8086后端交易服务模块
pay-service8087后端支付服务模块

远程服务器环境部分:

服务名端口号备注
nacos8848注册中心及配置中心,非容器镜像
nginx18080/18081线上前端运行环境,容器镜像
mysql3306线上数据库,容器镜像
docker-hm8080线上后端运行环境,容器镜像

注意事项:

  • 远程环境中非容器镜像指nacos是后单独配置的容器,而mysql、nginx、docker-hm是使用compose统一部署的。
  • 在本节实验中,线上环境的nginx和docker-hm我们不会使用到,而是使用本地的nginx和后端项目
  • 请你确保在配置线上环境时,mysql必须先比nacos启动。如果nacos先启动将无法连接数据库。此时你需要停止nacos容器,先启动mysql。


前言

这是微服务相关技术学习记录的第四篇文章啦!到此为止,微服务开发相关的技术其实已经分享了七七八八了。本篇则是着重介绍微服务配置文件管理相关、配置更新相关的内容。

回顾先前我们学习的微服务开发技术,我们已经解决了以下几个问题:

  • 实现微服务远程调用 (OpenFeign)

  • 实现微服务注册、发现  (nacos)

  • 微服务请求路由、负载均衡  (nacos)

  • 微服务登录用户信息传递  (网关 + 拦截器)

还有哪些问题需要解决呢?

试想一下,假如网关路由发生了更新,你是打算重新启动网关模块更新配置么?那么停机更新会导致服务不可用怎么办?

再来,目前每个微服务都需要编写配置文件,但是实际上里面很多内容是一样的。如果对这一部分公共内容进行修改,是不是又得大规模停机更新微服务呢?

这就概括出了目前微服务配置方面存在的弊端和我们本篇改进的方向:

  • 如何简化重复配置,降低维护成本?
  • 如何实现项目配置的不停机维护?
  • 如何实现网关路由的动态维护?

一、基于Nacos的管理中心整体方案

上述问题我们不难想到,那就将所有配置文件统一管理起来,成立一个管理中心即可。确实,这个管理中心还可以由我们的Nacos一并担任!

整体方案图示

微服务共享的配置可以统一交给Nacos保存和管理,在Nacos控制台修改配置后,Nacos会将配置变更推送给相关的微服务,并且无需重启即可生效,实现配置热更新。

网关的路由同样是配置,因此同样可以基于这个功能实现动态路由功能,无需重启网关即可修改路由配置。

二、配置共享动态维护

我们可以把微服务共享的配置抽取到Nacos中统一管理,这样就不需要每个微服务都重复配置了。

主要步骤如下:

  • 在Nacos中添加共享配置

  • 微服务拉取配置

配置共享最大的好处就是将重复配置封装成一份交Nacos统一管理。其他微服务只需要提供一些配置参数就行了,极大的简化了开发。接下来我们以cart-service为例,实现配置文件拆分。

2.1 分析哪些配置可拆,需要动态提供哪些参数

2.2 在nacos 分别创建共享配置

在 配置管理->配置列表 中点击 新建一个配置:


创建jdbc相关配置文件

spring:datasource:url: jdbc:mysql://${hm.db.host:192.168.186.140}:${hm.db.port:3306}/${hm.db.database}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driverusername: ${hm.db.un:root}password: ${hm.db.pw:123}
mybatis-plus:configuration:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandlerglobal-config:db-config:update-strategy: not_nullid-type: auto
  • 数据库ip:通过${hm.db.host:192.168.150.101}配置了默认值为192.168.150.101,同时允许通过${hm.db.host}来覆盖默认值

  • 数据库端口:通过${hm.db.port:3306}配置了默认值为3306,同时允许通过${hm.db.port}来覆盖默认值

  • 数据库database:可以通过${hm.db.database}来设定,无默认值


 创建日志相关配置文件

logging:level:com.hmall: debugpattern:dateformat: HH:mm:ss:SSSfile:path: "logs/${spring.application.name}"

创建接口文档配置文件

knife4j:enable: trueopenapi:title: ${hm.swagger.title:黑马商城接口文档}description: ${hm.swagger.description:黑马商城接口文档}email: ${hm.swagger.email:weizhicong@stu.gpnu.cn}concat: ${hm.swagger.concat:weizhicong}url: https://www.weizhicong.cnversion: v1.0.0group:default:group-name: defaultapi-rule: packageapi-rule-resources:- ${hm.swagger.package}
  • title:接口文档标题,我们用了${hm.swagger.title}来代替,将来可以有用户手动指定

  • email:联系人邮箱,我们用了${hm.swagger.email:weizhicong@stu.gpnu.cn},默认值是weizhicong@stu.gpnu.cn,同时允许用户利用${hm.swagger.email}来覆盖。

2.3 拉取本地合并配置文件

现在线上的配置文件创建好了,我们需要想办法让微服务项目接下来拉取共享配置。将拉取到的共享配置与本地的application.yaml配置合并,完成项目上下文的初始化。但是这其中存在一些拉取的问题,让我们逐步分析。

2.3.1 拉取出现的先后顺序问题——SpringCloud 和 SpringBoot 读取顺序问题

读取Nacos配置是发生在SpringCloud上下文初始对象(ApplicationContext)时发生的,而Nacos地址配置在application.yaml却是SpringBoot启动时才发生的。也就是说在读取Nacos配置时,根本不知道去哪里读取Nacos地址信息。从而造成无法加载Nacos配置文件的问题。

2.3.2 解决措施——添加引导配置文件,放在Springcloud初始化时读取

.2.3.3 引入依赖

在cart-service模块引入依赖:

  <!--nacos配置管理--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!--读取bootstrap文件--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId></dependency>

2.3.4 新增bootstrap.yaml文件

在cart-service中的resources目录新建一个bootstrap.yaml文件:

spring:application:name: cart-service # 服务名称profiles:active: devcloud:nacos:server-addr: 192.168.186.140 # nacos地址config:file-extension: yaml # 文件后缀名shared-configs: # 共享配置- dataId: shared-jdbc.yaml # 共享mybatis配置- dataId: shared-log.yaml # 共享日志配置- dataId: shared-swagger.yaml # 共享日志配置

2.3.5 重启测试购物车功能

2.3.6 同理修改其他微服务的配置文件

user-service成功!

trade-service成功!

item-service成功!

pay-service成功!

联调测试成功!


三、配置热更新(不停机更新)

配置热更新 :当我们修改配置文件中的参数属性时,无需重启服务即可生效。

3.1 前提条件

3.2 业务实践——热更新购物车容量

购物车业务,购物车数量有一个上限,默认是10,对应代码如下:

第一步:创建容器配置类,并添加@ConfigurationProperties注解指定服务配置

第二步:注入使用修改业务代码

第三步:增加Nacos配置

文件名称由三部分组成:[服务名]-[spring.active.profile].[后缀名]

  • 服务名:我们是购物车服务,所以是cart-service

  • spring.active.profile:就是spring boot中的spring.active.profile,可以省略,则所有profile共享该配置

  • 后缀名:例如yaml

3.3 测试热更新效果

四、网关动态路由监听

目前,我们的网关路由是写死在配置文件的。一旦路由有所变动,必须重启网关才能生效。对此我们希望网关路由也能和上一小节的配置热更新一样,动态维护,无需停机即可更新。

但是,网关的路由配置全部是在项目启动时由org.springframework.cloud.gateway.route.CompositeRouteDefinitionLocator在项目启动的时候加载,并且一经加载就会缓存到内存中的路由表内(一个Map),不会改变。也不会监听路由变更,

所以,我们无法利用上节课学习的配置热更新来实现路由更新。

因此,我们必须监听Nacos的配置变更,然后手动把最新的路由更新到路由表中。这里有两个难点:

  • 如何监听Nacos配置变更?

  • 如何把路由信息更新到路由表?

监听Nacos配置变更

官方文档:Nacos 监听配置 Java SDKicon-default.png?t=O83Ahttps://nacos.io/zh-cn/docs/sdk.html

第一步:注入Nacos依赖

第二步:配置bootstrap.yaml文件

spring:application:name: hm-gateway # 服务名称profiles:active: devcloud:nacos:server-addr: 192.168.186.140:8848 # nacos地址config:file-extension: yaml # 文件后缀名shared-configs: # 共享配置- dataId: shared-log.yaml # 共享日志配置

第三步:修改application.yaml文件,删除路由

server:port: 8080 # 网关端口 前端请求统一处理hm:jwt:location: classpath:hmall.jksalias: hmallpassword: hmall123tokenTTL: 30mauth:excludePaths:- /search/**- /users/login- /items/**- /hi

第四步:编写动态路由加载器框架


@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {// 用来获取ConfigServer中的配置信息,与nacos建立连接private final NacosConfigManager nacosConfigManager;private final String dataId = "gateway-routers.json";private final String group = "DEFAULT_GROUP";@PostConstruct // 初始化方法,这个Bean一初始化就会执行public void initRouteConfigListener() throws NacosException {//1. 项目启动,先拉取一次配置,并且添加监听器String configInfo = nacosConfigManager.getConfigService().getConfigAndSignListener(dataId, group, 3000, new Listener() {@Override// 线程池public Executor getExecutor() {return null;}@Overridepublic void receiveConfigInfo(String s) {//TODO 2. 监听器,当配置发生变化时,需要更新路由表}});// 3.第一次读取到配置,也需要更新到路由表updateConfigInfo(configInfo);}public void updateConfigInfo(String configInfo) {// TODO 更新路由表}
}

第五步:更新路由方法拆解

更新路由要用到org.springframework.cloud.gateway.route.RouteDefinitionWriter这个接口:

【问题】如何获取RouteDefinition对象?如何更好的转换RouteDefinition对象?

我们知道通过前面编写的这个方法可以获取到字符串格式的配置文件,保存在configInfo。它就和我们的配置文件一模一样,只不过是字符串表示。

所以第一个问题:如何获取RouteDefinition对象?我们需要解析字符串形式的yaml文件,将它转成java对象。

但是啊,将yaml文件转成Java的对象比较困难,那么什么格式转java对象比较方便呢?——JSON格式。

所以第二个问题:如何更好的转换RouteDefinition对象?我们将路由等信息存储成JSON格式而不是yaml格式,这样可以更好地转换成Java对象

第六步:动态路由加载器完整实现

package com.hmall.gateway.routers;import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.hmall.common.utils.CollUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {private final RouteDefinitionWriter writer;// 用来获取ConfigServer中的配置信息,与nacos建立连接private final NacosConfigManager nacosConfigManager;// 配置文件id 和 分组private final String dataId = "gateway-routers.json";private final String group = "DEFAULT_GROUP";// 保存更新过的路由idprivate final Set<String> routeIds = new HashSet<>();@PostConstruct // 初始化方法,这个Bean一初始化就会执行public void initRouteConfigListener() throws NacosException {//1. 项目启动,先拉取一次配置,并且添加监听器String configInfo = nacosConfigManager.getConfigService().getConfigAndSignListener(dataId, group, 3000, new Listener() {@Override// 线程池public Executor getExecutor() {return null;}@Overridepublic void receiveConfigInfo(String configInfo) {//2. 监听器,当配置发生变化时,需要更新路由表updateConfigInfo(configInfo);}});// 3.第一次读取到配置,也需要更新到路由表updateConfigInfo(configInfo);}/*** configInfo 是nacos中配置的json字符串* @param configInfo*/public void updateConfigInfo(String configInfo) {// 更新路由表log.debug("监听到路由配置变更,{}", configInfo);// 1.反序列化List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);// 2.更新前先清空旧路由// 2.1.清除旧路由for (String routeId : routeIds) {writer.delete(Mono.just(routeId)).subscribe();}routeIds.clear();// 2.2.判断是否有新的路由要更新if (CollUtils.isEmpty(routeDefinitions)) {// 无新路由配置,直接结束return;}// 3.更新路由routeDefinitions.forEach(routeDefinition -> {// 3.1.更新路由writer.save(Mono.just(routeDefinition)).subscribe();// 3.2.记录路由id,方便将来删除routeIds.add(routeDefinition.getId());});}
}

第七步:在Nacos配置初始化的JSON格式路由

当前还没配置Nacos时,此时的网关没有任何路由,可以测试一下:

第八步:无需重启,测试网关生效

五、Nacos配置相关知识追问巩固

1. 如何简化重复配置,降低维护成本?谈谈具体实施步骤

2. 如何解决读取远程Nacos配置时无法从Springboot中获取到Nacos地址的配置信息?

3. 如何实现配置热更新?谈谈具体的实施步骤

4. 如何实现动态路由热更新?谈谈具体的实施步骤

5. 请你谈谈 @PostConstruct 注解的作用?

6. 请你谈谈监听Nacos配置中如何获取Nacos的ConfigServer中的配置信息?

7. 谈谈更新路由监听器方法是怎么实现的?你是否赞同采用先删除后更新的策略,为什么?

8. Nacos提供了RouteDefinitionWriter接口用于实现添加路由,路由对象是RouteDefinition类型的。请问你在路由监听中如何获取该对象?如何更好、更快的获取该对象?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/467275.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

线程级耗时统计工具类TimeWatcher

线程级耗时统计工具类TimeWatcher 先看效果 假设我们的业务代码逻辑是这样的 那么最终的日志打印效果为&#xff08;注&#xff1a;此为美化输出&#xff0c;也可设置为常规一行输出&#xff0c;还可自定义&#xff09; 2024-11-08T23:48:53.39008:00 INFO 31472 --- [nio-…

arkUI:Flex弹性布局的各个属性

arkUI&#xff1a;Flex弹性布局的简单使用 1 主要内容说明2 相关内容2.1 Flex弹性布局的方向2.1.1 源码1的简答说明2.1.2 源码1 &#xff08;Flex弹性布局的方向&#xff09;2.1.3 源码1运行效果2.1.3.1 当direction: FlexDirection.RowReverse2.1.3.2 当direction: FlexDirect…

详解Gemini API的使用:在国内实现大模型对话与目标检测教程

摘要&#xff1a;本博客介绍了如何利用Gemini API实现多轮对话和图像目标检测识别功能&#xff0c;在Python中快速搭建自己的大模型完成实际任务。通过详细的步骤解析&#xff0c;介绍了如何申请Gemini API密钥&#xff0c;调用API、对话实现的代码&#xff0c;给出了上传图片识…

5G时代已来:我们该如何迎接超高速网络?

内容概要 随着5G技术的普及&#xff0c;我们的生活似乎变得更加“科幻”了。想象一下&#xff0c;未来的智能家居将不仅仅是能够听你说“开灯”&#xff1b;它们可能会主动询问你今天心情如何&#xff0c;甚至会推荐你一杯“维他命C芒果榨汁”&#xff0c;帮助你抵御夏天的炎热…

算法每日练 -- 双指针篇(持续更新中)

介绍&#xff1a; 常见的双指针有两种形式&#xff0c;一种是对撞指针&#xff08;左右指针&#xff09;&#xff0c;一种是快慢指针&#xff08;前后指针&#xff09;。需要注意这里的双指针不是 int* 之类的类型指针&#xff0c;而是使用数组下标模拟地址来进行遍历的方式。 …

理解鸿蒙app 开发中的 context

是什么 Context是应用中对象的上下文&#xff0c;其提供了应用的一些基础信息&#xff0c;例如resourceManager&#xff08;资源管理&#xff09;、applicationInfo&#xff08;当前应用信息&#xff09;、dir&#xff08;应用文件路径&#xff09;、area&#xff08;文件分区…

贝尔不等式,路径积分与AB(Aharonov-Bohm)效应

贝尔不等式、路径积分与Aharonov-Bohm&#xff08;AB&#xff09;效应 这些概念分别源于量子力学不同的理论分支和思想实验&#xff0c;但它们都揭示了量子力学的奇异性质&#xff0c;包括非局域性、相位效应和波粒二象性。以下详细解析每一概念&#xff0c;并探讨其相互联系。…

python 爬虫 入门 六、Selenium

Selenium本来是一个自动测试工具&#xff0c;用于模拟用户对网站进行操作。在爬虫领域也有其用处。 一、下载安装Selenium及附属插件 pip install Selenium 安装完成后还需要安装一个浏览器驱动&#xff0c;来让python能启动浏览器。 如果是Edge或者其他基于Chromium的浏览器…

Linux(CentOS)yum update -y 事故

CentOS版本&#xff1a;CentOS 7 事情经过&#xff1a; 1、安装好CentOS 7&#xff0c;系统自带JDK8&#xff0c;版本为&#xff1a;1.8.0_181 2、安装好JDK17&#xff0c;版本为&#xff1a;17.0.13 3、为了安装MySQL执行了 yum update -y&#xff08;这个时候不知道该命令的…

uniapp uni-calendar日历实现考勤统计功能

根据日历组件代码结构 构成相应结构的状态统计数据 list 再遍历到每日的子组件中 <view class"uni-calendar__weeks-item" v-for"(weeks,weeksIndex) in item" :key"weeksIndex"><calendar-item class"uni-calendar-item--hook&q…

环境配置与搭建

安装pytorch 官网连链接&#xff1a;https://pytorch.org/ 特殊包名 cv2 pip install opencv-python sklearn pip install scikit-learnPIL pip install Pillow使用jupyter notebook pip install jupyter安装显卡驱动 Windows Linux 视频教程&#xff1a; 【ubuntu2…

【数据库实验一】数据库及数据库中表的建立实验

目录 实验1 学习RDBMS的使用和创建数据库 一、 实验目的 二、实验内容 三、实验环境 四、实验前准备 五、实验步骤 六、实验结果 七、评价分析及心得体会 实验2 定义表和数据库完整性 一、 实验目的 二、实验内容 三、实验环境 四、实验前准备 五、实验步骤 六…

SpringBoot健身房管理:技术与实践

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

CulturalBench :一个旨在评估大型语言模型在全球不同文化背景下知识掌握情况的基准测试数据集

2024-10-04&#xff0c;为了提升大型语言模型在不同文化背景下的实用性&#xff0c;华盛顿大学、艾伦人工智能研究所等机构联合创建了CulturalBench。这个数据集包含1,227个由人类编写和验证的问题&#xff0c;覆盖了包括被边缘化地区在内的45个全球区域。CulturalBench的推出&…

python登录功能实现

一.用python实现基本的登录功能 #-----------------1.基本登录功能------------------- nameinput("qq账号&#xff1a;") if name"jc":passwdinput("密码&#xff1a;")if passwd"123456":print("登录成功")else:print(&q…

如何使用Python管理环境变量

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 环境变量 📒📝 环境变量简介📝 Python 中的环境变量操作📝 获取环境变量📝 设置环境变量🔖 临时设置🔖 永久设置📝 删除环境变量📝 临时删除📝 永久删除📝 小结⚓️ 相关链接 ⚓️📖 介绍 📖 环境变量…

设置允许多用户远程登录 Windows 云服务器

操作场景 本文档以 Windows Server 2016 操作系统云服务器为例&#xff0c;指导您配置多用户远程登录 Windows 云服务器。 注意&#xff1a; 微软提供的多用户远程登录功能试用期为120天&#xff0c;若未购买多用户登录授权&#xff08;RDS CALs&#xff09;&#xff0c;则试…

喜报!景联文科技成功通过DCMM数据管理能力成熟度二级认证

10月30日&#xff0c;中国电子信息行业联合会公示了新一批DCMM贯标企业&#xff0c;景联文科技成功通过DCMM数据管理能力成熟度二级认证&#xff08;乙方认证&#xff09;。 DCMM是《数据管理能力成熟度评估模型》的简称&#xff0c;是我国在数据管理领域首个正式发布的国家标准…

Android setContentView执行流程(1)-生成DecorView

setContentView的流程主要就是讲在Activity的onCreate方法中调用setContentView方法之后&#xff0c;我们自定义的xml文件加载的过程&#xff0c;学习它可以让我们对整个View树的理解更加透彻&#xff0c;并且通过源码的学习&#xff0c;我们可以从根本上理解一些问题&#xff…

《操作系统 - 清华大学》2 -1:操作系统的启动

文章目录 0. 内容摘要1. 计算机体系机构概述2.启动2.1 启动时计算机内存和磁盘布局2.2. 内存映射 3. 系统调用、异常、中断3.1 定义3.2 背景3.3 中断、异常和系统调用的不同点3.3.1 源头3.3.2 处理时间3.3.3 响应 0. 内容摘要 两部分的内容 第一部分是启动。知道操作系统怎么是…