Skip to main content

Attitude is altitude

Tag: go

如何使用 Go 语言写区块链的 SDK (一)

“ 最近在使用 Go 语言写一个区块链的 SDK,从设计到上线总结了其中的一些问题以及思考,如果你也在写 SDK 或者要写 SDK,希望这篇文章可以帮到你。这篇文件不会区分是否是区块链的 SDK,下一篇文章主要写区块链的 SDK 与普通系统 SDK 的一些区别以及相关思考” 当写一个 SDK 时,第一需要考虑的问题应该是这个 SDK 的功能,或者说这个 SDK 是给什么样的用户来用,或者用户使用这个 SDK 能做什么。弄明白了这个问题后才可以开始动手。 一个系统的接口肯定是知道的,但有些情况并不是一个接口就能够完成一个用户的操作或者需求,此时需要系统的多个接口组合调用才可以,同时在调用多个接口时,有很多逻辑是需要在客户端完成的,那么接口调用以及用户的逻辑就需要在 SDK 中完成。所以简单来说,一个 SDK 的基础功能就是系统的接口调用与用户的部分逻辑(当然这只是我抽象出来的,实际情况 SDK 有很多种)。 我个人把 SDK 的出生过程总结如下: SDK 功能确认; 系统接口逻辑确认; SDK 接口设计; SDK 架构设计; SDK 使用文档或者示例代码; 测试; 接下来就分这七步来聊一聊如何开发一个易用、可靠的 SDK。 1. SDK 功能确认 某个系统的 SDK 会有官方的 SDK 也有其他人贡献的 SDK,比如有的企业或者开发者觉得官方提供的 SDK 功能不全,或者使用不方便,满足不了现在的需求. 而且官方很大几率不会再开发一个专门的 SDK 来满足个别的需求,所以开发者有可能根据系统的接口来开发或者在官方 SDK 基础上添加功能。 对于这种现象的出现,我觉得主要原因在于官方的 SDK 在设计之初就是存在问题的,或者说 SDK 的功能确认是错误。 系统可能有几十个接口,但是对于用户有用的或者最常用的可能只有一半或者三分之一,所以 SDK 并不需要把所有的接口都支持,而是要把核心的功能筛选出来,这一点并不与上面提到的功能确认存在问题。

Go Sysmon

1. 简介 很多系统中都有守护进程,Go 中也不例外。Go 的系统监控主要目的就是隔一段时间来检查一下运行时是否进入异常状态。本文会介绍一下 sysmon 的创建以及其在系统中的作用。 关于本文整理了一个大体的概括图(建议保存此图片,以后复习就就不需要再通篇读文章了): 2. sysmon 的创建 对于其创建非常简单,在 runtime.main 函数(位于:go/src/runtime/proc.go 文件中)中启动程序时创建。基本流程如下: 调用 runtime.newm 函数,创建一个 runtime.m 结构对象。 runtime.newm 函数再调用 runtime.newm1 函数,然后调用 runtime.newsproc 函数,系统调用 clone 一个线程。 此时在创建好的 runtime.m 对象中已经存在了 sysmon 函数,在创建的线程中执行此函数,开始系统监控。 第一步检查是否有死锁。 然后开始无限循环。 循环最开始休眠时间为20us。 大约1ms后每次休眠时间翻倍(50个循环中都没有唤醒 goroutine,休眠时间翻倍)。 最长每次休眠时间10ms。 上面就是一个整体流程,本文没有将所有源码都复制过来,大家可以参考源码来理解上面这一过程。 3. sysmon 的作用 系统监控的左右主要如下: debug 监控程序 强制垃圾回收 本质就是定期检查上一次垃圾回收是什么时间,如果长时间未进行垃圾回收,就强制执行一次。 网络轮询 10ms进行网络轮询 检查是否有待执行的 fd 将就绪的 goroutine 加入全局队列 如果存在空闲P,就用这个P来执行任务 抢占 可抢占系统调用的 P: 如果你不了解 Go 的 runtime 或者 MPG 模型,那么这部分理解可能困难一些。 我们知道一个 G 是运行的 P 上的,那么当这个 G 进行系统调用时,当前的 P 会怎样呢?是的,这时 P 其实就在等当前的 G,那么假如对于 P 的本地 G 队列还有在等待的 G时,对于这种情况我们肯定是不允许的。那么我们就需要对这个 P 进行抢占了。 在任务量过多时,所有的处理器(P)都在认真工作,此时对于系统调用的 P 就需要抢占了,目的就是大家都这么拼命,你就不要等你的那个系统调用的 G 了。 除了以上两种情况,假如这次系统调用时间过长,那么也会发起抢占的。 可抢占运行时间过长的 P: 当一个 G 在 P 上运行了很长时间,说明这个 G 开始欺负其他的 G 了,此时就需要对这个运行时间过长的 G 做出惩罚,抢占其 P 并将这个 G 放入全局队列。 此时关于 sysmon 的东西就讲完了,大家可以边看源码边理解此文章,但是建议对 Go 的 runtime 有一些了解之后再阅读抢占部分的内容。

Go Map

1 哈希表 哈希表属于编程中比较常见的数据结构之一,基本上所有的语言都会实现数组和哈希表这两种结构,Hash table 的历史是比较悠远的,我们在编程时也是离不开的,这种数据结构的作用其实很简单,就是我们可以根据一个 key 可以查找到对应的 value,也就是说这种数据结构存储的是键值对的“列表”。 1.1 原理 首先哈希表中第一个点就是哈希函数,也就是我们需要一个函数,根据我们的 key 计算出一个值,然后根据这个值可以直接找到对应的 value。因为我们的哈希表的一个作用就是 O(1) 复杂度找到 key 对应的 value。 完美的哈希函数是可以做到将任何一个 key 值都可以计算出一个唯一且固定大小的值,不幸的是目前世界上还没有这种完美的哈希函数。因此我们需要解决的另外一个问题就是哈希冲突的解决。 1.1.1 哈希冲突 假如我们有两个不同的 key,通过哈希函数计算出的结果相同,那么我们是不能认为这两个 key 在 map 中是相同的,也就是如果出现了这种情况,我们的 map 结构是可以解决这个问题的。目前解决办法有很多,这里只说三个比较常见的解决方案: 开放地址法(Open Addressing): 写入时:假如 key Alice 与 Bob 通过哈希函数计算出结果冲突。当 map 中已经存在 key Alice,再写入 key 为 Bob时,发现哈希结果对应位置已经存在 Alice,此时在 Alice 位置之后再寻找位置,一直找到为空的位置,将 Bob 写入。 读取时:此时 map 中已存在 key Alice、Bob,且哈希结果相同,此时想查找 Bob 对应 value 时,先计算 Bob 哈希结果,再通过哈希结果在 map 中查找位置,此时由于和 Alice 哈希结果相同,并且 Alice 先于 Bob 存入 map,所以会直接找到 Alice 的位置,发现 key 是 Alice 不是 Bob,接着在 Alice 位置后面查找,直到找到 key Bob 或者找到空。 再哈希法(Re-Hashing):