分类 有趣的 bug 下的文章

SRE重案调查组 第五集 | 为什么我的服务器又双叒不响应了?!

这是本人发表在 eBay 微信公众号 eBay技术荟 上的 一系列文章, 原文地址如下. 编辑非常给力, 请查看原文, 这里只是供搜索引擎访问.
https://mp.weixin.qq.com/s/-CTHVLxgzTn79vOqEe_CQQ

如果原文由于某种原因不存在了, 请查看这个pdf 版本:SRE重案调查组 第五集 | 为什么我的服务器又双叒不响应了?!.pdf

导读

在SRE的日常工作中,经常会碰到很多典型问题。其中一种便是:为什么我的服务器不响应了? 

原因可能是服务器软件或硬件损坏,应用程序未启动,网络防火墙,服务器配置等基本的环境问题,也有可能是与应用程序关联比较紧密的代码问题。后者不易发现,又或是长期累积的结果,查起来不像环境问题那么直接。这里列举几个发生在生产环境的真实案例,希望对大家诊断类似的问题有所启发。

- 阅读剩余部分 -

SRE重案调查组 第四集 | JVM元数据区的内存泄漏之谜

这是本人发表在 eBay 微信公众号 eBay技术荟 上的 一系列文章, 原文地址如下. 编辑非常给力, 请查看原文, 这里只是供搜索引擎访问.
https://mp.weixin.qq.com/s/h8ayDuk7SSQ-0-jNmGmydw

如果原文由于某种原因不存在了, 请查看这个pdf 版本: SRE重案调查组 第四集 | JVM元数据区的内存泄漏之谜.pdf

导读

从Java 8开始,元数据区(Metaspace)被引入,替换永久代 (Permanent Generation),用以保存JVM用到的类的元数据、字节码及编译后的代码。这两者最大的区别是元数据区在原生内存(Native Memory)申请一块区域来保存这些数据。本文将分享一个SRE处理的与元数据区相关,且发生在生产环境的真实案例。


- 阅读剩余部分 -

SRE重案调查组 第三集 | 探秘HTTP异步请求的“潘多拉魔盒”

这是本人发表在 eBay 微信公众号 eBay技术荟 上的 一系列文章, 原文地址如下. 编辑非常给力, 请查看原文, 这里只是供搜索引擎访问.
https://mp.weixin.qq.com/s/kVA4vrrk-UbttChu2zhaFA

如果原文由于某种原因不存在了, 请查看这个pdf 版本: SRE重案调查组 第三集 _ 探秘HTTP异步请求的“潘多拉魔盒”.pdf

导读

HttpAsyncClient 是基于Httpcore NIO 和Httpclient基础上做的进一步封装,主要利用异步回调方式,能够高效处理HTTP请求。可一旦使用不当,就会产生问题。本文将跟随eBay SRE部门人员视角,探索HTTP 异步请求导致的系统性能问题。


- 阅读剩余部分 -

SRE重案调查组 第二集 | 挖掘应用处理变慢的“真相”

这是本人发表在 eBay 微信公众号 eBay技术荟 上的 一系列文章, 原文地址如下. 编辑非常给力, 请查看原文, 这里只是供搜索引擎访问.
https://mp.weixin.qq.com/s/Sh7ddc-3mGXWCGppi50EHg

如果原文由于某种原因不存在了, 请查看这个pdf 版本:SRE重案调查组 第二集 _ 挖掘应用处理变慢的“真相”.pdf

导读

eBay SRE部门经常处理生产环境中各种各样的疑难杂症,此次探究的是应用处理变慢问题。从监控系统入手,到找出根本原因是锁形成的顺序队列,不仅完美解决该问题,还为本部门积累了一笔宝贵的经验。


- 阅读剩余部分 -

诊断由 TLS 的 SNI 引起的 https 服务调用握手失败

某一天, 开发人员说遇到一个奇怪的 bug: 某服务调用使用同步的 http 调用时是成功的, 最近他们迁到异步的 http 调用, 总是失败. 下面是出错的 error message 及出错栈:

19:25:53.183 [SocketConnectorIoProcessor-0.0] DEBUG org.apache.ahc.codec.HttpIoHandler - [partner-listing.thredup.com/104.18.23.236:443] Unexpected exception from SSLEngine.closeInbound().
javax.net.ssl.SSLException: Inbound closed before receiving peer's close_notify: possible truncation attack?
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
    at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1647)
    at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1615)
    at sun.security.ssl.SSLEngineImpl.closeInbound(SSLEngineImpl.java:1542)
    at org.apache.mina.filter.support.SSLHandler.destroy(SSLHandler.java:167)
    at org.apache.mina.filter.SSLFilter.sessionClosed(SSLFilter.java:367)
    ... 中间省略大部分 org.apache.mina.common 相关类
    at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:51)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

看上去是一个 TLS 握手失败. 但是error message 里面并没有说明具体什么原因.

访问的网站是: https://partner-listing.thredup.com/api/v1.0/webhooks/xxxx, 同步访问是成功的, 使用各种浏览器访问也是成功的. 所以开发人员怀疑是平台部门提供的的异步访问 jar 包有问题. 于是他们找到平台部门帮忙.

单从这段出错 error message 看, 没有任何头绪, 于是 SRE 的下一步就是抓包看具体的tcp 内容. 下面就是这个尝试连接的 tcp dump:
tcpdump-sni.png

从tcp dump 可以看到这个连接很快失败:

  1. 首先, 前3行tcp握手建立;
  2. 第4行: client 发送 TLS client hello;
  3. 第5行: server ack client hello package;
  4. 第6行: server 发送 Fatal error: 握手失败;
  5. 第7行: server 发送 fin 表示要结束连接, 不玩了.

这里的最有提示的是第6行, 在下面的详细 tab 展示了, 具体出错的类型和描述:

Content type: alert (21)
Alert Message:
   Level: Fatal(2)
   Description: Handshake Failure (40)

Handshake failure 40 并没有告诉我们具体的是什么原因, 不过google还是针对这种出错给出了几种可能, 其中一种就是 server 那边需要 SNI(Server Name Indication). 我们这里就是 SNI 问题.

使用 openssl client, 我们很容易验证这个问题:

openssl s_client -tls1_2 -connect  partner-listing.thredup.com:443
openssl s_client -tls1_2 -connect  partner-listing.thredup.com:443 -servername partner-listing.thredup.com

后边的参数 -servername 就是制定 SNI. 前面一个命令会出错, 后面一个就是成功的.

什么是 SNI? 为什么需要 SNI?

SNI 是 Server Name Indication 的缩写. 假如一个服务器上部署了多个网站, 每个网站都有自己的域名, 并且这个服务器只有一个 IP 地址, 就会出现: 当客户端连接到这个服务器的时候, 服务器不知道你是要访问那个网站, 因为你的连接过来, 从 tcp 层来看, 连的都是同一个 IP, 它不做区分. 应用层, 比如 http 层, 就要区分不同的网站, 应用层不知道这个请求应该发送到那个网站. 所以 http 协议里面有个 header 是: Host, 它能让应用层知道不同的网站. 在应用层和 TCP 层的中间, TSL 也要区分你是访问那个网站, 所以它也需要一种机制, 去区分进来的请求是访问那个证书, 这里的 SNI 就是做这个事情的. SNI 是 TLS 的一个扩展字段, 在 2003 年 6 月份 加入 RFC 3546, 很多实现 TLS 客户端的实现都是在 2010 年之后才具体实现的这个 feature.

为什么我们的异步请求出错?

因为我们的异步客户端使用了一个很老的 jar 包, 它里面没有实现这个 SNI 的功能.

从一个连接成功的请求 tcp 数据来看, 这个里面是包含这个 SNI 扩展字段的:
sni-ext.png