涂鸦智能落地 Koupleless 合并部署,实现云服务降本增效

7bef22ee2c89070f8e6258b0b38dc3a5.gif

文|八幡、朵拉

杭州涂鸦智能技术专家

主要研究微服务与可观测、消息引擎、任务调度、数据层中间件等领域。 

本文 5389 字 阅读 15 分钟

当前涂鸦通过 Koupleless 的静态合并部署能力,很好地解决了资源浪费问题。

为了进一步提升研发效率,涂鸦与 Koupleless 正在开发动态合并部署能力,为公有云大规模合并部署打下基础,也为未来更长远的 Serverless 能力提供基座。

44e8ddd0a3b6bf720871228b7fcfae68.gif

Koupleless 在涂鸦智能的落地效果探讨

背景     

Background

涂鸦智能是全球领先的云平台服务提供商,致力于构建智慧解决方案的开发者生态,赋能万物智能。基于全球公有云,涂鸦开发者平台实现了智慧场景和智能设备的互联互通,承载着每日数以亿计的设备请求交互;拥有亿级海量数据并发处理能力,为用户提供高稳定性的不间断计算服务。

f2a62ddaf03b34d7e92a89287a741f39.png

涂鸦智能不仅服务于公有云用户,同时也为大客户提供混合云及私有云解决方案,满足不同层次客户的需求。因此,涂鸦需要同时关注公有云和私有云服务中潜在的问题。

公有云用户基数大、设备多、核心业务服务实例多,但也符合二八原则——20%的服务承担了 80%的流量。还有大量的配套周边管理服务及正处于发展中的业务等,虽然服务实例数量少,但为了保障业务的稳定性及可用性,至少得部署 2 个实例。

私有云在服务刚落地时,通常规模较小,随着业务的逐步发展,接入的用户数和设备数才会随之上涨。如果一开始就生搬硬套公有云的交付模式,将上百个服务部署在私有云客户环境中,基础设施成本占比太大,不仅造成了硬件资源成本浪费,同时也提升了运维复杂度。 

综合考虑公有云部分服务和私有云前期服务存在的硬件资源利用率低、弹性扩容慢等问题,涂鸦开始着手调研解决方案。 

方案调研 

Research

首先看下内存占用情况:

内存占用分析

涂鸦绝大部分业务应用都是采用 Java 语言实现的,基于微服务架构,部署在 K8S 上。来看下内存占用情况:

  • 每个 POD 都需要启动精简的 Linux 系统,约几十兆初始内存;

  • K8S 节点一般会附带一些 POD 监控、日志收集 Agent,约几十兆初始内存;

  • Java Agent 之类的字节码增强消耗的初始内存;

  • JVM 类库,Spring、Netty、Dubbo 等框架,内嵌 WEB 容器,占用的 Metaspace 和堆栈空间;

  • 业务应用代码启动占用的 Metaspace 和堆栈空间;

  • 业务流量处理产生的堆栈空间。

从上面分析可以看出,除了业务应用代码本身和业务流量处理之外,其他的内存占用当然是越少越好。一个简单的控制台应用内存占用如下图所示:

0f64c12d39c5e8b693b1b952c8ee1f34.png

在微服务架构的视角下,Java 应用不但没有百多兆的 JRE 和框架之类的基础内存需要问题,更重要的是,单个微服务也不再需要再数十 GB 的内存。有了高可用的服务集群,也无须追求单个服务 7×24 小时不间断运行,在一天内随着业务流量的波动、高峰和低谷,服务随时可以进行弹性扩缩容。

但相应地,Java 的启动时间相对较长、需要预热才能达到最高性能等特点就显得相悖于这样的应用场景。在无服务架构中,矛盾则可能会更加突出,比起服务,一个函数的规模通常会更小,执行时间会更短。在这样的场景下,我们看下有哪些方案可以降本增效:

⭕️ 搁浅方案:Native Image

⭕️无法支持全场景、Spring 支持不足、异常难预见、问题难排查。

提前编译(Ahead of Time Compilation,AOT)可以减少即时编译带来的预热时间,减少 Java 应用长期给用户带来的“第一次运行慢”的不良体验,让用户能放心地进行很多全程序的分析行为,使用更大的优化措施。而随着 Graal VM 技术的成熟,它能显著降低内存占用及启动时间。由于 HotSpot 本身就会有一定的内存消耗(通常约几十 MB),根据 Oracle 官方给出的测试数据,运行在 Substrate VM 上的小规模应用,其内存占用和启动时间与运行在 HotSpot 相比有了 5 倍到 50 倍的下降。

但是提前编译的坏处也很明显,它破坏了 Java“一次编写,到处运行”的承诺,必须为每个不同的硬件、操作系统去编译对应的发行包。它也显著降低了 Java 链接过程的动态性,要求加载的代码必须在编译期就是全部已知的,而不能再是运行期才确定,大多数运行期对字节码的生成和修改操作也不再行得通。特别是在整个 Java 的生态系统中,数量庞大的第三方库要一一进行适配。随着 Graal VM 团队与来自 Pivotal 的 Spring 团队的紧密合作,解决了 Spring 全家桶在 Graal VM 上的运行适配问题。

为此,我们进行了 JDK17 升级,并将 SpringBoot 升至 3.x 版本,Dubbo 升至 3.x 版本。在验证过程中出现了许多组件的兼容性问题,如 Apollo、Guava、Jedis、MyBatis 等,也进行了逐一解决。

虽然该方案在简单的内部应用上验证通过,并上线试运行了一段时间,取得了一定的效果。但业务应用使用的第三方库数量众多,达到数百个,在如 GroovyShell、BouncyCastle、Agent 等场景上还无法很好地支持。同时 Spring 的支持不足,如 Spring XML Bean 的构造参数或 properties 配置出现 TypedStringValue 类型无法识别,Spring-AOT 不支持 setter-inject 方式的循环依赖等,导致需要对应用进行较大调整和修改,另外会出现一些无法预见的异常,且出现问题时不方便定位排查。最后涂鸦还是决定暂时搁浅该方案。

✅ 使用中:Koupleless

Koupleless 是一种模块化的 Serverless 技术解决方案,它能让普通应用以比较低的代价演进为 Serverless 研发模式,让代码与资源解耦,轻松独立维护,与此同时支持秒级构建部署、合并部署、动态伸缩等能力为用户提供极致的研发运维体验,最终帮助企业实现降本增效。

不同模块支持完全类隔离加载,对于应用开发来说相对透明,当然 Koupleless 也提供了更多插件机制,还有进程间跨 ClassLoader 的 JVM 调用能力。主要有如下优势:

  • 类隔离:通过对业务应用的类隔离加载,原有业务系统几乎无侵入支持合并部署;

  • 插件机制:通过插件机制解决依赖冲突问题;

  • 基座和模块:可以将中间件和基础框架下沉到基座,框架与中间件升级维护成本降低。通过进程内 JVM 调用替代远程调用,节省网络 IO 和序列化反序列化成本,提升性能和稳定性。见下图:

0a42106e5d5e7b2a6a021e623b4d14d2.png

  • 静态合并部署:方便快速验证,简化部署,适合私有云环境;

  • 动态合并部署:支持模块的热更新,提高发布效率,降低启动时间,适合公有云环境。

就内存节省效率方面来说,可以节省 POD 容器、Tomcat 容器、公共类库加载 Metaspace 等内存资源,主要是节省启动内存开销。对于低流量业务,几个应用合并部署到一个,资源利用更充分:10 个低流量 1C2G 应用,合并后仅需能一个 2C4G 就能搞定;单个应用突发的高流量可以有更大的水位池,稳定性也会提升。而对于高流量业务,虽然在内存方面优势不大,但可以实现极速弹性,更有利于 Serverless 架构。

❌ 其他弃选方案

  • 代码合并:简单粗暴,降本优势明显,但同时也降低了开发和运维效率,不利于维护和业务快速发展

  • 基于代码组织的模块化:服务打成 jar 包引入,但没有 ClassLoader 隔离,容易造成类库依赖冲突,beanName 冲突等

  • 基于 Tomcat 的多 WAR 包部署:业务系统无任何侵入,也能一定程度上节省多个应用的 POD 和应用内置 Tomcat 内存。但脱离了主流的 DevOps 体系,需要针对这种部署方式提供额外支持,同时能节省的内存也非常有限。


综合考虑,涂鸦决定使用 Koupleless 进行改造升级。

改造升级

Adaption

Koupleless 是一种多应用的架构,而传统的中间件可能只考虑了一个应用的场景,故在一些行为上无法兼容多应用共存的行为,会发生共享变量污染、ClassLoader 加载异常、Class 判断不符合预期等问题。由此,在使用 Koupleless 中间件时,我们需要对一些潜在的问题做补丁,覆盖掉原有中间件的实现,使开源的中间件和自研的组件也能兼容多应用的模式,涉及到以下的使用方式,可能需要多模块化适配改造:

适配点

系统变量被共享

在使用的时候,需要考虑到全局共享的情况下是否会与别的模块冲突,包括但不限于:Appid 、环境变量、System 配置等。

静态变量、静态单例、静态缓存被共享

正常情况下公共代码都是由基座进行加载,而基座的类加载器是唯一的,所以不同的模块对静态变量的操作都是施加在同一个对象上的,解决方案如下:

  • 公共包从基座引用改成每个应用单独引入

  • 将静态变量调整为按 ClassLoader 进行缓存,每次操作只对当前线程的 ClassLoader 对应的对象进行操作。

类找不到异常

一般存在于通过基座加载相应的类或者静态调用的情况下。由于基座类加载器无法访问到模块的类加载器,所以在公共代码中加载类时优先使用 Thread.currentThread().getContextClassLoader()

  • XX Class Not Found

  • XX Class No Defined

  • ServiceLoader 异常

日志适配

  • logback 通过 condition 进行适配

<if condition='property("sofa.ark.embed.enable").contains("true")'>
        <then>
            <springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="NO_APP_CONFIG"/>
            <property name="BASE_PATH" value="${user.home}/logs/${APP_NAME}"/>
        </then>


        <else>
            <springProperty scope="context" name="loggingRoot" source="logging.file.path"/>
            <property name="BASE_PATH" value="${loggingRoot}/"/>
        </else>


    </if>
  • log4j 通过 properties 进行适配

<Property name="loggingRoot">${sys:user.home}/logs/${spring:tuya.sofa.ark.app:-}</Property>
  • log4j2 和 logback 日志在同一个基座中混用

一般应用使用 logback,但是存在某些应用使用 log4j2 的情况,目前 Koupleless 不支持两种应用放在一个基座中,可以考虑在 Koupleless 上调整日志系统的判断,或在 Koupleless 上调整由模块决定某些 plugin 是否被加载。

健康检查

提供基座应用的健康检查实现:

  • 静态部署模式:合并部署的所有应用的状态都健康,健康检查才会通过。

  • 动态部署模式:提供配置,让用户自行决定模块热部署结果是否影响应用整体健康状态(默认配置为:不影响整体应用原本的健康状态)

Web 容器共享

  • 多 Host 模式:使用多个 port 进行区分。该模式的问题首先在于重复创建了 Tomcat 相关的资源,造成资源的浪费;其次是每个 Biz 有自己的端口,不利于整个 Ark 包应用整体对外提供服务。

  • 单 Host 模式:Koupleless 提供了类似独立 Tomcat 部署多 webapp 的方式。所有 Biz 共用同一个 Server 及 Host,每个 Biz 只创建自己的 Context,通过 Context 中的 contextPath 将自身接口与其它 Biz 接口做区分。

c3ef2ae1e0427a5bf114e0003c8d79a8.png

考虑到资源共享及整体性,我们采用了单 Host 模式。

实践心得

  • 将下沉的组件配置抽离到父 POM 中,方便统一管控;

  • 基座的 AutoConfig 被模块的依赖触发,但是初始化的时候报错,基座需要 exclude 相应的 AutoConfig。建议使用 Spring Boot 框架的自动装配功能时将相应组件下沉到基座;

  • ContextPath:可以通过参数动态配置,方便应用运行在单模块独占基座多模块合并部署场景中;

3a3c820cb730c0de22f591eea6c314ce.png

  • 模块需要开启 https 协议时:用于支持自定义证书场景,可以在模块获取到 web server 并主动添加一个新的 Connector。

f1197c3a6410e5c713e0149d2b89bdb5.png

DevOps 改造

Adaption of DevOps

测试应用验证通过后,涂鸦团队开始着手进行发布系统的改造。由于静态合并部署比较适合部署相对稳定不频繁的场景(如私有云),而公有云更适合动态合并部署模式。但为了给私有云交付打前战,涂鸦一期先在公有云上实现静态合并部署,快速试点验证。而静态合并部署是需要所有模块和基座一起发布的,为了尽可能不影响开发同学效率,我们同时支持单模块独占基座多模块合并部署两种发布模式,方便在开发、日常、预发、线上等环境进行实地试运行,完整验证业务链路。 

首先按业务域及流量情况将应用拆分为基座或模块:

bbede363efe4f03dbaeaaaafc617b578.png

然后发布系统根据基座与模块的映射关系,进行打包发布。目前我们是通过 K8S 部署服务的,源代码经过编译打包成 Docker 镜像再进行发布,为复用原有流程,快速上线,模块和基座都打包为镜像。模块由模块负责人进行打包,基座由业务域负责人打包,并根据迭代节奏进行定期发布。

13623ff828d3f1a16998311b05fa4170.png

InitContainert 先启动模块镜像,再将模块的 ark 包拷贝到/home/docker/module/biz目录下:

38ae5ad2a05e431292760cf67044c96e.png

通过-Dcom.alipay.sofa.ark.static.biz.dir 参数实现合并部署:

132adeeda04fac4e503c7cbdeb6e5eab.png

日志目录

日志路径由原来的 home/docker/logs,在合并模块部署后调整为 home/docker/logs/appname/,日志收集模板按此进行调整。std 输出的日志会打印在基座应用的 std 中,比如异常类的 printStackTrace,所以尽量避免使用 stdout 和 stderr。

域名切换

由于应用合并,原来的域名需要切换到新的域名上来。由单一应用过渡到合并部署模式的发布过程中需要注意对等的流量切换。

有两种域名切换方式:  

1、调用方切换到新域名。✅优点:简单,不需要路由。❌缺点:需要调用方配合调整,应用模块与基座关系调整时同样需要再次调整。

2、域名路由。调用方还是访问原域名,通过内部网关路由到新域名。✅优点:灵活。❌缺点:多一次调用。

我们最终采用路由的方式来进行域名切换。 

上线实施  

Implementation

目前一期按业务域划分为 18 个基座,60 多个模块在一些数据中心(如中国区、美西区等)进行合并部署后稳定运行。

9a716251ac6558f746cb1d6e4c0aa5a1.png

如某基座合并部署了 8 个模块:

b94839a57d035f4213aa4d81705b214a.png

合并后,每个模块由原来的 1C2G 变为 2C4G,节省资源 6C12G。  

a73d5f90342bd049495116285a0fd478.png

8 个模块非合并部署前的启动时间如下图 8 条记录显示,在 1-3 分钟之间。 ‍

837c0846b0c3c01962b44849d1c4cf90.jpeg

而静态合并部署后的基座启动时间见下图,约 2 分 45 秒。8 个模块分别交付,每个 1-3 分钟,统一成 2 分 45 秒一次性完成交付,大大提高了整体交付效率

89c3c508e9293a671d1b03aa93385803.jpeg

总体来说,在内存方面,对于应用初始启动内存占总消耗内存比例较高、低流量的业务场景,应用合并部署的内存节省收益越大。60 个模块共节省 70G 内存。线上 POD 数约有 6000 多个,预计可节省 6000 多 G 内存。相应地在 CPU 资源利用率方面,大部分业务应用都是 IO 密集型,而合并部署能将 CPU 较为充足地利用起来,线上 Pod 合并部署后能节省 3000 核

在调用性能和网络 IO 优化上,大流量的业务场景从远程调用变成本地调用,网络 IO、序列化反序列的 CPU 开销以及调用性能、稳定性上都能得到大幅度提升

后续演进 

Evolution

当前我们完成的是静态合并部署能力,很好地解决了资源浪费问题。为了进一步提升研发效率,我们正在开发动态合并部署能力,为公有云大规模合并部署打下基础,也为未来更长远的 Serverless 能力提供基座。

为了打造对开发者更友好的 Serverless 能力和平台:

1、我们需要增强模块热卸载能力。

2、进一步的模块瘦身,将更多的通用组件下沉到基座中,减少公共类库和框架加载和初始化运行开销。不但节省内存,还能加速子模块的启动速度,如尝试复用基座数据源和基座拦截器。

3、弹性伸缩,目前依赖应用的 CPU、内存等指标对进程进行弹性伸缩,而合并部署后,可以通过预热基座的方式,让模块的扩缩容速度更快。另外由于模块级别资源的监控指标较难获取,也可以考虑 QPS 等其他维度的指标。 

在整个调研、升级改造及实施的过程中,社区同学多次和我们进行线上和线下交流讨论,提供了许多蚂蚁内部和其他外部企业的最佳实践, 给予了我们很多帮助与指导,在共同的努力下最终成功落地 Koupleless!

希望后续社区在模块应用的卸载稳定性上有进一步的优秀表现,为我们的动态发布打下坚实的基础!也欢迎更多同学加入社区,一起参与共建!

Koupleless Star 一下✨:

https://github.com/koupleless/koupleless

  本周推荐阅读  

635ae863ce683b5f70af267d7d63deb5.png

深度案例解读 Koupleless 在南京爱福路的落地实践

1ea4f4e36dc5ad96e9a83818dd9b1abc.png

Koupleless 单进程多应用如何解决兼容问题

9d771756dcc76e93c314cce0515b6595.png

Koupleless 模块化隔离与共享带来的收益与挑战

b47a3b4edf9ccdae6510e7f0b462e262.png

Koupleless 带来拆分插件,帮你提高协作开发效率!

b486df0724dc45a27a391830b1f6eb36.png

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

强化学习与深度强化学习:深入解析与代码实现

个人主页&#xff1a;chian-ocean 文章专栏 强化学习与深度强化学习&#xff1a;深入解析与代码实现 强化学习&#xff08;Reinforcement Learning, RL&#xff09;是一种机器学习方法&#xff0c;通过智能体&#xff08;agent&#xff09;与环境&#xff08;environment&am…

[Linux] 创建可以免密登录的SFTP用户

本文主要包含: 创建新用户创建密钥对用于免密登录新用户将新建用户改造为SFTP用户为SFTP上传数据设置限速 1. 创建新用户 sudo useradd sftp_user sudo passwd sftp_user # 输入密码2. 创建密钥对 参考这篇文章 [Linux] 生成 PEM 密钥对实现服务器的免密登录 3. 将新建用户…

性能测试:流量回放工具-GoReplay!结合一款无需CA证书即可抓取HTTPS明文的工具,简直无敌

性能测试&#xff1a;流量回放工具-GoReplay&#xff01;结合一款无需CA证书即可抓取HTTPS明文的工具&#xff0c;简直无敌。 GoReplay 是一个开源网络监控工具&#xff0c;可以将实时 HTTP 流量捕获并重放到测试环境。 应用成熟的过程中&#xff0c;测试所需的工作量往往会成…

设计模式之组合模式(Composite)

一、组合模式介绍 组合模式(Composite Pattern) 的定义是&#xff1a;将对象组合成树形结构以表示整个部分的层 次结构。组合模式可以让用户统一对待单个对象和对象的组合。 如在windows操作系统中的目录结构&#xff0c;其实就是树形目录结构&#xff0c;可以通过 tree /f 命令…

PCL 点云配准-4PCS算法(粗配准)

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 加载点云数据 2.1.2 执行4PCS粗配准 2.1.3 可视化源点云、目标点云和配准结果 2.2完整代码 三、实现效果 3.1原始点云 3.2配准后点云 PCL点云算法汇总及实战案例汇总的目录地址链接…

面试八股(自用)

什么是java序列化&#xff0c;什么时候需要序列化? 序列化是指将java对象转化成字节流的过程&#xff0c;反序列化是指将字节流转化成java对象的过程。 当java对象需要在网络上传输 或者 持久化到存储文件中&#xff0c;就需要对java对象进行序列化处理。 JVM的主要组成部分…

Lumerical学习——分析工具(Analysis tools)

一、分析工具和模拟环境&#xff08;Analysis tools and the simulation environment&#xff09; 模拟计算完成后&#xff0c;模拟计算数据紧接着写到模拟工程文件中&#xff1b;甚至当模拟计算提前结束时计算得到的部分数据集也会写到文件中。当模拟完成后单击退出按钮、或者…

为什么inet_ntoa会返回错误的IP地址?

目录 1、调用inet_addr和inet_ntoa实现整型IP与点式字符串之间的转换 1.1、调用inet_addr将点式字符串IP转换成整型IP 1.2、调用inet_ntoa将整型IP转换成点式字符串IP 2、调用inet_ntoa返回错误点式字符串IP的原因分析 3、解决多线程调用inet_ntoa返回错误点式字符串IP的办…

RTSP与ONVIF协议的区别及其在EasyCVR视频汇聚平台中的应用

在视频监控和物联网设备领域&#xff0c;RTSP&#xff08;Real Time Streaming Protocol&#xff09;和ONVIF&#xff08;Open Network Video Interface Forum&#xff09;是两个重要的协议&#xff0c;它们各自在视频流的传输和控制上发挥着不同的作用&#xff0c;并在实际应用…

,1.2,unity动画Animator

1.步骤&#xff1a; &#xff08;1&#xff09;导入模型 &#xff08;2&#xff09;添加状态机 &#xff08;3&#xff09;添加动画控制器 &#xff08;4&#xff09;通过脚本触发条件 (5)控制脚本代码 using System.Collections; using System.Collections.Generic; usin…

HarmonyOS中ArkUi框架中常用的装饰器

目录 1.装饰器 1&#xff09;Component 1--装饰内容 2&#xff09;Entry 1--装饰内容 2--使用说明 3&#xff09;Preview 1--装饰内容 2--使用说明 4&#xff09;CustomDialog 1--装饰内容 2--使用说明 5&#xff09;Observed 1--装饰内容 2--使用说明 6&#xff09;ObjectLin…

我的创作纪念日-365天的感悟

时光荏苒&#xff0c;岁月如梭。转眼间&#xff0c;自己在CSDN注册已经整整15个年头了。回想起当初&#xff0c;还是个满怀憧憬、对未来充满无限好奇的学生哥。如今&#xff0c;虽然身份和角色发生了诸多变化&#xff0c;但CSDN始终陪伴着我&#xff0c;见证了我的成长与蜕变。…

HDFS详细分析

目录 一、HDFS架构 &#xff08;1&#xff09;Block - 数据块 &#xff08;2&#xff09;MetaData - 元数据 &#xff08;3&#xff09;NameNode - 主结点 &#xff08;4&#xff09;DataNode - 从结点 &#xff08;5&#xff09;SecondaryNameNode 二、HDFS的特点 &…

Spring Security 基础配置详解(附Demo)

目录 前言1. 基本知识2. Demo3. 实战 前言 基本的Java知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09;【Java项目】实战CRUD的功能整理&#xff08;持续更新&#xff09; 1. 基本知识 HttpSecurity 是 Spri…

ubuntu 安装docker, docker-compose

1. 安装curl apt-get update apt upgradeapt install curl 2.安装&#xff1a; curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun 3. 验证&#xff1a; docker -v 4. 安装docker-compose : # 下载 curl -L "https://github.com/docker/compose/rel…

无人机之定高算法篇

一、无人机高度测量原理 无人机的高度测量通常依赖于多种传感器&#xff0c;其中主要包括&#xff1a; 气压计&#xff1a;通过测量大气压力的变化来确定高度。在大气中&#xff0c;随着高度的增加&#xff0c;气压会逐渐降低。无人机搭载的气压计会感知大气的压力变化&#…

DBSwitch和Seatunel

一、DBSwitch 什么是DBSwitch?它主要用在什么场景&#xff1f; 通过步骤分析可以看到这个是通过配置数据源&#xff0c;采用一次性或定时方案&#xff0c;同步到数据仓库的指定表&#xff0c;并且指定映射关系的工具。有点类似于flinkcdc的增量同步。 参考&#xff1a; dbs…

【SpringBoot】13 XML格式的请求和响应

Gitee仓库 https://gitee.com/Lin_DH/system 介绍 可扩展标记语言 (Extensible Markup Language, XML) &#xff0c;标准通用标记语言的子集&#xff0c;可以用来标记数据、定义数据类型&#xff0c;是一种允许用户对自己的标记语言进行定义的源语言。 XML是标准通用标记语言…

重学SpringBoot3-集成Redis(十二)之点赞功能实现

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-集成Redis&#xff08;十二&#xff09;之点赞功能实现 1. 点赞功能的场景分析2. 项目环境配置2.1. 依赖引入2.2. Redis 配置 3. 点赞功能的实现3.1. 点…

【R语言】gadm全球行政区划数据库

我R语言不熟、也不是学GIS的。仅用于记录。 文章目录 一、gadm 数据库简介二、R 语言示例三、sf 包的函数 一、gadm 数据库简介 GADM&#xff08;全称Database of Global Administrative Areas&#xff09;是一个高精度的全球行政区划数据库&#xff0c;它包含了全球所有国家和…