一个解决问题的思路 Didn’t find class “javax.net.ssl.SNIHostName”

TL;DR

Android开发,targetSdkVersion 23,任务是给APP集成AWS Android IoT SDK(2.16.12),Android Studio build APP过程中遇到了标题所示的问题。

原因是SDK其中的一个依赖'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.2' 有个bug

我的解决方式是:Downgrade AWS SDK Android IoT SDK 到2.14.2,因为这个版本依赖的是org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0,该版本没有兼容旧版本API Level 23的问题。

4天前( 2020Jun19) AWS SDK Android已经发布2.16.13 fix了上述的问题,Issue是: https://github.com/aws-amplify/aws-sdk-android/pull/1572

正文

这篇文章我想表达的点是,可以通过降低版本去解决新版可能导致的兼容性问题。

但是在我遇到标题所示的问题时,我并没有马上降低AWS SDK的版本,我的解决过程是:

1. Google Search Android Studio console the build error:

关键信息是: Didn’t find class “javax.net.ssl.SNIHostName”

2. 定位到这个Github Issue

https://github.com/eclipse/paho.mqtt.java/issues/633

3. 看了上述很长的Issue之后,发现是 org.eclipse.paho:org.eclipse.paho.client.mqttv3 的版本1.2.2的有Bug

但是1.2.3已经解决,而且release了。

https://github.com/eclipse/paho.mqtt.java/milestone/9?closed=1

4. 发现AWS SDK Android 2.16.12 Gradle 依赖的正是 1.2.2版本😂

SDK 2.16.12 的发布时间是11 Apr。其实只要AWS SDK更新一下 mqtt的版本到1.2.3即可解决问题。

https://github.com/aws-amplify/aws-sdk-android/blob/release_v2.16.12/aws-android-sdk-iot/build.gradle

5. 我当时想到的解决问题的方式

5.1 给AWS SDK 提交PR去更新mqtt的版本到1.2.3,然后等待review release。

5.2 自己把AWS SDK 源码下来了,然后本地修改,本地导入修改过的module。

我用的是5.2的方式,aws-android-sdk-iot只是整个sdk的一个gradle subproject。

这个类库的 gradle 依赖链是: aws-android-sdk-iot => aws-android-sdk-core & aws-android-sdk-testutils

也就是说,为了能修改aws-android-sdk-iot的gradle依赖,我要把相关类库的源码都放在自己的代码库里面,这个方式有点 overkill了。最后我还是放弃了这个方法。

我是如何想到Downgrade的?

问题没有解决,我就下班了。下班后一直还在想着怎么fix,洗澡过程中突然灵感乍现,为何不downgrade呢?

于是开电脑,检查AWS SDK android IoT Git file changes history找到了一个合适的版本后,修改build.gradle,sync gradle之后 build app 马上成功。👍

API服务监听 0.0.0.0 & 如何给本地localhost API使用HTTPS (API Gateway, SSH远程端口转发)

TL;DR

API 服务监听 0.0.0.0 IP地址,既可以被同一个局域网的主机通过改API主机的IP访问。

正文

后端开发中,各种API服务(比如,Django, Flask, Express.js)启动的时候,一般默认监听的都是127.0.0.1地址。

如果要使用DHCP分配的IP地址访问,可以把host改为0.0.0.0再启动API服务。这样同局域网的主机,比如说前端的同事就可以通过你的IP地址访问后端API了。

0.0.0.0的意思是分配给改主机的所有可能的IPv4地址。IPv6是::/0

可能分配给主机的IP地址是未知的,但是你启动API的时候是需要绑定一个IP地址的,如果你没有分配到IP地址,这个时候127.0.0.1就有用了,或者localhost也可以。

如果启动API的时候绑定不是你主机的IP会怎样?

你应该会有绑定失败,Django会抛出这个错误: Error: That IP address can't be assigned to.

所以,0.0.0.0就是有点类似通配符的效果。不需要指定Hard code 未知的地址。

竟然0.0.0.0这么方便,那些API框架怎么默认不使用?

我觉得是为了安全,0.0.0.0会直接让你服务器可能直接暴露在公网里面。一般开发过程中都是没有什么认证措施的,这个时候附近有个人端口扫描一下,可能就会泄露重要的资料。


为什么有了这个文章?

近期空闲时间在写Side project,需要暴露自己的API服务在公网,给一个SaaS服务回调。

当然我可以用ngrok,但是对比于4年前,这个服务现在越来越抠门。以前是可以单机多开,而且不限时长,现在玩了付费订阅制之后,限制越来越多。

我的解决方案是:

  1. API服务监听0.0.0.0端口,给路由器访问。
  2. 路由器做端口转发,让宽带公网IP的某个端口映射到本机API服务端口。
    1. 为什么不使用路由器DMZ?
    2. DMZ会直接暴露本机到公网。只使用端口转发所需的服务,这样安全点。

最后我把这个地址放到SaaS,即可访问http://58.123.123.123:8000/

延伸一下,如何给SaaS使用上HTTPS,而不是直接用本机公网地址?

方案一 AWS API Gateway Proxy :

SaaS Callback URL => AWS API Gateway => Public IP => Router Port forwarding => API (Listen 0.0.0.0)

  1. AWS API Gateway会给你分配一个HTTPS地址,你需要创建一个Stage才会有。
  2. 直接创建一个Resource,然后把所有路由都转发到本机的公网IP,比如上述的http://58.123.123.123:8000/
  3. 如果你不想使用AWS API Gateway分配的域名,你可以使用AWS ACM认证域名,然后在API Gateway创建一个漂亮的custom domain。
  4. 重要的是,上述的1 2 3都是免费的。如果超出了AWS free tier期限,AWS API Gateway是按需付费,零星的使用量基本上是免费的。
  5. ⚠️这个不是全程加密,API Gateway到本机的API服务之间的通信没有使用HTTPS。

方案二 本站Apache反向代理 + SSH远程转发(这个是我写这篇文章的时候想到的方法😂):

SaaS Callback URL => This WordPress Site Apache Reverse Proxy => SSH Remote forwarding => API (Listen on localhost)

  1. ✅ 这个是全程加密通信。
    1. SaaS到Apache是HTTPS,我还启用了Http2.0
    2. Apache到我本机的API服务是SSH。
  2. Apache的反向代理可以使用二级域名,然后创建一个VirtualHost。
  3. SSH使用的是远程转发。因为要远程主机访问本机服务,本机没有公网IP。

Apache VirtualHost代码:

<VirtualHost _default_:443>
  # 一个新的二级域名,记得配置DNS,还有SSL/TLS证书
  ServerName xx.adamliu.net
  SSLEngine on
  SSLCertificateFile "/opt/bitnami/apache2/conf/adamliu.net.crt"
  SSLCertificateKeyFile "/opt/bitnami/apache2/conf/adamliu.net.key"
  Protocols h2 h2c http/1.1

  ProxyPass "/"  "http://localhost:8000/"
  # 这句是让3xx之类的重定向地址可以被Apache捕获然后修改为apache的对外URL
  ProxyPassReverse "/"  "http://localhost:8000/"

  # Error Documents
  ErrorDocument 503 /503.html

</VirtualHost>

SSH远程端口转发:

# ServerAliveInterval和ServerAliveCountMax是为了保持session常驻
ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=5  -R localhost:8000:localhost:8000 ubuntu@18.123.123.123

旺角报摊

每天上班的路上,我都会路过一个报摊。这个报摊刚好位于街角的转弯处,路人每次走路转弯都会很自然地看到报摊上销售的杂志报纸。

利益最大化

为了利益最大化,报摊摊主在长年的经营中不断调整自己的策略,最后自然地演变出了现在这个状态:

  1. 巧妙地占用公共空间,摆放和展示更多的货物。这种占用空间的比例多少,是长时间慢慢磨合过程。最后路人觉得走起来也没什么问题不投诉,食环署也觉得没什么问题不整改,摊主就慢慢占到了便宜。
  2. 还记得前文提到说,路人可以很自然地看到报摊上的杂志吗?这个曝光度最大的位置,摆放的其实是著名的四大地道香港成人杂志。我每天路过都会很自然的扫几眼,而且总会觉得自己不小心就会把桌子上的杂志撞到到地上。

为什么摊主会把成人杂志放在最明显的位置

(一般情况下🔞杂志都是放在不明显的位置。)

我一直在思考这个摆方方式背后的原因,如何才能照顾好买家的情绪得体地面对面卖出一些尴尬的商品(这类商品包括:1.低情商技术宅直男在人多的便利店买安全套。2. 温柔贤惠的少妇妈咪在便利店买烟。),我为了方便理解,我设想了下面的场景:

  • 某啤酒肚男:要个《xx马经》,顺便要埋呢个。(左手顺势把马经叠在杂志上面,右手给出了50)
  • 摊主:(摊主见状,直接收50,找钱,交易完毕,不用说一句话。)

注意:

  1. 啤酒肚男为何能快速把马经叠在上面?因为这里最明显的位置就是客人最方便的位置,你只要站在报摊前面,杂志就在你最顺手的位置。
  2. 而且,摊主把《xx马经》交给你,你把《xx马经》放在桌子上掏钱时,马经已经跌在杂志上了。

人性

我观察了一下,这个报摊主要卖:

  1. 成人杂志 (黄)
  2. 马经 (赌)
  3. 烟(毒)
  4. 水 (基本需求)
  5. 口含润喉的陈皮(服务第3点,吸烟人喜欢的东西)

有没有发现这个报摊其实完全在销售符合人性原始欲望的东西。这里是旺角,想想你脑海里对旺角的印象或者电影之类的,这个报摊有没有觉得很切合旺角的风格?

最近在看云云姐(温云云)刑诉法讲解,一个人犯罪动机大概分为:财欲、性欲和攻击欲三大类。攻击欲是什么东西?当我看到这个报刊对面的有家五金店的招牌写着《浩男 五金装饰材料有限公司》,我茅塞顿开。

*本文提到的报摊位于上海街和砵兰街之间的某个街区。

[教表弟搭博客]📝课程记录 20200517 启用http/2 (Apache WordPress)

副标题:在表弟面前翻车是种什么样的感觉?🤔

视频工具: 腾讯会议

时长:约2小时

课程内容:

  1. 介绍 http/2.0
  2. 如何看出才能看到自己用了http/2.0
  3. 记录升级前后的测试结果,by Pingdom
  4. 登陆服务器更改Apache配置
    1. 安装http/2 模块
    2. 配置VirtualHost,启用http/2
  5. 翻车过程

一切都是按剧情进行。

介绍如何查看当前浏览器使用的 http 协议版本。

更新为 http/2.0 之前的 PingDom 测试。
https://tools.pingdom.com/#5c8858e403800000

启用 http/2 模块。重启 apache 。

配置 Apache VirtualHost 也是按照节奏进行。

翻车过程

弄完上面的各个步骤,我和表弟马上使用 Chrome 查看了协议版本,然而发现 http/2 并没有生效。

虽然我们是隔着屏幕教学,然而我还是略觉得尴尬😅,怎么又翻车了,说好的半个小时弄好,最后用了2个小时。

这个时候我的疑问是:

  1. 我的也是 Bitnami wordpress,也是 按照上述步骤配置好的,怎么表弟的网站不行?(后面会解答)
  2. 难道是阿里云有什么神秘的东西?(其实并没有)
  3. 是不是有 typo ?(我再三检查了一下,发现没有typo)

经过一番 Google Search, 我发现了apache 启用 http/2的必须条件,以及我表弟网站的支持情况:

  1. 网站要支持 https (虽然http/2 也可以支持 http 非安全方式链接 (h2c) 但是大多数浏览器只支持 https 的 2.0版本) ✅
  2. Apache 版本 要大于 2.4.24 ✅
  3. 要使用 php-fpm ❌
  4. 关于Apache MPM(Multi-Processing Modules), http/2 不支持 Prefork 模式,需要使用 Event 模式 ❌

启用 php-fpm 和 MPM Event Mode

只需要根据这篇文章操作就可以启用:https://docs.bitnami.com/aws/apps/wordpress/administration/enable-phpfpm/

解答一下上面的疑问一:

我的 Bitnami WordPress 是默认启用的,因为我使用 EC2 AMI Marketplace 直接安装Wordpress。

我表弟的Bitnami WordPress是使用阿里云ECS手动安装,过程很多特性并没有默认启用,比如 Apache MPM event 模式是没有启用的,这是因为 Prefork 模式有跟好的操作系统兼容性(来源)。

延伸内容

如果你想继续更深入了解,可以看看 Apache 的 Multi-Processing Modules (MPMs)。 以及三种 MPM 的工作模式:prefork, worker, 和 event。

这个链接在:https://httpd.apache.org/docs/2.4/mpm.html