当前位置: 首页 > 科技观察

说说Sdk和排错

时间:2023-03-19 20:25:15 科技观察

不分享任何知识,说说最近的一些想法和看到的一些内容。这两个内容看似无关,实则有关联。sdk大家都不陌生,比如我们经常使用的npm包。当我们以SDK的形式提供能力时,我们的实现不仅决定了业务的使用和成本,也决定了用户是否愿意使用。所以我们不能只考虑功能,还需要考虑使用方式和sdk本身对业务的影响,无论是稳定性还是性能。当业务刚好需要我们的sdk时,如果sdk有问题,业务可能会联系我们处理,因为他需要这个sdk。但是如果sdk不是业务刚需,业务可能会直接卸载我们的sdk,删除相应的代码。这对于提供sdk的我们来说显然不是什么好事。但不管是不是刚需,作为提供者,我们都需要努力做好我们提供的服务。1嵌入表单##1.1嵌入到业务代码中的表单我们使用的sdk大多是引入到业务代码中,然后使用它提供的功能。在这种情况下,有两种模式。首先是业务需要感知sdk提供的API。我们需要知道什么时候使用哪个API。二是业务不需要感知sdk提供的API,或者sdk此时不提供API。它本身就像一个黑盒子。业务引入后,内置了一些功能,比如我们提供了业务内存使用的定时报表sdk,那么业务就不需要关注sdk的具体实现了。下面以统计请求耗时为例,看看这个sdk是如何实现的。1.第一种{start(...){}end(...){}}第一种方法是比较简单的实现。sdk提供了开始和结束API,业务开始和结束请求分别执行这两个API,这样sdk就可以计算出本次请求的耗时。但是这个方法似乎并不那么友好。首先,它会侵入业务的代码逻辑。第二,业务需要感知sdk。需要考虑什么时候调整start,什么时候调整end,sdk也依赖于传入的业务request和response。上下文只能计算某个请求的耗时。一般来说,这种方法比较麻烦。2对于第二种,我们希望对业务的侵入性较小,对业务的感知程度较低,所以我们决定直接劫持Node.js中的API。可以按以下形式在Node.js中创建服务器。http.createServer((req,res)=>{})那么我们直接劫持这个createServer。constcreateServer=http.createServer;http.createServer=(cb)=>{returncreateServer((req,res)=>{conststart=Date.now();res.on('finish',()=>{constcost=end-start;});cb();});}通常sdk是提供API,被业务主动调用,或者触发sdk的代码,因为sdk无法捕捉到业务代码需要使用某个功能的时候的SDK。但是当我们能够捕捉到业务需要我们的时候,我们就可以更好的提供这个sdk。这样业务就不需要太了解sdk了。比如上面的例子,业务只需要保证我们的sdk在调用http.createServer之前执行完毕即可。将业务代码嵌入到sdk中是一种很常见的形式,但是我们希望尽量减少对业务的侵入,或者说减轻业务的精神负担。你可能有过这样的经历。当你看到一个提供密集参数的sdk时,第一反应是不要用它。2以脱离业务代码的形式,是否可以采用与业务代码分离的方式提供sdk,不仅不会影响业务代码,而且更容易升级sdk。不过这个方法往往并不简单,主要还是看场景。比如业务需要通过sdk上传文件,那么sdk内嵌就比较合适。但是,在某些场景下,可以使用与业务代码分离的SDK,例如故障排除工具。在Node.js中,我们调试或诊断进程的方式通常是在业务代码中嵌入相关代码,然后在需要的时候执行相应的代码,比如对堆进行快照。因为我们的代码只有在进程中才能获取到这些信息。但并不是所有的信息都需要在进程中获取,比如系统级的数据。之前遇到一个问题,就是在某个场景下,WebSocket连接会很快断开。通过客户端wireshark抓包的流量,发现服务器端会发一个fin包给客户端,这样就知道是服务器端了,但是由于客户端和真实服务器之间有很多层,所以不可能知道是哪一层服务器主动断开了连接。最后通过服务器提供的工具找到了主动发送fin包的服务器,解决了问题。问题。但是我发现服务端的工具使用起来非常复杂。如果不经常使用它们,您很快就会忘记各种命令和参数。这样的场景,可以封装sdk,供业务使用。这种形式不仅可以帮助业务排查问题,还不需要黑进业务代码。3故障处理我们通常使用日志来排查问题,但很多时候日志并不能解决问题。日志是静态埋点。如果日志过多,不仅会浪费存储空间,还会消耗性能。如果记录太少,则可能缺少故障排除的上下文。但无论如何,关键是日志是一个静态埋点。如果我们要添加埋点,就得重启服务。有些问题是短暂的,重启后可能难以重现。因此,除了静态跟踪技术,动态跟踪技术是非常必要的,也是非常酷的。之前看过ebpf,后来没看。最近重新研究了一下ebpf以及衍生出来的一些排错工具。我也看了看openresty作者的文章《动态追踪技术漫谈》,可以用精彩来形容。当某个流程或系统出现问题时,我们希望保留现场,然后慢慢分析。但是我们如何在进程外获取进程的数据呢?除了系统本身提供的一些命令外,这里要说的是一个更复杂但更强大的技巧。操作系统和我们写的业务代码是一样的。这是一些代码的逻辑。我们在写代码的时候,经常会用到钩子或者劫持技术。同样,操作系统也不例外,但要提供这种技术,操作系统的实现要复杂得多。这个技术就是ebpf。ebpf将用户编写的代码注入内核。内核有一个虚拟机,当条件满足时,我们的代码就会执行。操作系统提供了钩子机制。例如,我们可以向系统注册一个钩子,当系统收到一个网络数据包时,它会回调给我们。另一种是劫持,比如kprobe实现。当我们写一段代码让操作系统在有人调用x的时候回调我们,操作系统会把这个地址对应的指令改成int3(x86架构),然后执行到x这个函数执行的时候,就会触发int3中断,相应的处理函数会执行我们注册的回调,然后执行真正的函数。许多技术依赖于ebpf,例如tcpdump。ebpf的强大之处在于内核编程是可编程的。在实际案例中,我们可以使用基于ebpf的工具从内核中查找到很多信息来帮助我们排查问题。ebpf很流行,也很复杂,我就不过多讨论了。您可以自行查看相关信息。