基于WASM的无侵入式全链路A/B Test实践
简介: 我们都知道,服务网格(ServiceMesh)可以为运行其上的微服务提供无侵入式的流量治理能力。通过配置VirtualService和DestinationRule,即可实现流量管理、超时重试、流量复制、限流、熔断等功能,而无需修改微服务代码。 本文所述的实践是根据请求Header实现全链路A/B测试。
1 背景介绍
我们都知道,服务网格(ServiceMesh)可以为运行其上的微服务提供无侵入式的流量治理能力。通过配置VirtualService和DestinationRule,即可实现流量管理、超时重试、流量复制、限流、熔断等功能,而无需修改微服务代码。
流量管理的前提是一个服务存在多个版本,我们可以按部署多版本的目的进行分类,简述如下,以方便理解余文。
- traffic routing:根据请求信息(Header/Cookie/Query Params),将请求流量路由到指定服务(Service)的指定版本(Deployment)的端点上(Pod[])。就是我们所说的A/B测试(A/B Testing)。
- traffic shifting:通过灰度/金丝雀(Canary)发布,将请求流量无差别地按比例路由到指定服务(Service)的各个版本(Deployment[])的端点上(Pod[])。
- traffic switching/mirroring:通过蓝绿(Blue/Green)发布,根据请求信息按比例进行流量切换,以及进行流量复制。
本文所述的实践是根据请求Header实现全链路A/B测试。
1.1 功能简述
从Istio社区的文档,我们很容易找到关于如何根据请求Header将流量路由到一个服务的特定版本的文档和示例。但是这个示例只能在全链路的第一个服务上生效。
举例来说,一个请求要访问A-B-C三个服务,这三个服务都有en版本和fr版本。我们期待:
- header值为user:en的请求,全链路路由为A1-B1-C1
- header值为user:fr的请求,全链路路由为A2-B2-C2
相应的VirtualService配置如下所示:
http:
- name: A|B|C-route
match:
- headers:
user:
exact: en
route:
- destination:
host: A|B|C-svc
subset: v1
- route:
- destination:
host: A|B|C-svc
subset: v2
我们通过实测可以发现,只有A这个服务的路由是符合我们预期的。B和C无法做到根据Header值路由到指定版本。
这是为什么呢?对于服务网格其上的微服务来说,这个header是凭空出现的,也就是微服务代码无感知。因此,当A服务请求B服务时,不会透传这个header;也就是说,当A请求B时,这个header已经丢失了。这时,这个匹配header进行路由的VirtualService配置已经毫无意义。
要解决这个问题,从微服务方的业务角度看,只能修改代码(枚举业务关注的全部header并透传)。但这是一种侵入式的修改,而且无法灵活地支持新出现的header。
从服务网格的基础设施角度看,任何header都是没有业务意义且要被透传的kv pair。只有做到这点,服务网格才能实现无差别地透传用户自定义的header,从而支持无侵入式全链路A/B Test功能。
那么该怎样实现呢?
1.2 社区现状
前面已经说明,在header无法透传的情况下,单纯地配置VirtualService的header匹配是无法实现这个功能的。
但是,在VirtualService中是否存在其他配置,可以实现header透传呢?如果存在,那么单纯使用VirtualService,代价是最小的。
经过各种尝试(包括精心配置header相关的set/add),我发现无法实现。原因是VirtualService对header的干预发生在inbound阶段,而透传是需要在outbound阶段干预header的。而微服务workload没有能力对凭空出现的header值进行透传,因此在路由到下一个服务时,这个header就会丢失。
因此,我们可以得出一个结论:无法单纯使用VirtualService实现无侵入式全链路A/B Test,进一步地说,社区提供的现有配置都无法做到直接使用就能支持这个功能。
那么,就只剩下EnvoyFilter这个更高级的配置了。这是我们一开始很不希望的结论。原因有两个:
- EnvoyFilter的配置太过复杂,一般用户很难在服务网格中快速学习和使用,即便我们提供示例,一旦需求稍有变化,示例对修改EnvoyFilter的参考价值甚微。
- 就算使用EnvoyFilter,目前Envoy内置的filter也没有直接支持这个功能的,需要借助Lua或者WebAssembly(WASM)进行开发。
1.3 实现方案
接下来进入技术选型。我用一句话来概括:
- Lua的优点是小巧,缺点是性能不理想
- WASM的优点是性能好,缺点是开发和分发相比Lua要困难。
- WASM的实现主流是C++和Rust,其他语言的实现尚不成熟或者存在性能问题。本文使用的是Rust。
我们使用Rust开发一个WASM,在outbound阶段,获取用户在EnvoyFilter中定义的header并向后传递。
WASM包的分发使用Kubernetes的configmap存储,Pod通过annotation中的定义获取WASM配置并加载。(为什么使用这种分发形式,后面会讲。)
2 技术实现
本节所述的相关代码:
https://github.com/AliyunContainerService/rust-wasm-4-envoy/tree/master/propagate-headers-filter
2.1 使用RUST实现WASM
1 定义依赖
WASM工程的核心依赖crates只有一个,就是proxy-wasm,这是使用Rust开发WASM的基础包。此外,还有用于反序列化的包serde_json和用于打印日志的包log。Cargo.toml定义如下:
[dependencies]
proxy-wasm = "0.1.3"
serde_json = "1.0.62"
log = "0.4.14"
2 定义构建
WASM的最终构建形式是兼容c的动态链接库,Cargo.toml定义如下:
[lib]
name = "propaganda_filter"
path = "src/propagate_headers.rs"
crate-type = ["cdylib"]
3 Header透传功能
首先定义结构体如下,head_tag_name是用户自定义的header键的名称,head_tag_value是对应值的名称。
struct PropagandaHeaderFilter {
config: FilterConfig,
}
struct FilterConfig {
head_tag_name: String,
head_tag_value: String,
}
{proxy-wasm}/src/traits.rs中的trait HttpContext定义了on_http_request_headers方法。我们通过实现这个方法来完成Header透传的功能。
impl HttpContext for PropagandaHeaderFilter {
fn on_http_request_headers(&mut self, _: usize) -> Action {
let head_tag_key = self.config.head_tag_name.as_str();
info!("::::head_tag_key={}", head_tag_key);
if !head_tag_key.is_empty() {
self.set_http_request_header(head_tag_key, Some(self.config.head_tag_value.as_str()));
self.clear_http_route_cache();
}
for (name, value) in &self.get_http_request_headers() {
info!("::::H[{}] -> {}: {}", self.context_id, name, value);
}
Action::Continue
}
}
第3-6行是获取配置文件中用户自定义的header键值对,如果存在就调用set_http_request_header方法,将键值对写入当前header。
第7行是对当前proxy-wasm实现的一个workaround,如果你对此感兴趣可以阅读如下参考:
- https://github.com/istio/istio/issues/30545#issuecomment-783518257
- https://github.com/proxy-wasm/spec/issues/16
- https://www.elvinefendi.com/2020/12/09/dynamic-routing-envoy-wasm.html
2.2 本地验证(基于Envoy)
1 WASM构建
使用如下命令构建WASM工程。需要强调的是wasm32-unknown-unknown这个target目前只存在于nightly中,因此在构建之前需要临时切换构建环境。
rustup override set nightly
cargo build --target=wasm32-unknown-unknown --release
构建完成后,我们在本地使用docker compose启动Envoy,对WASM功能进行验证。
2 Envoy配置
本例需要为Envoy启动提供两个文件,一个是构建好的propaganda_filter.wasm,一个是Envoy配置文件envoy-local-wasm.yaml。示意如下:
volumes:
- ./config/envoy/envoy-local-wasm.yaml:/etc/envoy-local-wasm.yaml
- ./target/wasm32-unknown-unknown/release/propaganda_filter.wasm:/etc/propaganda_filter.wasm
Envoy支持动态配置,本地测试采用静态配置:
static_resources:
listeners:
- address:
socket_address:
address: 0.0.0.0
port_value: 80
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
...
http_filters:
- name: envoy.filters.http.wasm
typed_config:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
value:
config:
name: "header_filter"
root_id: "propaganda_filter"
configuration:
"@type": "type.googleapis.com/google.protobuf.StringValue"
value: |
{
"head_tag_name": "custom-version",
"head_tag_value": "hello1-v1"
}
vm_config:
runtime: "envoy.wasm.runtime.v8"
vm_id: "header_filter_vm"
code:
local:
filename: "/etc/propaganda_filter.wasm"
allow_precompiled: true
...
Envoy的配置重点关注如下3点:
- 15行 我们在http_filters中定义了一个名称为header_filter的type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
- 32行 本地文件路径为/etc/propaganda_filter.wasm
- 20-26行 相关配置的类型是type.googleapis.com/google.protobuf.StringValue,值的内容是{"head_tag_name": "custom-version","head_tag_value": "hello1-v1"}。这里自定义的Header键名为custom-version,值为hello1-v1。
3 本地验证
执行如下命令启动docker compose:
docker-compose up --build
请求本地服务:
curl -H "version-tag":"v1" "localhost:18000"
此时Envoy的日志应有如下输出:
proxy_1 | [2021-02-25 06:30:09.217][33][info][wasm] [external/envoy/source/extensions/common/wasm/context.cc:1152] wasm log: ::::create_http_context head_tag_name=custom-version,head_tag_value=hello1-v1
proxy_1 | [2021-02-25 06:30:09.217][33][info][wasm] [external/envoy/source/extensions/common/wasm/context.cc:1152] wasm log: ::::head_tag_key=custom-version
...
proxy_1 | [2021-02-25 06:30:09.217][33][info][wasm] [external/envoy/source/extensions/common/wasm/context.cc:1152] wasm log: ::::H[2] -> custom-version: hello1-v1
2.3 WASM的分发方式
WASM的分发是指将WASM包存储于一个分布式仓库中,供指定的Pod拉取的过程。
1 Configmap + Envoy的Local方式
虽然这种方式不是WASM分发的终态,但是因为它较为容易理解且适合简单的场景,本例最终选择了这个方案作为示例讲解。虽然configmap的本职工作不是存WASM的,但是configmap和Envoy的local模式都很成熟,两者结合恰能满足当前需求。
阿里云服务网格ASM产品已经提供了这种类似的方式,具体可以参考 为Envoy编写WASM Filter并部署到ASM中。
要把WASM包塞到配置中,首要考虑的是包的尺寸。我们使用wasm-gc进行包裁剪,示意如下:
ls -hl target/wasm32-unknown-unknown/release/propaganda_filter.wasm
wasm-gc ./target/wasm32-unknown-unknown/release/propaganda_filter.wasm ./target/wasm32-unknown-unknown/release/propaganda-header-filter.wasm
ls -hl target/wasm32-unknown-unknown/release/propaganda-header-filter.wasm
执行结果如下,可以看到裁剪前后,包的尺寸对比:
-rwxr-xr-x 2 han staff 1.7M Feb 25 15:38 target/wasm32-unknown-unknown/release/propaganda_filter.wasm
-rw-r--r-- 1 han staff 136K Feb 25 15:38 target/wasm32-unknown-unknown/release/propaganda-header-filter.wasm
创建configmap:
wasm_image=target/wasm32-unknown-unknown/release/propaganda-header-filter.wasm
kubectl -n $NS create configmap -n $NS propaganda-header --from-file=$wasm_image
为指定Deployment打Patch:
patch_annotations=$(cat config/annotations/patch-annotations.yaml)
kubectl -n $NS patch deployment "hello$i-deploy-v$j" -p "$patch_annotations"
patch-annotations.yaml如下:
spec:
template:
metadata:
annotations:
sidecar.istio.io/userVolume: '[{"name":"wasmfilters-dir","configMap": {"name":"propaganda-header"}}]'
sidecar.istio.io/userVolumeMount: '[{"mountPath":"/var/local/lib/wasm-filters","name":"wasmfilters-dir"}]'
2 Envoy的Remote方式
Envoy同时支持local和remote形式的资源定义。对比如下:
vm_config:
runtime: "envoy.wasm.runtime.v8"
vm_id: "header_filter_vm"
code:
local:
filename: "/etc/propaganda_filter.wasm"
vm_config:
runtime: "envoy.wasm.runtime.v8"
code:
remote:
http_uri:
uri: "http://*.*.*.216:8000/propaganda_filter.wasm"
cluster: web_service
timeout:
seconds: 60
sha256: "da2e22*"
remote方式是最接近原始Enovy的,因此这种方式本来是本例的首选。但是实测过程中发现在包的hash校验上存在问题,详见下方参考。并且,Envoy社区的大牛周礼赞反馈我说remote不是Envoy支持WASM分发的未来方向。因此,本例最终放弃这种方式。
- https://stackoverflow.com/questions/65871312/how-to-set-the-sha256-hex-in-envoy-wasm-remote-config
- https://envoyproxy.slack.com/archives/C78M4KW76/p1611496672017500
3 ORAS + Local方式
ORAS是OCI Artifacts项目的参考实现,可显著简化OCI注册表中任意内容的存储。
使用ORAS客户端或者API/SDK的方式将具有允许的媒体类型的Wasm模块推送到注册库(一个OCI兼容的注册库)中,然后通过控制器将Wasm Filter部署到指定工作负载对应的Pod中,以Local的方式进行挂载。
阿里云服务网格ASM产品中提供了对WebAssembly(WASM)技术的支持,服务网格使用人员可以把扩展的WASM Filter通过ASM部署到数据面集群中相应的Envoy代理中。通过ASMFilterDeployment Controller组件, 可以支持动态加载插件、简单易用、以及支持热更新等能力。具体来说,ASM产品提供了一个新的CRD ASMFilterDeployment以及相关的controller组件。这个controller组件会监听ASMFilterDeployment资源对象的情况,会做2个方面的事情:
- 创建出用于控制面的Istio EnvoyFilter Custom Resource,并推送到对应的asm控制面istiod中
- 从OCI注册库中拉取对应的wasm filter镜像,并挂载到对应的workload pod中
具体可以参考:基于Wasm和ORAS简化扩展服务网格功能。
后续的实践分享将会使用这种方式进行WASM的分发,敬请期待。
类似地,业界其他友商也在推进这种方式,特别是Solo.io提供了一整套WASM的开发框架wasme,基于该框架可以开发-构建-分发WASM包(OCI image)并部署到Webassembly Hub。这个方案的优点很明显,完整地支持了WASM的开发到上线的生命周期。但这个方案的缺点也非常明显,wasme的自包含导致了很难将其拆分,并扩展到solo体系之外。
阿里云服务网格ASM团队正在与包括solo在内的业界相关团队交流如何共同推进Wasm filter的OCI规范以及相应的生命周期管理,以帮助客户可以轻松扩展Envoy的功能并将其在服务网格中的应用推向了新的高度。
2.4 集群验证(基于Istio)
1 实验示例
WASM分发到Kubernetes的configmap后,我们可以进行集群验证了。示例(源代码)包含3个Service:hello1-hello2-hello3,每个服务包含2个版本:v1/en和v2/fr。
每个Service配置了VirtualService和DestinationRule用来定义匹配Header并路由到指定版本。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: hello2-vs
spec:
hosts:
- hello2-svc
http:
- name: hello2-v2-route
match:
- headers:
route-v:
exact: hello2v2
route:
- destination:
host: hello2-svc
subset: hello2v2
- route:
- destination:
host: hello2-svc
subset: hello2v1
----
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: hello2-dr
spec:
host: hello2-svc
subsets:
- name: hello2v1
labels:
version: v1
- name: hello2v2
labels:
version: v2
Envoyfilter示意如下:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: hello1v2-propaganda-filter
spec:
workloadSelector:
labels:
app: hello1-deploy-v2
version: v2
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_OUTBOUND
proxy:
proxyVersion: "^1\\.8\\.*"
listener:
filterChain:
filter:
name: envoy.filters.network.http_connection_manager
subFilter:
name: envoy.filters.http.router
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.wasm
typed_config:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
value:
config:
name: propaganda_filter
root_id: propaganda_filter_root
configuration:
'@type': type.googleapis.com/google.protobuf.StringValue
value: |
{
"head_tag_name": "route-v",
"head_tag_value": "hello2v2"
}
vm_config:
runtime: envoy.wasm.runtime.v8
vm_id: propaganda_filter_vm
code:
local:
filename: /var/local/lib/wasm-filters/propaganda-header-filter.wasm
allow_precompiled: true
2 验证方法
携带header的请求curl -H "version:v1" "http://$
ingressGatewayIp:8001/hello/xxx"通过istio-ingressgateway进入,全链路按header值,进入服务的指定版本。这里,由于header中指定了version为v2,那么全链路将
为hello1 v2-hello2 v2-hello3 v2。效果如下图所示。
验证过程和结果示意如下。
for i in {1..5}; do
curl -s -H "route-v:v2" "http://$ingressGatewayIp:$PORT/hello/eric" >>result
echo >>result
done
check=$(grep -o "Bonjour eric" result | wc -l)
if [[ "$check" -eq "15" ]]; then
echo "pass"
else
echo "fail"
exit 1
fi
result:
Bonjour eric@hello1:172.17.68.205<Bonjour eric@hello2:172.17.68.206<Bonjour eric@hello3:172.17.68.182
Bonjour eric@hello1:172.17.68.205<Bonjour eric@hello2:172.17.68.206<Bonjour eric@hello3:172.17.68.182
Bonjour eric@hello1:172.17.68.205<Bonjour eric@hello2:172.17.68.206<Bonjour eric@hello3:172.17.68.182
Bonjour eric@hello1:172.17.68.205<Bonjour eric@hello2:172.17.68.206<Bonjour eric@hello3:172.17.68.182
Bonjour eric@hello1:172.17.68.205<Bonjour eric@hello2:172.17.68.206<Bonjour eric@hello3:172.17.68.182
我们看到,输出信息Bonjour eric来自各个服务的fr版本,说明功能验证通过。
3 性能分析
新增EnvoyFilter+WASM后,功能验证通过,但这会带来多少延迟开销呢?这是服务网格的提供者和使用者都非常关心的问题。本节将对如下两个关注点进行验证。
- 增加EnvoyFilter+WASM后的增量延迟开销情况
- WASM版本和Lua版本的开销对比
3.1 Lua实现
Lua的实现可以直接写到EnvoyFilter中,无需独立的工程。示例如下:
patch:
operation: INSERT_BEFORE
value:
name: envoy.lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inlineCode: |
function envoy_on_request(handle)
handle:logInfo("[propagate header] route-v:hello3v2")
handle:headers():add("route-v", "hello3v2")
end
3.2 压测方法
1 部署
- 分别在3个namespace上部署相同的Deployment/Service/VirtualService/DestinationRule
- 在hello-abtest-lua中部署基于Lua的EnvoyFilter
- 在hello-abtest-wasm中部署基于WASM的EnvoyFilter
hello-abtest 基准环境
hello-abtest-lua 增加EnvoyFilter+LUA的环境
hello-abtest-wasm 增加EnvoyFilter+WASM的环境
2 工具
本例使用hey作为压测工具。hey前身是boom,用来代替ab(Apache Bench)。使用相同的压测参数分别对三个环境进行压测。示意如下:
# 并发work数量
export NUM=2000
# 每秒请求数量
export QPS=2000
# 压测执行时常
export Duration=10s
hey -c $NUM -q $QPS -z $Duration -H "route-v:v2" http://$ingressGatewayIp:$PORT/hello/eric > $SIDECAR_WASM_RESULT
请关注hey压测结果文件,结果最后不能出现socket: too many open files,否则影响结果。可以使用ulimit -n $MAX_OPENFILE_NUM命令配置,然后再调整压测参数,以确保结果的准确性。
3.3 报告
我们从三份结果报告中选取4个关键指标,如下图所示:
基准 |
WASM |
LUA |
|
1000并发1000QPS持续10秒钟 |
|||
平均延迟 |
0.6317 secs |
0.6395 secs |
0.7012 secs |
延迟99%分布 |
0.9167 secs |
0.9352 secs |
1.1355 secs |
QPS |
1541 |
1519 |
1390 |
Total |
16281 |
16109 |
1390 |
2000并发2000QPS持续10秒钟 |
|||
平均延迟 |
1.2078 secs |
1.3290 secs |
1.4593 secs |
延迟99%分布 |
1.8621 secs |
1.8354 secs |
2.2116 secs |
QPS |
1564 |
1421 |
1290 |
Total |
17622 |
16009 |
14662 |
3.4 结论
- 相对于基准版本,增加EnvoyFilter的两个版本,平均延迟多出几十个到几百个毫秒,增加耗时比为
- wasm 1.2% (0.6395-0.6317)/0.6317和1% (1.3290-1.2078)/1.2078
- lua 11%(0.7012-0.6317)/0.6317和20% (1.4593-1.2078)/1.2078
- WASM版本的性能明显优于LUA版本
注:相比LUA版本,WASM的实现是一套代码多份配置。因此WASM的执行过程还比LUA多出一个获取配置变量的过程。
4 展望
4.1 如何使用
本文从技术实现角度,讲述了如何实现并验证一个透传用户自定义Header的WASM,从而支持无侵入式全链路A/B Test这个需求。
但是,作为服务网格的使用者,如果按照本文一步步去实现,是非常繁琐且容易出错的。
阿里云服务网格ASM团队正在推出一种ASM插件目录的机制,用户只需在插件目录中选择插件,并为插件提供自定义的Header等极少数量的kv配置,即可自动生成和部署相关的EnvoyFilter+WASM+VirtualService+DestinationRule。
4.2 如何扩展
本例只展示了基于Header的匹配路由功能,如果我们希望根据Query Params进行匹配和路由该如何扩展呢?
这是ASM插件目录正在密切关注的话题,最终插件目录将提供最佳实践。
以上。
作者:六翁
本文为阿里云原创内容,未经允许不得转载
基于WASM的无侵入式全链路A/B Test实践的更多相关文章
- Hook 无侵入式埋点(页面统计)
一.技术原理 Method-Swizzling 黑魔法 方法交换(不懂的可以查) 二.页面统计 某盟页面统计SDK需要开发者在APP基类里实现ViewDidAppear和viewDidDisappea ...
- 基于html5和css3响应式全屏滚动页面切换效果
分享一款全屏响应式的HTML5和CSS3页面切换效果.这个页面布局效果对于那些页面要求固定100%高度和宽度的网站和APP来说是十分有用的.效果图如下: 在线预览 源码下载 HTML wrappe ...
- 使用AOP思想无侵入式申请权限,解决组件化中权限问题(一)
首先介绍AspectJx使用 https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx 在根项目的build.gradle ...
- php使用装饰模式无侵入式加缓存
<?php namespace App\Services; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\ ...
- Spring Boot 无侵入式 实现RESTful API接口统一JSON格式返回
前言 现在我们做项目基本上中大型项目都是选择前后端分离,前后端分离已经成了一个趋势了,所以总这样·我们就要和前端约定统一的api 接口返回json 格式, 这样我们需要封装一个统一通用全局 模版api ...
- 基于 Istio 的全链路灰度方案探索和实践
作者|曾宇星(宇曾) 审核&校对:曾宇星(宇曾) 编辑&排版:雯燕 背景 微服务软件架构下,业务新功能上线前搭建完整的一套测试系统进行验证是相当费人费时的事,随着所拆分出微服务数量的不 ...
- Android新技术学习——阿里巴巴免Root无侵入AOP框架Dexposed
阿里巴巴无线事业部近期开源的Android平台下的无侵入运行期AOP框架Dexposed,该框架基于AOP思想,支持经典的AOP使用场景.可应用于日志记录,性能统计,安全控制.事务处理.异常处理等方面 ...
- Android平台免Root无侵入AOP框架Dexposed使用详解
Dexposed是基于久负盛名的开源Xposed框架实现的一个Android平台上功能强大的无侵入式运行时AOP框架. Dexposed的AOP实现是完全非侵入式的,没有使用任何注解处理器,编织器或者 ...
- .NET无侵入自动化探针原理和主流实现
前言 最近,我在微信公众号和博客园分享了一篇关于.NET微服务系统迁移至.NET 6.0的故事的文章,引起了许多读者的关注.其中,许多人对基于 OpenTelemetry .NET 的观测指标和无侵入 ...
- 高德全链路压测平台TestPG的架构与实践
导读 2018年十一当天,高德DAU突破一个亿,不断增长的日活带来喜悦的同时,也给支撑高德业务的技术人带来了挑战.如何保障系统的稳定性,如何保证系统能持续的为用户提供可靠的服务?是所有高德技术人面临的 ...
随机推荐
- 使用TS封装操作MongoDB数据库的工具方法
使用TS封装操作MongoDB数据库的工具方法 前言 在做毕业设计过程中采用了MongoDb存储应用的日志信息,总结了一些CRUD方法与大家分享一下,最终使用效果可跳转到业务调用示例这一小节查看 关于 ...
- 三维模型OBJ格式轻量化的纹理压缩和质量关系分析
三维模型OBJ格式轻量化的纹理压缩和质量关系分析 三维模型的OBJ格式通常包含纹理信息,而对纹理进行轻量化压缩可以减小文件大小和提高加载性能.然而,在进行纹理压缩时需要权衡压缩比率和保持质量之间的关系 ...
- 网页端实现Excel转JSON
1. 引言 有时工作中拿到的数据是Excel表格,要在前端网页上使用,通常需要把文件转为JSON 微软的Microsoft Excel没有导出为JSON的功能,其他的第三方网站又不太信任 开源的Exc ...
- 记录--求你了,别再说不会JSONP了
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 JSONP是一种很远古用来解决跨域问题的技术,当然现在实际工作当中很少用到该技术了,但是很多同学在找工作面试过程中还是经常被问到,本文将带 ...
- uni-app 应对微信小程序最新隐私协议接口要求的处理方法
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一,问题起因 最新在开发小程序的时候,调用微信小程序来获取用户信息的时候经常报错一个问题 fail api scope is not de ...
- iptables-save 命令使用总结
转载请注明出处: iptables-save 命令在 Linux 系统中用于将当前运行的 iptables 防火墙规则导出到一个文件中.这对于备份规则.迁移规则或在不同系统间共享规则配置非常有用. 基 ...
- 快速上手系列:传智播客Java基础学习笔记
配置环境,把JDK的bin所在路径复制到Path,末尾加; 基本语法 一 常见的DOS命令 盘符的切换 d:回车 目录的进入 cd javase c ...
- #分治#JZOJ 4211 送你一颗圣诞树
题目 有\(m+1\)棵树分别为\(T_{0\sim m}\),一开始只有\(T_0\)有一个点,编号为0. 对于每棵树\(T_i\)由T_{a_i}\(的第\)c_i\(个点与\)T_{b_i}\( ...
- #Tarjan,SPFA,差分约束系统#BZOJ 2330 AcWing 368 银河
题目 分析 首先这明显是一道差分约束题,但是无解的情况确实比较恶心, 考虑它的边权为0或1,无解当且仅当某个强连通分量内的边至少一条边边权为1, 那么用有向图的Tarjan缩点后跑SPFA就可以了 代 ...
- OpenHarmony技术挑战课题征集
OpenHarmony技术挑战课题征集 OpenAtom OpenHarmony(以下简称"OpenHarmony")是由开放原子开源基金会(OpenAtom Foundation ...