查看原文
其他

被自己坑了...

苏三呀 苏三说技术 2022-04-09

大家好,我是苏三,又跟大家见面了。文末送书啦!!!

前言

前段时间,我们线上系统出现了一个事故:用户创建了商品,在商城的商品列表页看不到,也搜索不到。、

这个问题持续了大概半个小时,最后发现竟然是我的锅。

这个事情怎么说呢,完全是我自己把自己坑了。到底怎么回事呢?

1. 从需求说起

1.1 背景

由于我们这个迭代是个大版本,上线的日子要临近了。这次上线需要运营配合提供很多商品属性的数据,他们需要把第三方的属性和我方系统的属性,在excel表格中匹配起来。

原本是这样规划的:由运营同学在excel表格中手动对应双方属性的映射关系。

但后来,他们觉得属性太多了,如果他们人工在excel表格中对应属性映射关系,可能时间上有点来不及。

于是,他们在某次会议上,特意给我提了需求,希望我可以通过程序帮他们在excel中,把双方的属性值映射上。

有一个要求就是要:

因为其他同事,还要基于这份excel数据,做一些后续处理。

1.2 原始需求

刚开始开会时,运营说的需求是:他们提供一个excel表格,里面有分类和属性字段,然后让我在程序中全匹配,把能够匹配上的属性编号和属性,在excel的另外两列中返回给他们。

然后,他们根据这份excel数据,把匹配不上的(即另外两列为空)数据,在我们系统中手动录入,这样最终都能匹配上。

1.3 加戏了

本来我觉得这个需求挺简单的。

但后来,运营加戏了(加新需求)。

其实,刚开始运营没说完。

后来发现,他们要接入两个厂商。

而且运营提供的两个厂商的excel表格中字段的格式不一样,没法用一套程序搞定。

而且,我们发现有部分属性中包含了一个区间范围,跟我们系统的数据肯定是对应不上的,必须要拆分属性后再匹配。

很显然,运营是不愿意做这种手动拆分的工作的,这事他们想我帮他们用程序处理,这就给我增加了不少工作量。

此外,厂商1还有一个特殊需求:运营手动把excel中的部分数据剔除掉,然后基于这份新数据重新匹配一份新excel数据。

2. 最快的方案

了解运营的需求之后,我简单的分析了一下。按需求的优先级,排了一个顺序:

  1. 导出厂商1的属性数据。
  2. 导出厂商2的属性数据。
  3. 给厂商1导一份特殊的属性数据。
  4. 导出区间范围能匹配上的数据。

如果这些需求都写程序处理,可能要写4个程序,而且还需要花时间部署代码,我怕时间上来不及。

于是我想了一个快速处理需求1、2、3的办法即:直接通过sql语句查询出所需数据

不过这套方案的前提是:需要把excel中的数据导入到生产环境中

为了保险起见,我先把excel中的数据导入dev环境中。等我写好sql,测试好数据之后,再导入生产环境。

使用数据库管理工具:Navicat PremiumImport Wizard功能,可以轻松将excel表格中的数据直接导入一张新表中。它里面可以指定excel的sheet对于哪张表,指定excel中的列对应表中哪些列。

由于这些需求都是新表,无需特别指定,我就按默认的表名和字段名导入数据了。

但有个问题就是:表名和字段名都是中文的,因为excel中的sheet名和sheet中的字段名都是中文的。

其实,我当时已经发现了这个问题。

但当时又想了想,表中字段比较多,要一一改成英文的,光起名字要花些时间。这些字段最终还是要转换成运营可以看得懂的中文的字段名,这样转来转去有点画蛇添足,浪费时间。

而且这张表导入生产环境之后,是一张临时表,用完了就会被删除的,影响不大。

此外,这张表是新加的,如果没有程序使用的话,应该是不会有问题的。

所以,当时没多想,就找人把数据导入生产环境了。

导出数据的方法很简单:使用Navicat PremiumDump SQL File中的Structure + Data即可。

这样该数据库工具,就会把相关表的建表的create语句,和插入数据的insert语句,导出到一个.sql后缀的文件中。

有个小问题就是:每条数据会生成一个insert语句,如果太多了放到生成环境执行,执行效率会比较低。

这时可以将insert脚本复制到idea或者其他的工具中打开,然后全文替换一下,去掉多余的insert,拼接成一个insert语句。

然后再用在线的sql压缩工具,比如:https://tool.lu/sql,压缩一下去掉多余的空格。

这样插入数据的sql放到生产环境执行,效率要快很多。

运营提供的excel表格中的数据,被导入生产环境之后。按计划,通过一条sql语句,把运营所需要的结果直接查询出来,然后把结果复制到excel表格中。(注意:如果查询结果的数据太多,不建议这么玩)。

按上面的做法,我很快完成了需求:1、2、3,并且把运营所需要的数据及时给他们了。

3. 一个插曲

原本按计划,导完数据之后,生产环境中临时表是要删除的。

但出现了一个小插曲,运营给我提了一个临时需求:需要重新导一份厂商2的数据给他们。

他们已经按照表格中的内容,把需要添加的属性已经添加到系统中了。需要我们重新导一份数据,确认一下,现在是否所有数据都能匹配上。

此时,我当时在庆幸幸好数据没删。

运营的这个临时需求,在线上执行相同的sql很快就把数据导出来了。

1分钟实现需求,当时那叫一个:

而且我观察了一下,系统没有出现异样。

给运营把数据导完之后,我就忙其他事情去了,把删除数据这个事情给忘了。

4. 线上出现问题

第二天上午,领导把我叫过去说:canal服务挂了

我们分析canal了异常日志后发现,这个问题是由于canal订阅者,读取中文的表名时,出现了乱码,没有成功读取到。该程序直接抛了异常,导致canal订阅者不能正常工作了。

这个问题对用户的影响是:用户创建了商品,在商城的商品列表页看不到,也搜索不到,有用户投诉到运营那边了。

我当时的第一反应是:这也能挂?

我当时不知道下游的业务系统,通过canal监听了我这边的整个数据库。

话不多说,先把问题解决了吧。

我们当时为了快速解决问题,先把中文的临时表都删了,然后把canal重启一下。

果然,这个办法是有效的。

canal监听者立马恢复正常了。

当天,没有再出现过问题。

第二天,领导把我叫过去说:canal服务又挂了

我当时一脸懵逼。我什么都没干呀。

Canal解析数据报错:column size is not match for table xxxx 8 vs 9

后来,我们经过分析之后发现,canal有一份缓存,如果canal出现异常,可能跟数据库真实的情况不一致。

然后,我们把canal的meta.dat删除了,然后重启服务,果然恢复正常了。

后面也一直没出现过问题。

6. 确定需求4的方案

前面说过运营总共提了4个需求,我通过前面的骚操作,完成了3个需求。

但第4个需求,里面还有点特殊要求,通过sql脚本不容易搞定,只能硬着头皮写java程序了。

运营的需求是把他们提供的excel表格中的数据导入系统,然后由系统匹配某个区间范围内的数据,把结果写入excel的另外两列中,最后返回该excel文件。

拿到这个需求我脑子里想了三个方案:

  1. 写一个可执行的springboot工具项目,直接放到线上环境中执行jar包。
  2. 使用一个job处理,本身已经有xxl-job了,接入非常方便。
  3. 写一个api接口。

最终我选择了第3个方案。

为什么?

其实这3个方案代码的工作量差不多,但前面两个方案需要先上传excel到应用服务器,或者到OSS文件服务器

而如果运营需要导多次数据,每次都需要上传一次excel,不仅浪费服务器资源,而且比较费时,还麻烦。

如果用api接口的话,可以直接使用postman远程调用,直接上传文件,通过输入流的形式读取数据,不保存到服务器。然后处理完数据,在将excel内容以输出流的形式返回给我们下载即可。

使用postman调用远程接口时,入参选择form-data格式,key那里输入File,然后在右侧下拉列表中选择File,就会出现Select Files按钮。

通过该按钮,就能选择我们需要上传的excel文件。

如果想调用接口后直接下载excel文件:在postman中可以选择Send and Download按钮,即可下载文件。

注意,在图片中的请求api接口地址是localhost,我只是举了个例子,实际情况中是接口的域名。

此时,有些小伙伴可能会问题:这个接口不需要登录就能访问?

答:确实不需要登录,我在网关层放开了该接口的访问权限。

那不是有安全问题?

答:为了解决接口安全问题,也避免发版影响正常用户的使用。我的想法是基于master分支新拉一个分支:hotfix,而pre环境(预生产环境,能访问生产环境的数据库)部署hotfix分支的代码。

还有一个非常关键,而且我们一直在用的策略是:访问pre的所有接口都必须使用指定的代理

公司外面的人肯定是不知道这个代理的存在的,换句话说,只有我们公司内部的人才能访问pre环境的接口。

因此,新加的excel处理接口是非常安全的,而且该接口只部署pre环境,对正常用户不会造成影响

这个方案看似挺完美的。

然后三下五除二,我把代码写完,本地测试通过了,准备发到pre环境导数据。

7. jar包冲突

该功能部署pre环境其实非常简单,只需要部署hotfix分支的代码即可。

代码部署好之后,我准备开始访问接口。

先在postman的这个地方配置pre的代理。代码部署好之后,就能通过上一节中介绍的内容上传excel文件,然后下载结果excel文件了。

但我第一次调用接口时,没有返回想要的数据。从应用服务器的日志中看到,该接口报错了。

报的竟然是某个类找不到。。。。

我这次为了快速导入和导出excel文件,选择了阿里的easyexcel工具类。

本地开发环境,我确认过,那个类是有的。而且我这个功能是可以正常运行的,我都导出数据了。

但pre环境却报了类找不到。

我猜可能是有jar包版本不兼容。

于是,调整了一下pom文件中引入的jar包的版本,之后,重新部署pre环境。

还真是这个原因,这一次接口能正常访问,能够返回数据了。

我心里暗自窃喜。

后来,把运营所需要的excel文件及时发给他们了。

8. pre环境网络异常

又过了两天,需求4有点调整。我把代码改了,还是那个hotfix分支,找人重新部署了pre环境。

打算用之前相同的方法导数据的。

但马上被啪啪打脸。

用postman请求该接口很久都不返回,我知道肯定是出了什么幺蛾子。

查了一下pre环境应用服务器的日志,竟然没有查到请求该数据处理接口的记录。

接着,我查了一下pre环境应用网关层的日志,竟然也没有记录。

不对呀。

然后又查了一下生产环境应用网关层的日志,原来是请求到生产环境了。

不是配置了代理吗?

为什么会访问到生产环境?

我带着这两个问题咨询了一下公司的IT部门同事。他们追查了一下原因,发现原来网络带宽被打满,导致pre环境的代理出问题了。

经过一段时间之后,pre环境的代理恢复正常了。

其实,pre环境代理出问题后,我们也尝试了一下登录到远程服务器上,执行相关curl命令,直接调用服务器的本地接口。最后,发现用这种方式不太好下载文件。

9. 部署错分支了

pre环境代理恢复之后,我满怀希望去用postman请求数据处理接口导数据。

但我发现导出的数据不对。

导出的excel文件根本打不开。

我打开excel文件看数据内容时,提示excel文件格式不对,或者已经被损坏了。

然后,我赶紧看应用服务器的日志,有请求记录,但是没有返回记录,从这个日志中看不出问题。

当时我灵机一动:既然保存成.xlsx后缀的excel文件打不开,如果把文件后缀改成.csv格式呢?

于是,我把导出的excel文件后缀改成了.csv格式,果然可以打开文件。

文件内容中提示404

这时我就明白了,可能是pre环境的接口没发成功,被其他分支的代码冲掉了。

然后,跟部署代码的同学沟通之后,他当时操作失误,部署的master分支的代码,果然把hotfix的代码冲掉了。

后来,他重新部署了hotfix的代码,我顺利的把数据导给运营了。

至此,这4个需求顺利完成了。

总结

这次给运营导数据,是一次比较难得的经历,遇到了很多问题,值得总结一下。

当然这其中有一部分是自己给自己挖的坑,也有一部分是被其他人坑了。

不要怕踩坑,其实踩坑,也是成长的机会,我通过这次经历也收获了不少宝贵的经验。

  1. 生产环境的表名或字段名,一定不能用中文的。不要抱侥幸心里,说不定哪天就出问题了。
  2. 生产环境创建的临时表,用完之后,一定要记得及时清理。
  3. 使用canal时,最好别全库监听。用到什么表,就监听什么表,避免出现一些意外事故。
  4. 版本不兼容,会导致类找不到问题。
  5. 如果使用了代理,要考虑代理出现问题的情况。
  6. 代码发版之后,一定要再三确认分支是否正确。
  7. 删除meta.dat文件,重新canal服务,可以解决canal的很多问题。
  8. postman真的非常强大,建议大家都好好用一下。
  9. 把多条insert语句合成一条执行,效率更高。可以使用https://tool.lu/sql,这里在线工具,压缩一下sql去掉多余的空格。
  10. excel导入和导出用阿里的easyexcel工具,真的非常方便。

还有挺多收获的,这里就不一一列举了。

希望你看了我的文章,自己也会有点收获,能从我的经历中,学到一点点东西,我就已经心满意足了。

如果这篇文章对你有点帮助的话,请记得给我点赞喔,谢谢你的支持。

我们下期再见。

最后欢迎大家在评论区留言,聊聊你们遇到的线上问题,拿出来大家一起交流交流,一起踩坑,共同进步。

文末送书

为了感谢一路支持苏三的小伙们,今天继续送书。规则非常简单:在本文留言,我会选择其中最走心的两个留言者,每人各送1本。

此外,在我的微信朋友圈再送2本,扫描下发二维码即可参加。

本次活动一共送4本书,即:公众号留言送2本,微信朋友圈再送2本。

《Kafka进阶》

《Kafka进阶》基于作者多年的教学与实践进行编写,重点介绍Kafka消息系统的核心原理与架构,内容涉及开发、运维、管理与架构。

全书共11章,第1章,介绍Kafka体系架构基础,包括消息系统的基本知识、Kafka的体系架构与ZooKeeper;第2章,介绍Kafka的环境部署,以及基本的应用程序开发;第3章,介绍Kafka的生产者及其运行机制,包括生产者的创建和执行过程、生产者的消息发送模式和生产者的高级特性等;第4章,介绍Kafka的消费者及其运行机制,包括消费者的消费模式、消费者组与消费者、消费者的偏移量与提交及消费者的高级特性等等...

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存