有奖:语音产品征文挑战赛火热进行中> HOT

简介

?应用性能监控(Application Performance Management ,APM)是一款应用性能管理平台,基于实时的多语言应用探针全量采集技术,为您提供分布式应用性能分析和故障自检能力,全方位保障系统的可用性和稳定性。协助您在复杂的业务系统快速定位性能问题,降低 MTTR(平均故障恢复时间)。实时了解并追踪应用性能情况,提升用户体验。 APM 基于 OpenTracing 开源协议,支持多种主流框架和编程语言,为您提供应用性能观测一站式解决方案。
SCF 已经接入 APM 产品,开启函数应用性能观测后,SCF 将使用基于 OpenTracing 的 Jaeger 实现将函数运行总耗时、冷启动耗时、执行耗时三段关键时间上报至 APM:
函数运行总耗时:作为父分段上报,对应 APM 链路中 invocation 接口,表示函数从接收到调用命令开始到函数执行完成总耗时。
冷启动耗时:作为函数运行总耗时的子分段上报,对应 APM 链路中initialization接口,表示函数从接收到调用命令开始,到实例准备完成、函数初始化逻辑执行完成耗时。(该分段仅出现在冷启动调用请求中)
执行耗时:作为函数运行总耗时的子分段上报,对应 APM 链路中execution接口,表示入口函数执行耗时(事件函数)或完成9000端口监听后每次执行耗时(Web 函数和镜像函数)。
除此之外,还可以根据业务需要自定义埋点进行上报。
注意
APM 为独立计费产品,APM 计费方式请参考 计费概述

权限说明

1. 首次使用请按照操作步骤引导完成授权。子账号如无权限操作,请联系主账号管理员完成授权流程。
2. 为保证 APM 数据正常查看,子账号下至少拥有应用性能观测 APM 只读权限 QcloudAPMReadOnlyFullAccess。主账号为子账号授权方法请参见 授权管理

设置应用性能观测

1. 登录 Serverless 控制台,单击左侧导航栏中的函数服务
2. 在页面上方选择地域,单击需要进行应用性能观测配置的函数名。
3. 函数配置页面,选择右上角的编辑,勾选启用应用性能观测。
?
?
4. APM 的资源单元为业务系统,请选择数据上报的地域并选择对应的业务系统。新建业务系统请参考 应用性能观测资源管理
?
?
?
注意
建议选择与函数所在地域相同的地域,如需跨地域上报,请在函数网络配置中启用公网访问。(函数通过公网上报 APM 可能会产生额外的费用,请按需使用)
业务系统选择完成后,会展示业务系统对应的接入点和 Token 信息,供业务代码自定义上报使用。
5. 单击保存完成函数应用性能观测配置。配置更新后对函数 $LATEST 版本即刻生效,已发布版本配置不会变更。

查看函数应用性能观测配置

1. 登录 Serverless 控制台,单击左侧导航栏中的函数服务
2. 在页面上方选择地域,并单击函数名,即可在“函数配置”中查看当前应用性能观测配置。
?
?
?

查看函数及所在业务系统健康情况

函数应用性能观测功能开启后,SCF 将上报函数执行基本信息到指定业务系统,一个函数将对应业务系统下的一个应用。应用命名方式为:命名空间名称/函数名称。

查看函数所在业务系统健康情况

1. 在函数应用性能观测配置中单击业务系统,即可跳转至应用性能观测控制台。
2. 单击左侧导航栏中的应用监控-应用列表,可查看业务系统下各应用的健康情况。
?
?
?
3. 单击系统看板右上角的拓扑,可查看该业务系统下应用之间的调用关系拓扑图。

函数调用链路分析

1. 开启了应用性能观测的函数,可在函数执行日志中获取到 APM TraceId。函数执行未重试场景下,TraceId 与函数执行 RequestId 一一对应;函数重试场景下,多次重试具有相同的 RequestId,TraceId 与函数执行 RequestId 为多对一的关系。
?
?
2. 登录 应用性能观测控制台,单击左侧导航栏链路追踪 > 调用查询
3. 在页面上方选择地域和业务系统。
4. 输入需要查询的 TraceID,单击查询。以下图一个冷启动调用的函数请求为例,查询结果中的内容含义如下:
TraceID/SpanID:本次请求的 TraceID(地域唯一)/当前 Span 的 SpanID(Trace 内唯一)
状态:状态码为 200 时为正常,其他为异常。
响应时间:分段运行耗时。
服务端:命名空间/函数名称。
接口:invocation表示函数运行总耗时,initialization表示冷启动耗时,execution表示函数执行耗时。
调用类型/状态码:云函数平台上报分段调用类型记为 scf,状态码为函数请求状态码。
?
?
5. 单击请求详情,可查看链路瀑布图及函数执行日志。
?
?
?
?

SCF 自定义参数及含义

Key
含义
示例
fass.status_code
函数执行 状态码
200
component
固定字段,填 scf。
scf
faas.namespace
函数所在命名空间。
default
faas.alias
触发的函数别名,可能为空。
$DEFAULT
faas.version
触发的函数版本。
$LATEST
faas.runtime
函数运行环境。
Python3.6
faas.trigger
函数触发源。
YUNAPI
faas.request_id
函数请求 id。
97fa4d93-xxxx-xxxx-xxxx-66e60cdcb977
faas.async_execution
异步执行函数标识,true 表示是,false 表示否。
false
faas.websocket
websocket 函数标识,true 表示是,false 表示否。
false
faas.function_type
函数类型,事件函数或 Web 函数。
Event
faas.region
函数所在地域,参考函数 支持地域
ap-guangzhou
faas.retrynum
请求重试次数。
0
faas.coldstart
是否为冷启动调用,true 表示是,false 表示否。
true

自定义埋点上报

启用应用性能观测后,平台会默认上报函数运行总耗时、冷启动耗时(仅在冷启动调用下上报)、执行耗时三段基本时间到 APM。除此之外,您还可以通过在需要监控的代码段前后埋点进行自定义上报。下文将描述如何进行自定义上报:
注意
SCF 平台使用基于 OpenTracing 的 Jaeger 上报函数信息到 APM,在函数代码中进行自定义上报时,如需自定义上报 Span 与 SCF 平台上报 Span 使用同一个 TraceId 进行串联,自定义上报需要使用 Jaeger SDK 或与 Jaeger 兼容的 SDK。

获取 TraceId

启用应用性能观测后,平台默认上报 APM 数据的同时,会将 APM 的 Trace 信息注入到函数环境中,可通过如下方式进行获取:
非镜像部署的事件函数: 可在函数入参 context 中获取 function_trace 中的 Uber-Trace-Id
镜像部署函数: 可在函数请求 header 中获取 Uber-Trace-Id

自定义上报 Span

Node.js
Python
1. 请参考 依赖安装 安装 jaeger-client SDK。
2. 创建 Node.js 函数,启用应用性能观测,并参考以下示例进行自定义埋点:
'use strict';
?
const initTracer = require('jaeger-client').initTracer;
const spanContext = require('jaeger-client').SpanContext;
?
function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
?
const config = {
serviceName: 'namespace/functionname', // 服务名称,根据业务需要自行修改,对应 APM 业务系统中的应用名称,SCF 默认上报应用名称命名规范为 命名空间/函数名称
sampler: {
type: 'const',
param: 1
},
reporter: {
logSpans: true,
collectorEndpoint: 'http://ap-shanghai.apm.tencentcs.com:14268/api/traces' //应用性能观测配置中的接入点信息
}
};
const options = {
tags: {
token: 'IJc**************aQ'//应用性能观测配置中的 Token 信息
}
};
?
// ******** 初始化 tracer 实例对象 ********
const tracer = initTracer(config, options);
?
exports.main_handler = async (event, context) => {
// ******** 获取 context 中 trace 信息,未启用应用性能观测状态下,该信息不存在 ********
const obj = JSON.parse(context['function_trace'])
const TraceId = obj['Uber-Trace-Id'][0]
?
// ******** 自定义创建一个子 span,命名为 custom-span ********
const ScfSpanContext = spanContext.fromString(TraceId)
const span = tracer.startSpan('custom-span', {
childOf: ScfSpanContext
})
?
sleep(200).then(() => {
console.log("custom span");
// ******** 标记 Span 结束 ********
span.finish();
tracer.close();
})
?
// ******** 自定义设置 span 的标签(可选,支持多个)********
//span.setTag('span.kind', 'server');
//span.setTag('tagName', 'tagValue');
//span.log({ event: 'timestamp', value: Date.now() });
?
return ("hello from SCF");
}
?
3. 接入点信息与 Token 可参考上文 查看函数应用性能观测配置 获取。
4. 函数调用后,各 Span 信息会上报至指定的 APM 业务系统。
注意
1. Jaeger 最新版 Python SDK 已经不支持 HTTP 协议上报,因此在 Python 运行环境下,推荐使用 Open Telemetry SDK。
2. Open Telemetry SDK 仅支持 Python 3.7 及以上版本,本文提供基于镜像部署的自定义上报示例。
1. 请参考 使用镜像部署函数 完成前提条件准备。
2. 参考以下示例代码准备镜像函数源码。
index.py
from flask import Flask, request
import time
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.trace import NonRecordingSpan, SpanContext, TraceFlags
from opentelemetry.trace import set_span_in_context
?
class OpenTelemetrySLSProvider(object):
?
def __init__(self, service="", token="", endpoint=""):
'''
:param service: Your Application Service Name
:param service: server name
:param token: scf project
'''
?
self.sls_otel_endpoint = endpoint
self.resource = Resource(attributes={
"service.name": service,
"token": token})
?
def initTracer(self):
trace.set_tracer_provider(TracerProvider(resource=self.resource))
otlp_exporter = OTLPSpanExporter(endpoint=self.sls_otel_endpoint)
trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(otlp_exporter))
?
?
server = "namespace/functionname" # 服务名称,根据业务需要自行修改,对应 APM 业务系统中的应用名称,SCF 默认上报应用名称命名规范为 命名空间/函数名称
endpoint = "ap-shanghai.apm.tencentcs.com:4317" # 应用性能观测配置中的接入点信息
instance = "apm-Q*******Q" # 应用性能观测配置中的业务系统 ID 信息
token = "Tr***********a" # 应用性能观测配置中的 Token 信息
?
# ******** 初始化 tracer 实例对象 ********
sls_ot_provider = OpenTelemetrySLSProvider(service=server, token=token, endpoint=endpoint)
sls_ot_provider.initTracer()
tracer = trace.get_tracer("__name__")
?
app = Flask(__name__)
?
# ******** 事件函数 ********
@app.route('/event-invoke', methods=['POST'])
def invoke():
# ******** 获取请求 header 中 trace 信息,未启用应用性能观测状态下,该信息不存在 ********
TraceId = request.headers.get("Uber-Trace-Id", "")
?
SCF_span = TraceId.split(":")
tid = int(SCF_span[0], 16)
sid = int(SCF_span[1], 16)
fid = int(SCF_span[3], 16)
span_context = trace.SpanContext(
trace_id=tid,
span_id=sid,
is_remote=True,
trace_flags=trace.TraceFlags(fid),
)
?
ctx = trace.set_span_in_context(NonRecordingSpan(span_context))
?
# ******** 自定义创建一个子 span,命名为 custom-span ********
child = tracer.start_span("custom-span", context=ctx)
?
time.sleep(0.2)
print("child")
# ******** 标记 Span 结束 ********
child.end()
?
return "hello from SCF"
?
# ******** Web 函数 ********
@app.route('/web-invoke/python-flask-http', methods=['POST', 'GET'])
def web_invoke():
# ******** 获取请求 header 中 trace 信息,未启用应用性能观测状态下,该信息不存在 ********
TraceId = request.headers.get("Uber-Trace-Id", "")
?
SCF_span = TraceId.split(":")
tid = int(SCF_span[0], 16)
sid = int(SCF_span[1], 16)
fid = int(SCF_span[3], 16)
span_context = trace.SpanContext(
trace_id=tid,
span_id=sid,
is_remote=True,
trace_flags=trace.TraceFlags(fid),
)
?
ctx = trace.set_span_in_context(NonRecordingSpan(span_context))
?
?
# ******** 自定义创建一个子 span,命名为 custom-span ********
child = tracer.start_span("custom-span", context=ctx)
?
time.sleep(0.2)
print("child")
?
# ******** 标记 Span 结束 ********
child.end()
?
return "hello from SCF"
?
?
if __name__ == '__main__':
app.run(host='0.0.0.0', port=9000)
requirements.txt
Flask==1.1.2
opentelemetry-api==1.7.1
opentelemetry-exporter-jaeger==1.7.1
opentelemetry-exporter-jaeger-proto-grpc==1.7.1
opentelemetry-exporter-jaeger-thrift==1.7.1
opentelemetry-exporter-otlp==1.7.1
opentelemetry-instrumentation-flask==0.26b1
opentelemetry-instrumentation-requests==0.26b1
opentelemetry-sdk==1.7.1
opentelemetry-semantic-conventions==0.26b1
dockerfile
FROM python:3.7.12-slim
?
WORKDIR /usr/src/app
COPY ./requirements.txt ./
RUN python -m pip install --upgrade pip && pip install --no-cache-dir -r ./requirements.txt
COPY . .
?
CMD [ "python", "-u", "./index.py" ]
3. 将步骤2中的 index.pyrequirements.txtdockerfile 文件放在同一目录下,参考 使用控制台创建函数 完成镜像构建与函数创建。上述示例代码执行后上报的链路详情如下:
?
?
?
?
?
?
?


http://www.vxiaotou.com