# wx.cloud.callContainer 技术原理和使用指南

无论是在微信小程序还是在公众号H5中,cloud.callContainer 是微信团队推荐的调用云托管服务的方式。

但云托管服务本身也提供公网访问链接,为什么不直接使用 wx.request 或者在网页中发起 XHR 请求呢?推荐引入 callContainer 这种方式有什么具体技术背景?

本篇给大家深度讲解 callContainer 调用

# 一、传统调用方式

我们先来看一下,wx.request 调用方式:

const result = await wx.request({
  url: 'https://www.example.com/postapi', //仅为示例,并非真实的接口地址
  method: 'GET' // 按照自己的业务开发,选择对应的方法
})

console.log(res);

wx.request 和 Web 浏览器中 XHR 都是一类,提供标准的网络请求方式,只不过 wx.request 是为微信小程序定制的。

这类请求大致分两个部分,DNSTCP连接

其中 DNS 是指将域名信息发送网络内 DNS 服务器,换取域名映射的服务器 IP 地址,为接下来连接做准备。

获取 IP 地址后,小程序就向此 IP 的服务器建立请求链接,按照请求方法发送数据包,然后等待服务器回包,完成整个请求。

在这里有两个变量,第一个是 DNS,如果用户网络中的 DNS 服务器被劫持,那么小程序收到的IP地址是错误的,而后向错误的IP地址发送请求包,这种情况被称为 DNS劫持

DNS 劫持的危害是非常大的,为了防止此情况,微信团队推出移动解析HttpDNS服务,避免 Local DNS 造成的域名劫持和跨网访问问题,这是一项付费服务

DNS 之后,第二个变量就是连接,小程序发送的请求会直接经过公网,通过网络的各个节点到达你的服务器所在地。

比如小程序用户在广州,你的服务器在北京,那么这个请求包会一路经过网络沿途的各个运营商交换中心的「路由节点」转发,一直到达你的服务器,这个过程中沿途受干扰变数较多,而且距离越远,变数越大。

当然解决办法也是有的,在全国各地布置接入点,每个接入点和服务中心直接拉专线网络,这叫私有专线;或者将服务端搬到各个地域,组成多地域服务;不管用什么方法,还是需要资金来建设。

# 二、微信链路请求

在微信小程序中,callContainer 方式的代码是这样子的:

const res = await wx.cloud.callContainer({
  config: {
    env: '填入云环境ID', // 微信云托管的环境ID
  },
  path: '/postapi', // 填入业务自定义路径和参数,根目录,就是 / 
  method: 'GET', // 按照自己的业务开发,选择对应的方法
  header: {
    'X-WX-SERVICE': 'xxx', // xxx中填入服务名称(微信云托管 - 服务管理 - 服务列表 - 服务名称)
  }
});

console.log(res);

但实际来讲,以上 callContainer 最终也是经过 wx.request 请求发出去的,但是走的却并不是常规的道路。(在 Web 网页中是经过 XHR 发出的)

先说结论:callContainer 本质仍然是 TCP 请求,只是路径是微信的专有路径

在上一部分我们讲到 DNS 和公网的一些问题,在你使用 callContainer 发送请求的时候就已经全部解决了。

首先,DNS 解析,通过 callContainer 发送请求时,小程序压根就不请求 DNS 解析,而是直接向微信服务器固定发送请求包,这个包中涵盖如下信息:

  • 云托管环境ID(env)
  • 云托管服务名称(service)
  • 业务路径(path)
  • 请求的数据(data)

这个请求包会直接经过就近节点进入微信遍布全国的专线网络,在就近地域内微信服务器中识别请求的内容,判断云托管环境ID在哪个地域,具体的地址是什么,然后向目标地址发送你的请求数据,并在这一过程中将用户的身份信息验证一下,带上用户的openid等其他身份信息。

当你云托管服务中收到请求时,header中就可以看到 x-wx 开头的微信认证信息,至于业务数据,和正常通过 wx.request 收到的数据一模一样。

你的服务返回数据包,又会经过同样的路径返回,一直回到小程序中,第一次请求就结束了,这个时候除了服务器给的信息正常返回给你的前端业务层,还有一些信息在经过 callContainer 的时候被截留了。

这些截留的信息就是在第一次请求中,微信探查的「用户小程序」到「云托管服务」之间最佳的路径,以及加密的用户身份令牌。

之后发送请求的时候,callContainer 就会直接按照这个最佳路径发送请求包,而身份令牌可以在经过微信链路网关时被解析出来,最终以明文形式展示在服务header中。

而从 callContainer 经过 wx.request 发送请求,一直到收到回复,整个请求包都是经过端到端加密的,也就是说,即使用户的手机网络被抓包了,也是一堆加密信息,并没有什么可参考的价值。

另外在整个微信链路中,可以有效过滤网络攻击,恶意请求,刷接口等行为,这些请求都会在进入链路时就被过滤到,最终到达云托管服务时,都是经过微信验证的正常请求。

如果你的业务本身并不需要用户的身份信息,则可以在header中添加如下记录:

'X-WX-EXCLUDE-CREDENTIALS': 'unionid, cloudbase-access-token, openid'

以上是排除指令,如果不填写则会在链路通信中附带,填写则不附带。

因为附带信息会在占用一些时间(大概10ms-50ms),所以根据自己的业务需要,合理选择附带信息。

例子如下:

const res = await wx.cloud.callContainer({
  config: {
    env: '填入云环境ID', // 微信云托管的环境ID
  },
  path: '/postapi', // 填入业务自定义路径和参数,根目录,就是 / 
  method: 'GET', // 按照自己的业务开发,选择对应的方法
  header: {
    'X-WX-SERVICE': 'xxx', // xxx中填入服务名称(微信云托管 - 服务管理 - 服务列表 - 服务名称)
    'X-WX-EXCLUDE-CREDENTIALS': 'unionid, cloudbase-access-token, openid' // 不附带用户unionid,openid,access—token
  }
});

console.log(res);

# 三、其他事项

1)当你使用未登录模式时,本身就没有用户信息,建议添加 X-WX-EXCLUDE-CREDENTIALS 能起到一定的加速作用。 2)未登录模式虽然没有用户信息,但是安全性和链路效率仍然有效存在。 3)除了云托管之外,云开发 callFunction 请求云函数的原理也基本类似。 4)在成本方面,callContainer 的微信链路效果相当于 WAF 和 DDOS 高防叠加,在云托管服务中这些都是标准配置,无需你支付任何费用。 5)callContainer 不仅可以应用在微信云托管中,如果你自有的 CVM 服务器也需要这种链路服务优化,请在官方交流群联系我们,我们会根据你的业务情况提供最佳的解决方案。

点击咨询小助手