«

»

Sep 08 2016

去你的吧,谈对软件故障的处理

我呀?
就这个呀?
去你的吧!
别挨骂了!
别招说了!
咳(hai)!

上面那几句都是相声的经典结尾句式。

 

昨天早上公司前方突然报产品出现严重问题,用户操作时,出现了整个用户组都无法显示的问题。

这是个严重问题,最好越快解决越好。

至少我是这么想的。然而这个故障和我关系不大,这个功能我没有参与过开发,我也不是产品负责人或前方接口,我只能作为旁观者。

今天早上则是因为一个和自己负责的代码相关的Bug,跟目前负责产品的人对吼。

前方问题

前提

公司这个产品是以软件形式卖的,而包括Apache + Mysql + WinCrond + FastMsg + 一大堆叫不上名字的服务,安装在用户提供的安装了Windows系统的机器里。

开会

这么严重的问题,开发已经开始在尝试复现问题和查看代码了,前方团队和测试团队突然说要开会。开会讨论一下这个问题。

开会开了半天,客户就扔在那里凉着。品牌形象就这么烂掉了。

程序员幻觉之一:讲理讲得清

程序员有很多幻觉:比如幻觉这段没测试的代码没问题的。

在会议上开发的几位成员一直在讲这个功能的逻辑是怎样,有怎样的可能性,应该从哪些方面入手解决这个问题。

然而测试团队的答案就是,嗯、啊、哦,仿佛和没听进去一样。

目前测试的负责人是某大公司出来的老油条,工作能力不怎么地,但是办公室政治玩的非常一流。

跟一个专门玩政治的讲道理,还觉得讲得通。

我还是那句话:有些问题不上升几个层次,是解决不了的。

客户

因为用户防火墙的问题,用户的服务器只有Web界面可以访问,看不到底层日志,看不到数据库,什么都看不到。问题不停地恶化,底层服务开始不正常,严重的最后连Web界面都挂了。

最后的协助方式是,客户或是支持人员在客户网络上开QQ远程协助,之后再用Windows远程登录登到客户的服务器上,战线拉得非常长。而且公司的网络巨渣无比,不停的断开和重连失败也非常锻炼人的耐心。

 

 

这让我想起在最初老东家时是怎么支持前方人员的了。

远程协助

老东家的产品是基于方案实现的。因为功能专一性,对外看起来像是卖硬件的。

所有产品的系统管理都有一个名为『远程协助』的功能,用户在这个页面可以填写客服人员提供的一个数字,之后点击确定,只要这台设备连着网,客服人员就能直接登录到这台设备上查看信息,不论Web管理前台,还是内部管理后台,还是数据库。

这可谓是一个超级便利的功能,不需要用户修改防火墙,不需要其他电脑开QQ或其他远程桌面工具,非常节省人力成本,并提升故障解决效率。

当然最能令大家惊讶的,是这个功能的所有代码,都是一位 产品经理 写的!你没看错! 产品经理

效仿

虽然听起来这个功能看似很NB,但实际上其就是个技术实现上非常简单的流量转发功能而已,但技术实现很简单,想要做出来又非常的困难。

技术实现起来其实就是这个样:

用户设备A (端口:22)←→(端口:22)远程协助服务器B(端口:10080)←→客服

用户的点击确定行为,将设备A的22端口与远程服务器22端口和10022端口穿起来,客服去访问远程协助服务器的10222端口时,流量直接传输到设备A的22端口。

模拟

底层想要实现这个功能,只需要做ssh tunnel就好了:

假设远程协助服务器B的地址是 remote.assistance.com,ssh 服务端口为 2222,则只要在用户设备A上执行:

即可以把 localhost 的 80 端口映射到 remote.assistance.com 的 10080 端口上,只要访问 remote.assistance.com:10080,就同等于访问客户机的80端口。

这中间会有对 remote.assistance.com 的证书确认,密码输入,等同于一次 ssh 登录行为。

成功:

实战面临的问题

问题一:ssh客户端

首先遇到的问题是,现在这个公司的设备是跑在客户提供的 Windows 上的,默认没有 ssh 客户端。那么就需要手工准备一个 ssh 客户端。尝试了 Win32-OpenSSH,果断PASS,连语法都不一样。

那还有个putty。

putty也是有这个功能的。

问题二:远程协助服务器

第二个问题是,需要有一台公网能够访问的远程协助服务器。

自己公司是不会提供这样的服务器的,不能指望公司。

那么就只能自己准备服务器。

问题三:复杂度

按照这个实现方式,客户或者支持工程师需要在客户的网络侧运行putty,需要在SSH Tunnels页填写2个参数和4个选项,需要在 Session 页填写一个地址,点击确定后还要输入登录密码,如果用免密码方式的话还要用户手动导入证书。

这个复杂程度的操作,如果是客户搞不定的话,或许还有耐心去指导。

但是根据我多年的工作经验,客户照着帮助文档或者通过电话沟通,配置成功率非常高;

而反过来自己公司的客服几乎完全是搞不定的,他们恨不得赶紧开个QQ远程,之后把所有工作都甩给你,自己赶紧把电话挂了开始玩手机。

问题四:安全性

按照这套操作去做的话,那么远程协助服务器的地址将会暴露,登录密码或证书会暴露,而这又是一台自己的服务器,而非公司提供的专用服务器。这样操作之后,还会打开到远程服务器的 shell,所以还要去putty的 ssh 设置页面勾选 不打开shell。

登录方式完全暴露之后,会使用这些东西的人就有可能入侵服务器,如果是专用服务器还好设置,然而这时自己的服务器,根本没理由给自己增加这么大的风险。

过于复杂

很明显这个方法行不通。而改进的方案就是,重新编译一个 windows-ssh 客户端,屏蔽掉 shell,将登陆用证书打包进客户端文件中,尽量少的对外暴露所有配置参数。

这只是最基本的,还要考虑各种不同网络环境下的问题。

 

无能

开发了快一个月的补丁版本,昨天终于内部发布了。

但昨天半夜,微信群里突然就开始报Bug。

 

其中一个功能点是我这个版本开发的:为『通知』功能增加一个回复全部按钮。我编码的时候忘记把原始发件人添加到回复对象列表中了。

我觉得这个功能非常容易测试出来,只要群发的时候不发送给自己,之后另一个人回复全部,就能看到了。

然而这是报出来的Bug:

简单问题复杂化。基本测试都没做,就在那绕圈子。

 

另外还有这么个玩意:

原本微信端已发通知就带有『回复』功能,不仅如此,Web网页端以及PC客户端的已发通知功能,都有『回复』和『回复全部』功能。这次开发就是给原本只是给已有的简化版微信端补充一下而已。

然而目前的产品负责人并不知道PC客户端的设计,也不知道Web网页端是否有这些功能。先是说其他两个平台没有这两个功能,之后又说自己不了解不是自己设计的(比我在公司工作还要久的产品负责人竟然都不知道自己产品有哪些功能)。之后又翻出来一个不知道从哪搞来的没见过的产品设计文档说他设计的时候截过图,声称微信端原本没有『回复』这个功能。

要不是研发经理出面阻止,我觉得我就能打起来了。

因为开发测试环境都已经升级到最新版本了,所以,他妈的,算他赢。


 

8月16日开始开发,18日我就提交了所有代码。结果一直拖了三周,直到最后产品都发布了,才发现这么基本且严重的Bug。

今天公司用自家的产品发了正式发布的通知,老总很高兴的用微信快速回复全部,结果Bug直接就暴露出来了。

 

一早上就把Bug修好了,其实就5行代码。但是版本已经发布了,所以不能提交。之后处理产品负责人过来找茬。剩下的这一天就又整个页面的代码都重新改写了一下。又翻出来几个测试一直没发现的古董Bug,自己提前修了。

之前的代码实在太恶心,每次看到都想吐。

单引号双引号混用,该有空格的地方没空格,没有空格的地方都是空格,缩进乱七八糟。

当然个人认为最NB的还是这段代码:

看似是把id当参数传给 checkIds: function(id),但实际上循环的是ids,而因为在其他函数中给ids赋值时没有使用var,导致ids成为了全局变量。一系列的编码错误组合到一起,反而运行起来了。

无用

(于2016年9月21日补充内容)

然而用了一天的时间修复的问题,直到今天仍没有提交。产品就带着这么多个严重的Bug对外推广和销售。现在不允许向SVN中提交代码。

所以什么都没有解决,什么都没有。

(于2016年10月20日补充内容)

这个Bug最终被分配到其他人头上了。具体见文章

想骂人

回头再想想自己想出来的那套远程协助方案。

思来想去之后,做出了决定:去你的吧!

8 comments

Skip to comment form

  1. 大致

    从开发流程上说,那个bug的功能如果在文档中有说明,没测到就是测试团队的事,否则就是开发人员的锅。
    你用的啥比较工具?我从没用过中文比较工具,感觉怪怪的。
    一直对js这类不用编译的语言非常不适应。

    1. 石樱灯笼

      文档?你觉得产品负责人那个样,能有对应功能文档?测试团队这么稀松,能有测试用例?
      我还是那个观点,问题不上升到企业管理甚至是企业文化,是解决不了的。
      比较工具用的BCompare,旧版本有中文的。
      js这种不用编译的其实也就是运行中编译而已,Bug比较好定位。

  2. 郑永

    这文章好长,写的好细心,不管怎么样,解决问题就ok。

    1. 石樱灯笼

      没有解决问题

  3. 小彦

    很详细的解决过程,作为自己的经验挺不错的~

    1. 石樱灯笼

      这种玩意要是能当经验也是负经验,先扣你200个等级

  4. 坠入银河

    看似是把id当参数传给 checkIds: function(id),但实际上循环的是ids,而因为在其他函数中给ids赋值时没有使用var,导致ids成为了全局变量。一系列的编码错误组合到一起,反而运行起来了。

    看到这句话,我想起我见过的一种编程方式:散弹枪式编程(程序还是要粘贴的,万一能运行起来了呢)

    1. 石樱灯笼

      我是典型的侦探型

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据