前言前段时间,我们的线上系统发生了一次事故:用户创建了一个商品,但是在商城的商品列表页面无法看到也无法搜索到,这个问题持续了大约半个小时,最后发现是我的错。这件事情怎么说呢,我完全骗了自己。这是怎么回事?1.从需求出发1.1背景由于我们的迭代是一个大版本,临近发布日期。本次上线需要运营的配合,提供大量的产品属性数据。他们需要把第三方的属性和我们系统的属性以excel的形式进行匹配。本来是这样计划的:操作学生手动对应excel表中双方属性的映射关系。但是后来,他们觉得属性太多了。如果他们手动映射excel表中的属性,则可能为时已晚。于是,在某次会议上,他们特意向我提出了一个要求,希望我能通过一个程序帮助他们在excel中映射双方的属性值。有一个要求:快。因为其他同事要根据这个excel数据做一些后续处理。1.2原始需求开会开始运营说他们提供了一个excel表格,有分类和属性字段,然后让我在程序里完全匹配,把可以匹配的属性号和属性放在另一个excel的一部分。将它们分两列返回。然后他们根据这个excel数据,把匹配不上的数据(也就是另外两列是空的)手工录入到我们的系统中,最终匹配上去。1.3增加了剧情。本来,我觉得这个需求很简单。但后来,操作增加了戏剧性(增加新的需求)。事实上,手术一开始并没有完成。后来发现他们必须访问两个制造商。而且运营商提供的两个厂家的excel表中字段的格式不一样,不是一套程序就能搞定的。而且我们发现有些属性包含了一个区间范围,这肯定和我们系统中的数据不对应,必须拆分属性才能匹配。很显然,运营是不愿意做这种人工拆分的工作的。他们要我用程序帮他们处理,这给我增加了很多工作量。另外,Vendor1有一个特殊的要求:操作员手动删除excel中的一些数据,然后根据这个新数据重新匹配一个新的excel数据。2.最快的解决方案了解了运营的需求后,简单分析了一下。按照需求的优先级顺序排列:导出厂商属性数据1.导出厂商属性数据2.导出一份特殊属性数据给厂商1.导出符合区间的数据范围。如果这些需求都写在一个程序里,可能要写4个程序,部署代码也需要时间。恐怕时间不够了。于是想了一个快速处理需求1、2、3的方法:直接通过sql语句查询需要的数据。但是这个方案的前提是需要将excel中的数据导入到生产环境中。为了保险起见,我先将excel中的数据导入到dev环境中。我写好sql,测试数据后,导入到生产环境中。使用数据库管理工具:NavicatPremium的导入向导功能,你可以轻松地将excel表格中的数据直接导入到新表格中。在里面可以指定excel表是针对哪个表的,指定excel列中的哪些列对应该表。由于这些需求都是新表,不需要特别指定,所以我就按照默认的表名和字段名导入数据。但是有一个问题:表名和字段名都是中文的,因为excel中的sheetname和sheet中的fieldname都是中文的。其实我当时就已经发现了这个问题。但是那个时候,我又想到了。表中字段较多,需要一一改成英文。只是命名它需要一些时间。这些字段还是要转换成操作能看懂的中文字段名,这么绕来绕去有点画蛇添足,浪费时间。而且这张表导入生产环境后,是临时表,用完就删除,影响不大。另外,这个表是新加的。如果没有程序可以使用它,应该没有问题。于是,当时没多想,就找人把数据导入到生产环境中。导出数据的方法很简单:使用NavicatPremium的DumpSQLFile中的Structure+Data。这样,数据库工具就会导出创建关联表的create语句和向.sql后缀的文件中插入数据的insert语句。有个小问题:每条数据都会生成一条insert语句,如果过多放到生产环境执行,执行效率会比较低。这时候可以把insert脚本复制到idea或者其他工具打开,然后全文替换,去掉多余的insert,拼接成一条insert语句。然后使用一个在线的sql压缩工具,比如:https://tool.lu/sql,压缩去除多余的空格。这样,插入数据的SQL在生产环境中执行,效率会快很多。将运营提供的excel表格中的数据导入生产环境后。按照计划,通过一条sql语句,直接查询操作需要的结果,然后将结果复制到excel表中。(注意:如果查询结果中数据过多,不建议这样玩)。按照上面的做法,我很快就完成了要求:1、2、3,并及时给了他们操作所需要的数据。3.原本计划有一集。导入数据后,需要删除生产环境中的临时表。但是有个小插曲,操作对我提出了一个临时的需求:我需要重新导入一份厂商2的数据给他们。他们已经根据表中的内容,将需要添加的属性添加到系统中。我们需要重新导入一份数据来确认现在所有的数据是否都可以匹配。这时候庆幸数据没有被删除。对于这种临时的操作需求,在线执行同样的SQL,可以快速导出数据。1分钟实现需求。当时叫一个:爽。而且我观察了一下,系统没有任何异常。将数据导入操作后,忙于其他事情,忘记删除数据了。4.上网有问题第二天早上,领导给我打电话说:canalservicedown了。通过分析canal异常日志,我们发现这个问题是由于canal订阅者,在读取中文表名时,出现乱码,没有成功读取。程序直接抛出异常,导致canalsubscriber无法正常工作。该问题对用户的影响是:用户创建了商品,但在商城的商品列表页无法看到或搜索到,部分用户已向运营方投诉。我当时的第一反应是:这也能挂?当时对下游业务系统不了解,通过canal在自己这边监控整个数据库。话不多说,先解决问题吧。为了快速解决问题,我们先删除了所有的中文临时表,然后重启了canal。果然,这个办法是有效的。运河听众立刻恢复了正常。那天,没有进一步的问题。第二天,领导把我叫过去说:运河服务又停了。我傻眼了。我什么都没做。Canal在解析数据的时候报错:columnsizeisnotmatchfortablexxxx8vs9。后来分析发现canal是有缓存的。如果canal出现异常,可能与数据库的真实情况不一致。然后,我们删除了canal的meta.dat,然后重启服务,就恢复正常了。从那以后就没有问题了。5.确定需求4的方案,前面说了运营一共提出了4个需求,我通过之前的show运营完成了3个需求。但是第四个需求,里面有一些特殊的需求,通过sql脚本不容易获取,只能硬着头皮写java程序。操作需求是将他们提供的excel表中的数据导入系统,然后系统匹配一定范围内的数据,将结果写入excel的另外两列,最后返回excel文件。拿到这个需求的时候,脑子里想到了三个方案:写一个可执行的springboot工具工程,直接放到线上环境执行jar包。使用job来处理,已经有xxl-job了,访问很方便。写一个api接口。最后我选择了选项3。为什么?其实这三个方案的代码工作量是差不多的,只是前两个方案需要将excel上传到应用服务器,或者上传到OSS之类的文件服务器。但是如果操作需要多次导入数据,每次都需要上传excel,既浪费服务器资源,又费时费力。如果使用api接口,可以直接使用postman远程调用,直接上传文件,以输入流的形式读取数据,无需保存到服务器。然后在处理完数据后,将excel内容以输出流的形式返回给我们,供我们下载。使用postman调用远程接口时,输入参数选择form-data格式,在key中输入File,然后在右侧的下拉列表中选择File,会出现SelectFiles按钮。通过这个按钮,我们可以选择我们需要上传的excel文件。如果调用接口后直接下载excel文件:可以在postman中选择发送下载按钮下载文件。注意图中请求api接口地址是localhost,我只是举个例子,实际是接口的域名。说到这里,可能有朋友会有疑问:这个界面不登录也能访问吗?答:确实不需要登录,我在网关层放开了这个接口的访问权限。没有安全问题吗?答:为了解决接口的安全问题,也是为了避免版本发布影响正常用户的使用。我的想法是在master分支的基础上拉一个新的分支:hotfix,preenvironment(预生产环境,可以访问生产环境的数据库)部署hotfix分支的代码。还有一个很关键的策略,我们一直在使用的策略是:所有访问pre的接口都必须使用指定的代理。公司外的人一定不知道这个代理的存在。也就是说,只有我们公司内部的人才可以访问前置环境的接口。因此,新增的excel处理接口是非常安全的,而且该接口只是部署在前置环境中,不会对正常用户造成影响。这个解决方案似乎很完美。然后写完代码,本地测试通过,准备发到pre环境导入数据。6.jar包冲突。部署这个功能的前置环境其实很简单。您只需要部署hotfix分支的代码即可。代码部署完成后,我就可以开始访问接口了。先在postman这个地方配置pre的代理。代码部署完成后,可以通过上一节介绍的内容上传excel文件,然后下载生成的excel文件。但是我第一次调用接口的时候,并没有返回想要的数据。从应用服务器的日志可以看出是接口报错了。结果是找不到某个类。...这次为了快速导入导出excel文件,选择了阿里的easyexcel工具类。在本地开发环境中,我已经确认了那个类存在。而我的功能可以正常运行,并且已经导出了数据。但是前置环境报找不到类。我猜可能是jar包版本不兼容。因此调整pom文件中引入的jar包的版本,然后重新部署前置环境。果然是这个原因,这次可以正常访问接口,可以返回数据了。我心中暗暗庆幸。后来操作需要的excel文件也及时发给了他们。7.又过了两天前置环境网络异常,需求4稍微调整了一下。改了代码,还是hotfix分支,找人重新部署了pre环境。我打算使用与之前相同的方法来导入数据。但他立即被打了耳光。我用postman请求接口半天没有返回,我就知道一定是出事了。我查看了前置环境应用服务器的日志,没有发现请求数据处理接口的记录。然后查看了前置环境应用网关层的日志,也没有任何记录。不对。然后查看了生产环境应用网关层的日志,原来是向生产环境发起了请求。代理没有配置吗?为什么要接入生产环境?带着这两个问题,我咨询了公司IT部门的同事。他们追查原因,发现是网络带宽满了,导致前置环境代理出现问题。一段时间后,前置环境中的agent恢复正常。其实在前置环境代理出现问题后,我们也尝试过登录远程服务器,执行相关curl命令,直接调用服务器本地接口。最后发现这种方式下载文件不是很好。8、部署错分支,恢复前置环境agent后,希望使用postman请求数据处理接口导入数据。但是我发现导出的数据是错误的。导出的excel文件根本打不开。打开excel文件查看数据内容时,提示excel文件格式不正确或已损坏。然后,我快速查看了应用服务器的日志。有请求记录,但没有返回记录。从这个日志看不出问题。当时我有一个想法:既然用.xlsx后缀保存的excel文件打不开,那如果把文件后缀改成.csv格式怎么办?于是,我将导出的excel文件的后缀改为.csv格式,果然可以正常打开了。文件内容提示404。这时候我明白了,可能是pre环境的接口没有发送成功,被其他分支的代码给洗掉了。然后跟部署代码的同学沟通后,当时运行出错,部署的master分支的代码真的刷出了hotfix代码。后来他重新部署了hotfix代码,我成功导入数据到操作中。至此,这四个需求已经圆满完成。总结一下,这次将数据引导到运营中是一次难得的经验,遇到了很多问题,值得总结。当然,这一部分是自己给自己挖的坑,一部分是别人给自己挖的坑。不要害怕踩坑,其实踩坑也是一种成长的机会。通过这次经历,我也获得了很多宝贵的经验。生产环境的表名或字段名不能是中文。不要冒险,说不定哪天会出事。生产环境创建的临时表用完后,一定要记得及时清理。使用canal时,最好不要对整个库进行监控。监视使用什么仪表以避免发生事故。版本不兼容会导致类找不到。如果使用代理,请考虑代理可能出错的情况。代码发布后,一定要仔细检查分支是否正确。删除meta.dat文件,重启canal服务,可以解决canal的很多问题。postman真的很强大,建议大家好好使用。将多个insert语句组合到一个执行中会更有效率。可以使用这里的在线工具https://tool.lu/sql来压缩sql,去掉多余的空格。阿里的easyexcel工具是用来导入导出excel的,真的很方便。收获还挺多的,这里就不一一列举了。我希望在阅读我的文章后,您自己会有所收获。如果你能从我的经验中学到一点点,我就满足了。
