title: 使用N8N工作流自动化解决三方API数据对接

0、n8n是什么?

n8n 是免费的基于节点的工作流自动化工具,可以轻松实现跨不同服务的任务自动化。它可以自托管,易于扩展,因此也可以与内部工具一起使用。

1、安装

参见n8n官方文档^[1],推荐docker安装

2、需求案例

在商业推广中往往有一些API对接的需求,细分下来有这几类
* ① 通用广告投放平台 如果是广点通、巨量引擎、橙子建站线索API对接等
* ② 我方开放API
* ③ 三方API对接
市场推广活动会产生三方API对接的需求。
这样的需求去基于代码开发的方式对接,对于开发者来说价值不高,使用N8N能解决这种对接问题

例如一个推广服务商使用API提供线索,需要实现全自动化同步到我方系统

工作流逻辑上就是: 计划任务周期运行 > 读取数据通过API(三方) > 数据转换处理 > 写入数据通过API(我方)

3、n8n实现

n8n工作流可视化效果图
使用N8N工作流自动化解决三方API数据对接

共需 4个 核心节点(node)(写入excel文件可忽略)
计划任务周期运行Cron CORE NODES > Flow > Cron
读取数据通过API(三方)DEVELOPMENT > HTTP Request
数据转换处理 CORE NODES > Function
写入数据通过API(我方)DEVELOPMENT > HTTP Request

主要说下数据转换处理

数据转换处理把接口返回数据变成 n8n中 items列表
接口返回数据在n8n中默认为items,由于返回的多条数据是在json中data字段中,需要做个转换取出来转换成n8n中 items列表供下游节点使用。
items[0].json.data 取接口返回数据,json数据,data字段

let rows = []; 

for (item of items[0].json.data) {
  rows.push(item);
}

console.log('Done!');
return rows;

在n8n中的函数代码都是js编写的

数据转换处理好后,items就有多个了,本工作流中取到了3个items,对于「写入数据通过API」就会调用3次

4、参考

1. n8n官方文档

基于FreeSWITCH自建呼叫中心中台 流水理鱼|wwek PPT分享

目录导航

  • 业务需求背景情况 – 业务需求、背景情况
  • 业务系统如何外呼 – 点呼、群呼、AI
  • 呼叫中心通话链路 – 通话序列图、呼叫线路、SIP协议介绍
  • 呼叫中心中台架构 – 呼叫中心中台架构设计(基础版)
  • FreeSWITCH-介绍 – FreeSWITCH电话软交换
  • FreeSWITCH-拨通第一个电话 – SIP Hello World
  • FreeSWITCH-集成 – FreeSWITCH系统集成设计和实现
  • FreeSWITCH-中台API封装 – 把FreeSWITCH的能力封装为中台API

业务需求背景情况

  • CRM中常见需要对ToB、ToC客户进行电话回访、电话销售
  • 在打电话这个事上,企业需求比个人需求要求更多,最基本的可系统集成、有话单、有录音等
  • 无论时代如何变,传统的基于运营商电话的接通需求是一直稳定存在的
  • 三方商业呼叫系统有很多,okcc、合力忆捷、天润、容联七陌、网易七鱼等
  • 自研呼叫解决3个主要核心问题。能力上,定制开发扩展性拉满;成本上,比购买商业呼叫系统便宜;安全上,数据在手
  • 自研呼叫需要具备条件,公司业务按年为单位长期有呼叫需求,有开发人员资源

业务系统如何外呼

  • 呼叫方式上,单个点击拨打(点呼)、批量呼通再分配排队坐席(群呼)、AI自动呼叫
  • 坐席(打电话人)软电话登录,登录呼叫中心的软电话客户端(无客户端的为网页浏览器客户端)
  • 业务系统中点击拨打、或者操作建立群呼、AI呼叫任务
  • 业务系统通过API调用呼叫中心控制发起每通电话,接下来看看通话序列图

查看文章

☎️呼叫中心通话链路 – 呼叫线路示意图

“`mermaid {theme: ‘neutral’, scale: 0.66}
graph LR
A[呼叫中心系统] –>|呼叫线路选择| B(选择线路网关1)
B –> |SIP|C{呼叫线路商1线路路由}
A[呼叫中心系统] –>|呼叫线路选择| Bn(选择线路网关N)
C –>|电信运营商落地1| D[A地固话号]
C –>|电信运营商落地2| E[B地固话号]
C –>|电信运营商落地3| F[C地手机号]
Bn –> |SIP|Cn{呼叫线路商N线路路由}
Cn –>|电信运营商落地1| Dn[A地固话号]
Cn –>|电信运营商落地2| En[B地手机号]
Cn –>|电信运营商落地3| Fn[C地手机号]

<pre><code><br /><br /># 呼叫中心通话链路 – 呼叫线路介绍
– 呼叫线路商提供的线路在 呼叫中心系统 有多个称呼,线路网关、SIP中继、中继网关、落地线路
– 呼叫线路的称呼,不论叫什么,他都是电话的通道,使用SIP协议对接
– 呼叫线路商使用VOS语言运营系统作为支持软件,VOS处于VOIP运营垄断地位
– 不使用呼叫线路商,直找电信运营商对接线路,用SIP协议对接
– 这样就相当于只有一条落地线路了,没有丰富的线路资源来优化线路路由,需按业务需求选择
– **呼叫线路商的本质是聚合多条、多地、多类型电信运营商线路资源**
一个典型的线路对接信息
</code></pre>

线路备注:xx线路
IP:8.8.8.8
UDP端口:5060 (SIP协议)
主叫送:20220601
被叫加前缀:6
并发:500
限制:一天一次
盲区:北、新、西

<pre><code><br /># SIP协议介绍
SIP(Session Initiation Protocol,会话初始协议)[^1]是由IETF(Internet Engineering Task Force,因特网工程任务组)制定的多媒体通信协议。广泛应用于CS(Circuit Switched,电路交换)、NGN(Next Generation Network,下一代网络)以及IMS(IP Multimedia Subsystem,IP多媒体子系统)的网络中,可以支持并应用于语音、视频、数据等多媒体业务,同时也可以应用于Presence(呈现)、Instant Message(即时消息)等特色业务。可以说,有IP网络的地方就有SIP协议的存在。SIP类似于HTTP
– **说人话!SIP协议用来在IP网络上做电话通讯**
[^1]: [一文详解 SIP 协议](https://www.cnblogs.com/xiaxveliang/p/12434170.html)

# SIP协议介绍-SIP Server
![](https://static.iamle.com/note/202207011111385.png)
– FreeSWITCH就是一个SIP Server,也是一个B2BUA,后面具体讲 FreeSWITCH
– **呼叫中心的SIP Server桥接 A leg 和 B leg,这样A和B就建立通话了**
– B2BUA看起来唬人,靠背嘛,就是A leg 和 B leg靠背桥接起来
– 支持SIP协议的软电话客户端常用的有:Linphone、MicroSIP、Eyebeam等

# SIP协议介绍-SIP Server 和 A leg 通讯
<img class="m-1 h-110 rounded" src="https://static.iamle.com/note/202207011131334.png" />

# SIP协议介绍-SIP Server 和 B leg 通讯
<img class="m-1 h-110 rounded" src="https://static.iamle.com/note/202207011124286.png" />

# 呼叫中心中台架构 – 呼叫中心中台架构设计(基础版)目标
– 基础版设计目标,实现点击拨打(点呼)
– **呼叫能力的高级抽象**
## 呼叫(主叫号码,被叫号码,[线路网关], [拓展数据])

– 主被叫号码既可以是内部的坐席分机号码,也可以是手机号码
– 线路网关是选填参数,支持多个逗号分割,为空使用系统默认配置网关
– 拓展数据是选填参数,呼叫系统在后续通话技术后原样传回业务系统
– 拓展数据,用于业务自身逻辑

layout: center

# 呼叫中心中台架构 – 呼叫中心中台架构设计(基础版)架构图
<img class="m-1 h-110 rounded" src="https://static.iamle.com/note/202207011731635.png" />

# FreeSWITCH – 介绍
– FreeSWITCH 是一个作为背靠背用户代理实现的开源运营商级电话平台。由于这种设计,它可以执行大量不同的任务,从PBX到传输交换机、TTS(文本到语音)转换、音频和视频会议主机,甚至是VoIP电话等等。
– 是一款非常好用的电话软交换框架,支持跨平台,扩展性良好,配置灵活。
– 可以在很多平台上运行,包括 Linux、Mac OS X、BSD、Solaris,甚至 Windows。
– 可以处理来自 IP 网络 (VoIP) 和 PSTN(普通的固定电话)的语音、视频和文本通信。
– 支持所有流行的 VoIP 协议以及与 PRIs 的接口。
– 支持 OPUS、iLBC、Speex、GSM、G711、G722 等多种语音编解码,支持 G723、G729 等语音编解码的透传模式。
– 可以当作 PBX、SBC、媒体服务器、业务服务器等不同的通信节点来使用
– 本身是在 MPL 1.1 (Mozilla 公共许可证) 下许可的,但是一些单独的模块可能使用其他许可证。
– **说人话 FreeSWITCH 是一个软件实现的电话交换平台,开源、模块化、功能丰富**
– **市面上绝大多商业呼叫中心都是基于 FreeSWITCH 为核心开发的**

layout: center

# FreeSWITCH – 总体结构
<img class="m-1 h-120 rounded" src="https://static.iamle.com/note/202207011403991.png" />

layout: center

# FreeSWITCH – 总体结构
<img class="m-1 h-120 rounded" src="https://static.iamle.com/note/202207011405842.png" />

layout: center

# FreeSWITCH – 配置文件目录结构
/etc/freeswitch# tree -Ld 3
</code></pre>

├── freeswitch.xml 主xml文件,就是它将所有配置文件“粘”到一起,生成一个大的xml文件
├── vars.xml 常用变量
├── autoload_configs 一般都是模块级的配置文件,每个模块对应一个。文件名一般以 module_name.conf.xml 方式命名。
│   ├── *.conf.xml
├── chatplan 聊天计划
├── dialplan 拨号计划
├── directory 用户目录,分级用户账号
│   ├── default 默认的用户目录配置
│   │   ├── *.xml SIP用户,每用户一个文件
├── sip_profiles
│   ├── external SIP中继网关配置
│   │   ├── *.xml
│   ├── external.xml
│   └── internal.xml
├── ivr_menus IVR 菜单
├── jingle_profiles 连接Google Talk的相关配置
├── lang 多语言支持
├── mrcp_profiles MRCP的相关配置, 用于跟第三方语音合成和语音识别系统对接
├── skinny_profiles 思科SCCP协议话机的配置文件
├── tls tls证书
├── extensions.conf

<pre><code>- FreeSWITCH的配置文件由众多XML配置文件构建

layout: two-cols

# FreeSWITCH – 控制客户端和开发者接口
FreeSWITCH如何操作控制和开发对接?[^1]
– ① fs_cli 为命令行控制接口,也就是敲命令控制[^2]
– ② ESL(Event Socket Library) 通过事件接口和FreeSWITCH交互控制,fs_cli本质上也是走的ESL一样的底层流程
– ③ mod_xml_curl、mod_xml_rpc、lua脚本语言、自编写模块等更多方式实现和FreeSWITCH交互控制
::right::
</code></pre>

输入 fs_cli 进入命令行控制
.=======================================================.
| _____ ____ ____ _ ___ |
| | <strong><em>/ ___| / ___| | |</em> <em>| |
| | |</em> _</strong> \ | | | | | | |
| | <em>| <strong><em>) | | |</em></strong>| |___ | | |
| |</em>| |____/ ____|_____|___| |
| |
.=======================================================.
| Anthony Minessale II, Ken Rice, |
| Michael Jerris, Travis Cross |
| FreeSWITCH (http://www.freeswitch.org) |
| Paypal Donations Appreciated: paypal@freeswitch.org |
| Brought to you by ClueCon http://www.cluecon.com/ |
.=======================================================.
Type /help to see a list of commands
+OK log level [7]
freeswitch@callcenter>

<pre><code>[^1]: [FreeSWITCH Client and Developer Interfaces](https://freeswitch.org/confluence/display/FREESWITCH/Client+and+Developer+Interfaces)
[^2]: [Command Line Interface (fs_cli)](https://freeswitch.org/confluence/pages/viewpage.action?pageId=1048948)

# FreeSWITCH – 拨通第一个电话 – 分机和分机
<img class="m-1 h-90 rounded" src="https://static.iamle.com/note/202207011645715.jpg" />
* fs_cli 命令行让分机1000和分机1002通话&gt; originate user/1000 'bridge:user/1002' inline
* 分机1000使用软电话客户端Linphone,分机1002使用软电话客户端MicroSIP注册在了FreeSWITCH服务器

# FreeSWITCH – 拨通第一个电话 – 分机和手机
<img class="m-1 h-90 rounded" src="https://static.iamle.com/note/202207041015836.png" />
* fs_cli 命令行让分机1000通过网关和手机通话&gt; originate user/1000 'bridge:{origination_caller_id_number=网关主叫}sofia/gateway/网关名/被叫前缀+手机号码' inline
* 分机1000使用软电话客户端Linphone,被叫为手机号

# FreeSWITCH – FreeSWITCH系统集成设计
FreeSWITCH 的配置都是 XML的,最朴素的想法,如何实现动态配置能力?

和 FreeSWITCH 集成需要解决下列问题
|问题|方案|
| —- | —- |
| 分机动态配置 | mod_xml_curl 提供分机动态配置能力,开发对应的API输出分机XML配置文件 |
| 拨号计划动态配置 | mod_xml_curl 提供拨号计划动态配置能力,开发对应的API输出拨号计划XML配置文件 |
| 网关动态配置 | 低频需求暂时手工加载配置文件,开发对应的API可以一键生成网关XML配置文件 |
| CDR话单存储 | mod_xml_cdr 提供话单推送能力,开发对应的API接收话单推送 |
| Record录音对象存储生成URL | api_hangup_hook挂机后回调处理上传录音文件,开发对应API接收上传的录音文件,并上传到对象存储 |
| WebHook回调CDR和Record录音到业务系统 | 在CDR和Record都有了的时候执行回调 |

# FreeSWITCH – FreeSWITCH系统集成实现
集成实现采用golang编程语言开发,框架采用goframe v2

实现如下API,提供给FreeSWITCH集成
|API|实现方式| 说明 |
| —- | —- | —- |
| /fsapi/xml_curl | 读取数据库分机表生成分机XML配置文件 | mod_xml_curl模块对接 |
| /fsapi/cdr | 接收CDR话单并存储数据库CDR话单表 | mod_xml_cdr模块对接 |
| /fsapi/upload_audio | 接收录音文件上传,并上传到对象存储生成录音URL | api_hangup_hook挂机后回调处理上传录音文件 |


layout: full

# FreeSWITCH – 呼叫中心中台API封装实现
中台API和 FreeSWITCH系统集成API放同一个golang项目

|API|实现方式| 说明 |
| —- | —- | —- |
| /v1/call/callback| 调用ESL接口发送事件命令 |呼叫-回拨 参数:主叫号码、被叫号码、网关名称(多个逗号分割)、拓展数据|
| /v1/extension/list| 读取数据库分机表 |分机列表|
| /v1/extension/detail| 读取数据库分机表 |分机详情 参数:分机号|
| /v1/gateway/list| 读取数据库分机表 | 网关列表|
| /v1/gateway/detail| 读取数据库分机表 | 网关详情|
| /v1/gateway/detail_xml| 读取数据库分机表 | 网关详情XML配置文件|
| /v1/extension/online/list| 调用ESL接口 |在线分机列表|
| /v1/gateway/online/list| 调用ESL接口 |在线网关列表|
<!–

<style>
h1 {<br />
font-size: 20px;<br />
}<br />
</style> –>

# FreeSWITCH – 呼叫中心中台API封装实现-呼叫实现
– **呼叫实现核心逻辑**
使用拨号计划组合多个"APP"实现录音、录音采样率设置、挂机后上传录音等能力
拨号计划使用inline即内联模式[^1],ESL接口发送一条实践命令即可完成
</code></pre>

'app1:arg1,app2:arg2,app3:arg3' inline

<pre><code>呼叫模板
</code></pre>

"originate [参数]user/分机号
'
set:media_bug_answer_req=true,
set:record_sample_rate=8000,
set:RECORD_STEREO=true,
set:cc_record_filename=$${recordings_dir}/${strftime(%%Y-%%m-%%d)}/${uuid}.mp3,
export:nolocal:execute_on_answer=record_session:${cc_record_filename},
set:curl_sendfile_url=%s,
set:api_hangup_hook=system curl -XPOST -F \"files=@${cc_record_filename}\" ${curl_sendfile_url}?uuid=${uuid} &,
set:continue_on_failure=true,
set:hangup_after_bridge=true,
set:session_in_hangup_hook=true,
set:ringback=$${sounds_dir}/music/8000/ponce-preludio-in-e-major.wav,
bridge:[参数]sofia/gateway/网关名/前缀+被叫号码,
playback:$${sounds_dir}/zh/cn/link/misc/misc-your_call_has_been_terminated.wav,
info:,
hangup:
'
inline"
“`

⏬基于FreeSWITCH自建呼叫中心中台 PDF

[TOC]

0、前言

image-20210606160820147

为什么使用Elasticsearch (以下简称 ES ) ?

全文搜索能力

ES本身就是一个搜索引擎,技术上基于倒排索引实现的搜索系统。我们做业务不必自己去开发一个搜索引擎,

ES已经满足绝大多数企业的搜索场景的需求。拼多多电商在发展前期,搜索业务使用ES支撑。

复杂查询(多维度组合筛选)

假如某个实体表,例如订单日订单量几十万,运营管理后台通常有十多个维度的组合筛选搜索。你可能会通过缩小查询时间范围来把数据量级降下来,MySQL也许也能一战,但是实际业务场景业务人员不太能接受这样一个小时间段筛选。如果稍微扩大时间段,MySQL这时候就无能为力了。那么把订单表做成“宽表”存入ES,十多个维度的搜索对于ES俩说是毫无压力的。ES群集能轻松支持亿级的宽表多维度组合筛选。这种需求在CRM、BOSS等场景是刚需。

滴滴司机端早期日订单量几十万,运营管理后台大时间跨度且多维度组合筛选就是用ES来实现的。

一个产品是否成熟,公有云是否提供基于该产品的服务是一个风向标。那么ES毫无疑问是成熟的产品。

虽然ES这么好,但是我的数据都在MySQL中那么怎么让数据实时同步到ES中呢?

1、基于应用程序多写

直接通过应用程序数据双写到MySQL和ES

image-20210606174354578

记录删除机制:直接删除

一致性: 需要自行处理,需要对失败错误做好日志记录,做好异常告警并人工补偿

优点: 直接明了,能够灵活控制数据写入,延迟最低

缺点: 与业务耦合严重,逻辑要写在业务系统中

应用双写(同步)

直接通过ES API将数据写入到ES集群中,也就是写入数据库的同时调用ES API写入到ES中

这个过程是同步的

应用双写(MQ异步解耦)

对 应用双写(同步)的改进,引入MQ中间件。

把同步变为异步,做了解耦。

同时引入MQ后双写性能提高,解决数据一致性问题。

缺点是会增加延迟性,业务系统增加mq代码,而且多一个MQ中间件要维护

2、基于binlog订阅

binlog订阅的原理很简单,模拟一个MySQL slave 订阅binlog日志,从而实现CDC(change data capture)

CDC,变更数据获取的简称,使用CDC我们可以从数据库中获取已提交的更改并将这些更改发送到下游,供下游使用。这些变更可以包括INSERT,DELETE,UPDATE等。

记录删除机制:直接删除

一致性: 最终一致性

优点: 对业务系统无任何侵入

缺点: 需要维护额外增加的一套数据同步平台;有分钟级的延迟

Canal

img

https://github.com/alibaba/canal/

阿里巴巴 MySQL binlog 增量订阅&消费组件

Databus

https://github.com/linkedin/databus

Linkedin Databus 分布式数据库同步系统

Maxwell

官网:http://maxwells-daemon.io/

https://github.com/zendesk/maxwell

Flink CDC

https://github.com/ververica/flink-cdc-connectors

flink-cdc-connectors 中文教程

基于flink数据计算平台实现 MySQL binlog订阅直接写入es

Flink CDC抛弃掉其他中间件,实现 MySQL 》Flink CDC》ES 非常简洁的数据同步架构

该方式比较新2020年开始的项目,目前在一些实时数仓上有应用

DTS(阿里云)

阿里云的商业产品,具备好的易用性,省运维成本。

CloudCanal

CloudCanal官网

CloudCannal数据同步迁移系统,商业产品

3、基于SQL抽取

基于SQL查询的数据抽取同步

这种方式需要满足2个基本条件

1、MySQL的表必须有唯一键字段(和ES中_id对应)

2、MySQL的表必须有一个“修改时间”字段,该记录任何一个字段修改都需要更新“修改时间”

有了唯一键字段就可以知道修改某条记录后同步哪条ES记录,有了修改时间字段就可以知道同步到哪儿了。

满足了这2个基本条件这样就可实现增量实时同步。

记录删除机制:逻辑删除,在MySQL中增加逻辑删除字段,ES搜索时过滤状态

一致性: 依赖修改时间字段;延迟时间等于计划任务多久执行一次

优点: 对业务系统无任何侵入,简单方便;课用JOIN打宽表

缺点: MySQL承受查询压力;需要业务中满足2个基本条件

logstash

我们使用 Logstash 和 JDBC 输入插件来让 Elasticsearch 与 MySQL 保持同步。从概念上讲,Logstash 的 JDBC 输入插件会运行一个循环来定期对 MySQL 进行轮询,从而找出在此次循环的上次迭代后插入或更改的记录。如要让其正确运行,必须满足下列条件:

  1. 在将 MySQL 中的文档写入 Elasticsearch 时,Elasticsearch 中的 “_id” 字段必须设置为 MySQL 中的 “id” 字段。这可在 MySQL 记录与 Elasticsearch 文档之间建立一个直接映射关系。如果在 MySQL 中更新了某条记录,那么将会在 Elasticsearch 中覆盖整条相关记录。请注意,在 Elasticsearch 中覆盖文档的效率与更新操作的效率一样高,因为从内部原理上来讲,更新便包括删除旧文档以及随后对全新文档进行索引。
  2. 当在 MySQL 中插入或更新数据时,该条记录必须有一个包含更新或插入时间的字段。通过此字段,便可允许 Logstash 仅请求获得在轮询循环的上次迭代后编辑或插入的文档。Logstash 每次对 MySQL 进行轮询时,都会保存其从 MySQL 所读取最后一条记录的更新或插入时间。在下一次迭代时,Logstash 便知道其仅需请求获得符合下列条件的记录:更新或插入时间晚于在轮询循环中的上一次迭代中所收到的最后一条记录。
input {
  jdbc {
    jdbc_driver_library => "<path>/mysql-connector-java-8.0.16.jar"
    jdbc_driver_class => "com.mysql.jdbc.Driver"
    jdbc_connection_string => "jdbc:mysql://<MySQL host>:3306/es_db"
    jdbc_user => <my username>
    jdbc_password => <my password>
    jdbc_paging_enabled => true
    tracking_column => "unix_ts_in_secs"
    use_column_value => true
    tracking_column_type => "numeric"
    schedule => "*/5 * * * * *"
    statement => "SELECT *, UNIX_TIMESTAMP(modification_time) AS unix_ts_in_secs FROM es_table WHERE (UNIX_TIMESTAMP(modification_time) > :sql_last_value AND modification_time < NOW()) ORDER BY modification_time ASC"
  }
}
filter {
  mutate {
    copy => { "id" => "[@metadata][_id]"}
    remove_field => ["id", "@version", "unix_ts_in_secs"]
  }
}
output {
  # stdout { codec =>  "rubydebug"}
  elasticsearch {
      index => "rdbms_sync_idx"
      document_id => "%{[@metadata][_id]}"
  }
}

配置Logstash的计划任务,定时执行

4、总结

前期建议采用 基于SQL抽取的方式做同步,后期数据量大了建议采用基于binlog订阅的方式同步。

如果本身有现成的Flink平台可用,推荐使用Flink CDC。

什么是最佳的 MySQL 同步 ElasticSearch 方案?

答案是选择缺点可以接受,又满足需求,拥有成本最低的方案。

“完美”的方案往往拥有成本会比较高,所以需要结合业务环境的上下文去选择。

流水理鱼觉得没有一招鲜的方案,因为每种方案都有利弊,所以选取适合你当下业务环境的方案。那么这样的方案就是最佳方案。

5、参考

MySQL 数据实时同步到 Elasticsearch 的技术方案选型和思考 by 万凯明

如何使用 Logstash 和 JDBC 确保 Elasticsearch 与关系型数据库保持同步

监听mysql的binlog日志工具分析:canal、Maxwell、Databus、DTS

0. 前言

关键字

号码隐私保护

虚拟号码保护

号码保护

手机小号

虚拟号码

隐私号码

定义

A、B一般代表用户真实号码

X一般代表PASS平台中的小号,通常和A进行绑定(也叫中间号、隐私号、虚拟号)

Y一般代表PASS平台中的小号,用于解决回呼,通常用于B回呼A

x小x代表分机号

1. 号码隐私保护通话模式介绍

1.1 AX (AXN)模式

只保护号码A,号码X不能复用

一对多隐私保护

呼叫前预先建立A和X的绑定关系

为用户A分配隐私号X,A对外的号码都以X替代,所有与A的通话都通过X建立,保护A号码不泄漏

隐私保护通话AX模式中,A为业务受益用户,为了保护A的真实号码不被泄露,在隐私保护通话平台为A绑定一个虚拟号码X。

  • 绑定关系建立后:
    • 所有人均可拨打X联系A,保护A的真实号码不被泄露。
    • 用户A呼叫其他用户时,企业需要通过API(AX模式设置临时被叫接口)指定呼叫对象(如B),然后A拨打X号码呼叫B。
  • AX模式下的X号码只专属于A号码,即一个虚拟号码X同时只能绑定一个A号码。但1个A号码可以同时绑定5个X号码。
  • 当X号码和A号码解除绑定关系后,该X号码可以被回收,供其他号码绑定。

AX模式下具有如下关键功能:

点击放大

1.2 AXB模式

保护号码A和B,号码X可以复用

一对一隐私保护

用户A与B之间建立一对一绑定关系,双方通过X号码联系对方,保护双方号码隐私。同一X号码可以复用在不同的绑定关系中

呼叫前A和B都必须是已知号码,预先建立AXB的绑定关系

用户A和B不知道对方真实号码,通过平台分配的临时隐私号X联系对方,保护双方号码不泄漏

隐私保护通话AXB模式中,A和B为相互保密的两个业务受益用户,A和B用户都不知道对方真实号码的存在,为了双方的真实号码不被泄露,在隐私保护通话平台为A和B用户绑定一个虚拟号码X,A和B用户对对方只呈现X号码,A和B之间的通信都是通过X号码进行转接。

  • AXB模式下X号码允许被多组号码(建议不超过1000组号码)绑定,即支持多路并发呼叫,但是多组号码中的A和B号码不能重复。例如,允许同时绑定AXB和CXD,但不允许同时绑定AXB和BXC。
  • 同一个A号码若要绑定不同B号码,需要使用不同的X号码进行绑定。如,若已存在AXB,还需绑定A和C,需使用别的X号码,如AX1C。
  • 当X号码和A、B号码解除绑定关系后,该X号码可以被回收,供其他号码绑定。

AXB模式下具有如下关键功能:

点击放大

1.3 X模式

该模式由企业自身维护绑定关系,控制力强

PASS平台仅提供呼叫和短信的管道能力,由企业管理隐私号X与用户的绑定关系,通过灵活利用绑定关系,节省号码资源,创新更丰富的应用

隐私保护通话X模式中,PASS平台对外提供X号码呼叫和短信能力,PASS平台侧不存储任何绑定关系,小号平台接收到呼叫或短信后到第三方系统获取绑定关系。

X模式下具有如下关键功能:

点击放大

点击放大

1.4 AXE (AXN分机、AXx分机)模式

AX模式上增加分机号E

一对多隐私保护

只保护号码A,号码X通过增加分机的方式复用

为用户A分配隐私号X+分机号,A对外的号码都以X+分机号替代,所有与A的通话都通过X+分机号建立,保护A号码不泄漏,提高号码利用率

隐私保护通话AXE模式中,A为业务受益用户,为了保护A的真实号码不被泄露,隐私保护通话平台为A绑定一个分机主号码X和一个分机号E。

  • 绑定关系建立后,其他用户拨打X号码再输入分机号E即可联系A用户。A用户拨打X号码可回呼之前通话用户或企业指定号码。
  • AXE模式下1个X号码可以绑定多个A号码,每个A号码分配不同的分机号E;分机号E最大4位(即0001~9999),但建议一个X号码不要绑定超过200个A号码。

AXE模式下具有如下关键功能:

点击放大

点击放大

1.5 AXG模式

一对组隐私保护

image-20200522234702286

1.6 AXYB模式

保护号码A和号码B

多关系隐私保护

隐私保护通话AXYB模式中,隐私保护通话平台为需要通话的一对或多对用户分别分配对应的隐私号码,保护通话的双方真实号码不被泄露。如隐私保护通话平台为A、B分别绑定隐私号码X和Y,建立了AXYB的绑定关系。

  • A用户对B用户只呈现X号码,与A用户建立的通信通过X号码建立;B用户对A用户呈现Y号码,与A用户建立的通信通过Y号码建立。
  • 一个X号码同时只能绑定一个A号码,一个A号码可以同时绑定5个X号码。Y号码可绑定的最大关系数量为1000(Y号码绑定一个AX关系计为一次绑定关系),但绑定的AX关系不可重复。例如,允许同时绑定A1X1Y1B1和A2X2Y1B2,但不允许同时绑定A1X1Y1B1和A1X1Y1B2。

AXYB模式下具有如下关键功能:

点击放大

1.7 AXxYB分机模式

AXxYB在AXYB上增加分机号码,小x就是分机号,通过增加分机号的方式节约号码X的使用

多关系隐私保护

2. 号码资源

目前号码资源有

虚商号码170/171

标准手机号码

固话号

95呼叫行业专用

3. 业务场景模式适配

AX (AXN)

1对多场景下的隐私保护,在不占用手机SIM卡槽的情况下为用户A增加一个第二号码,保护用户A的隐私,其他人都是通过拨打X号码接通用户A。

商业号码随身行

隐私号码作为商业号码绑定私人手机,随身接通客户电话。非工作时段可设置关机,防止骚扰

优势:

  • 客户资源不遗失

    员工离职,公司收回隐私号码,原有客户资源保持企业所有

  • 企业号码不变更

    客户只需拨打1个企业号码,不需随业务人员离职而频繁变更联系号码

  • 商业行为可追溯

    员工与客户的呼叫记录可追溯,方便管理;支持录音,促进企业服务质量提升

AXB

AXB中间号于1对1场景下的隐私保护,前置条件是A、B的联系方式已知;业务在绑定时候把A、B的联系方式通过api传递到号码隐私保护平台。应用场景:打车、短租、O2O服务等

用车出行、网约车出行

司机和乘客通过平台临时分配的隐私号码呼叫对方,不暴露自己的真实号码

在打车出行场景中,用车订单生成后,司机与乘客间,建立绑定关系,服务过程双方通过隐私号码联系对方,有效保护隐私,服务结束后,解除绑定,避免骚扰纠纷。

X

企业自行维护绑定关系

扫码挪车

车主申请隐私号码并和二维码绑定,其他人扫描二维码,拨打隐私号码联系车主

优势:

  • 隐私保护

    保护双方号码不泄漏,阻断第三方数据采集;可设置二维码关闭,防止骚扰

  • 方便快捷

    无繁琐的界面设定,手机扫码,一键通知挪车

  • 多管齐下

    电话和短信多渠道联系车主,确保通知到位

AXE (AXN分机、AXx分机)

一对多场景下的隐私保护;针对单一面单一利润较低,为了实现X号码复用,引入分机号概念大大降低了单一面单分摊的X号码接通用户A。应用场景:房产中介等

快递派送

快递员拨打隐私号,听到语音提示后,输入分机号转接至收件人

优势:

  • 隐私保护

保护收件人号码不泄露,派送结束隐私号码失效,防止后续骚扰

  • 节省号码资源

    1个隐私号可设置1万个分机号,提高号码复用率,降低号码成本

  • 灵活易用

    分机号根据业务规模灵活设置;交易结束快速回收隐私号,循环利用,无需设置号码冷却期

AXG

一对组场景下的隐私保护;主要针对有团队协作场景的行业,如招聘,通过API将A和企业的人事和用人部门等绑定,进行联系。应用场景:招聘、银行、保险等

AXYB

A和B双方保护

此模式主要针对企业严格把控企业会员隐私情况而定,适用场景较符合当前市场的房产中介类企业。此模式对于保护会员隐私效果最好

AXxYB

A和B双方保护

美团外卖典型的场景

骑手手机号B,拨打号码X 分机号x 转接到点餐人A,你的餐到了

点餐人A拨打 Y 转接到骑手B,我的餐为什么还没到

4. 参考

https://cloud.tencent.com/product/npp

https://support.huaweicloud.com/PrivateNumber/index.html

https://www.aliyun.com/product/pls

5. 提供商

如果你遇到了号码隐私保护上的技术问题,可以加流水理鱼的微信,拉你进技术群讨论

Android、iOS的webview注入JavaScript代码修改网页内容

需要修改webview中的网页内容、网页元素动作

1.实现原理

webview导航栏方法中执行JavaScript代码

浏览器地址栏是支持运行JavaScript代码的

javascript:开头后跟要执行的语句

// 弹窗
javascript:alert('hello world!');

ps:不可以复制粘贴的方式来测试,这样是无效的,至少“javascript:”是手写补全才可以

那么webview是否也可以呢?

在webview中同样适用本方法

虽然webview没有可见的地址栏,但是webview提供操作导航导航栏的方法

2.常用JavaScript代码片段

// 通过class查找隐藏本element
javascript:(function() {
   document.getElementsByClassName('your_class_name')[0].style.display='none'; 
})();
                
// 通过id查找因此本element
javascript:(function() {
   document.getElementById('your_id').style.display='none';
})();


// 某个element点击事件 并且修改打开一个弹窗后的页面element
javascript:(function() {
// 首次页面加载必须有的element
var url = 'https://www.baidu.com';
var text = 'p标签文本被替换了';
var bottom = document.getElementsByClassName('bottom')[0];
bottom.onclick = function(){
  // 弹窗element
    var dialogButton = document.getElementsByClassName('button')[0];
    var dialogItem = document.getElementsByClassName('item')[2].getElementsByTagName('p')[1];
    dialogButton.onclick = function(){window.open(url, '_self');}; // 点击事件跳转
    dialogItem.replaceWith(text); // 修改标签文本内容
};
})();

// 如果某些页面元素是在页面完成后出现的
// 也就是webview 到了 onPageFinished 周期中,页面元素还未加载出来,需要使用定时器来处理
// 定时器轮询检查页面元素对象,直到找到需要的处理的页面元素对象后进行处理,然后销毁定时器
var timer = setInterval(function () {
    if (document.getElementsByClassName("class name")[0]) {
      // 你的业务代码
      
      clearInterval(timer); // 销毁定时器
    }
  }, 1000);

3.Android中实现webview注入JavaScript代码

// java
final WebView webview = (WebView)findViewById(R.id.browser);
 
    webview.getSettings().setJavaScriptEnabled(true);
 
    webview.setWebViewClient(new WebViewClient() {
     @Override
    public void onPageFinished(WebView view, String url)
    {
        // hide element by class name
        webview.loadUrl("javascript:(function() { " +
                "document.getElementsByClassName('your_class_name')[0].style.display='none'; })();");
        // hide element by id
        webview.loadUrl("javascript:(function() { " +
                "document.getElementById('your_id_name').style.display='none';})();");
 
    }
    });
 
webview.loadUrl(url);

4.iOS中实现webview注入JavaScript代码

// objective-c

5.Flutter中实现webview注入JavaScript代码

参考 Android 和 iOS

6.进阶注入外部引入的hook.js代码

注入的JavaScript代码需要修改重新发包?

注入的JavaScript很大一段硬编码到APP包中如何管理?

进阶实现注入外部hook.js

把JavaScript代码单独写在hook.js并放置在cdn上

// https://www.iamle.com/hook.js
'v0.0.1 app webview hook'

;(function (window) {
    //your code
})(window)


// app 用webview.loadUrl方法中注入js
javascript:(function() {
   var script = document.createElement('script');
   script.type = 'text/javascript';
   script.src = 'https://www.iamle.com/hook.js';
     document.body.appendChild(script);
})();

// Android java为例
 @Override
    public void onPageFinished(WebView view, String url)
    {
        String js = "javascript:(function() {";
        js += "var script = document.createElement('script');";
        js += "script.type = 'text/javascript';";
        js += "script.src = 'https://www.iamle.com/hook.js';";
        js += "document.body.appendChild(script);";
        js += "})();";
        view.loadUrl(js);
    }

这样后续修改只需要修改hook.js即可,APP不用重新打包📦

7.经验

在webview中不支持window.location.href进行网址导航

但是支持使用window.open, window.open(‘https://www.iamle.com‘, ‘_self’);

应用错误跟踪系统:对软件系统运行过程中产生的错误日志进行收集从而实现监控告警。

虽然软件错误❌是不可避免的,但是可以降低错误数。

提高对错误的治理能力能让错误带来的损失降到最低 ​👍🏻 。

错误日志监控在最顶层的业务层监控,有他将会帮助你打造更好的软件!

我们需要业务场景下自己发现Bug的速度快于用户报告Bug的速度,毕竟让用户报告已经晚了。

典型的这套系统的架构:错误日志(前端、后端) => 传输(HTTP)=>错误跟踪平台(收集、展示、分析、告警)。

本文不讨论哪款软件来解决这个问题,只讲Sentry这个轮子,SASS版本和私有部署版体验几乎一致

img

本文主要讲了

应用错误需要监控的场景案例

Sentry有哪些功能

Sentry如何在k8s中私有化部署

本文由 www.iamle.com 流水理鱼 原创,wx公众号搜索 流水理鱼 或 liushuiliyu

SEO 关键字

Sentry私有化部署

Sentry helm部署

Sentry kubernets部署 k8s部署

1. 需要应用错误监控的场景案例

  • 多年的老web项目需要https适配,因为老项目大量写死了http://所以文件修改数巨大,即便测试很仔细也不能保证完全没问题
  • 运营发现落地页转化异常了,有访问没表单提交,找到技术,技术发现是某个JS报错,导致表单无法提交,造成推广费用的损失
  • 上线了一个功能,由于环境差异,只在生产环境才触发,用户报告了才去查问题
  • 应用错误日志虽然通过ELK采集了,但是缺乏及时分析和告警
  • 特定的环境才产生错误,要解决问题的先去构建环境并复现错误,因为不清楚发生错误时的具体参数

2. Sentry有哪些功能

Sentry is cross-platform application monitoring, with a focus on error reporting.

Sentry跨平台应用监控,专注错误报告。

Sentry英文直译中文叫“哨兵”。

Sentry提供了一个应用程序监视平台,可以帮助您实时识别问题。

  • 提供WEB UI
  • 提供SASS版和私有部署2种方式
  • 开源,授权协议为BSL,只要你不拿他来做SASS服务卖钱,自用可以免费商用
  • 提供几乎所有主流开发语言和框架的SDK
  • 提供完整的错误详情
  • 支持自动上报错误和手动上报错误
  • 支持WEB前端、后端、APP
  • 支持多项目管理
  • 支持账号权限管理
  • 提供统一错误的聚合分析

  • 今日头条等很多大公司都在用

img

3. 在Kubernets中部署Sentry

目前网上大部分都是讲DockerCompose的部署方式

其实Helm Hub上可以找到Sentry的helm charts包(⑤),那么直接用Helm部署Sentry是最快速方便的

3.1 Sentry部署资源要求

下面给出一个部署资源情况参考,这基本也就是最低资源消耗情况了

  • Kubernets存储已经支持了动态PVC

  • Kubernets已经支持Ingress

  • 准备一个子域名 (例如, sentry.iamle.com)

  • Helm部署Sentry会部署sentry-corn、sentry-web()、sentry-worker、Redis、PostgreSQL

  • 空负载资源详细sentry-corn(220MB/0.01Core)、sentry-web(850MB/0.012Core)、sentry-worker(2048MB/0.04Core)、Redis(132+179MB/0.04+0.054Core)、PostgreSQL(506MB/0.03Core)

  • 空负载整体资源占用情况,内存:3935MB、CPU核数:0.2 Cores、存储PVC:34G

image-20200419182509260

3.2 Helm 部署 Sentry

helm为helm3

使用国内加速的charts(微软azure)

helm repo add stable http://mirror.azure.cn/kubernetes/charts
helm repo add incubator http://mirror.azure.cn/kubernetes/charts-incubator
helm repo update
helm search repo sentry
#NAME                              CHART VERSION   APP VERSION DESCRIPTION
#stable/sentry                     4.2.0           9.1.2       Sentry is a cross-platform crash reporting and ...
#已经可以在stable repo 找到 sentry
## helm安装sentry
kubectl create namespace sentry
helm install sentry stable/sentry \
-n sentry \
--set persistence.enabled=true,user.email=i@iamle.com,user.password=i@iamle.com \
--set ingress.enabled=true,ingress.hostname=sentry.iamle.com,service.type=ClusterIP \
--set email.host=smtp.yourhost.com,email.port=25 \
--set email.user=user,email.password=password,email.use_tls=false \
--wait
#参数一看就懂,更多安装参数看文末的参考⑤
#第一次安装需要耗时10分钟以上,等等等,db-init-job初始化数据库的时候花费了太多时间
#在安装完成之前,访问sentry.iamle.com会出现服务器内部错误

#出现下面的内容就表示部署好了
#NAME: sentry
#LAST DEPLOYED: Sun Apr 19 21:01:26 2020
#NAMESPACE: sentry
#STATUS: deployed
#REVISION: 1
#TEST SUITE: None
#NOTES:
#1. Get the application URL by running these commands:
#  export POD_NAME=$(kubectl get pods --namespace sentry -l "app=sentry,role=web" -o jsonpath="{.items[0].metadata.name}")
#  echo "Visit http://127.0.0.1:8080 to use your application"
#  kubectl port-forward --namespace sentry $POD_NAME 8080:9000
#
#2. Log in with
#
#  USER: i@iamle.com
#  Get login password with
#    kubectl get secret --namespace sentry sentry -o jsonpath="{.data.user-password}" | base64 --decode

#查看登陆密码,也就是user.password设置的值
#kubectl get secret --namespace default sentry -o jsonpath="{.data.user-password}" | base64 --decode

#删除sentry
#helm uninstall sentry -n sentry

ingress根据自己实际环境做一些微调,笔者的环境下ssl证书使用 cert-manager 自动管理,备注annotations就自动配置ssl

# sentry ingress示例
kubectl apply -f - <<EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: sentry
  namespace: sentry
  annotations:
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  rules:
  - host: sentry.iamle.com
    http:
      paths:
      - path: /
        backend:
          serviceName: sentry
          servicePort: 9000
  tls:
  - hosts:
    - sentry.iamle.com
    secretName: sentry-cert
EOF

浏览器访问

https://sentry.iamle.com 使用安装时设置的账号密码登陆即可

3.3 Sentry新增一个项目

理论上是需要给每个项目都在Sentry中创建一个对应的项目

但是实际上这么干会比较麻烦,笔者建议对访问量不大,比如后台类的相同类型的新建一个项目即可

发现混在一起已经不好区分了,再去拆开也不迟

对于本身访问量巨大的“前端”项目,建议在Sentry管理后台的一对一配置

Projects 》 Add new Project 选择一个JavaScript类型

image-20200419211855759

image-20200419212037861

⚠️ 官方提供的SDK是国外的CDN,实际使用的时候需要把https://browser.sentry-cdn.com/5.5.0/bundle.min.js下载后放在自己的OSS+CDN上

SDK和初始化代码一般放入全站头部当中,当页面有js错误的时候已经会自动上报了

我们用chrome的console手动上报一个消息测试下sentry是否工作正常

Sentry.captureMessage("流水理鱼 www.iamle.com")

image-20200419212444197

image-20200419212641447

3.4 Helm安装sentry常见问题及解决

  • 解决数据库不能初始化的问题

    如果安装过程数据库不能初始化,可以手动初始化

kubectl exec -it -n sentry $(kubectl get pods  -n sentry  |grep sentry-web |awk '{print $1}') bash
sentry upgrade

  • 手动创建一个新的管理员账号
kubectl exec -it -n sentry $(kubectl get pods  -n sentry  |grep sentry-web |awk '{print $1}') bash
sentry createuser

4. 总结

本文看起来洋洋洒洒一大篇,实际上15分钟就能部署好sentry🎉

Sentry不仅仅支持WEB前端,也支持桌面、APP、后端,全平台💯

注意如果要升级sentry,先备份数据先,之前的数据会被清空 ⚠️

如果遇到Sentry的部署、使用等问题可以在博客 www.iamle.com 中找到我的wx,加群讨论📣

5. 参考