SaaS 电商设计 (十) 记一次 5000kw 商品数据ES迁移 (详细的集群搭建以及线上灰度过程设计)

news2024/11/29 2:53:51

目录

  • 一.背景
  • 二.技术目标
  • 三.技术方案
    • 3.1 整体流程
    • 3.2 ES 切换前:完成整体新集群的搭建.
        • i:拓扑结构设计
        • ii: 如何选择整体的 **ES** 集群配置.
    • 3.3 **ES** 版本切换中
      • 3.3.1 多client版本兼容
      • 3.3.2 Router的设计
    • 3.4 ES 切换后
    • 3.5 开箱即用
  • 四.总结

专栏系列

-SaaS 电商设计 (一) 如何设计一套适应多规格的商品服务
-SaaS 电商设计 (二) 私有化部署-缓存中间件适配
-SaaS 电商设计 (三) 电商黄金流程(商详,购物车,提单)梳理,持续更新(建议收藏)
-SaaS 电商设计 (四) 谈一谈电商系统高并发多耦合上下游的系统压测怎么做
-SaaS 电商设计 (五) 私有化部署-实现 binlog 中间件适配(附源码)
-SaaS 电商设计 (六) 实现 id 生成器本地化生产 (附源码)
-SaaS 电商设计 (七) 利用 Spring 扩展点 ImportBeanDefinitionRegistrar 实现 toB 系统对接(附源码)

-SaaS 电商设计 (八) 直接就能用的一套电商商品池完整设计方案(建议收藏)
-SaaS 电商设计 (九) 动态化且易扩展的实现购物车底部弹层(附:一套普适的线上功能切量的发布方案)

一.背景

    目前商品模块整体的商品数据存储频繁会使用到 Elasticsearch(以下简称 ES ) . 主要场景是一些复杂的 B 端查询以及一些非即时性的二方商品服务提供.

    由于非技术原因,不得不从技术侧发起了一个 ES 迁移的技术性改造.具体的原因有以下几点:

  • 原本的 ES 存储托管于云平台.由于资源到期(半年后)后续不再支持. 屋漏偏逢连夜雨,恰逢最近由于业务突增一些技术问题不得不进行技术支持,沟通尔尔,发现时下响应效率不高.最终也没有实质性解决业务问题.
  • 现有的 ES 拓扑结构在目前的业务场景下难以更好的动态扩缩容.

基于以上的重重原故,计出无奈不得不进行 ES 内迁至内网.

以下为现有的拓扑结构.

每个节点的信息如下:

PS:如上的node信息可以通过 _nodes/ 得到.

从以上的节点拓扑以及系统查看可知,目前的 ES 4个节点均为主节点&数据节点.

目前的 Transport Client 形式的链接,如果需要水平扩容无法做到动态扩容,系统无感知,需要重新修改链接完成项目重启,对于分布式系统来说,在高峰时刻无疑是致命的.搞不好裁员广进的下一个就是我.

  • 目前的链接方式存在一个跨网的网络链接.

目前是一个内部网络需要跨网去链接云端 ES ,中间不可避免涉及网络损耗.

基于以上的这三点,不得不发起一个 ES 集群迁移.

二.技术目标

  • Objectives: 完成 ES 集群迁移

  • Key Results

    • 保证线上 租户150+ ,5000w+ 商品数据.平稳迁移.其中 重点 KA 客户数据 3000w 必须重点保证可用性;

    • 迁移过程完成拓扑结构升级.保证后续的 ES 水平扩容能够对于服务透明;

    • 迁移过程保证出现问题能够随时回切;

三.技术方案

3.1 整体流程

图中以数据同步流程为例.查询的流程类似.

3.2 ES 切换前:完成整体新集群的搭建.

i:拓扑结构设计

升级结构的必要性:

在整体的第一版过程中我们使用的是 5.4.3 版本.并采用的 TCP 节点直连的方式.用过的同学都知道.节点直连太坑了,连上了就别动.一旦集群发生节点变更.管你是水平还是集群重建都得动代码.生产环境哪里由得你乱动.就这样无法做到动态水平扩容后代码的无改动.
(使用 Http 方式链接可以或者增加协调节点后通过链接协调的节点的方式).且在整体的集群搭建中仅使用了数据节点.那么就不可避免出现现有的数据节点既承担数据存储,索引结构维护,归并排序等本不属于数据节点的角色内容.当然在数据量并不大的场景现有的拓扑结构是满足业务的.那随着业务的发展,势必是需要更新演进的.

延伸一下:节点角色

  • 协调节点:转发来自 ES client 的请求至各自的数据节点.数据节点本地查询符合条件的数据后返回给协调节点.协调节点通过本地归并排序分页等操作,返回最终符合条件的查询结果.通过以上描述可知,协调节点需要大量的 CPU ,以及内存资源去完成节点的工作.

通过下面的配置创建一个仅用于协调的节点:

node.master: false
node.data: false
node.ingest: false
  • 主节点:维护集群内的索引增加,删除,更新,以及集群的管理工作.主节点是全局唯一的,一旦出现故障将从有资格成为 Master 的节点中进行选举.所以实际过程中配置为 Master 节点的数量一般建议为奇数个(但是集群中将只有一个生效),防止分布式常见的脑裂问题出现.
node.master: true
node.data: false

如上我们通过配置了数据节点以及主节点两个配置,将一个节点的角色同时设置为了主节点以及数据节点.那么在实际过程中面对数据量较多的场景,该节点就有可能因为角色的多种承受了不该承受的压力,进而导致集群的不稳定.所以这也是本次改造的一个原因之一.

  • 数据节点:顾名思义就是单纯用以处理数据(存储,查询).所以数据节点就实际部署过程中对于内存以及 cpu 的要求都需要比较高.

如何配置一个ES数据节点:

node.master: false
node.data: true
node.ingest: false
ii: 如何选择整体的 ES 集群配置.
  • 存储的配置

  • 这里建议参考腾讯云官方的计算逻辑.虽然是腾讯云我觉得还是考虑的比较中肯.主要是以下几点:

    • 副本的数量,相应的倍数即可.如:1个副本.那么单节点来说就是主(20g)+副(20g)=40g
    • 原本宿主机的其他资源损耗5%.40+0.05*40=42g;
    • es 内部任务开销的资源损耗20%.42+42*0.2=50.4g;
    • es 本身的结构存储10%.50.4+50.4*0.1=55.8g.
      https://cloud.tencent.com/document/product/845/19551
  • 节点的选择
    建议有一定查询的量级的场景.比如说千万级大概100T-200T的级别的查询都配置上协调节点.主节点.数据节点.保证各个节点都能够各司其职.而且也利于后期的垂直(单节点内存,cpu扩展),水平(新增同配置节点).

  • 分片的选择

    • 首先主副分片的选择.一般场景下如果对于主副没有特别要求的情况是 1:1 的处理就好了. 虽然之前查阅腾讯的官方文档表明无单点故障考虑可以不用增加副本. 我想说的是都是生产环境多少还是要考虑的,除非你真的是在搞一个玩具.
    • 建议分片大小.这里其实是有一些约定俗成的逻辑.一般大小 10g-50g .避免出现大的分片,以至于故障发生时影响集群的稳定性.另外在重新 rebalance 时也难以在节点之间移动.具体参考官方的文档:
      https://www.elastic.co/guide/en/elasticsearch/reference/7.9/size-your-shards.html#shard-size-recommendation

我是怎么配置的?

主节点3个:4C16G100GB
网关节点3个:4C16G100GB
数据节点8个:8C16G500GB

3.3 ES 版本切换中

3.3.1 多client版本兼容

通过在业务代码中插入自定义 Router . Router 其实就是本次在整体ES 迁移过程中比较核心的内容.主要核心职责就是为了完成在流量进入的过程中(写入,读取)的版本 ES client 获取.下面通过图示来放大 ES Router 的核心设计内容.

首先是整体 spring 容器对于 client 的兼容处理.尝试了几个版本之后最后使用的 maven 是.
也就是如下版本才能完成 两种不同 TCP 版本 ES client 的同时链接(这一次选择了先不更改 ES client 的链接方式不然更改为 Rest sdk 工作量可能兜不住)

 <dependency>
      <groupId>org.elasticsearch.client</groupId>
      <artifactId>transport</artifactId>
      <version>5.6.10</version>
  </dependency>
  <dependency>
      <groupId>org.elasticsearch</groupId>
      <artifactId>elasticsearch</artifactId>
      <version>5.6.10</version>
  </dependency>
  <dependency>
      <groupId>org.elasticsearch.plugin</groupId>
      <artifactId>transport-netty4-client</artifactId>
      <version>5.6.10</version>
  </dependency>
<!-- ES集群5.x -->
    <bean name="esFiveClient" class="com.xxx.xxx.ElasticsearchTemplate" init-method="init">
        <property name="clusterNodes" value="${es.clusterNodes.own}"/>
        <property name="clusterName" value="${es.clusterName.own}"/>
        <property name="clientTransportSniff" value="false"/>
        <property name="username" value="${es.cluster.username}" />
        <property name="password" value="${es.cluster.password}" />
    </bean>

    <!-- 新集群 -->
    
    <!-- ES集群6.x -->
    <bean name="esSixClient" class="com.xxx.xxx.ESForSixTemplate" init-method="init">
        <property name="clusterNodes" value="${es6.clusterNodes.own}"/>
        <property name="clusterName" value="${es6.clusterName.own}"/>
        <property name="clientTransportSniff" value="false"/>
        <property name="username" value="${es6.cluster.username}" />
        <property name="password" value="${es6.cluster.password}" />
    </bean>
<!-- 门店商品 -->
  <!-- 门店商索引 同时注入两个版本client -->
    <bean id="storeSkuInfoIndexBeanService" class="com.xxx.xxx.StoreSkuInfoIndexServiceImpl">
        <property name="index" value="${es6.storeSku}"/>
        <property name="type" value="${es6.storeSku}"/>
        <property name="elasticsearchTemplate" ref="esFiveClient"/>
        <property name="esTemplate" ref="esSixClient"/>
    </bean>
    
....
/**
* 通过配置内容判定是否读取新版 6.0 client 还是 5.0 client
* 
*/
private SearchRequestBuilder getSearchRequestBuilderBySwitch(Boolean isGoToJes) {
        Client client = isGoToJes ? esSixClient.getClient() : esFiveClient.getClient();
        String indexReal = isGoToJes ? indexForSix : index;
        String typeReal = isGoToJes ? typeForSix : type;
        return client.prepareSearch(indexReal).setTypes(typeReal);
    }
....

在完成maven的适配之后我们就可以完成 spring 容器的两个 client 注入了.其实到了这一步算是其中一个非常关键的技术点被解决了.因为只要拿到了 client 后续相当于无论我们是用什么手段去实施切量就比较好做了,尽可能是去做到稳定性和可回滚.
在这里插入图片描述
具体调用的链路以及实施切量的过程.

3.3.2 Router的设计

核心的思路是通过线上configServer配置来完成租户白名单控制,如果在白名单内我们将通过切到 es 6.0 client 来实现新集群切量.这样一来就能够做到线上的无缝切量以及即时的回退.

白名单配置

{
  "2": "ALL_STORE_ID",
  "100001601": "10005201",
  "100005405": "ALL_STORE_ID",
  "100008006": "ALL_STORE_ID",
  "100008007": "10023233,10023835" 
}

key:为具体租户Id.
value:ALL_STORE_ID :表达则是全部门店进行切换.
具体的门店值,则是表达指定租户指定门店完成切换.

具体灰度切量过程.

第一阶段:完成数据量在10w下租户的切量.
第二阶段:完成数据量在100w下租户的切量.
第三阶段:完成数据量在1000w下租户切量.
第四阶段:完成重点客户切量.

最终我们大概是花了三个月的时间完成了整体的切量.

3.4 ES 切换后

  • 持续的流量监控

    其实并不仅仅是 中间件切换 ,包括代码下线,老开关的移除.都需要一定的监控数据观测.在一定时间段后从监控的数据上能够确认流量的关闭.那么才能够继续往下继续后续的代码动作.

    一般具体的方式就是在代码中插入指定的观测入口.比如上报流量监控的key,通过流量观测平台来完成日常的监控.比如3个月(具体的业务场景请参考具体的指标和时间跨度)后仍然没有流量.那么确认切换完成.

  • 流量监控之后的代码移除以及资源的销毁

    从资源和成本的角度,特别是时下降本增效的背景.有能力的前提下肯定是资源和成本能够进一步收缩是为更好.

3.5 开箱即用

老规矩:

https://github.com/Baixiu-code/elasticsearch-util-starter 一键直达

四.总结

    行文总结了一次线上的由技术侧发起的一起 ES 集群数据迁移过程.从集群搭建到线上灰度以及灰度切换之后的资源释放的这样一个完整技术方案上线的闭环过程.欢迎大家一起讨论交流. 最后也放上了一个比较传统可能大家仍在使用的 Transport client 使用 api 封装.持续更新中.后续将更新 Restful ,欢迎大家的关注,交流.

赠人玫瑰 手有余香 我是柏修 一名持续更新的晚熟程序员
期待您的点赞,关注加收藏,加个关注不迷路,感谢
您的鼓励是我更新的最大动力
↓↓↓↓↓↓

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每日面经分享(pytest装饰器)

pytest装饰器 a. pytest.mark.parametrize&#xff1a;这个装饰器用于标记测试函数&#xff0c;并为其提供多组参数进行参数化测试。可以使用元组、列表、字典等形式来指定参数组合。 import pytestpytest.mark.parametrize("num1, num2, expected", [(2, 2, 4), (5…

2024 3.23~3.29周报

上周工作 SVInvNet论文研读 本周计划 加入DenseNet&#xff0c;修改网络架构&#xff0c;跑代码 总结 DenseNet 密集块&#xff1a;DenseNet将网络分成多个密集块&#xff08;Dense Block)。在每个密集块内&#xff0c;每一层都连接到前面所有的层。这种跳跃连接有助于解…

【Laravel】07 快速套用一个网站模板

【Laravel】07 快速套用一个网站模板 1. 新增post表2.补充 &#xff1a;生成Model、Controller、迁移文件3. 使用php artisan tinker4. 网站模板下载 课程地址 1. 新增post表 在Model中创建Post (base) ➜ example-app php artisan make:model Post Model created successfu…

手机有线投屏到直播姬pc端教程

1 打开哔哩哔哩直播姬客户端并登录(按下图进行操作) 2 手机用usb数据线连接电脑(若跳出安装驱动的弹窗点击确定或允许),usb的连接方式为仅充电(手机差异要求为仅充电),不同品牌手机要求可能不一样,根据实际的来 3 在投屏过程中不要更改usb的连接方式(不然电脑会死机需要重启) …

verilog中的testbench语句——display,fopen,fread,fwrite——更新中

一、fopen bmp_file_read $fopen("../pic/picture.bmp","rb"); 要注意这类操作文件的函数&#xff0c;在vivado2018自带的仿真软件里&#xff0c;不综合直接仿真&#xff0c;它的当前文件夹如图所示。 一、fwrite $fwrite(bmp_file_write,"%c"…

LeetCode - 岛屿数量

200. 岛屿数量 第一种写法&#xff1a;遍历岛屿&#xff0c;当遇到岛屿的时候&#xff0c;就开始进行深搜&#xff0c;遇到岛屿就将岛屿从1变为0。 class Solution { public:int dx[4] {0,0,1,-1};int dy[4] {1,-1,0,0};void dfs(int i, int j, vector<vector<char>…

【第十二届“泰迪杯”数据挖掘挑战赛】【2024泰迪杯】B题基于多模态特征融合的图像文本检索—解题全流程(论文更新)

【第十二届“泰迪杯”数据挖掘挑战赛】【2024泰迪杯】B题基于多模态特征融合的图像文本检索更新&#xff08;论文更新&#xff09; ​ 本节主要更新了论文、训练日志的log数据提取&#xff08;Loss、ACC、RK&#xff09;等数据可视化作图的代码 B题交流QQ群&#xff1a; 4583…

C#调用FreeSpire.Office读取word数据的基本用法

FreeSpire.Office是Spire.Office的免费版本&#xff0c;后者支持全面、复杂的office文件操作功能&#xff0c;包括文件格式转换、文档操作、文档打印等&#xff0c;详细介绍见下图及参考文献1。本文学习FreeSpire.Office的基本用法并用其获取word文档的基本信息。   新建Win…

JavaScript动态渲染页面爬取——Splash的使用

JavaScript动态渲染页面爬取 JavaScript动态渲染得页面不止Ajax一种。例如&#xff0c;有些页面的分页部分由JavaScript生成&#xff0c;而非原始HTML代码&#xff0c;这其中并不包含Ajax请求。还有类似淘宝这种页面&#xff0c;即使是Ajax获取的数据&#xff0c;其Ajax接口中…

Unix中的进程和线程-1

目录 1.如何创建一个进程 2.如何终止进程 2.2遗言函数 3.进程资源的回收 4.孤儿进程和僵尸进程 孤儿进程 (Orphan Process)&#xff1a; 僵尸进程 (Zombie Process)&#xff1a; 代码示例&#xff1a; 5. 进程映像的更新 在Linux中&#xff0c;进程和线程是操作系统进行工作调…

【Docker笔记02】【常用软件安装】

一、前言 本系列是根据 B 站 尚硅谷 Docker 视频 学习记录笔记。因为没有视频课件&#xff0c;部分内容摘自 https://www.yuque.com/tmfl/cloud/dketq0。 本系列仅为自身学习笔记记录使用&#xff0c;记录存在偏差&#xff0c;推荐阅读原视频内容或本文参考笔记。 本文主要介…

Adobe ColdFusion 任意文件读取漏洞复现(CVE-2024-20767)

0x01 产品简介 Adobe ColdFusion是美国奥多比(Adobe)公司的一套快速应用程序开发平台。该平台包括集成开发环境和脚本语言,将可扩展、改变游戏规则且可靠的产品的愿景变为现实。 0x02 漏洞概述 由于 Adobe ColdFusion 的访问控制不当,未经身份认证的远程攻击者可以构造恶…

go语言基础学习-通道(续)

1、如何有序的从通道取值 第一种 判断ok的值 package day13import "fmt"func D131() {ch1 : make(chan int)// 开始goroutine将0-100的数据发送到ch1中go func() {for i : 0; i < 100; i {ch1 <- i}close(ch1)}()for {i, ok : <-ch1 // 通道关闭后再取值o…

计算机网络-HTTP相关知识(一)

HTTP基础 基本概念&#xff1a;HTTP是一种计算机之间交流通信的规范&#xff0c;它允许数据在两点之间传输&#xff0c;这个过程可以包括中转或接力。HTTP不仅仅包括文本&#xff0c;还可以包括图片、音频等超文本。状态码&#xff1a;HTTP状态码分为五类&#xff1a; 2xx&…

移植DM9000驱动至内核(linux-2.6.32.24)

目录 概述 1 移植 DM9000驱动 1.1 添加设备驱动代码 1.2 调整 DM9000 所用的位宽寄存器 1.3 配置MAC地址 2 配置和编译DM9000 Driver至内核 3 验证 概述 本文主要介绍如何移植DM9000的驱动到linux-2.6.32.24内核&#xff0c;笔者详细记录了内核移植过程中遇见的问题&…

【InternLM 实战营第二期笔记】InternLM1.8B浦语大模型趣味 Demo

体验环境 平台&#xff1a;InternStudio GPU&#xff1a;10% 配置基础环境 studio-conda -o internlm-base -t demo 与 studio-conda 等效的配置方案 conda create -n demo python3.10 -y conda activate demo conda install pytorch2.0.1 torchvision0.15.2 torchaudio2…

如何使用 Python 本地客户端操作读写云服务器 Redis 缓存数据库详细教程(更新中)

Redis 基本概述 Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的使用 ANSI C 语言编写的、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库&#xff0c;并提供多种语言的 API。它通常被称为数据结构服务器&#xff0c;因为值&#xff08;value…

Docker配置Mysql

1.首页搜索mysql镜像 2.选择对应版本的MySQL&#xff0c;点击pull 3.pull完成以后&#xff0c;点击images&#xff0c;这里可以看到刚刚pull完成的mysql版本 4.打开命令界面&#xff0c;运行命令 docker images ,查看当前已经pull的images 5.运行命令设置mysql docker run -it…

PHP三种方式读取RSA密钥加解密、签名验签完整教程

目录 第一步、生成公私钥 第二步、三种方式读取RSA密钥 第1种&#xff1a;公私钥弄成一行&#xff0c;必须一行没有空格和换行 第2种&#xff1a;直接复制生成公私钥 第3种;复制密钥存储为.pem文件后缀 第三步、RSA加解密 第四步、RSA签名以及验证签名 第五步、封装完整…

[图像处理] MFC载入图片并进行二值化处理和灰度处理及其效果显示

文章目录 工程效果重要代码完整代码参考 工程效果 载入图片&#xff0c;并在左侧显示原始图片、二值化图片和灰度图片。 双击左侧的图片控件&#xff0c;可以在右侧的大控件中&#xff0c;显示双击的图片。 初始画面&#xff1a; 载入图片&#xff1a; 双击左侧的第二个控件…