2021-01-19 记录一次以太坊nonce值的问题
之前在做后端接口的时候,封装了构造交易及发送交易这一层,其中构造交易的时候,获取用户的nonce这里,没有自己维护,而是从链上获取,且之前由于一些业务这里没有做队列,导致前端并发调用的时候,会产生一个账户同时构造两个相同nonce值得交易,最终会导致失败一条。?
Client.PendingNonceAt?是从pending中获取该账户的本次交易改用的nonce,本以为这里已经处理了就没管,不曾想,还是会出现上面的交易重复的bug。
经修改,如果是特殊账户,可在业务层自行维护计数器做nonce值,维护成本较大,且复杂。
第二种? 就是这里加个队列,毕竟及时性不是区块链该有的东西。
英译汉 Pending
pending
adj. 悬而未决的; 迫近的
prep. 悬而未决的; 迫近的#当...的时候, 在...期间
例句用法:
The lawsuit was then pending.
那件讼案因而尚未解决.
A decision on this matter is pending.
此事即将作出决定.
She was held in custody pending trial.
她被拘留候审.
处理coredns Pending故障
生产环境中,遇到coredns Pending问题,如下
当前业务并无异常,只是pod状态不正常。删除pending状态的coredns,会立即启动一个,但依然是pending状态。查看一个pending状态的pod详细描述,可以看到报错原因
大概意思是现有的17个节点不满足节点亲和性,所以pod无法运行。
当前k8s集群里有17个node,coredns启动了22个,有5个为Pending状态。结合当前业务正常的情况猜测节点亲和性设置为每个节点只能运行一个coredns,于是有5个pod在其节点上由于已经有运行的coredns pod,无法运行,只能为Pending状态。查看节点亲和性。
集群中node节点是17个,为什么coredns设置为22个?只好先看下副本管理器中coredns设置的副本数。查看旧版本的k8s副本管理器用 kubectl get rc -n kube-system ,而比较新的版本用rs代替rc。
看到副本管理器中确实设定了副本数位22,先将副本数改为17,观察Pending状态的pod是否会被删除。
但是保存此配置后,删除Pending状态的pod,还是会自动启动一个,总数并没有改变。使用命令修改副本数:
提示修改成功,但coredns pod数量还是没有改变。
尝试修改deployments
保存退出后,发现pod数量依然是22个。
使用patch修改deployment副本数,结果pod数量还是不变。
由此可以推测coredns数量由某个进程或配置管理,不受rs、deployments管理。
这时注意到dns-autoscaler这个deployment,然后联想到cluster-autoscaler。CA(cluster-autoscaler)是用来弹性伸缩kubernetes集群的,dns-autoscaler应该是弹性伸缩coredns这个pod集群的。
为了验证猜想,先停掉dns-autoscaler,再将pod数量调整为17个。
再查看coredns pod数量,已经变为17个, 而且全都是running状态,问题解决。后续再研究下dns-autoscaler为什么会把coredns pod目标数量设定为22个,怎么修改这个预设数量。
汇款状态pending是什么意思
pending支付确认中【订单费用到账待确认】
这个状态表示已经付款成功,但是客服还没有开始处理的状态。这个时候就需要大家耐心等待客服处理订单就好啦~
其实在海淘的过程中,小编也发现了不少订单状态,索性给大家整理搜集了全面的海淘订单状态的中英文对照说明,给大家做个科普!
Pending Payment:待支付【订单费用待支付】
Processing:处理中【商家正在处理订单中(采购/退换货)】
Purchased:已订购【商品已订购】
Arrived:已送达【商品已送到目的地】
in warehouse:备货中【正在处理订购的商品】
Processed:已打包【您订单已处理完毕,等待发货】
Shipped:已寄送【该商品已为您寄出】
Complete:已完成【商品已送达】
Canceled :已取消,砍单【包裹取消寄送,订单被取消】
ordered:已完成【代表已完成的订单】
Rejected:订单被拒绝【可能因为信用卡未验证或身份证未验证】
Backordered:延期发货【一般是因为库存不足需要补货】
submitted:未审核【订单未审核,耐心等等就行】
fulfillment: 处理中【订单正在处理中】
on hold:等待处理【订单等待处理中】
in process:处理中【订单正在处理中】
in stock:无货【通常会在商品无货时,在详情页面显示】
confirmed:订单已确认【不会砍单了】
unsuccessful:订单失败
以太坊源码分析--p2p节点发现
节点发现功能主要涉及 Server Table udp 这几个数据结构,它们有独自的事件响应循环,节点发现功能便是它们互相协作完成的。其中,每个以太坊客户端启动后都会在本地运行一个 Server ,并将网络拓扑中相邻的节点视为 Node ,而 Table 是 Node 的容器, udp 则是负责维持底层的连接。下面重点描述它们中重要的字段和事件循环处理的关键部分。
PrivateKey - 本节点的私钥,用于与其他节点建立时的握手协商
Protocols - 支持的所有上层协议
StaticNodes - 预设的静态 Peer ,节点启动时会首先去向它们发起连接,建立邻居关系
newTransport - 下层传输层实现,定义握手过程中的数据加密解密方式,默认的传输层实现是用 newRLPX() 创建的 rlpx ,这不是本文的重点
ntab - 典型实现是 Table ,所有 peer 以 Node 的形式存放在 Table
ourHandshake - 与其他节点建立连接时的握手信息,包含本地节点的版本号以及支持的上层协议
addpeer - 连接握手完成后,连接过程通过这个通道通知 Server
Server 的监听循环,启动底层监听socket,当收到连接请求时,Accept后调用 setupConn() 开始连接建立过程
Server的主要事件处理和功能实现循环
Node 唯一表示网络上的一个节点
IP - IP地址
UDP/TCP - 连接使用的UDP/TCP端口号
ID - 以太坊网络中唯一标识一个节点,本质上是一个椭圆曲线公钥(PublicKey),与 Server 的 PrivateKey 对应。一个节点的IP地址不一定是固定的,但ID是唯一的。
sha - 用于节点间的距离计算
Table 主要用来管理与本节点与其他节点的连接的建立更新删除
bucket - 所有 peer 按与本节点的距离远近放在不同的桶(bucket)中,详见之后的 节点维护
refreshReq - 更新 Table 请求通道
Table 的主要事件循环,主要负责控制 refresh 和 revalidate 过程。
refresh.C - 定时(30s)启动Peer刷新过程的定时器
refreshReq - 接收其他线程投递到 Table 的 刷新Peer连接 的通知,当收到该通知时启动更新,详见之后的 更新邻居关系
revalidate.C - 定时重新检查以连接节点的有效性的定时器,详见之后的 探活检测
udp 负责节点间通信的底层消息控制,是 Table 运行的 Kademlia 协议的底层组件
conn - 底层监听端口的连接
addpending - udp 用来接收 pending 的channel。使用场景为:当我们向其他节点发送数据包后(packet)后可能会期待收到它的回复,pending用来记录一次这种还没有到来的回复。举个例子,当我们发送ping包时,总是期待对方回复pong包。这时就可以将构造一个pending结构,其中包含期待接收的pong包的信息以及对应的callback函数,将这个pengding投递到udp的这个channel。 udp 在收到匹配的pong后,执行预设的callback。
gotreply - udp 用来接收其他节点回复的通道,配合上面的addpending,收到回复后,遍历已有的pending链表,看是否有匹配的pending。
Table - 和 Server 中的ntab是同一个 Table
udp 的处理循环,负责控制消息的向上递交和收发控制
udp 的底层接受数据包循环,负责接收其他节点的 packet
以太坊使用 Kademlia 分布式路由存储协议来进行网络拓扑维护,了解该协议建议先阅读 易懂分布式 。更权威的资料可以查看 wiki 。总的来说该协议:
源码中由 Table 结构保存所有 bucket , bucket 结构如下
节点可以在 entries 和 replacements 互相转化,一个 entries 节点如果 Validate 失败,那么它会被原本将一个原本在 replacements 数组的节点替换。
有效性检测就是利用 ping 消息进行探活操作。 Table.loop() 启动了一个定时器(0~10s),定期随机选择一个bucket,向其 entries 中末尾的节点发送 ping 消息,如果对方回应了 pong ,则探活成功。
Table.loop() 会定期(定时器超时)或不定期(收到refreshReq)地进行更新邻居关系(发现新邻居),两者都调用 doRefresh() 方法,该方法对在网络上查找离自身和三个随机节点最近的若干个节点。
Table 的 lookup() 方法用来实现节点查找目标节点,它的实现就是 Kademlia 协议,通过节点间的接力,一步一步接近目标。
当一个节点启动后,它会首先向配置的静态节点发起连接,发起连接的过程称为 Dial ,源码中通过创建 dialTask 跟踪这个过程
dialTask表示一次向其他节点主动发起连接的任务
在 Server 启动时,会调用 newDialState() 根据预配置的 StaticNodes 初始化一批 dialTask , 并在 Server.run() 方法中,启动这些这些任务。
Dial 过程需要知道目标节点( dest )的IP地址,如果不知道的话,就要先使用 recolve() 解析出目标的IP地址,怎么解析?就是先要用借助 Kademlia 协议在网络中查找目标节点。
当得到目标节点的IP后,下一步便是建立连接,这是通过 dialTask.dial() 建立连接
连接建立的握手过程分为两个阶段,在在 SetupConn() 中实现
第一阶段为 ECDH密钥建立 :
第二阶段为协议握手,互相交换支持的上层协议
如果两次握手都通过,dialTask将向 Server 的 addpeer 通道发送 peer 的信息
Miner 流程
以太坊的矿工出块的流程,不同版本有过变更,下面基于1.7.3版本和1.8.4版本来分享
channel: 用于1发1收
发送 :sampleChan
接收 :
Feed:用于1发多收,参考chainHeadCh
接收者注册 :Subscribe(sampleChan)
发送 :send, 发送的地方不太好找,需要通过send和event/channel类型查找,例如miner中主要涉及到的就是 PostChainEvents
接收 :
数据结构:
可以理解为操作间(eth)中有了矿(tx),那么矿主(miner)安排工人(worker)挖矿(seal)。结构体定义如下:
Type Miner struct {? -- - 理解为矿主
? ? mux? ? ? ? *event.TypeMux
? ? worker? ?? *worker? ? ---- 理解为干活的工人
? ? coinbase? ? common.Address
? ? eth? ? ? ? ? ? Backend? ? - --- 理解为操作间
? ? engine? ? ? consensus.Engine? ? ---- 理解为挖矿的工具
? ? exitCh? ? ? ? chan struct {}
? ? canStart? ? ? ? int32 //canstart indicates whether we can start the mining operation
? ? shouldStart? int32 //shouldstart indicates whether we should start after sync
}
?流程图如下:
1.? 节点启动: backend.new->miner.new->worker.new: 调用commitNewWork,里面使用push把work传递给cpuAgent, 之后在geth命令行敲miner.start()后->miner.start->worker.start->cpuAgent.start,调用Seal,计算nonce值,再发送 recv 消息,通知 worker . wait ,在收到之后将块打包插入到区块链,之后调用PostChainEvents,发送消息chainHeadCh, Worker.update 在收到消息后,重新调用 commitNewWor k,形成一个循环。
?2.? 创世块: 调用geth的init命令触发调用initGenesis->SetupGenesisblockquan, 里面具体强调一下time是使用的genesisblockquan.json中的值,一般都是0.
? 3.? 正常情况: worker . wait ,在收到之后将块打包插入到区块链,之后调用PostChainEvents,发送消息chainHeadCh, Worker.update 在收到消息后,重新调用 commitNewWor k,形成一个循环。
Miner .new: 在backend new的时候调用,即在节点启动的时候调用。
Miner . update :在节点启动的时候调用,用于监控是否有块同步,如果有则停止挖矿,如果没有启动挖矿,这个在POW这种竞争性出块的环境中需要。
Worker .new: 在miner.new的时候调用,记载节点启动的时候调用?
Worker.update: 节点启动的时候调用,如果是非全节点的话用于监控接受交易transaction,关键函数 commitTransactions ,还用于调度在收到 chainHeadCh 的消息后,触发 commitNewWork
其中 commitNewWork :? 用于将pending的tx输入到系统,计算trie等等操作,生成blockquan,并将work push到cpuAgent处理,注意没有盖章
Worker. wait (对应于 1.8.4 的 resultLoop ) :节点启动的时候调用,循环监听 recv 消息,将携带的blockquan插入区块链中、发送广播消息( NewMinedblockquanEvent )、发送消息 PostChainEvents (发送 ChainHeadEvent ,即 chainHeadCh ),其中的关键函数是 WriteblockquanAndState 。
cpuAgent .update() :? 在cpuAgent.start()->worker.start->miner.start->geth的命令行调用之后启动循环,用于接收 commitNewWork 分配下来的work,关键函数 mine ,里面调用 Seal ,主要是完成POW寻找nonce值的操作,发送 recv 消息通知worker,也可以叫做盖章。
类图如下:
具体结构不再赘述
流程:
Miner.update:用于监控是否有块同步,如果有则停止挖矿,这个在POW这种竞争性出块的环境中需要
mainLoop:收到newWorkCh消息后处理,调用commitNewWork中的commit发送taskCh消息
newWorkLoop:收到startCh消息和chainHeadCh消息后发送newWorkCh消息
resultLoop:循环监听resultCh(seal发送)消息,将携带的blockquan插入区块链中,并发送广播消息,关键函数WriteblockquanAndState,并发送chainHeadCh消息
taskLoop:以前agent做的事情,收到taskCh消息后,调用seal,里面发送resultCh消息
如何处理Pending Orders?
雨果网给出回答1.不要对Pending Orders发货,即便是买家主动联系您也不要。
2.当出现Pending Orders的时候,该订单在管理订单页面是呈灰色的。卖家不能对Pending Orders进行确认发货或者取消订单的操作。Pending Orders不会显示在您的订单报表(Orders Report)和您的未发货订单报表(UnshippedOrders Report)中。
3. Pending Orders要等它转入到Unshipped状态才能进行发货操作,发货操作尽量在您后台上传产品时候设置的Handing Time的时间段内,超出了就是发货延迟,如果超出了30天都不发货,即使30天后最终发货了,亚马逊也不会把这笔订单的钱给您。