阅读视图
英伟达将推出面向AI智能体的开源平台
微软推出Copilot Wave 3及AI软件套装
ElevenLabs:准备在未来2-3年IPO
亚马逊:英伟达Nemotron 3 Nano大模型现已登陆Amazon Bedrock平台
特朗普称暂时免除部分石油相关制裁
美联储3月维持利率不变的概率为97.3%
深圳市创想三维科技股份有限公司递表港交所
思格新能源(上海)股份有限公司递表港交所
亚马逊旗下的Zoox扩大服务范围,自动驾驶出租车竞争日趋激烈
美能源部长:美政府“正在讨论”协调释放战略石油储备
苹果推迟智能家居显示设备发布,以待AI助理就绪
美股三大指数集体收涨,大型科技股多数上涨
Taro 支付宝小程序可观测最佳实践
为什么要做可观测?—— 解决“不可见”带来的业务风险
在 Taro 开发的支付宝小程序中,可观测性(Observability)的本质是通过数据化、实时化的监控手段,让小程序的运行状态、用户行为及系统交互“可见、可分析、可追溯”。其必要性源于小程序在实际运行中面临的三大核心挑战:
1、“黑盒化”运行环境:问题难以感知,故障发现滞后
小程序部署在支付宝客户端及云端环境中,开发者无法直接接触用户设备的真实运行状态。当出现以下问题时,若无可观测能力,往往只能依赖用户反馈或事后投诉。
典型案例:某电商小程序上线后,用户投诉“提交订单按钮点击无效”,但开发团队通过日志仅能看到“按钮渲染成功”,因缺乏用户操作路径和接口调用的关联数据,耗时 3 天才定位到是“异步数据未加载完成时按钮未禁用”的逻辑缺陷。若提前部署可观测能力,可通过用户行为流+接口状态实时发现该问题。
2、业务依赖复杂:跨端、跨服务的故障定位成本高
Taro 小程序通常依赖多方服务:前端通过 Taro 框架调用支付宝小程序 API(如支付、登录)、与后端服务交互(如订单查询、用户数据同步),甚至涉及第三方服务(如地图 SDK、营销活动平台)。任何一个环节异常都可能导致整体功能失效,但问题根源可能隐藏在任意一层,若无统一的观测数据(如接口调用链、用户操作路径、错误上下文),排查效率极低,可能导致用户流失或业务损失。
3、用户体验敏感:微小问题可能引发大规模负面反馈
支付宝小程序的用户对体验的容忍度极低——页面加载慢 1 秒可能导致 5% 的用户流失,支付流程卡顿可能直接引发投诉。但用户通常不会详细描述问题(如“首屏渲染慢是因为图片未压缩”),而是给出模糊反馈(“用不了”“太卡了”)。若无可观测能力,开发者只能通过“猜测+灰度测试”被动优化,无法精准识别影响用户体验的关键节点。
这些问题的解决依赖于对用户行为、性能指标的实时监控,而非事后统计。
把观测云作为 Taro 支付宝小程序可观测最佳实践的核心工具--落地路径与关键动作
- 将观测云定位为小程序的“统一可观测平台”,以统一标签(如 service、env、version)贯穿指标、日志、链路、用户访问四类数据,形成端到端的观测视图。
- 在 Taro 多端(含支付宝小程序)场景中,前端接入 RUM(用户访问监测),后端与基础设施接入 Logging/Tracing/Metrics,实现跨端、跨服务的统一观测与关联。
- 通过“Dashboard + Explorer”构建业务与技术双视角的可观测体系,既看得到用户体验,也定位得到根。
前端 RUM 接入(Taro 支付宝小程序)
本最佳实践实验版本为 Taro v4.1.7
查看编译环境
➜ ~ taro --version
? Taro v4.1.7
4.1.7
➜ ~
1、观测云后台->用户访问监测->新建应用,选择应用类型为小程序,输入应用名称、应用ID,系统会自动根据配置信息生成引入 SDK 的代码,可选择 NPM 接入或 CDN 文件接入(这里我们选择 CDN 文件方式接入)。
- 应用名称:用于识别当前用户访问监测的应用名称。
- 应用 ID :应用在当前工作空间唯一标识,对应字段:app_id 。
2、根据链接下载 CDN 文件,打开项目代码,在小程序项目中的 app.ts 入口文件引入观测云后台生成的配置代码。
注意项目中引入代码的位置,必须要在 App() 初始化之前,否则会出现数据无法正常采集上报,或者只能上报一小部分数据的情况。
3、本地运行项目或者打包发布测试后,通过观察接口调用发现 /v1/write/rum 接口成功调用,说明数据成功上报,可在对应应用的查看器查看上报数据。
4、js 会自动生成一个 trace_id,如果在 allowedTracingOrigins 配置了正则(本最佳实践中配置的是 *,意味着所有接口都会带 ),会在对应的请求头中加入一些参数,ddtrace 的是这几个参数。
实现效果
1、小程序部署在支付宝客户端及云端环境中,开发者直接接触用户设备的真实运行状态
进入「用户访问监测」页面—> 选择创建的对应微信小程序—> 分析看板,可以查看小程序运行中的部分重点信息。
进入「用户访问监测」页面—> 选择创建的对应微信小程序—> 查看器,可以查看小程序应用中用户访问的动作,请求资源以及项目中的报错信息等。
2、多方服务全链路打通
3、后端服务透明化,可以观测到更详细的代码级方法,开发可以快速定位
4、用户体验可视化,全局可观测
观测云为 Taro 支付宝小程序创建一份“数字神经系统”
在 Taro 开发的小程序中,可观测性不是“可选能力”,而是保障业务健康增长的必备基础设施。它通过将“不可见的运行状态”转化为“可见的数据洞察”,帮助开发者:
- 从被动救火到主动预防(快速发现问题,降低故障损失);
- 从经验驱动到数据驱动(精准优化体验,提升转化效率);
- 从局部优化到全局可控(支撑规模化增长,保障业务稳定性);
- 从单点协作到团队协同(提升研发效率,加速产品迭代)。
一次有诚意的升级,一份加量不加价的体验:iPhone 17e 首发体验
一次常规的升级,一个适合更多人的选择:新款 iPad Air 首发体验
每日一题-找出所有稳定的二进制数组 I🟡
给你 3 个正整数 zero ,one 和 limit 。
一个 二进制数组 arr 如果满足以下条件,那么我们称它是 稳定的 :
- 0 在
arr中出现次数 恰好 为zero。 - 1 在
arr中出现次数 恰好 为one。 -
arr中每个长度超过limit的 子数组 都 同时 包含 0 和 1 。
请你返回 稳定 二进制数组的 总 数目。
由于答案可能很大,将它对 109 + 7 取余 后返回。
示例 1:
输入:zero = 1, one = 1, limit = 2
输出:2
解释:
两个稳定的二进制数组为 [1,0] 和 [0,1] ,两个数组都有一个 0 和一个 1 ,且没有子数组长度大于 2 。
示例 2:
输入:zero = 1, one = 2, limit = 1
输出:1
解释:
唯一稳定的二进制数组是 [1,0,1] 。
二进制数组 [1,1,0] 和 [0,1,1] 都有长度为 2 且元素全都相同的子数组,所以它们不稳定。
示例 3:
输入:zero = 3, one = 3, limit = 2
输出:14
解释:
所有稳定的二进制数组包括 [0,0,1,0,1,1] ,[0,0,1,1,0,1] ,[0,1,0,0,1,1] ,[0,1,0,1,0,1] ,[0,1,0,1,1,0] ,[0,1,1,0,0,1] ,[0,1,1,0,1,0] ,[1,0,0,1,0,1] ,[1,0,0,1,1,0] ,[1,0,1,0,0,1] ,[1,0,1,0,1,0] ,[1,0,1,1,0,0] ,[1,1,0,0,1,0] 和 [1,1,0,1,0,0] 。
提示:
1 <= zero, one, limit <= 200
找出所有稳定的二进制数组 I
方法一:动态规划
题目要求二进制数组 $\textit{arr}$ 中每个长度超过 $\textit{limit}$ 的子数组同时包含 $0$ 和 $1$,这个条件等价于二进制数组 $\textit{arr}$ 中每个长度等于 $\textit{limit} + 1$ 的子数组都同时包含 $0$ 和 $1$(读者可以思考一下证明过程)。
按照题目要求,我们需要将 $\textit{zero}$ 个 $0$ 和 $\textit{one}$ 个 $1$ 依次填入二进制数组 $\textit{arr}$,使用 $\textit{dp}_0[i][j]$ 表示已经填入 $i$ 个 $0$ 和 $\textit{j}$ 个 $1$,并且最后填入的数字为 $0$ 的可行方案数目,$\textit{dp}_1[i][j]$ 表示已经填入 $i$ 个 $0$ 和 $\textit{j}$ 个 $1$,并且最后填入的数字为 $1$ 的可行方案数目。对于 $\textit{dp}_0[i][j]$,我们分析一下它的转换方程:
-
当 $j = 0$ 且 $i \in [0, \min(\textit{zero}, \textit{limit})]$ 时:我们可以不断地填入 $0$,所以 $\textit{dp}_0[i][j] = 1$。
-
当 $i = 0$,或者 $j = 0$ 且 $i \notin [0, \min(\textit{zero}, \textit{limit})]$ 时:我们没法构造可行的方案,所以 $\textit{dp}_0[i][j] = 0$。
-
当 $i > 0$ 且 $j > 0$ 时:$\textit{dp}_0[i][j]$ 可以分别由 $\textit{dp}_0[i - 1][j]$ 和 $\textit{dp}_1[i - 1][j]$ 转移而来,分别考虑两种情况:
-
对于 $\textit{dp}_1[i - 1][j]$:显然可以通过在 $\textit{dp}_1[i - 1][j]$ 对应的所有填入方案后再填入一个 $0$ 得到对应的可行方案。
-
对于 $\textit{dp}_0[i - 1][j]$:当 $i \le \textit{limit}$ 时,显然可以通过在 $\textit{dp}_1[i - 1][j]$ 对应的所有填入方案后再填入一个 $0$ 得到对应的可行方案;当 $i \gt \textit{limit}$ 时,我们需要去除一些不可行的方案数。因为 $\textit{dp}_0[i - 1][j]$ 对应的所有填入方案都是可行的,而只有一种情况会在额外填入一个 $0$ 时,变成不可行,即先前已经连续填入 $\textit{limit}$ 个 $0$,对应的方案数为 $\textit{dp}_1[i - \textit{limit} - 1][j]$。
-
根据以上分析,我们有 $\textit{dp}_0[i][j]$ 的转移方程:
$$
\textit{dp}_0[i][j] = \begin{cases}
1, & i \in [0, \min(\textit{zero}, \textit{limit})], j = 0 \
\textit{dp}_1[i - 1][j] + \textit{dp}_0[i - 1][j] - \textit{dp}_1[i - \textit{limit} - 1][j], & i > limit, j > 0 \
\textit{dp}_1[i - 1][j] + \textit{dp}_0[i - 1][j], & i \in [0, limit], j > 0 \
0, & otherwise
\end{cases}
$$
同理,我们也可以获得 $\textit{dp}_1[i][j]$ 的转移方程:
$$
\textit{dp}_1[i][j] = \begin{cases}
1, & i = 0, j \in [0, \min(\textit{one}, \textit{limit})] \
\textit{dp}_0[i][j - 1] + \textit{dp}_1[i][j - 1] - \textit{dp}_0[i][j - \textit{limit} - 1], & i > 0, j > limit \
\textit{dp}_0[i][j - 1] + \textit{dp}_1[i][j - 1], & i > 0, j \in [0, limit] \
0, & otherwise
\end{cases}
$$
最后,稳定二进制数组的数目等于 $\textit{dp}_0[\textit{zero}][\textit{one}] + \textit{dp}_1[\textit{zero}][\textit{one}]$。
###C++
class Solution {
public:
int numberOfStableArrays(int zero, int one, int limit) {
vector<vector<vector<long long>>> dp(zero + 1, vector<vector<long long>>(one + 1, vector<long long>(2)));
long long mod = 1e9 + 7;
for (int i = 0; i <= min(zero, limit); i++) {
dp[i][0][0] = 1;
}
for (int j = 0; j <= min(one, limit); j++) {
dp[0][j][1] = 1;
}
for (int i = 1; i <= zero; i++) {
for (int j = 1; j <= one; j++) {
if (i > limit) {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1] - dp[i - limit - 1][j][1];
} else {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1];
}
dp[i][j][0] = (dp[i][j][0] % mod + mod) % mod;
if (j > limit) {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0] - dp[i][j - limit - 1][0];
} else {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0];
}
dp[i][j][1] = (dp[i][j][1] % mod + mod) % mod;
}
}
return (dp[zero][one][0] + dp[zero][one][1]) % mod;
}
};
###Java
class Solution {
public int numberOfStableArrays(int zero, int one, int limit) {
final long MOD = 1000000007;
long[][][] dp = new long[zero + 1][one + 1][2];
for (int i = 0; i <= Math.min(zero, limit); i++) {
dp[i][0][0] = 1;
}
for (int j = 0; j <= Math.min(one, limit); j++) {
dp[0][j][1] = 1;
}
for (int i = 1; i <= zero; i++) {
for (int j = 1; j <= one; j++) {
if (i > limit) {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1] - dp[i - limit - 1][j][1];
} else {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1];
}
dp[i][j][0] = (dp[i][j][0] % MOD + MOD) % MOD;
if (j > limit) {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0] - dp[i][j - limit - 1][0];
} else {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0];
}
dp[i][j][1] = (dp[i][j][1] % MOD + MOD) % MOD;
}
}
return (int) ((dp[zero][one][0] + dp[zero][one][1]) % MOD);
}
}
###C#
public class Solution {
public int NumberOfStableArrays(int zero, int one, int limit) {
const long MOD = 1000000007;
long[][][] dp = new long[zero + 1][][];
for (int i = 0; i <= zero; i++) {
dp[i] = new long[one + 1][];
for (int j = 0; j <= one; j++) {
dp[i][j] = new long[2];
}
}
for (int i = 0; i <= Math.Min(zero, limit); i++) {
dp[i][0][0] = 1;
}
for (int j = 0; j <= Math.Min(one, limit); j++) {
dp[0][j][1] = 1;
}
for (int i = 1; i <= zero; i++) {
for (int j = 1; j <= one; j++) {
if (i > limit) {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1] - dp[i - limit - 1][j][1];
} else {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1];
}
dp[i][j][0] = (dp[i][j][0] % MOD + MOD) % MOD;
if (j > limit) {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0] - dp[i][j - limit - 1][0];
} else {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0];
}
dp[i][j][1] = (dp[i][j][1] % MOD + MOD) % MOD;
}
}
return (int) ((dp[zero][one][0] + dp[zero][one][1]) % MOD);
}
}
###Go
func numberOfStableArrays(zero int, one int, limit int) int {
dp := make([][][2]int, zero + 1)
mod := int(1e9 + 7)
for i := 0; i <= zero; i++ {
dp[i] = make([][2]int, one + 1)
}
for i := 0; i <= min(zero, limit); i++ {
dp[i][0][0] = 1
}
for j := 0; j <= min(one, limit); j++ {
dp[0][j][1] = 1
}
for i := 1; i <= zero; i++ {
for j := 1; j <= one; j++ {
if i > limit {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1] - dp[i - limit - 1][j][1]
} else {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1]
}
dp[i][j][0] = (dp[i][j][0] % mod + mod) % mod
if j > limit {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0] - dp[i][j - limit - 1][0]
} else {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0]
}
dp[i][j][1] = (dp[i][j][1] % mod + mod) % mod
}
}
return (dp[zero][one][0] + dp[zero][one][1]) % mod
}
###Python
class Solution:
def numberOfStableArrays(self, zero: int, one: int, limit: int) -> int:
dp = [[[0, 0] for _ in range(one + 1)] for _ in range(zero + 1)]
mod = int(1e9 + 7)
for i in range(min(zero, limit) + 1):
dp[i][0][0] = 1
for j in range(min(one, limit) + 1):
dp[0][j][1] = 1
for i in range(1, zero + 1):
for j in range(1, one + 1):
if i > limit:
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1] - dp[i - limit - 1][j][1]
else:
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1]
dp[i][j][0] = (dp[i][j][0] % mod + mod) % mod
if j > limit:
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0] - dp[i][j - limit - 1][0]
else:
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0]
dp[i][j][1] = (dp[i][j][1] % mod + mod) % mod
return (dp[zero][one][0] + dp[zero][one][1]) % mod
###C
#define MOD 1000000007
int numberOfStableArrays(int zero, int one, int limit) {
long long dp[zero + 1][one + 1][2];
memset(dp, 0, sizeof(dp));
for (int i = 0; i <= (zero < limit ? zero : limit); ++i) {
dp[i][0][0] = 1;
}
for (int j = 0; j <= (one < limit ? one : limit); ++j) {
dp[0][j][1] = 1;
}
for (int i = 1; i <= zero; ++i) {
for (int j = 1; j <= one; ++j) {
if (i > limit) {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1] - dp[i - limit - 1][j][1];
} else {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1];
}
dp[i][j][0] = (dp[i][j][0] % MOD + MOD) % MOD;
if (j > limit) {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0] - dp[i][j - limit - 1][0];
} else {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0];
}
dp[i][j][1] = (dp[i][j][1] % MOD + MOD) % MOD;
}
}
int result = (dp[zero][one][0] + dp[zero][one][1]) % MOD;
return result;
}
###JavaScript
const MOD = 1000000007;
var numberOfStableArrays = function(zero, one, limit) {
const dp = Array.from({ length: zero + 1 }, () =>
Array.from({ length: one + 1 }, () => [0, 0])
);
for (let i = 0; i <= Math.min(zero, limit); i++) {
dp[i][0][0] = 1;
}
for (let j = 0; j <= Math.min(one, limit); j++) {
dp[0][j][1] = 1;
}
for (let i = 1; i <= zero; i++) {
for (let j = 1; j <= one; j++) {
if (i > limit) {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1] - dp[i - limit - 1][j][1];
} else {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1];
}
dp[i][j][0] = (dp[i][j][0] % MOD + MOD) % MOD;
if (j > limit) {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0] - dp[i][j - limit - 1][0];
} else {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0];
}
dp[i][j][1] = (dp[i][j][1] % MOD + MOD) % MOD;
}
}
return (dp[zero][one][0] + dp[zero][one][1]) % MOD;
};
###TypeScript
const MOD = 1000000007;
function numberOfStableArrays(zero: number, one: number, limit: number): number {
const dp: number[][][] = Array.from({ length: zero + 1 }, () =>
Array.from({ length: one + 1 }, () => [0, 0])
);
for (let i = 0; i <= Math.min(zero, limit); i++) {
dp[i][0][0] = 1;
}
for (let j = 0; j <= Math.min(one, limit); j++) {
dp[0][j][1] = 1;
}
for (let i = 1; i <= zero; i++) {
for (let j = 1; j <= one; j++) {
if (i > limit) {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1] - dp[i - limit - 1][j][1];
} else {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1];
}
dp[i][j][0] = (dp[i][j][0] % MOD + MOD) % MOD;
if (j > limit) {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0] - dp[i][j - limit - 1][0];
} else {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0];
}
dp[i][j][1] = (dp[i][j][1] % MOD + MOD) % MOD;
}
}
return (dp[zero][one][0] + dp[zero][one][1]) % MOD;
};
###Rust
const MOD: i32 = 1000000007;
impl Solution {
pub fn number_of_stable_arrays(zero: i32, one: i32, limit: i32) -> i32 {
let mut dp = vec![vec![vec![0; 2]; one as usize + 1]; zero as usize + 1];
for i in 0..=zero.min(limit) as usize {
dp[i][0][0] = 1;
}
for j in 0..=one.min(limit) as usize {
dp[0][j][1] = 1;
}
for i in 1..=zero as usize {
for j in 1..=one as usize {
if i > limit as usize {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1] - dp[i - limit as usize - 1][j][1];
} else {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1];
}
dp[i][j][0] = (dp[i][j][0] % MOD + MOD) % MOD;
if j > limit as usize {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0] - dp[i][j - limit as usize - 1][0];
} else {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0];
}
dp[i][j][1] = (dp[i][j][1] % MOD + MOD) % MOD;
}
}
(dp[zero as usize][one as usize][0] + dp[zero as usize][one as usize][1]) % MOD
}
}
复杂度分析
-
时间复杂度:$O(\textit{zero} \times \textit{one})$,其中 $\textit{zero}$ 和 $\textit{one}$ 分别为 $0$ 和 $1$ 的出现次数。
-
空间复杂度:$O(\textit{zero} \times \textit{one})$。
两种方法:动态规划 / 组合数学(Python/Java/C++/Go)
本题和双周赛第四题是一样的,请看 我的题解。