最近线上某个服务出现了 JVM Crash 现象,并且每次都发生在比较相近的时间段里,下面是某一次的日志
|
|
从日志中能获取到的重要信息
|
|
从线程名中找到对应的代码逻辑块,并没有发现对 ArrayList.contains 的调用,随后通过使用 udcli 转译指令成汇编,貌似也没有找到匹配的代码
|
|
暂时没有什么头绪,想借助 core dump 来进行更深入的分析,但因为我们应用跑在 Docker 里,core dump 默认没有打开。
首先找 SRE 团队配合改一下 Docker 的配置,另外在应用内加上了更多的定位日志,希望能定位到具体行。
|
|
等待下一次 crash 期间也做了几件事
猜测是否是使用了 forkjoin 框架本身的问题,随后替换成标准的线程池,无用
根据日志定位到了大概的代码块,是一个普通是 foreach 中,仔细分析后似乎没有发现问题
再次仔细的分析了下crash 日志,发现每次都会有相应的 jni 调用及某个类的异常,如下
|
|
根据线程分析,这是一个MQ的触发事件,但如果是JNI导致的错误,为什么是另外一个线程的异常 Crash,这里没有想通
拿到 core dump 后,首先想通过 jstack 获取到具体的异常栈信息,栈信息已丢失
|
|
尝试使用 jmap 导出内存 dump,失败
|
|
随后尝试使用 gdb 加载分析
|
|
Backtrace 最后一帧是一个qsort 函数,这里和之前汇编 cmp 操作能对应上
至此大概猜测是某种原因破坏了堆内存,例如 JNI 调用使用了错误的参数触发了空指针等
最后尝试一下更新 JDK 版本,1.8.0_45 -> 1.8.0_201,尝试之后至今还未再次发生Crash 事件,难道真的是一个JDK 的bug 吗?
]]>最近线上某个服务出现了 JVM Crash 现象,并且每次都发生在比较相近的时间段里,下面是某一次的日志
|
|
以上这些改动虽然非常多但较容易发现,或是编译失败或无法启动。整个升级过程中最可怕的是一些隐性的风险,非常容易忽略。
在以前你可以任意修改路径后缀名都能得到正确的解析,例如 /fund/000961.xyz 也能正确解析到 /fund/000961。但是 2.0 默认禁止了后缀匹配模式,这也是 Spring 的官方建议。
其实在做升级的过程中已经知道有这个改动,但我们团队一直使用的 REST 风格不带后缀的形式,所以没太在意。但万万没想到,其他一个团队的使用了我们的开放接口,但却采用了他们自己的路径规则,加上了 .json 后缀,在以前一直稳定运行,但现在这部分接口全部挂掉。这也是整个升级过程造成的唯一线上事故,幸好及时发现修复,没有产生大的影响。
解决方案就是把后缀匹配打开:
|
|
在升级后的测试过程中发现部分接口明明参数正确却一直提示失败,经排查发现 PUT 类型的参数出现了数据重叠
|
|
随后跟踪源码 debug 发现新增了几个 Filter,其中 HttpPutFormContentFilter 用来处理 PUT 及 PATCH 的 body 传参问题(这个补丁在 1.5 版本后就已经引入并默认开启)。
因为浏览器 form 表单只支持 GET 与 POST 请求,所以早期的 Spring 不支持 PUT 请求使用 body 传参,正确的解决方案应该是在后端做 Method 的隐式转换。但之前并没有使用此方案,而是客户端同学把参数改成以 param 传递,这本身没有太大问题,但却忘记了把 body 里参数去掉,在新版本就变成了重名参数,以字符数组接收(数值类的不受影响)
解决方案是暂时关闭这个 Filter,待客户端修复后,在未来不再支持这些有问题的老版本客户端时再打开:
|
|
在做完了所有的升级后发现 Swagger 无法使用,去官方 repo 寻求方案,发现有人已经问过了,官方回应暂不支持 2.0 版本😱。幸好在其他帖子内发现有人说使用成功,但没有给出清晰的解决方案,只好自己一点点摸索了,好在最终还是解决了。
方案是去除老的 security 配置以及添加部分资源的映射
|
|
本次升级涉及十多个子系统模块,从开发->测试->灰度->上线大概 3 周多时间,除了上面引发的小事故外整个升级过程比预期要顺利很多,总结下经验有以下几点
有些人因为担心风险而不愿意升级,觉得够用就行。但在我看来合理的升级是必要的,收益也是显而易见的,只要使用了正确的升级方法,大部分风险还是可控的。对于升级这件事来说,时间越久,版本差异越大意味着风险也越高,制定合适的升级周期也能有效的降低风险。
]]>有一天领导找来了小李…
领导:你看某某公司这个功能不错,吸了很多新用户,我们可以考虑做一下
小李:恩,是的,不过我们这个版本需求满了,可以加在下月的版本迭代中
经过半个多月的深度学(chao)习(xi),脑海中反复的迭代构思,小李终于想出一个绝佳的方案,一定能让老板满意。
小李:来来来,我们开下个月的需求评审会,确定一下这个版本的需求
(会上大家非常踊跃,天马行空的提出了各自的想法和意见。)
B:这个需求看起来简单,为了保证通用性和稳定性,改动并不小,需要仔细的考虑下模块的设计。
D:这个功能确实不错,但是不太容易加入到我们目前视觉设计中,可能要想想其他办法
F:我建议引入 H5,这样更加的容易扩展和维护,这样APP的代码也不会太臃肿。
C:引入需谨慎啊,这样会影响测试的便利性
O:其实我觉得这个功能应该换一个方式可能更好…(此处省略500字),这样用户更容易理解,运营也更容易出效果
F:我觉得 O 的提议不错,这样前端目前的模块已经可以支持了
B:都可以,反正我们后端设计上都兼容,至于效果好坏可能还得调研下,可以做个 A/B Test
D:对了,这个输入的地方情况很多,如何给用户提示要仔细设计,不然体验非常不好
小李:是的,应该是弹框提醒吧
F:这不行,到处弹框太恶心了,给个 Toest 得了
D:重要信息还得弹啊,不然不够明显
A:不要在输入的时候主动发请求,让用户自主点击后再触发验证弹框,减少服务端压力
大家还在紧张的讨论中,小李一看手表,不知不觉快12点了,公司食堂要开饭了。
小李:好了,这次只是先让大家了解下需求,具体细节做的时候再讨论,吃饭吧。
饭后准备睡一会儿整理下混乱的大脑…
哇啦哇啦哦哇啦… 叫醒闹钟准时响起。下午大家各自进入到紧张的工作中,小李准备把下个迭代内容细化一下…
C: 李哥,这个需求需要你确定一下,功能上和需求文档不太一致
小李:好的,这不对吧,这俩处的文案需要不同,效果不一样
C:工程师说这样不行,结构上不统一,不好处理
B:你不能这么处理,一样的数据各处展现不一致,这没法处理,不能做成一致吗?
小李:em…我找设计同学来问问吧
D:这不行,外面这个宽度不适合这么长,太影响美观了
F:不能因为美观去改数据结构啊,这模块都不能抽象了!
小李:对,这里没必要放那么长
O:一个数据不同地方名称不一样,显得也太不专业了吧
B:那不然把里面的数据改成短的吧,这样就一致了
D:这样也行,短的大家应该也能理解吧?
F:可以的可以的,这样好
小李:……那就都改短吧。
说完手机传来嗡嗡声「15:06 luckin 送您一张2折优惠券,仅限今日哟」,去喝杯 coffee 冷静一下…
F/C/D/B:帮我带一杯,谢谢!
——
O:李哥,这产品的文案赶紧出一个,四点就上线推广了,还有十分钟!
小李:噢,好的,就写「内有红包,点击领取」吧。
O:这样不行,有合规问题,不能出现红包字眼
小李:「点我观看有奖励!」这个如何
O:不太专业,我觉得「学习小知识,领取奖学金」不错,还有内涵。
小李:恩,那这个吧
…
O:推广效果还不错,才半个小时有一百多个用户点击了呢!
小李:购买用户呢?
O:不太理想,只有三个
小李:持续观察吧,我先去听个内部分享
领导:小李,来我们开个会,对下当前进度和下个版本需求
小李:好…
会后室友来短信「今天我加班,记得早点回去遛狗,今天它有点拉肚子,多在外面待会儿…」
小李:同学们,明天上午我们进行这版本效果演示,都准备一下,家里有事先撤了…
]]>有一天领导找来了小李…
领导:你看某某公司这个功能不错,吸了很多新用户,我们可以考虑做一下
小李:恩,是的,不过]]>
如果手机不支持双卡双待,多卡带来的问题就是得多备一个手机,多花钱不说还得天天带着。对于我这种 iPhone党简直是噩梦,势必要解决它
需要解决的有俩部分
1.电话:主卡电话呼叫转移到副卡
2.短信: 希望把主卡短信全部转发到副卡上
目前大部分智能手机已系统支持呼叫转移,注意的是,这部分是需要另付资费哦例如A/B俩卡都是免费接听,但如果 A 转接到 B 需要收取 A 的呼叫费用
要解决短信的转移前提是能获取到本机短信的权限,iOS 因为隐私保护暂时做不到,所以需要一台Android 手机供主卡使用,如果是小米还需要在安全中心里给 IFTTT 开启读取服务类短信,不然无法收到银行验证码等。
建议使用 Google Play 版本的 IFTTT(需翻墙)
下面表格列出邮件、Telegram、微信三种方案各自优缺点。个人推荐使用微信,毕竟每天都使用,更加习惯。
转发方案 | 优点 | 缺点 |
---|---|---|
邮件 | 配置简单,稳定 | 响应稍慢 |
Telegram | 安全,及时 | 俩个手机都需要翻墙 |
微信 | 习惯,及时 | 需要第三方推送服务 |
具体配置方法都很简单,就不详述了。推送到微信要配置成 Webhooks 的方式,推荐 Server酱 服务,绑定下微信就可以收到推送啦。
其实还有个更简单的方案,就是使用 IFTTT 自身APP。俩部手机都下载 IFTTT 并用同一个账号登录,打开 Applets 的推送即可~
]]>它的原理是在编译期操作 AST(抽象语法树) 去改变字节码,根据注解生成对应的代码。因为在静态编译期做的,相对可控,使用也简单。
Google 开源的 AutoValue 也是用来解决这个问题,功能很全面,还可以定制化。但有一些的问题
- 代码侵入重,所有定义的对象是抽象类,使用都是封装后的 AutoValue_XXX
- 生成的 POJO 都是 immutable 的,需要频繁操作的类会很麻烦。
当你的 POJO 中出现单字母驼峰名称时,例如:iType,Lombok生成出来的 getter/setter 方法
|
|
乍一看没啥问题,但是影响很严重,首先 Spring 是不认此方法的,其次也影响到 Jackson 的解析,直接导致无法使用
一般来说,代码规范和 API 规范有所区别,参考之前的文章「浅谈 Api 设计」。从后端输出 Api 时会做一些转换,例如驼峰转小写+下划线格式,这个 Jackson 有对应支持
|
|
开始我觉得这是 Lombok 的 bug,随后我下载了 Lombok 的源码来验证
|
|
显然这是特意而为之,随后我上官方 issue 中看了下,在15年已经有人提出了这个问题,而官方的答复是 Java Bean 的规范就是如此,且不应该使用首字母小写第二个字符大写的命名规则,Spring 的处理方式是自己发明的,我们不能跟着 Spring 乱搞,如果 Oracle 官方推荐如此或者所有人都是这么处理的话我们才改~
虽然我没有仔细的看过 Bean 规范,但如果 Spring 这么大厂都不支持的话会很难推广,在我刚和同事推荐的时候大家都非常激动,一听有这么个坑几乎都放弃了,也许是 xXxx 这种命名非常常见吧,比如 iPhone……
不光 Spring,Jackson,编译器( IDEA)自动生成的也是如此,只能说 Lombok 性格真是倔。既然如此只好自己修改下源码了…
|
|
PS:据官方说从 JAVA 讨论组数据统计,半数多人支持 getxXxx() 的做法。但他们大多数人的选择未必是正确的做法~
啊呸…
]]>这次雪场的选择在 Kiroro 和 二世谷中犹豫,由于没有订上雪场内的酒店,只能选择住在较近的小樽,这里去 Kiroro 大巴约50分钟。去二世谷要坐 JR 一个多小时。最终选择了较近的 Kiroro
这里必须要推荐下我选择的这家酒店,Dormy Inn Premium Otaru(小樽豪华多米酒店),就在 JR 小樽站对面,下车步行约1min。旁边是大巴站,隔壁是小樽最大的商场。 店内提供洗衣房及公共温泉,重点晚上还有免费拉面吃,哈哈。(偶然发现在札幌狸小路旁边也有一家)
Kiroro雪场巴士有两种,一种免费的巴士到小樽驻港站,一种收费到达JR小樽站,都需要预约,因为座位有限,但基本坐不满。有一天我们只约到一个人,经过沟通也让我们上车了。小樽大巴早上八点十分出发,每日一班,不要迟到,不会等人。
正逢圣诞节,雪场也搞了一些活动。在日本没有滑雪票的概念, 只有Lift price(缆车费) 和Rental price(租赁费用)。自带装备的话只需购买缆车费,根据年龄和时长价格不一,这次我们买的是3天联票(含夜场),折合人民币850左右,是不是比国内还便宜?
会发一张实名卡乘缆车用,印有你的个人信息,遇到问题能迅速找到你的资料,理论上只限本人使用。
Kiroro 雪场由两座山头组成,最长的雪道(Asari Dynamic) 4km 左右,覆盖初中高级雪道,也是人最多的雪道,缺点就是中午后雪就不粉了。我更爱的是最右侧的 Asari Course ,人烟稀少,大粉雪。
Asari 山顶雪季非常的美,像这样。
也可以这样
第一天下午四点左右暴风雪就开始来了,山顶雪雾大到戴着雪镜只能看到一片白茫茫,当时在高级道的我吓的不太敢滑,害怕冲到树林里的深雪堆里爬不出来。加上只穿了一件保暖内衣和软壳,感觉异常的冷~ 下山就收包返程了
在小樽基本没有夜生活。六点多我们去觅食已经很多店关门了,有些出名的店还要预定,真是难死我们了~ 饭后就开启了买买买节奏
运动一天后泡个温泉简直不能再爽,泡饿了再干它两碗免费拉面(拉面23点截止,千万别泡过头了)温泉图片就不放了….
提示:女汤需要凭密码进入哦,密码每天都更换,记得找前台要。
恰好那几天北海道遭遇大暴雪,缺点就是天气不好,也比较冷,好处是雪真是松软到不行,雪场下午都放弃压雪了,雪道内和野雪一样棉花糖似得脚感,趁最后一天天气晴朗拍了个视频,可惜已经没有棉花糖的雪了~
PS:在北海道遇到了几个热心的台湾人,解决了一些沟通问题,买 JR 函馆站的一位台湾姐姐带我们到售票厅中买到了更实惠的往返联程票,在雪场丢失了一台 wifi 设备也是一位台湾 MM 帮我们做了相关的登记(虽然最终也没有找回来)
]]>
信仰(英语:Faith)是指對一個人(同樣的對他的能力)、事物、神、宗教的教條或教導、沒有經驗證據的觀點(例如擁有強烈的政治信仰)抱有信心和信任 —wikipedia
如今「信仰」在网络上也被大家用来形容自己对某件事物的热爱,例如信仰是 Google,必须使用 Google 的全套服务,不论是否好用。手机要用亲儿子Nexus(即将变为 Pixel)。锤子的情怀、Apple 的果粉,都算是一种信仰。
今天收到了「信仰之盾」,其实是 Dota2 内的一款纪念品,每逢大比赛前会推出小本子供玩家购买,本子等级越高可以收获更多的饰品,不乏有一些极其珍惜的物品。当然想要本子等级高可以花更多的钱,今年的第六届国际邀请赛(最大的一场比赛,简称 Ti6)出了新的花样,达到 1000 级的玩家可以收获实体冠军盾一枚,这手段无非就是促使玩家买买买。
尽管知道是在「骗钱」,还是跳下了火坑,充值信仰。庆幸的是今年在大家心灰意冷的时候,一匹中国黑马拿到了久违的冠军,拿到史上最高奖励 $912w 约 6000 多万人民币,也算自己出了一份力。
实物上手图,我这批应该是九月第一批发货的。做工也还不错,比较精致但边缘有点瑕疵
]]>信仰(英语:Faith)是指對一個人(同樣的對他的能力)、事物、神、宗教的教條或教導、沒有經驗證據的觀點(例如擁有強烈的政治信仰)抱有信心和信任 —wikipedia
有一次我跟长辈们出去吃饭,桌上一位做 IT 周边行业的叔叔问我,说你们赚多少钱一个月啊。我说没多少,万八千的。叔叔说那肯定得天天加班到半夜吧。
我:o(︶︿︶)o ……
我司虽然是互联网公司,但很少加班,还享受互联网公司各种福利待遇… 有兴趣的同学可以联系(相关职位信息)
福利
我司码农日常工作图!拖拉板、大裤衩、MacBook Pro、iPhone、机械键盘,不拘一格,不修边幅。
有一次我跟长辈们出去吃饭,桌上一位做 IT 周边]]>
墨菲定律:可能会出错的地方一定会出错,如果有多个可能,必定朝最坏的情况发展。大俗话:明明之前没有错,偏偏关键时刻就是出错了 —wikipedia
这是这两天遇到线上事故的总结
早前做了一个系统维护的解决方案,目的阻挡一些涉及维护系统的请求,避免造成数据上的错误。但因业务的扩展,并且多次升级维护都比较顺利,此方案并没有更新覆盖到一些新业务。
升级的过程为:停服务-> 备份数据库 -> 升级服务 -> 重启服务
升级后上线测试业务失败,经过半小时检测未找到原因(事后查出是三方升级包BUG,部分用户可以成功)。随后进行回滚操作,回滚后出现系统数据同步异常,检测发现在维护期间有部分新数据写入,回滚导致部分数据丢失。
好在没有对用户造成经济上的损失,但修数据耗费了大家一整天时间。
又一次用血泪印证了墨菲定律。
]]>墨菲定律:可能会出错的地方一定会出错,如果有多个可能,必定朝最坏的情况发展。大俗话:明明之前没有错,偏偏关键时刻就是出错了 —wikipedia
这是这两天遇到线上事故的总结