# 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服务器也需要这种链路服务优化,请在官方交流群联系我们,我们会根据你的业务情况提供最佳的解决方案。
点击咨询小助手