免修改源码的常见AOP埋点方案
2021-01-03 / modified at 2023-05-20 / 1.1k words / 4 mins

为了提高软件开发的效率,开源/商业软件的引入也越来越广泛,但是一旦涉及到修改源码,将带来高成本的分支维护问题。本文介绍基于代码、容器、网关等AOP免修改源码的设计方案。

常见的免修改源码的切片方案如下

$2CodeHardcode(Modify required)Spring的AOP修改源码并维护分支OpenTelemetrisPluginJenkins/Sonarqube的jar插件c/go中的so插件py中的pypi插件license服务器插件RuntimeJVM AgentLD_PRELOADContainer SideCarConsulLogstashNetworkLoad BalancerShardingRound robin and moreMiddlewareForward Auth(SSO/SAML/LDAP)LimiterAOP

其中Code方案跳过,其它简要介绍

Runtime层

JVM Agent插件

假如你的程序老是挂掉,除了分析日志还可以参考Uber的免硬编码的JVM profiler方案,用于监控内存、CPU、FD等问题

https://github.com/uber-common/jvm-profiler

此方案代码量很小,而且相当成熟,你可以拿过来自己编译定制

LD_PREDLOAD替换glibc

参考此文档 https://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html

此方案会优先glibc库加载,是高阶且危险方案,可以替换容器中的glibc中的标准函数,比如printf,malloc,exec/execv等。这种方案相当于是二进制级别的AOP,就算是C程序也可以魔改,当然代价就是需要充分测试,否则很容易影响更多功能。

本方案主要用于安全审计(但是仍然可能绕过)、使用分析、性能分析,APM等,一般还会与ptrace一起使用。如果你对性能等因素要求更高,可以参考这些

如果要求没那么高,与可以用alias/PATH等方案进行替换,可以用environment module等方案进行无缝替换。

Container SideCar

容器的SideCar一般指同时启动两个容器,一个是业务逻辑,另一个用于日志、命名服务、localhost代理等。

参考 https://docs.microsoft.com/en-us/azure/architecture/patterns/sidecar

此方案需要运维一套高可用的PaaS,比如Kubernetes或者Nomad,至少10台机器才能回本,因此需要看你的项目是否能够覆盖这些成本。

Network

Load Balancer

LB层主要用于支持副本的高可用或者扩容,当软件本体不支持高可用部署时,你就要通过下面来实现。这里无论是生产使用,还是面试System Design,都是重点场景

切片LB

开源版的Jenkins、Gitlab等项目是不支持横向扩容的,而只能最多做到Binlog一样的复制备份,因此网上的开源方案就比较折腾了。比如阿里云就是将上述组件高度定制,但是代码分支也要自己维护了。

比较偷懒的方案是基于项目ID等Hash值进行切片,这样就可以一定程度上进行多机使用

  • 如果使用纯Nginx/Caddy等静态配置,后续再扩容时一定需要停机
  • 建议加一层metadata进行KV路由的持久化(比如Consul),后续比较方便,这个也是分布式存储的主流方案
  • 再偷懒可以用子域名,比如pool1,pool2,用Web系统做一个审批流来分配也是可以的

现实生产要与成本匹配,100%高可用虽然很好,但是它是非常昂贵的方案。常见的IT项目生命周期一般是3年,停机加班只要几天,而开发元数据切片可能要几个月。

非切片方案

如果负载均衡后面的应用是无状态的(或者是状态存储到Redis中),那么可以这么搞。

Middleware

此处重要是用于业务切片,比如用户IAM、登陆(AuthN)、鉴权(AuthZ)、白名单、限流等,网上教程很多,可以参考Treafik的设计。我在真实项目中,就通过Traefik实现了AuthN与AuthZ,给业务系统只提供当前用户的Context,但是这类系统早晚都会在低代码和强业务间犹豫

总结

如同芯片一样,软件开发后续一定会走向集成化、供应链化、采购化,个人做的工作虽然看似复杂,但是每个人都是可替代的螺丝钉,因此对个人来说,要尽可能提高知识广度。