2019年8月

诊断由 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

SRE重案调查组 第一集 | 高延迟问题的罪魁祸首System.gc()

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

如果原文由于某种原因不存在了, 请查看这个pdf 版本: SRE重案调查组 第一集 _ 高延迟问题的罪魁祸首System.gc().pdf

导读

eBay SRE 每天都处理生产环境中出现的各种各样的问题,有些问题很具有代表性,这次我们将分享与Java语言中System.gc()相关的两个典型案例,介绍如何分析这种问题,并且如何定位且修复这些问题。

- 阅读剩余部分 -

使用MAT 分析heap dump

  1. 下载及设置
    官方下载: https://www.eclipse.org/mat/
    JDK 最好用最新的 JDK, 因为最新的基本优化最多.
    根据你分析的 heap dump 的大小, 有时候需要调整 MAT 的 heap 的大小. 这个参数在 MAT 根目录的 MemoryAnalyzer.ini 文件里面. 我经常分析 30G 以下的 heap, 基本设置为 27G, 是 JVM 使用压缩指针来加速.

sjc-sreop-001.png

另外, 对于 HPROF 的 dump 来说, 我经常设置为非严格 parse, 因为有时候有点错误, 不影响分析:

sjc-sreop-002.png

文档: MAT 自带文档在 Help -> Help Contents 菜单里面, 或者[在线版本的文档][3]
  1. 分析
    正常情况下当你打开一个 heap dump 之后, 它会问你是否自动诊断内存泄漏, 如果你不是为了诊断内存泄漏, 可以取消这步.
    Histogram: 是按照类的实例数量聚集, 能很快发现包含大量实例的类. 一般情况下 char[] 或者 String 都在最上面, 这基本没有问题.
    Dominator Tree: 对于诊断内存泄漏非常有用, 如果能抓到一个对象 dominate 很多实例, 基本你找到了问题所在.
    OQL: 就像查询数据库的 SQL 语言, 非常方便的查找任何对象, 实例;
    Threads: 查看当前heap的所有线程, 对于发现某个对象是怎么被创建, 或引用的非常有帮助.
  2. 如何分析 IBM J9 dump
    参看: https://help.eclipse.org/2020-03/index.jsp?topic=/org.eclipse.mat.ui.help/welcome.html
    MAT 安装 DTFJ 插件步骤:

    1. 打开 MAT, 菜单: Help -> Install New Software ...
    2. 点 Add 按钮 添加新的 Repository.

    name: DTFJ
    Location: http://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/runtimes/tools/dtfj/
    点 Add 按钮

    1. 选中 IBM Monitoring and Diagnostic Tools
    2. 一路 next 下去, 中间接受 license, 然后重启 MAT

    更多关于如何使用 OQL: Java heap dump OQL samples - where

jmap & jhat

jmap 用来输出 JVM 的heap 相关信息, 或者生成 heap dump. 可以对一个正在运行的 JVM 进程使用这个命令, 也可以对一个 core dump. 它的功能基本都被 jcmd 命令所替代

jmap -histo <pid>  //jcmd <pid> help GC.class_histogram
jmap -histo:live <pid>   //jcmd <pid> help GC.class_histogram -all
jmap -clstats <pid>   //jcmd <pid> GC.class_stats
jmap -finalizerinfo <pid>  //jcmd <pid> GC.finalizer_info
jmap -dump:format=b,file=/tmp/heapdump.hprfo <pid>  //jcmd <pid> GC.heap_dump
jmap -dump:live,format=b,file=/tmp/heapdump.hprfo <pid>  //jcmd <pid> GC.heap_dump -all

jhat 对 heap dump 分析, 然后起一个本地 web 服务器, 开 7000 端口, 使用户可以在浏览器通过 OQL 查看 heap 里面的信息. 因为功能不够强大, 基本都用其他工具, 比如 MAT 或 Java VisualVM.

Microservices Patterns 读书笔记

  1. Preface

松耦合, 小团队(Amazon 2 个披萨团队), 快速部署, 快速迭代, 清晰的接口定义(API), 推到重来

  1. 如何拆解为 micro service? 拆到哪种粒度? 如何权衡?
  2. 微服务带来的问题?
    a. 服务发现?
    b. 服务快速部署?
    c. 监控, 隔离, 修复?
    d. 服务拆分的艺术?
  3. 分布式服务带来的挑战?
    a. 数据一致性/最终一致性;
    b. 如何从组件 crash 中恢复?
    c. 如何整体可控?
  4. 异步消息带来的问题?
  5. 如何保证数据的一致性?