一个叫木头,一个叫马尾

一个价值百万的开发调试经验:使用nginx和ssh打通线上和本地服务

文章标题有些耸人听闻,但只要理解了其中的思路,谓之「受益一生」也不算为过...


一言以蔽之,我们有时需要把线上的某些请求转发到本地以方便调试。

这里存在几个不得不跨的槛:

  1. 怎么确定要转发哪些请求
  2. 线上网络和本地是不通的,怎么打通它们
  3. 转发建立后,怎么不影响其它的请求

仍然比较抽象?我再提供一个具体的例子。

在做公众号自动回复开发时,我们需要在微信后台配置一个回调url,这个url必须是公网可访问的,不然微信无法发送消息到我们自己的服务器上。通常情况下,我们需要把开发好的代码部署到服务器上才能验证自动回复是否成功,这样 开发<-->部署<-->测试 不断循环,实时效果比较差。

所以我们想,能不能在我们自己的服务器端做手脚,将特定的消息直接转发到本地机器上?这样免去了部署的麻烦,本地就可以实时测试。


背景交待完,再来就是捋顺这根扭曲的绳子了。下面仍以开发公众号自动回复为例,抛砖引玉。

一、怎么确定要转发哪些请求

这些功能是技术开发的,为了方便技术调试,可以把技术人员的特征提取出来,做为转发条件。对应到微信公众号上,我们发现,每个消息通知请求都会携带消息发送者的openid:

POST /wechat?nonce=735&openid=ojhd0wb_4l6mt

(在其它的应用场景中,我们还可以借助cookie、header等,不要限制自己的想像力)

二、线上网络和本地是不通的,怎么打通它们

大家都知道ssh可以用来登录远程服务器,其实它还有另一个强大的功能:端口转发(port forward)。它既允许你将发向服务器的请求转发到本地,又允许你将发向本地的请求转发到服务器。很明显,我们需要前者:当有微信消息发向我们服务器时,我们将其转发到本地(技术人员的电脑)。

远程转发命令示例:

ssh -v -R 3001:127.0.0.1:3001 -N example.com

其含义是,在 example.com机器 上对于 端口3001 的访问,将转发到 本机3001端口 上。

关于ssh的使用技巧,参见我之前的文章:ssh二三事。掌握它,真的受用无穷。

三、转发建立后,怎么不影响其它的请求

要不影响其它请求,这意味着有消息过来时,服务器需要配有2类处理逻辑。一类是符合特征的请求要转发到本地,另一类请求则直接由服务器上的应用处理。

通常大家都是以nginx来处理请求转发,这里也以它为例来解决这个问题吧。

下面是重点部分,读者要细心去品...品品...

首先定义一组上游服务,它们用来直接处理消息通知:

upstream wx {
  # 为了保持高可用,线上服务通常要部署多个
  server a:3001;
  server b:3001;
}

再定义一个map,用来根据请求者的 openid 来分派服务:

map $arg_openid $proxy {
  # 默认使用线上服务
  default "wx";
  # 当openid为ojhd0wb_4l6mt,使用本地的3001服务
  ojhd0wb_4l6mt 127.0.0.1:3001;
}

最后,在我们的微信回调处理路径中,加上以上代理:

# 微信通知的回调路径
location /wechat {
  # 消息转发到proxy下,即上面定义的map
  proxy_pass http://$proxy;
}

以上所有步骤完成后,让技术人员在微信公众号内发个消息,会发现他们自己的电脑上正常接收到了请求。


用nginx和ssh将线上和本地服务打通,对调试一些回调的问题特别有用,比如上面的消息通知,以及经常用到的第三方登录等。

你有更好的办法吗?欢迎到我的公众号留言。