-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearchindexes.xml
1036 lines (1036 loc) · 285 KB
/
searchindexes.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8" standalone="yes"?><search><entry><title>配置pdf</title><url>/post/config_pdf/</url><categories><category>utils</category></categories><tags/><content type="html"><![CDATA[配置pdf Resources <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script> <script type="text/x-mathjax-config"> MathJax.Hub.Config({ tex2jax: {inlineMath: [['$', '$']]}, messageStyle: "none" });</script> https://oxygentw.net/blog/computer/vscode-markdown-pdf-latex/ ]]></content></entry><entry><title>Chainlink</title><url>/post/chainlink/</url><categories><category>blockchain</category></categories><tags/><content type="html">Chainlink Price Feed (0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419).latestRoundData()
VRF</content></entry><entry><title>Crypto</title><url>/post/crypto/</url><categories><category>Crypto</category></categories><tags><tag/></tags><content type="html">Crypto Digital Signature v,r,s是用于数字签名的创建和验证.
私钥是一整串随机生成的整数, 通过sepc256k1算法产生公钥.(算法曲线x轴对称)
pubKey是椭圆曲线上的一点, 意味着它是满足椭圆曲线方程的一个(x,y)坐标. 用于计算这些点的椭圆曲线算法是单向函数.
Signature = Fsig(Fkeccak256(m), k); k是privKey, m是 message. ECDSA签名算法将{r,s}作为输出
验证签名, 返回true/false. v是因为使用椭圆曲线加密技术生成pubKey时,在每个实例有两个pubKey, v指定r的奇偶性和有限性.</content></entry><entry><title>The Ethereum Virtual Machine</title><url>/post/solidity_evm/</url><categories><category>blockchain</category></categories><tags><tag>solidity</tag></tags><content type="html">The Ethereum Virtual Machine Internal Function ecrecover https://extremelysunnyyk.medium.com/how-does-ethereum-work-under-the-hood-understanding-evm-basics-in-simple-english-bdba2d888d63</content></entry><entry><title>UTILS</title><url>/post/utils/</url><categories/><tags/><content type="html">UTILS makeBook: https://www.ruanyifeng.com/blog/2023/08/weekly-issue-266.html</content></entry><entry><title>Contract Audit</title><url>/post/contract_audit/</url><categories><category>blockchain</category></categories><tags><tag>solidity</tag></tags><content type="html"><![CDATA[Contract Audit 合约审计定位于审计步骤,审计工具和审计中需要注意的地方.
审计步骤: 运行slither工具, 执行测试文件, 人工审计
Slither Detector
inheritance-graph
call-graph
Reentrancy A reentrancy attack is a type of vulnerability in smart contracts that allows attackers to execute a function multiple times before the previous call completes. This can lead to unexpected and harmful behavior, such as the theft of funds or unauthorized access to data.
use check-effect-interaction pattern
use mutex
same method for the same contract
pragma solidity 0.8.9; contract VulnerableContract { mapping (address => uint256) public balances; function withdraw(uint256 amount) public { require(balances[msg.sender] >= amount, "Insufficient balance"); (bool success, ) = msg.sender.call{value: amount}(""); if (success) { balances[msg.sender] -= amount; } } function deposit() public payable { balances[msg.sender] += msg.value; } } different methods for the same contract
different methods for different contracts
read-only reentrancy
Access Control Arithmetic issue unchecked, 溢出
Bad randomness Timestamp dependence External data source dependence Chainlink
Transaction order risk ENS front attack
Dos Attack Dos with (unexpected) revert Dos with block gas limit forcibly send ether to contract Deprecated attack call depth attack
// SPDX-License-Identifier: MIT pragma solidity ^0.8.5; // DO NOT USE!!! contract Auction { address highestBidder; uint256 highestBid; function bid() external payable { if (msg.value < highestBid) revert(); if (highestBidder != address(0)) { payable(highestBidder).send(highestBid); // refund previous bidder } highestBidder = msg.sender; highestBid = msg.value; } } Prior to the implementation of EIP-150, the contract above could be susceptible to “Call Depth Attack.” This is because a malevolent bidder could initiate a recursive call to itself, causing the stack depth to increase to 1023 before calling the bid() function. As a result, the send(highestBid) call would fail silently, meaning that the previous bidder would not receive a refund, while the new bidder would still become the highest bidder.
EIP150: all gas would be consumed well before reaching the 1024 call depth limit
闪电贷 去中心化借贷平台攻击事件bZx
黑客通过闪电贷从去中心化数字资产衍生交易平台dYdX借出10000枚ETH; 使用其中的5000枚ETH抵押在去中心化借贷平台Compound借出112枚wBTC; 剩下的5000枚ETH到去中心化借贷平台bZx开空单; 用借出的112枚wBTC到去中心化交易所Uniswap砸盘,让wBTC价格下跌; 常见的攻击原理 AbiReencodingHeadOverflowWithStaticArrayCleanup: https://etherscan.io/solcbuginfo?a=AbiReencodingHeadOverflowWithStaticArrayCleanup tx.origin 漏洞攻击
避免使用extcodesize用于检查外部拥有的账户
Testing And Verification Gas Optimization Packing variable into a single slot
Use Merkle tree proof
stateless contracts
calldata, memory, storage之间的复制
Stop using bools for True/False
EVM将bool存储为uint8类型,占用两个字节;因为EVM字长32字节,需要额外的逻辑来解析此值.
将false存储为uint256(0),true -> uint256(1), 这也会产生大量的gas, 因为将零值flip转为其它值需要支付固定费用.
最有效的方法是1表示false, 2表示true.(非零值会使合约部署成本高一些)
]]></content></entry><entry><title>Solidity Language description</title><url>/post/solidity_language_description/</url><categories><category>blockchain</category></categories><tags><tag>solidity</tag></tags><content type="html"><![CDATA[Solidity Language Description contract address
functions
receive()和fallback()
函数调用success和returndata
accounts[0].transfer(to=accounts[1].address, amount=1, data=“112”)
pragma solidity ^0.8.9; import "@openzeppelin/contracts/utils/Address.sol"; contract Bridge { } contract Bridge2 { fallback() external{} } interface CC { function H() external; function H2() external returns(uint256); function H3() external returns(bool); function H4() external returns(bytes memory); } contract A { address public addr; event EV(bool, bytes); function setAddr(address _addr) external { addr = _addr; } //addr = EOA, revert //addr = ContractWithoutFallback, revert //addr = ContractWithFallbck, normal function H() external { CC(addr).H(); } //addr = EOA, revert //addr = ContractWithoutFallback, revert //addr = ContractWithFallbck, revert function H2() external { CC(addr).H2(); } //addr = EOA, revert //addr = ContractWithoutFallback, revert //addr = ContractWithFallbck, revert function H3() external { CC(addr).H3(); } //addr = EOA, revert //addr = ContractWithoutFallback, revert //addr = ContractWithFallbck, revert function H4() external { CC(addr).H4(); } } contract A2 { using Address for address; address public addr; event EV(bool, bytes); function setAddr(address _addr) external { addr = _addr; } //addr = EOA, success=true, returndata=0x0 //addr = ContractWithoutFallback, success=false, returndata=0x0 //addr = ContractWithFallbck, success=true, returndata=0x0 function H() external { (bool success, bytes memory returndata) = addr.call(""); emit EV(success,returndata); } //addr = EOA, success=true, returndata=0x0 //addr = ContractWithoutFallback, success=false, returndata=0x0 //addr = ContractWithFallbck, success=true, returndata=0x0 function H2() external { (bool success, bytes memory returndata) = addr.call(""); emit EV(success,returndata); } //addr = EOA, success=true, returndata=0x0 //addr = ContractWithoutFallback, success=false, returndata=0x0 //addr = ContractWithFallbck, success=true, returndata=0x0 function H3() external { (bool success, bytes memory returndata) = addr.call(""); emit EV(success,returndata); } //addr = EOA, success=true, returndata=0x0 //addr = ContractWithoutFallback, success=false, returndata=0x0 //addr = ContractWithFallbck, success=true, returndata=0x0 function H4() external { (bool success, bytes memory returndata) = addr.call(""); emit EV(success,returndata); } } k
proxy
Metamorphic合约本质,create2
size
如何缩减合约大小:
一个是合并同类项,将相同逻辑部分作为一个内部函数存在
使用库
使用代理合约
避免额外的变量
缩短错误信息
runs调整
默认值为 200,表示它试图在一个函数被调用 200 次的情况下优化字节码。 如果您将其改为 1,相当于告诉优化器针对每个函数只运行一次的情况进行优化。 一个仅运行一次的优化函数意味着它对部署本身进行了优化。 请注意,这将会增加运行函数的 gas 成本,所以,您可能不想这样做。
bytecode
Name Decription bytecode hardhat编译时文件, 同creationCode creationCode 同type(C).creationCode , transaction的input deployBytecode hardhat编译时文件, 存储在区块链中的code,type(C).runtimeCode At a high level, the wallet deploying the contract sends a transaction to the null address with the transaction data laid out in three parts:
<init code><creation code><constructor paramters> creationcode:
<init code><runtimecode> There are two types of code for smart contracts, as show below: the creation code: this is the contract’s bytecode that includes the instruction for deploying the contract and running the constructor logic.the runtime code: this is the final bytecode of the contract once it has been deployed on the blockchain.
The runtime bytecode for a contract can be retrieved on-chain by using an assembly block and calling excodecopy(a).The hash of the runtime bytecode is returned from excodehash(a). web3.eth.getCode(B[0].address)
solidity存储变量寻址计算 Layout in storage storage > memory > calldata.
calldata
memory
four 32-byte slots.
0x00-0x3f(64 bytes): scratch space for hashing methods 0x40-0x5f(32 bytes): currently allocated memory size 0x60-0x7f(32 bytes): zero slot scratch space can be used between statements.
zero slot is used as initial value for dynamic memory array and should never be written to(the free memory pointer points to 0x80 initially).
solidity always places new objects at the free memory pointer and memory is never freed.
stroage
stack
stack数量限制.(error: stack too deep)
Resources bytecode
https://www.rareskills.io/post/ethereum-contract-creation-code calcaulate variant position: https://www.whatsblockchain.com/posts/c052872a.html stateDB: https://learnblockchain.cn/books/geth/part3/statedb.html https://solidity-by-example.org/hacks/deploy-different-contracts-same-address/ ]]></content></entry><entry><title>blockchain</title><url>/post/blockchain/</url><categories><category>blockchain</category></categories><tags/><content type="html">Blockchain 区块链当前是六层架构:数据层,网络层,共识层,激励层,合约层,应用层
区块链的不可能三角是: 去中心化,安全性和可扩展性
Layer0, 1, 2 Lyaer0
使不同的区块链网络组件能够交流和交换数据。其中代表layerZero
LayerZero
LayerZero是一个omnichain interoperability protocol 设计为light message pass across chains. LayerZero提供 authetic and guaranteed message delivery and 可配置的去信任化.
通过layerZero,直接与网络上的任何其他链执行跨链交易。用户可以自由在链之间移动流动性,允许单个池参与多种去中心化金融的流动性跨不同链和生态系统的应用程序。
LayerZero Endpoint
LayerZero Endpoint是一个面向用户interface, implemented as a series of on-chain smart contracts. An endpoint&rsquo;s purpose is to allow the user to send a message using LayerZero protocol backend, guranteeing for valid delivery.
A LayerZero Endpoint is split into four modules:Communicator, Validator, Network, and Libraries. LayerZeor每支持一条新链需要添加一个额外的library. This design allows us to add support for new chains without modifying the three core modules
Oracle
Oracle是一个第三方服务,read a block header from one chain and send it to another chain
Relayer
Relayer是一个off-chain服务,功能类似Oracle。它拉取一个特定交易的证明
A single LayerZero message
Step1: App A send a request to the LayerZero Communicator containning the following information t: The unique transaction identifier for T dst: A global identifier pointing to a smart contract on chain B payload: Any data that App A wishes to send to App B relayer_args: Arguments describing payment information in the event that App A wishes to use the reference Relayer Step2: The Communicator constructs a LayerZero packet containing dst and payload, referred to as Packet(dst, payload), and sends it along with t and relayer_args, to the Validator. Step3: The Validator sends t and dst to Network. This step notifies Network that the block header for the current block on chainA needs to be sent to chain B Step4: Validator forwards Packet(dst, payload), t and relayer_args to the Relayer, notifying the Relayer that the transaction proof for T needs to be prefetched and eventually sent to chain B. This happends concurrently with Step3 Step5: Network sends dst and the block ID of the current transaction(cur_blk_id) to the Oracle. This notifies the Oracle to fetch the block header for the current block on chain A and send it to chain B. In the event that multiple LayerZero transactions occured in the same block, Step5 is only executed once Step6: Oracle reads the block header(blk_hdr) from chain A = Step7: The Relayer reads the transaction proof associated with transaction T(proof(t)) from chain A, and stores if off-chain. Step6 and Step7 occur asynchronously to each other Step8: The Oracle confirms that the block corresponding to blk_hdr is stably committed on chain A and then sends blk_hdr to Network on chain B.不同的链等待的区块时间不一样 Step9: Network sends the block hash, specified as blk_hdr_hash, to the Validator Step10: The Validator forwards blk_hdr_hash to the Relayer Step11: After receiving blk_hdr_hash, the Relayer sends a list of any Packet(dst, payload), t, proof(t) tuples that match the current block. Step12: The Validator use the received transaction proofs in conjunction with block headers stored by Network to validate whether the associated transaction T is valid and committed. If the do match, then Packet(dst, payload) is sent to the Communicator Step13: The Communicator emits Packet(dst, payload) to App B whitepapaer: https://layerzero.network/pdf/LayerZero_Whitepaper_Release.pdf Layer1
Layer1是一个去中心化的分布式账本. 它使用一个网络节点来维护,验证和记录transactions on the ledger.包含共识机制。
Ethereum
Layer2
Layer2是一个在已存在的区块链上构建的二层框架或协议,主要目标是解决交易可扩展性
由主链提供安全性和去中心化,二层提供可扩展性
Layer2解决方案大致可分为两个部分:一个是负责处理交易的网络;另一个是部署在底层区块链上的智能合约,负责解决任何分歧并将Layer2网络达成的共识传输到底层区块链进行验证
Bitcoin Lighting Network
闪电网络基于状态通道
Polygon
Polygon的前身是Matic Network,它使用侧链进行可扩展,安全和即时的区块链交易,同时使用Plasma框架和分散的权益证明(PoS)验证器网络确保资产安全
Resources The most trusted platform to manage digital assets on Ethereum: https://gnosis-safe.io/#getting-started ECDSA algorithm: https://zhuanlan.zhihu.com/p/97953640 https://medium.com/swlh/create-raw-bitcoin-transaction-and-sign-it-with-golang-96b5e10c30aa</content></entry><entry><title>coolshell</title><url>/post/chenhao/</url><categories><category>system_design</category></categories><tags/><content type="html">coolshell 陈浩老师(左耳朵耗子)文章摘录
我做系统架构的一些原则: https://coolshell.cn/articles/21672.html 从一次经历谈TIME_WAIT: https://coolshell.cn/articles/22263.html ETCD的内存问题: https://coolshell.cn/articles/22242.html “一把梭: REST API全用POST&quot;
如何做一个有质量的技术分享: https://coolshell.cn/articles/21589.html 程序员如何把控自己的职业: https://coolshell.cn/articles/20977.html 计时攻击TIMING ATTACKS: https://coolshell.cn/articles/21003.html 与程序员相关的CPU缓存知识: https://coolshell.cn/articles/20793.html MegaEase的远程工作文化: https://coolshell.cn/articles/20765.html 使用简单的逻辑方法进行独立思考: https://coolshell.cn/articles/20533.html 别让自己&quot;墙&quot;了自己: https://coolshell.cn/articles/20276.html HTTP的前世今生: https://coolshell.cn/articles/19840.html 如何超过大多数人: https://coolshell.cn/articles/19464.html 谈谈我的三观: https://coolshell.cn/articles/19085.html 如何免费的让网站启用HTTPS: https://coolshell.cn/articles/18094.html 我看绩效考核: https://coolshell.cn/articles/17972.html 程序员练级攻略: https://coolshell.cn/articles/18360.html 技术人的发展之路: https://coolshell.cn/articles/17583.html 待续: https://coolshell.cn/page/5</content></entry><entry><title>security audit in solidity</title><url>/post/security_audit_in_solidity/</url><categories><category>blockchain</category></categories><tags><tag>solidity</tag></tags><content type="html">security audit in solidity 一个合约审计,首先是将其编译通过,之后是看其测试覆盖率
再通过静态分析工具sliter</content></entry><entry><title>分布式任务调度</title><url>/post/distributed_scheduler/</url><categories><category>system_design</category></categories><tags><tag>distributed</tag></tags><content type="html">分布式任务调度 Resources 扫盲篇-什么是分布式任务调度: https://cloud.tencent.com/developer/article/1828830</content></entry><entry><title>概率算法</title><url>/post/probability_algorithm/</url><categories><category>algorithm</category></categories><tags/><content type="html">概率算法 一个经典概率算法(用于抽奖等场景) https://www.cnblogs.com/liulangmao/p/4533293.html</content></entry><entry><title>Uniswap</title><url>/post/uniswap/</url><categories><category>blockchain</category></categories><tags><tag>solidity</tag></tags><content type="html">uniswap uniswapv2 resources
https://docs.uniswap.org/protocol/V2/introduction whitepaper: https://uniswap.org/whitepaper.pdf uniswapv2-core
uniswapv2-core 架构图:
uniswapv2-periphery
tips
uniswapv2的Oracle
uniswapv2版本起,就已经提供了预言机功能,用来计算交易对的时间加权平均价(TWAP, Time Weighted Average Price), 但是存在下面几个问题:
v2版本的预言机使用门槛比较高,官方合约中只提供了最新的价格累积值,开发者需要自己实现历史价格的记录和抓取 在使用DEX预言机时,但是在v2版本我们无法通过简单的方式获取交易对深度变化的历史数据 uniswap v2版本中,Pair合约中记录了price0CumulativeLast, price0CumulativeLast两个状态值,分别表示上一次价格更新时,价格的时间累积值,计算公式为: At = P1 + P2 + ... + Pi(i=t) 通过访问不同时间点的价格时间累积值,我们可以计算一个交易对在过去一段时间内的价格平均值。假设当前时间为t1, 5min前的时间为t2,那么可以计算过去5min交易对的平均价格:
Pt2,t1 = (At2 - At1) / (t2- t1) 这会开发者带来一个问题是: 开发者需要自己记录历史时间点中的价格累积值At, 而这些工作是相对复杂的。Github中有人开源通用的 Uniswap v2 Oracle , Uniswap v2官方也给出了一个相对简单的 Oracle 合约实现 , Fei Protocol中使用的Oracle就是参考这个样例实现的.
Router2合约相对Router1合约的更改
getAmountIn()函数计算
# UniswapV2Router01 function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) public pure override returns (uint amountIn) { return UniswapV2Library.getAmountOut(amountOut, reserveIn, reserveOut); } # UniswapV2Router02 function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) public pure virtual override returns (uint amountIn) { return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut); } router1方式导致计算有稍微误差
Router合约exploit
因为合约本身持有erc20,导致某些合约对持有人发放holder_profit时发放在router地址,而不是用户地址
如果pair上有lp,直接调用burn()可以获取对应交易对
通缩和通胀代币对uniswap有一定影响
添加流动性时transfrom(msg.sender, pair, amount) 移除流动性时transfer(to, amount);msg.sender = pair Uniswap router2 Fee事件: https://zhuanlan.zhihu.com/p/403487991 uniswap-v3 https://learnblockchain.cn/article/3055( 有部分未写明)
https://uniswap.org/whitepaper-v3.pdf resources
https://mirror.xyz/adshao.eth/tgZjDXOtL999iuPjXWrolR7Ns1nTZDADA6NLJaJpJJM</content></entry><entry><title>Defence</title><url>/post/defence/</url><categories><category>career</category></categories><tags/><content type="html">Defence T族(技术研发)通道晋升答辩: https://cloud.tencent.com/developer/article/1786861</content></entry><entry><title>Lend and Borrow</title><url>/post/lend_and_borrow/</url><categories><category>blockchain</category></categories><tags/><content type="html">Lend and Borrow 借贷协议方式 NFT借贷协议的3种模式
借贷协议主要解决三个问题: 一是高效地匹配、撮合资金供求双方;二是安全地保管抵押品;三是借款人违约时按约定处置抵押品。现有的Peer-to-Pool模式和Peer-to-Peer模式没能解决好第一个问题,它们的撮合效率都不高,要么隐性资金成本高,要么时间成本高
Peer-to-Pool模式的优点在于时间成本低,能够实现[Instant Borrowing],缺点在于隐性资金成本高且依赖预言机报价。Peer-to-Peer模式的优点在于隐性资金成本低且无需预言机报价,缺点在于时间成本高.
基于Peer-to-Pool模式和Peer-to-Peer模式的前车之鉴,可以设想一种兼顾两者优点的Peer-to-Orderbook模式,可以将抵押品、可贷金额上限和期限相同,但利率不同的订单集中到一个订单簿上,让借贷双方在不同利率水平上竞价并进行撮合,从而降低隐性资金成本和时间成本,实现更高匹配效率。
Peer-to-Pool模式
这一模式主要代表是AAVE, 缺点是资金利用率不高;借贷利率存在较大利差;依赖预言机报价来判断是否达到触发清算的条件。
由于利率曲线的设置,贷款人存入的资金基本不会被全部借出,实际的资金利用率往往低于50%, 而这一问题又进一步导致了借贷利率存在较大利差,因为借款人支付的利息需要分给所有的贷款人。这大大地增加了匹配借贷双方的隐性资金成本。例如,本来贷款人愿意为市场提供 100,000 枚 ETH 的流动性,但借款人实际只愿意借出 50,000 枚(再多将无法承受高利率);本来借款人愿意支付 36% 的年利率,但贷款人平均只能收到 12%。
在匹配资金供求双方时,协议代替贷款人做了决策,贷款人并不能决定哪些是被贷出资金的抵押品,也无法控制贷款的利率和期限。于是乎,为了控制系统的风险、保障贷款人利益,Peer-to-Pool 模式需要引入外部预言机报价,实时确保抵押品能够偿付贷出资金。
然而,由于为 NFT 评估公允价格仍是一大难题,依赖预言机报价的缺点在 NFT 借贷中被放大了。例如,对于不成熟的外部预言机的依赖可能导致协议错误地估计市场中的流动性,为后续清算环节埋下流动性风险的隐患。
综合而言,目前的 Peer-to-Pool 模式并不高效,借贷双方隐含支付的匹配成本较高,且存在依赖预言机的风险,并不是理想的模式。
Peer-to-Peer模式
的确如此,在以 NFTf 为代表的 Peer-to-Peer 模式中,由于接受哪个 NFT 作为抵押品、借贷金额的上限、期限、利率和清算条件与方式等关键参数都是由借款人与贷款人双方共识过的,所以贷款人提供多少资金,借款人就能借出多少资金;借款人支付多高利率,贷款人就能得到多高利率。并且,只要借款人能够在到期日前偿付本息,就不会触发清算,也就不需要依赖预言机。
尽管以 NFTfi 为代表的 Peer-to-Peer 模式解决了 Peer-to-Pool 模式的问题,但这种解决方法也是有所牺牲的,同样不是完美的方案。
Peer-to-Peer 模式的缺点在于,匹配过程的时间较长,借贷双方达成共识往往需要来回好几轮的报价;并且,由于目前尚未支持一个借款人向多个贷款人借款(Peer-to-multiPeer),阻挡了资金体量较小的潜在贷款人进入市场。
Peer-to-Orderbook模式
如果将这些分散的订单集中到一个公开的订单簿上,就能够在保留 Peer-to-Peer 模式优点的前提下,降低匹配的时间成本。因为,在借贷前,双方是在一个 Pool(Orderbook)里寻找对手方,有 Peer-to-Pool 模式的优点;借贷后,实际的借贷关系是精准的、点对点的,也就有 Peer-to-Peer 模式的优点。例如,可以将抵押品、可贷金额上限和期限相同,但利率不同的订单集中到一个订单簿上,让多个贷款人在不同利率水平上提供流动性,借款人则能够随时从订单簿上提取他们愿意接受的资金,实现所谓的「Instant Borrowing」。
借贷协议如何计算利息 https://learnblockchain.cn/article/5036</content></entry><entry><title>Transaction Price mechanism</title><url>/post/transaction_price_mechanism/</url><categories><category>blockchain</category></categories><tags/><content type="html">Transaction Price mechanism eip-155之前rlp编码rlp(nonce, gasprice, startgas, to, value, data);
eip-155之编码rlp(nonce, gasprice, startgas, to, value, data, chainid, 0, 0);
EIP-1559升级后费用机制: https://imtoken.fans/t/topic/40041 Resources EIP-1559: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md EIP-2718: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2718.md https://www.ethereum.cn/Eth1.x/gas-costs-after-berlin EIP-2930: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2930.md EIP-155: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md</content></entry><entry><title>Ethereum</title><url>/post/eth/</url><categories><category>blockchain</category></categories><tags/><content type="html">Ethereum ethereum architecture beacon chain是一个单独的链,它协调所有的sharding chain. 一个beacon chain的block 负责管理1~100的shard block。它需要validator通过pos共识算法给每一个block投票,每12秒会有一个时间同步slot, 每个slot里面会产生一个block. 32个slot组成一个epoch.
在每轮epoch里,validator将会被随机分配到一个committee进行投票。每个slot会随机选择一个proposer做block producer,其它的validator会投票(pos)验证成这个块是否合法。如果超过2/3就同意,否则无法达成共识. 一个validator有可能同时兼具proposer和validator两种角色
为防止共谋发生,每轮epoch中validator只会被随机分配到一个committee里面,每个 committee 不会让他们连续投相邻两个区块的票。
从以上我们可以看出 slot 的作用是给 validator 之间的分布式环境做同步用的,还有就是每个 slot 的分割投票减少 validator 的共谋,每轮 epoch 后 validator 和 committee 会重新进行洗牌投票。
Intro to Ethereum https://ethereum.org/en/developers/docs/intro-to-ethereum/ Accounts https://ethereum.org/en/developers/docs/accounts/ Transactions https://ethereum.org/en/developers/docs/transactions/ Blocks https://ethereum.org/en/developers/docs/blocks/ EVM https://ethereum.org/en/developers/docs/evm/ OPcodes: https://ethereum.org/en/developers/docs/evm/opcodes Gas https://ethereum.org/en/developers/docs/gas/ Ethereum Node 搭建节点
./geth --syncmode full --ws --http --mainnet --datadir &#34;/data/ethereum&#34; --authrpc.jwtsecret /root/tt/jwtsecret ./prysm.sh beacon-chain --execution-endpoint=http://localhost:8551 --jwt-secret=/root/tt/jwtsecret --checkpoint-sync-url https://sync.invis.tools 节点论述
以太坊协议是以太坊节点运行的一组规则,这些节点体现了数千个去中心化应用程序的核心基础设施。
以太坊上的交易由全节点签署,它们将签名的交易广播到验证节点网络,验证节点执行交易(执行层),其资格并达成状态共识(共识层/信标链)。然后,新的商定状态再次存储在具有最新块的完整节点中。
而执行层运行在全球数千台计算机上的执行客户端管理,它们维护以太坊区块链的整体状态,同时还使用evm完成交易。
共识层则是由运行在同一台计算机上的共识客户端管理,它们在数千台计算机之间就以太坊区块链的最新状态达成共识,同时验证其准确性。
以太坊节点分为全节点和验证节点:
全节点
全节点执行以太坊协议的共识规则,这样它们就不会被欺骗接受不遵循它们的区块,节点将会进行:
签署交易 存储完整的区块链数据 验证所有区块和状态 将签名的交易广播到验证者节点 验证节点
验证者节点接收来自全节点的广播交易,然后执行交易,验证其资格并达成状态共识(在共识层)。
由于客户端软件,所有节点仅与协议工作和交互。客户端是以太坊的实现,它根据协议规则验证数据并保证网络安全。以太坊网络中的每个验证者都必须使用这些客户端,无论用户是在家中质押还是通过某种服务,客户端都需要使用以太坊协议并与之交互,它们构成了质押堆栈的第一级。
合并后的以太坊有两部分:执行层和共识层,两个层都运行不同的客户端并扮演特定的角色。
Execution Client
监听网络中广播的新交易,在以太坊虚拟机中执行它们,并保存一个数据库和当前以太坊数据的最新状态。以下是一些主要执行客户端: Besu , Erigon , Geth , Nethermind Consensus Client
实现PoS共识算法,使网络能够根据来自执行客户端的经过验证的数据达成一致。一些客户端: Lighthouse , Lodestar , Nimbus , Prysm , Teku 客户端多样性是建立弹性网络的重要组成部分。要在网络上达到最终确定行,则需要66.6%的验证者。如果客户拥有超过 66.6% 的市场份额并且他们分叉到自己的链上,他们便能够完成。一旦发生分叉并最终确定,验证者将无法返回原始(真实)链,并不会受到惩罚;如果 66.6% 的链同时被 slash,则 slashing 惩罚为 32 枚 ETH。
Smart Contract - https://ethereum.org/en/developers/docs/smart-contracts/formal-verification/ Ethereum client API Data and analytics https://ethereum.org/en/developers/docs/data-and-analytics/ Decentralized Storage https://ethereum.org/en/developers/docs/storage/ Briges https://ethereum.org/en/developers/docs/bridges/ MEV https://ethereum.org/en/developers/docs/mev/ Oracles https://ethereum.org/en/developers/docs/oracles/ Scaling https://ethereum.org/en/developers/docs/scaling/ Optimistic rollups Zero-knowledge rollups State channels Sidechains Plasma Validium 以太坊线路</content></entry><entry><title>MEV</title><url>/post/mev/</url><categories><category>blockchain</category></categories><tags/><content type="html">MEV 论述 最大可提取价值(MEV)是指通过更改或排除一个区块中的交易顺序,可以从超过标准区块奖励和gas费的区块生产中提取的最大值,这个概念最初是由工作证明(PoW)矿工应用的
以太坊合并后,该如何提取MEV?被称为searchers的独立网络参与者将提取大部分MEV,searchers运行算法来检测有利可图的MEV机会,并使用机器人将这些有利可图的交易提交到网络。验证者则会收到此MEV的一部分,因为搜索者支付更高的gas费用(这些费用将交给验证者)以确保他们的交易包含在一个区块中.
MEV机会如何产生的
MEV机会主要来自使用整个DeFi生态系统并与之交互的市场参与者.
清算
借贷协议在DeFi生态系统中非常流行,借贷协议允许用户存入一些抵押品,然后以该抵押品借入资产。该抵押品的价值随着ETH市场价值的变化而波动,如果抵押品的价值低于某个阈值,协议将允许任何人清算抵押品(以偿还借入的资产)。如果头寸被清算,通常会涉及清算人分摊的清算费用&mdash;这就是MEV机会出现的地方,而搜索者则是竞相寻找清算交易并首先提交以赚取清算费用
Dex套利
套利交易是在一个交易所购买Token,然后在另一个交易所以更高价格出售相同Token的行为。由于区块链的机制,用户可以一次性完成此交易,搜索者将监控DEX的价格差异并寻找此类交易的机会。
Sandwich tranding
Sandwich tranding是在DEX套利机会的背后进行的。假设一个大的DEX套利交易被提交到内存池,交易很可能会推高/压低所购买Token的价格。而搜索者将观察内存池并计算该交易对货币对可能产生的影响,如果它可能会大幅推高/下跌价格,它们可以在大宗交易完成之前买入/卖出订单,然后在不久之后出售/购买它,从交易中获利。(不一定需要实际购买,也可以借贷购买)
Flashbots从何而来
MEV-Boost 则是一个开源中间件,验证者可以运行它来进入竞争激烈的区块构建市场。中间件允许验证者从构建者市场访问区块,MEV-boost 将简单地插入用户的共识客户端,允许其外包专门的区块构建,而无需了解它如何工作的技术细节,这些构建者生成的区块包含交易订单流和区块提议验证者的费用。
此外,Flashbots 团队发现,将提议者与区块构建者的角色分开可以促进以太坊的更大竞争、去中心化和审查阻力。此外,一些搜索者还会
观察内存池(交易所在的位置)以获取有利可图的交易; 复制可能有利可图的交易并用他们自己的地址替换; 一旦领跑者确认交易确实有利可图,他们就会提高交易的gas价格以领先于原始交易并获取原始搜索者的MEV 作为回应,便出现了新的服务。Flashbots是一个独立项目,它通过一项服务扩展了Go-ethereum客户端,该服务允许搜索者向矿工提交MEV交易,而无需向公共内存池透露交易,这解决了领先者造成的问题,并有助于降低高昂的gas价格
MEV-Boost 则是一个开源中间件,验证者可以运行它来进入竞争激烈的区块构建市场。中间件允许验证者从构建者市场访问区块,MEV-boost 将简单地插入用户的共识客户端,允许其外包专门的区块构建,而无需了解它如何工作的技术细节,这些构建者生成的区块包含交易订单流和区块提议验证者的费用。
此外,Flashbots 团队发现,将提议者与区块构建者的角色分开可以促进以太坊的更大竞争、去中心化和审查阻力。
防止MEV Resources MEV探索: https://foresightnews.pro/article/detail/10426 https://medium.com/@alexeuler/a-novel-approach-for-onchain-mev-protection-using-uniswap-82a54e83c6fb https://medium.com/@solidquant/what-i-learned-from-a-month-of-intensive-mev-bot-study-38a4e357da0b</content></entry><entry><title>Layer2</title><url>/post/layer2/</url><categories><category>blockchain</category></categories><tags/><content type="html"><![CDATA[Layer2 Layer2技术主要目的是扩展去快乐的性能,同时保留分布式协议的去中心化优势
闪电网络
闪电网络的主要思路十分简单–将大量交易放到比特币区块链之外进行,只把关键环节放到链上进行确认。闪电网络主要通过引入智能合约的思想来完善链下的交易渠道。核心概念有两个:RSMC(Recoverable Sequence Maturity Contract) 和 HTLC (Hashed Timelock Contract)前者解决了链下>交易的确认问题,后者解决了支付通道的问题。
RSMC
RSMC类似资金池机制。首先假定交易双方存在一个"微支付通道"(资金池).交易双方先预存一部分资金到"微支付通道"里,初始情况下双方的>分配方案等于预存的金额。每次发生交易,需要对交易后产生资金分配方案等于预存的金额。每次发生交易,需要对交易后产生资金分配结果共同进行确认,同时签字把旧版本分配方案作废掉,任何一方需要提现时,可以将他手里双方签署过的交易结果写到区块链网络中,从而被确认。从这个过程中可以看到,只有在提现时候才需要通过区块链。
任何一个版本的方案都需要经过双方的签名认证才合法,任何一方在任何时候都可以提出提现,提现时需要提供一个双方都签名的资金分配方
RSMC交易结构
https://blocking.net/1516/bitcoin-lightning-network-rsmc/ HTLC
微支付通道是通过 Hashed Timelock Contract 来确定的,这个功能就是限时转账,理解起来也很简单,通过智能合约,双方约定转账方先冻结一笔钱,并提供一个哈希值,如果在一定时间内有人能提出一个字符串,使得它哈希后的值跟已知值匹配(实际上意味着转账方授权了接收方来提现),则这笔钱转给接收方。
推广一步,甲想转账给丙,丙先发给甲一个哈希值。甲可以先跟乙签订一个合同,如果你在一定时间内能告诉我一个暗语,我就给你多少钱。乙于是跑去跟丙签订一个合同,如果你告诉我那个暗语,我就给你多少钱。丙于是告诉乙暗语,拿到乙的钱,乙又从甲拿到钱。最终达到结果是甲转账给丙。这样甲和丙之间似乎构成了一条完整的虚拟的“支付通道”。
HTLC 机制可以扩展到多个人的场景
RSMC保障了两个人之间的直接交易可以在链下完成,HTLC保障了任意两人之间的转账都可以通过一条“支付”通道来完成。闪电网络整合这两种机制,就可以实现任意两个人之间的交易都在链下完成了。
侧链
侧链协议允许资产在比特币区块链和其他区块链之间互转,这一项目也来自比特币社区。侧链可以是一个独立的区块链,有自己的按需定制的账本、共识机制、交易类型、脚本和合约的支持等。
SPV(Simplified Payment Verification)可以实现这一点.SPV能够以较小的代价判断某个支付交易是否已经被验证过,以及得到了多少算力保护(定位包含该交易的区块在区块链中的位置)。SPV客户端只需要下载所有区块的区块头,并进行简单的定位和计算工作就可以给出验证结论
侧链协议中,用SPV来证明一个交易确实已经在区块中发生过,称为SPV证明,一个SPV证明包括两部分内容:一组区块头列表,表示工作量证明;一个特定输出(output)确实存在于某个区块中的密码学证明
Plasma
侧链是独立运行的,如果侧链上出现问题,会导致侧链执行交易有误,返回给layer1的信息也是虚假的,安全性不够. plasma不是完全被托管的,它使用了欺诈证明的退出机制,当检测到plasma链上出现错误时,用户可以安全的从plasma链上退出,因此plasma拥有更高的安全性.
plasma的安全性比较强,但是它给layer1返回的仅有交易结果,没有交易信息。而rollup则是同时返回交易结果和交易的信息,并将这些数据进行压缩后返回给layer2.
rollup
Optimistic and Zero-Knowledge(ZK), dozens and dozens of transactions are recorded on the layer 2 chain, then “rolled up” into a single transaction that is then fed back to the more expensive, slower blockchain. By doing this, the cost of that one transaction is split across lots of users.
Optimistic Rollup 靠的是欺詐證明,zk-Rollup 靠的是有效性證明)。
Optimistic rollups
The first variety is called an Optimistic rollup. This is because it optimistically assumes that all the transactions contained within a rollup are valid. Optimistic rollups give everyone on the network a certain amount of time, usually a week, to contest fraudulent transactions. The benefit of the Optimistic rollup is that it’s quick; by assuming things are correct, the network doesn’t have to waste time confirming things. The drawback is that it usually takes about a week to officially withdraw your funds from popular networks like Optimism or Arbirtrum.
ZK-rollup
The second kind of rollup is a Zero-Knowledge rollup, also known as a ZK-rollup. These protocols use a complex piece of cryptography called a Zero-Knowledge proof to determine that a transaction is valid using only minimal information about that transaction. It’s privacy-preserving, sleek and, most important, fast and cheap. Compared with an Optimistic rollup, which requires funds to stay on the network until the dispute resolution period has closed, ZK-rollups allow users to withdraw their funds with less of a delay.
https://www.coindesk.com/learn/what-are-rollups-zk-rollups-and-optimistic-rollups-explained/ optimistic-rollup: https://ethereum.org/en/developers/docs/scaling/optimistic-rollups/ zk-rollup: https://ethereum.org/en/developers/docs/scaling/zk-rollups rollup: https://www.8btc.com/article/6734992 分片
进行中 一文理清layer2和跨链方式: https://learnblockchain.cn/article/2603 ]]></content></entry><entry><title>Randao_reveal</title><url>/post/randao_reveal/</url><categories><category>blockchain</category></categories><tags/><content type="html">Randao_reveal block header介绍
Mixhash is a hash of the index, nonce, content, and previous node’s hash. This is unique, non-reversible, and of fixed length. It serves to encrypt the data of a particular block.
Nonce stands for the Number used only once. It is a unique whole number that shall be used only once in the blockchain. The purpose of this number is to make the hash of the current cell conform to a certain standard as is set by the blockchain. The mining of the nonce represents the Proof of Work that miners display in order to get the block reward.
Used in tandem, these values are instrumental in providing the resistance to the mutation that the concept of blockchain is known for.
Randomness and RANDAO: https://twitter.com/SalomonCrypto/status/1576951211659390981 BLS签名: https://learnblockchain.cn/2019/08/29/bls Resources eip-4399: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4399.md randomness-blockchain: https://soliditydeveloper.com/2019-06-23-randomness-blockchain</content></entry><entry><title>POS</title><url>/post/pos/</url><categories><category>blockchain</category></categories><tags/><content type="html">POS POW的出块属性是无过程性的,不论试多少次,它都不会影响挖出下个块的概率。下一个块的出块者,还是需要经历大量的计算。影响当前这个区块的哈希值,对下一个区块的区块难度实际上没有任何帮助。
但在PoS里,大家都要使用以往历史的区块来作为随机数源头,也就用一个随机数的源头来生产一个随机数,用这个随机数来决定下一个出块是由谁出,或者下十个块分别由谁出,它就损失了无过程性的特点。
这意味着什么?意味着当前谁挖出了这个区块,挖出了这个区块数据上的特征,就可以决定接下来一个块甚至十个块的出块者。
那么大家会做一个事情是什么?是虽然我在这个时间点,比如本来这十秒钟之内只能有一块,但其实我自己在偷偷挖,已经算了 100 个区块,我就看哪个区块能够让我在下一个区块依然是出块者,或者是找哪一个区块,能够最大化我在接下来链条当中出块的机会,这就叫做权益研磨攻击。它会不断尝试在任何一个区块点位上挖矿,并且试图干预未来出块者的选择
权益磨损攻击通过惩罚机制来解决。Casper惩罚机制是凡出现了一些在检查点投票当中的不轨行为,包括双重投票和环绕投票,具体来说就是你会不会投两个相互竞争的检查点,以及在投票检查点的时候有没有跳到另一条分叉链上。当整条链没有办法达成共识时候,它会惩罚所有参与者
Long range attack
Casper算法其实有两个部分,一个部分被称之为分叉选择规则,分叉选择规则是基于这些我们所谓的见证者去发送对区块的签名,叫attestation,另一部分是所谓的Casper FFG,即通过Casper的投票去不断刷新检查点.它的安全性不基于时间同步的假设,即时间上异步也是可以的,而持续出块的活性基于所谓的半同步假设,也就是说通信的时延是有上限的(Balancing Attach)</content></entry><entry><title>Optimizer in solidity</title><url>/post/optimizer_in_solidity/</url><categories><category>blockchain</category></categories><tags><tag>solidity</tag></tags><content type="html"><![CDATA[Optimizer in solidity 合约设计及实现变迁
v1.x版本实现,每次创建同样的合约
v2.x版本实现, 逻辑单独抽象成library,每次创建合约仅包含storage variables
v3.x版本实现, 分为代理合约和逻辑合约,每次创建代理合约(不包含逻辑和storage variables)
summary: 如果某个合约需要部署很多次,可以采用library或proxy来部署合约,每个合约仅是初始化值不同,采用proxy模式更好
contract Table is Proxy { bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; constructor( address _implAddr ) { _setImplementation(_implAddr); } function _implementation() internal view override returns (address) { return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; } function _setImplementation(address newImplementation) private { require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; } } constract Logic { } contract Table { using TableLogic for TableLogic.TableInfo; TableLogic.TableInfo tableInfo; } library TableLogic { } solidity中的optimizer
大部分论述来自 https://docs.soliditylang.org/en/v0.8.17/internals/optimizer.html solidity使用两种编译器:The opcode-based optimizer 和 The Yul-based optimizer.
solidity产生EVM bytecode有两种方式: solidity语言直接翻译成evm opcode, 或先翻译成Yul后再翻译成evm opcode(IR-based code generator).
The IR-based code generator不仅更透明和可审计的,而且可以跨函数优化code。同样的solidity代码可能产生不同的语义变化( https://docs.soliditylang.org/en/v0.8.17/ir-breaking-changes.html) .
The opcode-based optimizer
这个优化器主要是简化相同的代码,删除无用的代码. 简化规则参考( https://github.com/ethereum/solidity/blob/develop/libevmasm/RuleList.h )
The Yul-based optimizer
优化组件
SSA Transform Common Subexpression Eliminator Expression Simplifier Redundant Assign Eliminator Full Inliner Optimization Step Sequence: https://docs.soliditylang.org/en/v0.8.17/yul.html#optimization-step-sequence Optimizer Parameter Runs
这个参数指定了部署代码在合约生命周期内执行的频率,是code size(deploy cost)和code execution(cost after deployment)的权衡
参数范围: [1, 2^32-1]
使用编译器配置
"optimizer": { // Disabled by default. // NOTE: enabled=false still leaves some optimizations on. See comments below. // WARNING: Before version 0.8.6 omitting the 'enabled' key was not equivalent to setting // it to false and would actually disable all the optimizations. "enabled": true, // Optimize for how many times you intend to run the code. // Lower values will optimize more for initial deployment cost, higher // values will optimize more for high-frequency usage. "runs": 200, // Switch optimizer components on or off in detail. // The "enabled" switch above provides two defaults which can be // tweaked here. If "details" is given, "enabled" can be omitted. "details": { // The peephole optimizer is always on if no details are given, // use details to switch it off. "peephole": true, // The inliner is always on if no details are given, // use details to switch it off. "inliner": true, // The unused jumpdest remover is always on if no details are given, // use details to switch it off. "jumpdestRemover": true, // Sometimes re-orders literals in commutative operations. "orderLiterals": false, // Removes duplicate code blocks "deduplicate": false, // Common subexpression elimination, this is the most complicated step but // can also provide the largest gain. "cse": false, // Optimize representation of literal numbers and strings in code. "constantOptimizer": false, // The new Yul optimizer. Mostly operates on the code of ABI coder v2 // and inline assembly. // It is activated together with the global optimizer setting // and can be deactivated here. // Before Solidity 0.6.0 it had to be activated through this switch. "yul": false, // Tuning options for the Yul optimizer. "yulDetails": { // Improve allocation of stack slots for variables, can free up stack slots early. // Activated by default if the Yul optimizer is activated. "stackAllocation": true, // Select optimization steps to be applied. // Optional, the optimizer will use the default sequence if omitted. "optimizerSteps": "dhfoDgvulfnTUtnIf..." } } } Peephole optimization: https://en.wikipedia.org/wiki/Peephole_optimization resources gas calculate: https://github.com/wolflo/evm-opcodes/blob/main/gas.md gas optimization in solidity: https://yamenmerhi.medium.com/gas-optimization-in-solidity-75945e12322f ]]></content></entry><entry><title>library in solidity</title><url>/post/library_in_solidity/</url><categories><category>blockchain</category></categories><tags><tag>solidity</tag></tags><content type="html">this变量 library MathLib { function multiply(uint a, uint b) public view returns (uint, address) { return (a * b, address(this)); } } contract Example { using MathLib for uint; address public owner = address(this); address public retAddr; function multiplyExample(uint _a, uint _b) public view returns (uint, address) { uint a; (a, retAddr) = _a.multiply(_b); return (a, retAddr); } } resource https://jeancvllr.medium.com/solidity-tutorial-all-about-libraries-762e5a3692f9</content></entry><entry><title>Protocol</title><url>/post/solidity_protocol/</url><categories><category>blockchain</category></categories><tags><tag>solidity</tag></tags><content type="html"><![CDATA[Protocol Permit2 Signature based transfer use ordered, non-monotonic nonces so that signed permits do not need to be transacted in any particular order.
PermitTransferFrom结构体中amount和SignatureTransferDetails结构体中requestAmount设计,为啥需要两个amount。是因为signature只验证PermitTransferFrom.
Custome Witness Data
Batch Transfer
Smart Nonce
Permit2 support erc1271
eth_signTypedData rpc接口
Uniswapv2 uniswapv2是一个AMM DEX协议. 用作两种ERC20代币的交换.
how uniswap works
UniswapPool LP Trader Other(Flash Swaps and Oracle) 价格定义
Smart Contracts
Core
Factory
address public feeTo; address public feeToSetter; mapping(address => mapping(address => address)) public getPair; address[] public allPairs; Pair
mint
uint balance0 = IERC20(token0).balanceOf(address(this)); uint amount0 = balance0.sub(_reserve0); bool feeOn = _mintFee(_reserve0, _reserve1); liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1); _mint(to, liquidity); MINIMUM_LIQUIDITY: 通过累积交易费用或通过向流动性池捐赠,理论上会导致最小流动性份额的value很大,以至于小流动性提供者无法提供任何流动性. 通过增加MINIMUM_LIQUIDITY, 对于任何token pair是微不足道的, 但它极大阻止了上述情况。为了使流动性份额value增加100, 攻击者需要向流动性池捐赠100*1000 = 100,000,它将作为流动性被永久锁定.
fee收取: 对$\sqrt{K}$值变动收取费用.(0.3%, 0.25%, 0.05%, 1/6).
oracle: 算术平均数, uint32 timeElapsed = blockTimestamp - blockTimestampLast;
burn
burn是先将burn数值计算出来,然后再进行实际的burn, trnasfer等操作
swap
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); balance0 = IERC20(_token0).balanceOf(address(this)); balance1 = IERC20(_token1).balanceOf(address(this)); sync()
sync()函数是一个恢复机制,用来重新调整交易对价格。 reserve=balanceOf(this)
skim()
skim()也是一个恢复机制,将balance-reserve部分发送给某个地址
Periphery
router02
factory() and WETH()
addLiquidity and removeLiquidity
addLiquidity
(amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin); address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA); TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB); liquidity = IUniswapV2Pair(pair).mint(to); (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) { IUniswapV2Factory(factory).createPair(tokenA, tokenB); } (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB); if (reserveA == 0 && reserveB == 0) { (amountA, amountB) = (amountADesired, amountBDesired); } else { uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB); if (amountBOptimal <= amountBDesired) { require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT'); (amountA, amountB) = (amountADesired, amountBOptimal); } else { uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA); assert(amountAOptimal <= amountADesired); require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT'); (amountA, amountB) = (amountAOptimal, amountBDesired); } } permit 移除流动性
FeeOnTransfer
添加流动性没有FeeOn是因为mint的计算是按照pair合约收到的token来计算
移除流动性amountToken计算按照balanceOf(router/this)计算
swap()
swapExactTokensForTokens(), swapTokensForExactTokens().
getAmountOut()
$\frac {amountIn}{amountOut} = \frac{reserveIn}{reserveOut}$ $ 997 * amountIn = \frac{reserveIn * amountOut * 1000}{reserveOut}$ $ amountOut = \frac{997 * amountIn * reserveOut}{reserveIn * 1000}$ $ amountOut = \frac{997 * amountIn * reserveOut}{reserveIn * 1000 + 997*amountIn}$ getAmountIn()
$\frac {amountIn}{amountOut} = \frac{reserveIn}{reserveOut}$
$ 997 * amountIn = \frac{reserveIn * amountOut * 1000}{reserveOut}$
$ amountIn = \frac{reserveIn * amountOut * 1000}{reserveOut*997}$
$ amountIn = \frac{reserveIn * amountOut * 1000}{(reserveOut - amountOut)*997}$
swap
amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path); require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, to); FeeOn
TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn ); uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to); _swapSupportingFeeOnTransferTokens(path, to); require( IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT' ); 计算amountOutput时机不一样
{ // scope to avoid stack too deep errors (uint reserve0, uint reserve1,) = pair.getReserves(); (uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0); amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput); amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput); } (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0)); address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to; pair.swap(amount0Out, amount1Out, to, new bytes(0)); Other
使用safeTransfer而不是IERC20的原因
如果sucess=false, 调用失败revert 如果sucess=true且没有返回数据,调用成功 如果success=true但有数据返回(数据类型解析成bool) 数据解析为true, 调用成功 数据解析为false, 调用失败 Flash swap
``` function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external; ``` EIP-3156(Flash Loans): https://eips.ethereum.org/EIPS/eip-3156 借贷callback参数: - msg.sender: 作为认证使用 - token: 告诉借贷的币种 - amount: 借贷的数量 - fee: 借贷手续费 - data: 闪电贷中执行的其它操作 Oralce
算术平均数 A = $ \frac{A_1+A_2+…+A_n}{n} $, 当数据结果是一个和时。
几何平均数 A = $ \sqrt[n]{A_1 * A_2 * … * A_n} $, 当数据结果是一个积时
算术平均数 > 几何平均数
[t1,t2]之间的价格平均数为$P_{t1,t2} = \frac {(A_{t2} - A_{t1})}{(t_2 - t_1)}$
本身交易造成的滑点与池子流动性关系
x = 100, y = 1000 , k = 100,000; x = 10, y = 100, k = 1,000
使用x = 1兑换y, 计算兑换后x的价格 x_before = 1000/100 = 10, x_after = 990.099 / 101 = 9.80296; x_before = 100/10 = 10, x_after = 90.90909 / 11 = 8.264;
显然,池子流动性越大,当前交易造成的滑点越小. 在稳定币交易中,期望在一定范围内滑点趋向于0.
当滑点=0, 一笔交易对当前价格无影响. 价格 = y/x, 即斜率k不变. 斜率k不变的公式有x+y=k.
价格影响
价格滑点是指外部大盘走势引起的价格变化(与当前交易无关),价格影响是指当前交易对池子中价格影响`
Poo info
USDC = 2,000,000 ETH = 1,000 constant = 2,000,000,000 price = 2,000 10,000 USDC for ETH
USDC = 2,010,000 constant = 2,000,000,000 ETH = 995.024(constat / USDC) ETH receive = 4.976 price = 2009.64 price impact = 0.48%
无常损失
impermanent_loss = 2 * sqrt(price_ratio) / (1+price_ratio) -1; prce_ratio > 1
Curve Curve is an exchange liquidity pool on Ethereum. Curve is designed for extremely efficient stablecoin trading and low risk supplemental fee income for liquidity providers, without an opportunity cost.
curve曲线
Curve引入一因子X, 用来调整恒定总和等式影响占比, 值越大,则曲线更加倾向于零滑点直线;值越小,则曲线更加倾向于高滑点的恒定乘积曲线
池子分类
Plain Pools: 两个或多个稳定币交易流动性池,用于交易的流动性池, 目前3CRV和其他稳定币的池子 Lending Pools: 这个交易池中,对原生的代币进行了一个再包装(wrapped token),例如用户质押的是DAI,但流动性池里是cDAI,这样做的目的是为了可以将流动性池中的代币再借贷出去赚取利润,而用于交易的流动性池中实际保存的是wrapped token. Meta Pools: 与另外一个流动性池中的流动性代币(LP)组成的流动性池,这样做的目的是为了给用户重新质押的机会,例如用户质押DAI到一个Plain pool获得LP,然后又将LP质押到metapool继续赚取收益,就像是买了一个债券,然后又用另一个债券换了一个理财. veCRV机制
veCRV = CRV * T/4, 质押CRV的时间越长,收到的veCRV就越多, 选择CRV锁仓4年才能获得1:1的veCRV。该质押锁仓行为不可逆转且veCRV不可流通 `
收取Curve协议50%的交易手续费,收取手续费比例按照veCRV占比均分 vote Power. veCRV的投票权机制非常简单,投票权跟锁仓时间成正比,投票权会随着时间衰退的, 所以如果为了保持足够的投票权要一直刷新锁仓的时间 Boost机制, 流动性挖矿的基数只有40%, 通过足够量级的veCRV,才能获得boost倍速增加,最高2.5x,是一个线性增加机制. Convex: 解决收益和流动性矛盾
Convex协议解决的是crv散户无法获取加速的事情.
Convex的协议token是CVX, 最大供应量1亿, 其中50%分配给Convex平台LP, 25%向CVX/ETH, cvxCRV/CRV提供流动性挖矿奖励(用户质押CRV获取cvxCRV, 1:1).
Convex本身像一个大银行,用户把钱存在这里,获取比Curve高一点的收益. Convex本身把所有的CRV都锁了四年.
Convex模式之所以可以良好的运行,本质上无法脱离CVX的价格支撑,因为一旦CVX价格过低,将导致收益率下降。所以Convex需要一些措施来减少CVX与cvxCRV的流通盘–Convex收取加速协议收益的16%作为平台费,并以此为基础,激励CVX与cvxCRV的持有者质押手中的token.
veCRV资金量少的散户无法获取足够的veCRV用以提高流动性池收益.使得用户不需要锁仓CRV即可享受加速后的收益.
Cow Protocol Cow Protocol is fully permissionless trading protocol that leverages Batch Auctions as its price finding mechanism.
cow协议和其它AMM交易不一样的是, 用户提交一笔交易是off-chain交易,这笔交易进入一个私有池,由solver去进行提交到链上.
solver通过对链下交易撮合或者从链上dex拿流动性,批量拍卖来减少用户的gas消耗和滑点
Cow Protocol use batch auctions as a core mechanism to facilitate CoWs.
cow面向用户有两种交易方式,一种是swap;一种是limit(price, expiration)
Benefits of Cow Protocol
First implementation of batch auctions promoting fair uniform clearing prices; Trades are protected from different sorts of MEV Cow协议中一笔完整的交易流程
Users need to approve the contract allowance manager to enable trading for a desired token. Users can place limit sell/buy orders off-chain at any time by simply signing a message that contains their trade details. Users don’t pay a gas fee for posting and canceling orders. Off-chain orders are picked up by the solvers who settle them in a batch auction The protocol select the solvers order settlement solution that maximize trader welfare the most and provides the best clearing prices for the orders in that batch All matched orders within a batch can either be settled off-chain via the liquidity found in the coincidence of wants(CoWs) across orders, or on-chain against AMM/aggregator liquidity. Batch Auctions
The two main reasons behind our development of batch auctions into a trading mechanism are:
Give the DeFi space in Ethereum a chance to establish the same price of any token pair in the same block. Improve the DEX trading price offering by combining new economic mechanisms together such as Uniform Clearing Prices and Coincidence of Wants. Batch Auctions allow the trades within a batch to have the same uniform clearing price.
Velodrome velodrome采用ve(3,3)模式: https://news.marsbit.co/20230508164914069116.html Uniswapv3 Compound Compound III is an EVM compatible protocol that enables supplying of crypto assets as collateral in order to borrow the base asset. Accounts can also earn interest by supplying the base asset to the protocol. The initial deployment of Compound III is on Ethereum and the base asset is USDC. --- 这几个如何计算 Oracle Price Collateral Factor Liquidation Factor Compound池子资产隔离吗?借贷利率如何计算,利息如何计算 --- CompoundII
// todo // 抵押借稳定币,稳定币哪里来
Compound是一个借贷协议,pool to peer。协议基于a floating interest rate. 每一个借贷市场是一个ERC20 token.
Supplying Tokens
不像p2p借贷,需要用户之间进行匹配和利率协商。Compound协议聚合the suply of each user;当用户supplies a token,它变成一个fungible resources.用户可以随时进行提现.
balances在a money market自然积累利率, 用户可以实时查看balance; update balance in (supply, transfer, withdraw).
Borrowing Tokens
Compound允许用户无摩擦贷款从协议中, using collateralized line of credit.
collateral ratio = user supply / user borrow
用户可以全部返还资产或部分返还资产.
Risk & Liquidation
当用户CR低于某一位置,用户的抵押品(supply)可以被purchase.
清算会有激励给予, 减少协议风险. (清算是全部清算还是部分清算)
Use Case
获取被动收入: 满足长期持有数字资产又希望能够产生额外收益的 通过贷eth用户可以为ICO融资 交易者看空某一个token, 融券卖出 Leger System
Cash_a + Borrows_a = Supply_a + Equity_a
Cash_a : 池子中余额
资金利用率 = Borrow_a / Cash_a + Borroow_a
贷款利率 = 10% + Ua*30%
supply interest Rate_a = 贷款利率 * U_a * (1-S), S 是 代表协议的经济利润
The history of each interest rate, for money market, is captured by Interest Rate Index
Index_a,n = Index_a,n-1 * (1 + r * t)
一个账户拥有的存储变量是(uint256 balance, uint256 index). global index. 每次发生一笔交易时, supply rate 和 borrow rate发生更新
从产品层面的问题
借贷逻辑如何实现?借款利率和存款利率计算?利息如何计算和分配?如何清算?存取款合约接口?展示的数据从哪里查询?
supply, 我要知道supply的利率,当前CR率,资金利用率, 协议费
池子是否隔离(单币资产在一个池,多币资产在同一池)
合约
COMP
一个COMP代币合约,具备delegate功能
Timelock
一个执行交易合约
Comptroller
启用抵押: Enter Market(address[]) eth:0xc2998238000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000004ddc2d193948926d02f9b1fe9e1daa0718270ed5 (CETH)
dai: 0xc2998238000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000005d3a536e4d6dbd6114cc1ead35777bab948e3643 (CDAI)
CToken
Governanace
AAVE Seaport Aggregator Universal Router
The Universal Router is an ETH, ERC20, and NFT swap router.
The flexible command stytle allows us to provide users with:
splitting and interleaving of Uniswap trades Purchases of NFTs across 8 marketplaces Partial fills of trades Wrapping and Uniswapping of ETH Time-bound, signature controlled token approvals using Permit2 Resources permit2
https://github.com/dragonfly-xyz/useful-solidity-patterns/tree/main/patterns/permit2 https://github.com/Uniswap/permit2 permit2: https://learnblockchain.cn/article/5161 uniswapv2
https://uniswap.org/whitepaper.pdf https://docs.uniswap.org/contracts/v2/concepts/advanced-topics/understanding-returns Curve
https://mirror.xyz/0x54c5Bd849c170f3Cbd5618D6f7B316B38f5729C4/2p6X1DoNJWPxf8w9D8pYBTz2eo-zefSI1M1ZeMviENY Cow
https://docs.cow.fi/ Compound
https://github.com/compound-finance/compound-money-market/blob/master/docs/CompoundWhitepaper.pdf https://docs.compound.finance/v2/ 剖析DeFi借贷产品之Compound-概述篇: https://learnblockchain.cn/article/2593 ]]></content></entry><entry><title>Go mod</title><url>/post/go_mod/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html"><![CDATA[Resource go mod使用: https://www.jianshu.com/p/760c97ff644c https://liujiacai.net/blog/2019/10/24/go-modules/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io 发展历史 GOPATH
go env -w GO111MODULE="off" GOROOT=/usr/local/go GOPATH=/home/dev/project/go /* 按如下路径+导包路径寻找所需要的包 /usr/local/go/src/ */ go build main.go go编译项目时,只会在GOROOT和GOPATH下找包,这限制了项目只能在GOPATH路径下(一般不放GOROOT).而且所有项目共用一份包,导致某些包需要升级后导致其它包引用失败;对于多个项目,只能引用某个包的同一版本
vendor
vendor将引用的包放在项目自身路径下,使得多个项目都有自己单独维护的包。项目只能在GOPATH路径下,但会在vendor目录下找包
mod
项目不再依赖GOPATH
生产中mod的运用 mode文件介绍
module gotest go 1.13 replace { } require ( ) 导入同一个项目内部包
import gotest/models 导入外部项目本地包
一般来说,GO111MODULE=on情况下,go get会将包下载在$GOPATH/src/pkg/mod下
import 后面是路径字符串,前缀路径需要mod文件;
每个导入路径中只允许出现一个package name;
import "github.com/mod/cc" internal包不能被导出
v2特性
多版本共存同一项目
]]></content></entry><entry><title>Design pattern</title><url>/post/design_pattern/</url><categories><category>system_design</category></categories><tags/><content type="html"><![CDATA[设计原则SOLID Single Responsibility Principle
表明一个类只具备一个职责
Open Close Principle
一个类应该对扩展开发,修改关闭
//设计一个关于api请求数相关告警类 public class Alert { private AlertRule rule; private Notification notification; public Alert(AlertRule rule, Notification notification) { this.rule = rule; this.notification = notification; } public void check(String api, long requestCount, long errorCount, long durationSeconds) { long tps = requestCount / durationSeconds; if (tps > rule.getMatchedRule(api).getMaxTps()) { notification.notify("...") } if (errorCount > rule.getMatchedRule(api).getMaxErrorCount())) { notification.notify("...") } } } 如果我们需要添加一个功能,当每秒钟接口超时请求个数,超过某个阈值时,也发送告警通知。那么如何改动代码
public class Alert { private AlertRule rule; private Notification notification; public Alert(AlertRule rule, Notification notification) { this.rule = rule; this.notification = notification; } public void check(String api, long requestCount, long errorCount, long timeoutCount, long durationSeconds) { long tps = requestCount / durationSeconds; if (tps > rule.getMatchedRule(api).getMaxTps()) { notification.notify("...") } if (errorCount > rule.getMatchedRule(api).getMaxErrorCount())) { notification.notify("...") } long timeoutTps = timeoutCount / durationOfSeconds; if (timeoutTps > rule.getMatchedRule(api).getMaxTimeoutTps()) { notification.notify("...") } } } 这样修改意味着调用这个接口的代码都做相应的修改,另一方面,修改了check函数相应的单元测试也需要修改
//新的修改方式 public class Alert { private List<AlertHandler> alertHandlers = new ArrayList<>(); public void addAlertHandler(AlertHandler alertHandler) { this.alertHandlers.add(alertHanlder); } public void check(ApiStatInfo apiStatInfo) { handler.check(apiStatInfo) } } public abstract class AlertHandler { AlertRule rule; Notification notification; public abstract void check(ApiStatInfo apiStatInfo); } 在讲具体的方法论之前,我们先来看一些更加偏向顶层的指导思想。为了尽量写出扩展性好的代码,我们要时刻具备扩展意识、抽象意识、封装意识。这些"潜意识"可能比任何开发技巧都重要。在试别出代码可变部分和不可变部分之后,我们要将可变部分封装起来,隔离变化,提供抽象化的不可变接口,给上层系统使用。当具体的实现发生变化时,我们只需要基于相同的抽象接口,扩展一个新的实现,替换掉老的实现即可
开闭原则不是免费的。有些情况下,代码的扩展性和可读性是存在冲突的。
L Substitution Principle
将一个基类对象替换成它的子类对象,程序将不会产生错误和异常,反过来则不成立。这要求子类不要重写基类函数
https://www.cnblogs.com/maaa/p/15670303.html Interface Segregation Principle
类不应该被迫依赖他们不使用的方法
Dependency Inversion Principle
高层模块不应该依赖底层模块。在类之间存在依赖关系的情况下,应使用抽象来定义他们,而不是直接引用类,这减少了由较低级别模块的变化导致高层的错误
设计模式 创建型 解决对象的创建问题
单例模式
//静态创建 InitObject() //动态创建 GetObject() 工厂模式
工厂模式是一种实例化对象模式。在当前面向对象编程中,类是对现实世界中某种事物的描述,工厂是对类进行一个组装,产生另外一种产品的设计方法
结构型 解决对象组装的我呢提
代理模式
代理模式为其他对象提供一种代理以控制这个对象的访问,在客户端和目标对象之间起到中介的作用
在不改变代码的情况下,通过引入代理类来给原始类附加功能。一般应用于框架代码和业务代码解耦
装饰者模式
适配器模式
适配器、代理和装饰都是通过组合/集成一个现存对象、通过调用该对象的方法来实现自己的功能,它们之间很相像。但从意图上来分析,代理模式着重将复杂部分抽到中间层来控制目标对象的访问,它要求代理层和目标对象接口相同;而适配器解决的是接口之间不能正常工作的问题;装饰模式强调动态扩展对象功能
行为型 解决对象之间交互的问题
观察者模式
观察者模式包含两个角色: 观察者和被观察对象.观察者行为根据被观察对象而改变
策略模式
职责链模式
状态模式
无用的设计模式 https://tech.youzan.com/useless-design-pattern/ 关于面向对象和面向过程的区别阐述 https://baijiahao.baidu.com/s?id=1726875692262887029 ]]></content></entry><entry><title>Prometheus</title><url>/post/prometheus/</url><categories><category>components</category></categories><tags/><content type="html">Resource prometheus docs: https://prometheus.io/docs/introduction/overview/ pushgateway数据上报: https://www.cnblogs.com/xiaobaozi-95/p/10684524.html</content></entry><entry><title>mem alloc in golang</title><url>/post/mem_alloc_in_golang/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html">memory allocate in golang Resource https://tonybai.com/2020/03/10/visualizing-memory-management-in-golang/ Golang什么时候会触发GC: https://www.jianshu.com/p/96a52a8127d9</content></entry><entry><title>Handle timeout</title><url>/post/handle_timeout/</url><categories><category>system_design</category></categories><tags/><content type="html">超时处理 我们要探讨的问题是:如果我们遇到一个非常非常慢的API调用最终超时,并且我们假设(a)它成功或(b)失败,我们就会遇到错误。超时(或更糟糕的是,无限长的等待)是分布式系统的一个基本事实,我们需要如何处理它们
解决方法:
假设成功 使用默认值 假设远程操作失败,然后自动重试 重试是否安全 同步重试还是异步重试 同步重试会减慢您的速度 重试多少次 重试之间延迟策略(等间隔、指数退避、使用抖动) 如果远程服务出现性能问题,重试是否会使他们的情况变得更差 检查请求是否成功,如果安全再试一次
让用户知道这个结果
Resource microservices aren&rsquo;t magic handing timeout: https://8thlight.com/blog/colin-jones/2018/09/18/microservices-arent-magic-handling-timeouts.html 超时、重试和抖动回退: https://aws.amazon.com/cn/builders-library/timeouts-retries-and-backoff-with-jitter/ 微服务调用超时处理: https://www.jianshu.com/p/d68d572b0613</content></entry><entry><title>Inventory system design</title><url>/post/inventory_system_design/</url><categories><category>system_design</category></categories><tags/><content type="html"><![CDATA[库存服务设计 需求收集
方案评审
浏览器优化, 限流器使用,监控告警提前预报
设计难点在于库存更新和超时处理
库存更新, mysql+redis: 先更新,再删除缓存 超时处理,mysql事务回滚;如果是redis,删除缓存
针对update加锁过多,请求排队处理
如果还不能支撑,分库存,一个库存分成不同的key存储
延展设计
秒杀服务
异步处理,成功处理后检测结果发给用户
如何设计一个秒杀系统
设计秒杀系统时应该注意的5个架构原则 秒杀系统整体的架构设计思路 “4要1不要"原则 不同场景下的不同架构案例 如何才能做好动静分离?有哪些方案可选? 什么是静态数据和动态数据 如何做动静分离的改造 动静分离的几种架构方案 二八原则:有针对性地处理好系统的热点数据 什么是热点,热点操作和热点数据 实现一个动态热点发现系统 思路:优化、限制、隔离 流量削峰这事应该怎么做 什么是流量削峰 队列缓冲、秒杀答题、分层过滤 具体的实现细节 影响性能的因素有哪些?又该如何提高系统的性能? 影响性能的因素 如何发现瓶颈 如何优化系统 秒杀系统"减库存"设计的核心逻辑 常见的几种减库存方案 减库存的最优实现方式 其中的坑以及关键优化点 准备PlanB(高可用):如何设计兜底方案? 如何才能保证系统的高可用 降级、限流、拒绝服务的核心逻辑 具体的实现细节 秒杀系统设计: https://www.cnblogs.com/fmys/p/10915818.html Resource 超时处理模式: https://blog.csdn.net/weixin_33894992/article/details/92376142 redis热点缓存思考: https://blog.csdn.net/ck784101777/article/details/101367821 ]]></content></entry><entry><title>Timer设计</title><url>/post/timer/</url><categories><category>system_design</category></categories><tags><tag/></tags><content type="html"> 定时任务设计
任务队列 + 轮询, cpu耗费较大+空转问题;
四叉堆 + 轮询, 空转问题;
单独时间轮 + tick轮询, cpu耗费+空转问题; 多个时间轮 + tic轮询,空转问题;
timeWheel设计: https://www.bbsmax.com/A/q4zVYKgKdK/</content></entry><entry><title>Network</title><url>/post/network/</url><categories><category>network</category></categories><tags/><content type="html">Resource 网络书籍: https://www.zhihu.com/question/19774914 Utils cat /proc/net/bonding/bondx
查看bond状态: https://blog.csdn.net/sdd220/article/details/77199166 bond模式: https://blog.csdn.net/qq_34870631/article/details/80625217 一个网卡配置多个IP: https://blog.csdn.net/qq_34870631/article/details/80625217 配置VLAN
#ifcfg-bond0 DEVICE=bond0 NAME=bond0 TYPE=BOND NM_CONTROLLED=no USERCTL=no BONDING_MASTER=yes BOOTPROTO=none #ifcfg-bond0.1102 DEVICE=bond0.1102 VLAN=yes BOOTPROTO=static USERCTL=no NM_CONTROLLED=no ONBOOT=yes IPADDR=30.102.74.20 NETMASK=255.255.254.0 网卡配置: https://www.cnblogs.com/xuanlv-0413/p/14672286.html</content></entry><entry><title>runtime in golang</title><url>/post/runtime_in_golang/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html">Resource 聊聊什么是Go runtime.KeepAlive: https://developer.51cto.com/article/692468.html runtime.SetFinalizer https://zhuanlan.zhihu.com/p/76504936 https://www.cnblogs.com/binHome/p/12901392.html</content></entry><entry><title>Monitor system</title><url>/post/monitor_system/</url><categories><category>system_design</category></categories><tags/><content type="html">Resource 大型互联网海量监控系统设计: https://cloud.tencent.com/developer/article/1037231 支付系统设计(六):支付系统的监控与报警: https://toutiao.io/posts/caeqfr/preview 如何设计一个监控平台: https://juejin.cn/post/6957957796088250405 美团外卖自动化业务运维系统建设: https://tech.meituan.com/2017/11/14/digger-share.html</content></entry><entry><title>Data structure</title><url>/post/data_structure/</url><categories><category>linux</category></categories><tags><tag/></tags><content type="html">Resource 跳跃表: https://www.jianshu.com/p/9d8296562806 一致性哈希: 原理: https://segmentfault.com/a/1190000015336117 实现: https://www.cnblogs.com/xrq730/p/5186728.html</content></entry><entry><title>version change in golang</title><url>/post/version_change_in_golang/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html"><![CDATA[Go1.14 本次更新大多数变化在于工具链的实现,runtime和libraries defer性能提升
在Go1.14之前,Go中的每一个defer函数,会在编译期在defer位置生成一个runtime.deferproc调用,并且在包含defer函数退出时生成一个runtime.deferreturn调用
go build main.go && objdump -S main 这使得使用defer时增加了go runtime函数掉哦那个开销。另外值得一提的是,Go runtime中使用了先进后出的栈管理着一个函数中多个defer调用,这也意味着defer越多,开销越大
这使得部分Go程序员在高性能编程场景下,舍弃了defer的使用。但是不使用defer,容易导致代码可读性下降,资源忘记释放问题。
性能测试
优化原理:在Go1.14,编译器会在某些场景下尝试在函数返回处直接调用被defer的函数,从而使得使用defer的开销就像一个常规函数调用一样
goroutine支持异步抢占
Go语言调度其的性能随着版本越来越优异,我们来了解一下调度器使用的G-M-P模型,
G: goroutine,由关键字go创建 M: 在Go中称为工作线程,由内核调度 P: 处理器P是线程M和Goroutine之间的中间层,主要是为了处理内核进行系统调用 Go语言调度器的工作原理就是处理器P从本地队列依次选择G放到M上调度执行,任务偷窃
在Go1.1版本中,调度器还不支持抢占式调度,只能依靠goroutine主动让出CPU资源,存在非常严重的调度问题
单独的goroutine可以一直占用线程运行,不会切换到其它的goroutine,造成饥饿问题 垃圾回收需要暂停整个程序,如果没有抢占可能需要等待几分钟的时间,导致整个程序无法工作 在Go1.12中编译器在特定时机插入函数,通过函数调用作为入口触发抢占,实现了协作式的抢占调度,但是这种需要函数调用主动配合的调度方式存在一些边缘情况,比如:协作式的调度不会使一个没有主动放弃执行权,且不参与任何函数调用的goroutine被抢占 Go1.14实现了基于信号的真抢占式调度,runtime.sighandler注册信号SIGURG函数runtime.doSigPreempt,在出发垃圾回收的栈扫描时,调用函数挂起goroutine并向M发送信号,M收到信号后,会让当前goroutine陷入休眠继续执行其它的G
preempt时机
一方面,Go进程启动时,会开启一个后台线程sysmon,监控执行时间过长的goroutine,另一方面,STW时会让所有的goroutine停止,两者都会调用preemptone()
preemptone() -> preemptM -> signalM -> sighanlder #doSigPreempt() if ok, newpc := isAsyncSafePoint(gp, ctxt.sigpc(), ctxc.sigsp(), ctxt.siglr()); ok { //Adjust the PC inject a call to asyncPreempt ctxt.pushCall(funcPC(asyncPreempt), newpc) } isAsyncSafePoint()返回当前goroutine能否被抢占,以及从哪一条指定开始抢占,返回的newpc表示安全的抢占地址
当执行完sighandler,执行流再次回到线程,由于sighandler插入了一个asyncPreempt函数调用,原来的函数流程就暂停了
asyncPreempt -> asyncPreempt2 -> mcall(gopreempt_m) -> goschedImp1 -> schedule mcall(fn)的作用是切换到g0栈去执行函数fn,fn永不返回;gopreempt_m直接调用goschedImp1
func goschedImp1(gp *g) { casgstatus(gp, _Grunning, _Grunnable) dropg() //解绑m, g lock(&sched.lock) golabrunqput(gp) //将goroutine丢到全局队列 unlock(&sched.lock) shcedule() } func dropg() { _g_ := getg() setMNOWB(&_g_.m.curg.m, nil) setGNOWB(&_g_.m.curg, nil) } timer定时器性能得到巨幅提升
Go1.14做到了直接在每个P上维护自己的timer堆,像维护本地队列runq一样
Go1.15 小整数缓存
xxx/go1.17.5/go/src/runtime/iface.go:522(convT64)
原理: staticuint64s预分配优化避免了小整数转换为interface{}的内存分配
linker
先说下golang编译过程.(/usr/local/go/src/cmd/compile/internal/gc/main.go:132 Main())
词法分析
调用parseFile()解析,获取抽象语法树
类型检查
静态类型检查,动态类型检查
常量、类型和函数名及类型(重写OMAKE节点) 变量的复制和初始化 函数和闭包的主体 决定如何捕获变量 检查内联函数的类型 进行逃逸分析 将闭包主体转换为引用的捕获变量 编译顶层函数 检查外部依赖声明 中间代码生成
SSA配置初始化
常数传播 值域传播 稀疏有条件的常数传播 消除无用的程式码 全域数值编号 消除部分冗余 寄存器分配 遍历和替换,堆AST中节点的一些元素进行替换(walk系列函数对关键词进行遍历和改写,转换成函数调用,compileSSA函数将AST转换为中间代码)
最终机器码生成
编译器将一些值重写成目标CPU架构的特定值,将SSA中间代码降级,汇编器将这些指令转换为机器码
go1.15完全重写了linker
新链接器变化
移动许多工作从编译期到链接器,这个是为了使其paralleization,充分利用多核 优化关键数据结构,主要是string,当前linker使用一个很大的符号表索引来替代symbo-number编码 避免马上加载input object files,这将使少量的内存就能编译large program Go1.16 metrics
字段意义: https://pkg.go.dev/runtime/metrics#example-Read-ReadingAllMetrics 释放内存给OS
释放内存给OS,MADV_DONTNEED(man madvise)
native embedding of static files
Go1.17 调用约定更改
程序性能提升about 5%,二进制大小减少2%
影响unsafe.Pointer
包含闭包的函数可以被内联
这个变化影响使一个闭包可能产生不同的闭包函数指针
Resource Go1.16 Release Notes: https://go.dev/doc/go1.16 ]]></content></entry><entry><title>C++ 类</title><url>/post/virtual_func_in_c_plus_plus/</url><categories><category>language</category></categories><tags><tag>c++</tag></tags><content type="html"><![CDATA[Resource C++对象布局: https://mp.weixin.qq.com/s?__biz=MzkyODE5NjU2Mw==&mid=2247484758&idx=1&sn=4e614430f666f63ab135c13a716d07c1&chksm=c21d37eaf56abefc8d2a1dc3e09a8146d242475cb0900ee5a94ab6a94a991168a887f7351821&scene=178&cur_album_id=1667018561883570181#rd ]]></content></entry><entry><title>C++11 new feature</title><url>/post/c++11_new_feature/</url><categories><category>language</category></categories><tags><tag>c++</tag></tags><content type="html">Resource c++11新特性,所有知识点都在这里了: https://mp.weixin.qq.com/s/kAH-402oYgAa3GAVgJ8EaQ</content></entry><entry><title>Stack in linux</title><url>/post/stack_in_linux/</url><categories><category>linux</category></categories><tags/><content type="html"><![CDATA[Resource Linux中的各种栈: https://mp.weixin.qq.com/s?__biz=MzkyODE5NjU2Mw==&mid=2247490208&idx=1&sn=c920ca7b7449222f8dd80efcb88fc36f&chksm=c21d281cf56aa10a7f07ed93c49e334203c00cdfe83acd40c0fb0c7f28fbc8052447cb5d3b44&scene=178&cur_album_id=1667018561883570181#rd ]]></content></entry><entry><title>进程间通讯</title><url>/post/inter_process_communication/</url><categories><category>linux</category></categories><tags/><content type="html">Resource 进程间通信: https://www.jianshu.com/p/c1015f5ffa74</content></entry><entry><title>Behavior question</title><url>/post/bq/</url><categories><category>career</category></categories><tags><tag/></tags><content type="html">Resource 什么是BQ行为问题?如何在大厂面试中搞定它?: http://www.360doc.com/content/21/0405/14/44716758_970691903.shtml 应对100道行为面试题,只需3各小故事: https://www.myopt.org/post/%E5%BA%94%E5%AF%B9100%E9%81%93%E8%A1%8C%E4%B8%BA%E9%9D%A2%E8%AF%95%E9%A2%98%EF%BC%8C%E5%8F%AA%E9%9C%803%E4%B8%AA%E5%B0%8F%E6%95%85%E4%BA%8B</content></entry><entry><title>Linux Kernel Map</title><url>/post/linux/</url><categories><category>linux</category></categories><tags/><content type="html">Resource Linux Kernel Map: https://blog.csdn.net/Rong_Toa/article/details/109430393</content></entry><entry><title>IPSEC Protocol</title><url>/post/ipsec_protocol/</url><categories><category>network</category></categories><tags><tag/></tags><content type="html">Resource IPSEC介绍: https://blog.csdn.net/NEUChords/article/details/92968314</content></entry><entry><title>How to negotiate your package</title><url>/post/how_to_negotiate_your_package/</url><categories><category>career</category></categories><tags/><content type="html">Resource Counter Offer Template: https://helloacm.com/an-example-email-to-negotiate-your-package/</content></entry><entry><title>Bug in etcd</title><url>/post/bug_in_etcd/</url><categories><category>components</category></categories><tags><tag>bug</tag></tags><content type="html"> 记一次ETCD watch问题导致缓存不一致: https://zhuanlan.zhihu.com/p/332618994</content></entry><entry><title>MQ</title><url>/post/mq/</url><categories><category>components</category></categories><tags><tag>mq</tag></tags><content type="html">Resources MQ详解及四大MQ比较: https://www.cnblogs.com/duanxz/p/3143570.html 技术选型 RocketMQ or Kaflka: https://zhuanlan.zhihu.com/p/60196818</content></entry><entry><title>optimization in golang</title><url>/post/optimization_in_golang/</url><categories><category>components</category></categories><tags><tag>optimization</tag></tags><content type="html">Resource 聊聊Go内存优化和相关底层机制: https://wudaijun.com/2019/09/go-performance-optimization/</content></entry><entry><title>rocketmq</title><url>/post/rocketmq/</url><categories><category>components</category></categories><tags><tag>mq</tag></tags><content type="html">Resources RocketMQ原理&amp;复杂分布式事务解法: https://www.jianshu.com/p/2838890f3284 rocketMQ消息发送流程: https://www.jianshu.com/p/3779291d8344 RocketMQ: https://www.cnblogs.com/weifeng1463/p/12889300.html</content></entry><entry><title>关于配置中心、元数据中心和注册中心</title><url>/post/about_configuration_center_metadata_center_and_registry/</url><categories><category>system_design</category></categories><tags/><content type="html">Resources 关于配置中心,元数据中心和注册中心: https://zhuanlan.zhihu.com/p/103476722</content></entry><entry><title>RabbitMQ</title><url>/post/rabbitmq/</url><categories><category>components</category></categories><tags><tag>mq</tag></tags><content type="html"><![CDATA[RabbitMQ作用及其出现背景 作用: 削峰,解耦,异步调用
RabbitMQ特性和好处:
开源 轻量级 面向大多数现代语言的客户端开发库 灵活控制消息通信的平衡性 高延迟性环境插件—-因为不是所有的网络拓扑和架构都是一样的,RabbitMQ既支持在低延迟环境下的消息通信机制,也提供了针对互联网的高延迟环境下的插件 第三方插件系统—–作为应用集成的一个关键要素,RabbitMQ提供了灵活的插件系统。当需要 RabbitMQ架构 producer, consumer, broker
AMQP协议
exchange queue route-key/bind-key 一个AMQP连接可以有多个信道,允许客户端和服务器之间进行多次会话,从技术上讲,这被称为多路复用。
在创建客户端庄用程序时,不要使用过多的信道使事情变得复杂。在编组帧的线路上,信道不过是分配给服务器和客户端之间所传递消息的一个整数值;而在 RabbitMQ 服务器和客户端中,它们代表更多的含义 因为会为每个信道设置内存结构和对象,连接中的信道越多, RabbitMQ 用于管理该连接的消息流所需的内存也就越多 如果你能合理地使用它们,你将会有一个更健康的 RabbitMQ服务器和一个更简洁的客户端应用程序
AMQP帧由五个不同组件组成: 帧类型,信道编号,帧大小,帧有效载荷,结束字节标记。
AMQP帧: 协议头帧,方法帧,内容头帧,消息体帧以及心跳帧,使用方法帧,内容头帧,消息体帧向broker发布消息
协议头帧用于连接RabbitMQ,仅使用一次 方法帧携带发送给RabbitMQ或从RabbitMQ接收的RPC请求或响应 内容头帧包含一条消息的大小和属性 消息体帧包含消息的内容 mandatory标志(属于方法帧)告知RabbitMQ必须完成消息路由,否则它应该发送一个Basic.Ruturn帧用于指明消息无法路由。
消息属性 content-type
expiration
reply-to
content-encoding
delivery-mode
1表示非持久化消息,2表示持久化消息
message-id
priority
correlation-id
消息发布 金发姑娘原则:
消费者发布时保证消息进入队列的重要性有多高? 如果消息无法路由,是否应将消息返回给发布者? 如果消息无法路由,是否应该将其发送到其他地方稍后进行重新路由? 如果RabbitMQ服务器崩溃,可以接收信息丢失吗? RabbitMQ在处理新消息时是否应该确认它已经为发布者执行了所有请求的路由和持久化任务? 消息发布者是否可以批量投递消息,然后从RabbitMQ收到一个确认用于表明所有请求的路由和持久化任务已经批量应用到所有的消息中? 如果你要批量发布消息,而这些消息需要确认路由和持久化,那么对每一条消息是否需要对目标队列实现真正意义上的原子提交? 在可靠投递方面是否有可接受的平衡性,你的发布者可以使用它来实现更高的性能和消息吞吐量吗? 消息发布还有哪些方面会影响消息吞吐量和性能? RabbitMQ与原子事务: 原子性确保事务中所有操作的完成都将作为事务提交的 部分。在 AMQP 中,这意味着直到事务中的所有操作都完成为止,你的客户端将不会收到 TX.CornmitOk响应帧。不幸的是,对于那些寻求真正原子性的人来说, RabbitMQ 只在每个发出的命令作用于单个队列时才执行原子事务。如果不止一个队列受到事务中任何命令的影响,则提交就不具备原子性。尽管当事务中的所有命令仅影响同一个队列时 RabbitMQ 会执行原子事务,但发布者通常不能很好地控制消息是否被投递到多个队列。使用 RabbitMQ 高级路由方法,很容易想象一个应用程序在发布消息到单个队列时启动原子提交,然后有人可能使用同一个路由键绑定一个新的队列。这样任何使用该路由键的发布事务将不再具备原子性。还值得指出的是,当将delivery-mode值设置为2从而对消息进行持久化时,真正的原子事务可能会导致发布者的性能问题。如果在发送TX.CommitOK之前,RabbitMQ正在等待服务器I/O密集型写入操作的完成,那么客户端可能比命令没有被包装在事务中的场景需要等待更长的时间。
HA队列
Channel.Flow和Backpressure机制(停止接受TCP套接字上的数据)
消费消息 为什么你应该避免拉取消息,而应该倾向于消费消息?Basic.Get会导致每条消息都会产生与RabbitMQ同步通信的开销;由于Basic.Get的临时性,RabbitMQ不能以任何方式优化投递过程
如何平衡消息的投递的可靠性与性能?
增加Linux操作系统接收套接字缓冲区的数量
Qos:预拉取数量
dead-letter exchange。死信交换器与备用交换器不同,过期或被拒绝的消息通过死信交换器进行投递,而备用交换器则路由那些无法由RabbitMQ路由的消息
队列 队列的行为有:自动删除自己,只允许一个消费者进行消费,自动过期的消息,保持有限数量的消息,将旧消息推出堆栈
集群 RabbitMQ提高了队列高可。节点类型和消息持久化,
erlang分布式
erlang的分布式特性在语言层面支持,可以使用语言内置的API函数。、
erlang的分布式是以erlang的两个基本特性为基础:
复制式进程通信
Erlang的进程间通信采用的是严格的异步消息传递(发送消息后无须等待网络上的确认),接受方收到数 据时实际上获取了数据的一份独立的副本;此后接收方将无法感知发送方对数据所做的任何操作,反之亦 然。后续的任何通信都必须借助额外的消息才能进行。无论是运行在同一台机器上的进程,还是运行在不 同机器上并通过网络互联的进程,这种模型都非常奏效.
位置透明性
erlang会确定进程标识符在多机网络上的唯一性,erlang的pid是一个结构化的对象,包含node id, process id, serial三个元素
实战指南 消息是指在应用间传送的数据,消息可以非常简单.
消息队列中间件是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成。
一般有两种传递模式:p2p, pub/sub
消息中间件的作用:
解耦 冗余 削峰 异步 vhost
Erlang在运行时使用线程池来异步执行I/O操作。线程池的大小可以通过环境变量来调节
大部分操作系统都限制了同一时间可以打开的文件句柄数。在优化并发连接数的时候,需确保系统有足够的文件句柄数来支撑客户端和Broker的交互。增大TCP缓冲区大小可以提高吞吐量,如果减少TCP缓冲区就可以减少每个连接上的内存使用量(并发更重要可修改此值)
存储机制
持久层实际包含两个部分.rabbit_queue_index和rabbit_msg_store。rabbit_queue_index负责维护队列中落盘消息,包括消息的存储地点,是否已被交付给消费者、是否已被消费者ack等。每个队列都有与之对应的一个rabbit_queue_index. rabbitmq_msg_store以键值对的形式存储消息,它被所有队列共享,在每个节点有且只有一个. rabbit_msg_store分为msg_store_persistent和msg_store_transient。
消息可以直接存储在rabbit_queue_index中,也可以保存在rabbit_msg_store中。消息大小的界定queue_index_embed_msgs_below(默认4096B)
rabbit queue index 中以顺序(文件名从 开始累加〉的段文件来进行存储,后缀为’ . idx ”,每个段文件中包含固定的 SEGMENT ENTRY COUNT 条记录,SEGMENT_ENTRY_COUNT 默认值为 1638 。每个 rabbit_queue_index 从磁盘中读取消息的时候至少要在内存中维护一个段文件,所以设置 queue index embed msgs below 值的时候要格外谨慎,一点点增大也可能会引起内存爆炸式的增长
经过 rabbit msg store 处理的所有消息都会以追加的方式写入到文件中,当 个文件的大小超过指定的限制( file size limit )后,关闭这个文件再创建一个新的文件以供新的消息写入。文件名(文件后缀是“ .rdq ”)从 开始进行累加,因此文件名最小的文件也是最老的文件。在进行消息的存储时, bb itM 会在 ET (Erlang Term storage )表中记录消息在文件中的位置映射( Index )和文件的相关信息( File Summary )。
在读取消息的时候,先根据消息的 ID Cmsg id )找到对应存储的文件,如果文件存在并且未被锁住,则直接打开文件,从指定位置读取消息的内容。如果文件不存在或者被锁住了,则发送请求由 rabbit_msg_store 进行处理。
消息的删除只是从 ETS 表删除指定消息的相关信息 同时更新消息对应的存储文件的相关信息。执行消息删除操作时,井不立即对在文件中的消息进行删除,也就是说消息依然在文件中。仅仅是标记为垃圾数据而已。当一个文件中都是垃圾数据时可以将这个文件删除。当检测到前后两个文件中的有效数据可以合并在一个文件中。并且所有的垃圾数据的大小和所有文件的数据大小比值超过gc阈值进行文件合并
执行合并的两个文件一定是逻辑上相邻的两个文件。执行合并时首先锁定这两个文件,并先对前面文件中的有效数据进行整理,再将后面的文件的有效数据写入到前面的文件,同时更新消息在ETS表的记录,最后删除后面的文件。
队列的结构
通常队列由rabbit_amqqueue_process和backing_queue这两部分组成,rabbit_amqqueue_process负责协议相关的消息处理,即接收生产者发布的消息、向消费者交付消息,处理消息的确认等。backing_queue是消息存储的具体形式和引擎,并向rabbitmq_amqqueue_process提供相关的接口以供调用.
如果消息投递的目的队列是空的,并且有消费者订阅了这个队列,那么消息会直接发送给消费者,不会经过队列这一步.而当消息无法直接投给消费者时,需要暂时将消息存入队列,以便重新投递。消息存入队列后,不是固定不变的,它会随着系统的负载在队列中不断地流动,消息的状态会不断发生变化。
alpha: 消息内容和消息索引都存储在内容中 beta: 消息内容保存在磁盘中,消息索引保存在内存中 gamma: 消息内容保存在磁盘中, 消息索引在磁盘和内存中都有 delta: 消息内容和索引都在磁盘中 对于持久化的消息,消息内容和消息索引都必须先保存在磁盘上,才会处于上述状态中的一种,而gamma状态的消息是只有持久化的消息才会有的状态.
RabbitMQ 在运行时会根据统计的消息传送速度定期计算一个当前内存中能够保存的最大消息数量 (target_ram_count),如果 alpha 状态的消息数量大于此值时,就会引起消息的状态转换,多余的消息可能会转换到 beta 状态、 gamma 状态或者 delta 状态。区分这状态的主要作用是满足不同的内存和 CPU 需求。 alpha 状态最耗内存,但很少消耗 CPU。 delta状态基本不消耗内存,但是需要消耗更多的 CPU 和磁盘 I/O 操作。 delta 状态需要执行两次I/O 操作才能读取到消息, 一次是读消息索引(从 rabbit queue index 中), 一次是读消息内容(从 rabbit_msg_store 中); beta gamma 状态都只需要一次 I/O 操作就可以读取到消息(从 rabbit msg store 中)。
内存告警和磁盘告警
当内存使用超过配置的阈值或者磁盘剩余空间低于配置的阈值时,RabbitMQ都会暂时阻塞客户端的连接并停止接收从客户端发来的消息,避免服务崩溃。与此同时,客户端与服务端的心跳检测也会失效。被阻塞的connection状态是blocking,要么是blocked。前者对应于并不是试图发送消息的connection。比如消费者关联的connection,这种状态的connection可以继续运行。如果一个broker节点的内存或者磁盘受限,都会引起整个集群中所有的connection被阻塞.
理想情况是当发生阻塞时可以在阻止生产者的同时而又不影响消费者的运行。
默认情况下RabbitMQ所使用的内存阈值设置为40%.在最坏情况下,Erlang的垃圾回收机制会导致两倍的内存消耗,也就是80%的使用占比
流控: 一个连接触发流控会处于"flow"的状态,也就意味着connection的状态每秒在blocked和unblocked之间来回切换数次,这样可以将消息发送的速率控制在服务器能支撑的范围之内。流控机制不只是作用于connection,同样作用于channel和队列。
镜像队列
slave会准确地按照master执行命令的顺序进行动作,故slave与master上维护的状态应该是相同的。如果master由于某种原因失效,那么"资历最老"的slave会被提升为新的master。根据slave加入的时间排序,时间最长的slave即为"资历最老"。发送到镜像队列的所有消息被同时发往master和所有的slave上,如果此时master挂掉了,消息还会在slave上,这样slave提升为master的时候消息也不会丢失。除发送消息(Basic.Publish)外所有动作都只会向master发送,然后再由master将命令执行的结果广播给各个slave.
master和slave是针对队列而言,而队列可以均匀地散落在集群地各个Broker节点中达到负载均衡地目的,因为真正地负载还是对实际物理机器而言地,而不是内存中驻留地队列进程.只要确保队列的master节点均匀散落在集群中的各个Broker节点即可确保很大程度的负载均衡(每个队列的流量会有不同,因此均匀散落各个队列的master也无法确保绝对的负载均衡)。读写分离得不到更好的收益,即读写分离并不能进一步优化负载,却会增加编码实现的复杂度。
GM模块实现的是一种可靠的组播通信协议,该协议能够保证组播消息的原子性,即保证组中活着的节点要么都收到消息要么都收不到。它的实现大致为:将所有节点形成一个循环链表,每个节点都会监控位于自己左右两边的节点,当有节点新增时,相邻的节点保证当前广播的消息会复制到新的节点上;当有节点失效时,相邻的节点会接管以保证本次广播的消息会复制到所有的节点。gm_group的信息会记录在mnesia中,不同的镜像队列形成不同的组。操作命令从 master 对应的 GM 发出后,顺着链表传送到所有的节点。由于所有节点组成了一个循环链表, master 对应的 GM 最终会收到自己发送的操作命令,这个时候 master 就知道该操作命令同步到了所有的 slave
网络分区
Resource RabbitMQ集群原理和完善: https://www.cnblogs.com/xishuai/p/rabbitmq-cluster.html <深入RabbitMQ> 惰性队列: https://blog.csdn.net/u013256816/article/details/77987216 普通队列和镜像队列: https://blog.csdn.net/u013256816/article/details/71097186 ]]></content></entry><entry><title>disruptor</title><url>/post/disruptor/</url><categories><category>system_design</category></categories><tags/><content type="html">Resources 高性能队列: https://tech.meituan.com/2016/11/18/disruptor.html jdk中的队列: https://juejin.cn/post/6844903648875528206#heading-13</content></entry><entry><title>log</title><url>/post/log_framework/</url><categories><category>system_design</category></categories><tags/><content type="html">Resources log4j架构: http://aofengblog.com/2011/05/02/Apache-Log4j-%E6%9E%B6%E6%9E%84/ golang日志入门与选型: https://segmentfault.com/a/1190000040977469?utm_source=sf-similar-article</content></entry><entry><title>Difference between atomic and mutex</title><url>/post/difference_between_atomic_and_mutex/</url><categories><category>linux</category></categories><tags/><content type="html">Resources 原子操作和互斥锁的区别: https://cloud.tencent.com/developer/article/1645697</content></entry><entry><title>split db practice</title><url>/post/split_db_practice/</url><categories><category>system_design</category></categories><tags/><content type="html">Resources: 大众点评订单系统分库分表实践: https://tech.meituan.com/2016/11/18/dianping-order-db-sharding.html</content></entry><entry><title>zero copy in linux</title><url>/post/zero_copy_in_linux/</url><categories><category>linux</category></categories><tags/><content type="html">Resources 深入剖析linux IO原理和几种零拷贝的实现: https://zhuanlan.zhihu.com/p/83398714 Go语言中的零拷贝优化: https://strikefreedom.top/pipe-pool-for-splice-in-go linux io and zero copy: https://strikefreedom.top/linux-io-and-zero-copy</content></entry><entry><title>io.WriteString()分析</title><url>/post/io_write_string_analysis/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html"><![CDATA[问题 内存管理在Golang中是棘手的,我们可能相信golang本身已具备内存自动管理功能,可以从此解放出来。但上周的某个功能测试结果让我改变了这个看法。问题可以描述为"预期分配8G内存,但实际程序运行过程中分配了12G多,相当于多分配了50%的内存,为何内存会多分配"
经过排查,具体原因是求md5值时使用了**io.WriteString()**函数,而没有使用md5.Write()
func main() { hash := md5.New() io.WriteString(hash, "ABC") hash.Sum(nil) //hash.Write([]byte("ABC")) } 进入io.WriteString(),最终调用hash.Write()。于是百思不得其解,为何最终调用的是同一个函数,但一个分配了内存。想string to byte 是否会分配内存
StringToByte分析 #demo.go package demo func StringToByte(s string) []byte { return []byte(s) } #demo_test.go package demo import ( "testing" ) func BenchmarkStringToByte(b *testing.B) { str := "ABC" //for i := 1; i < 1*11; i++ { // str += "ABC" //} b.ResetTimer() for i := 0; i < b.N; i++ { StringToByte(str) } } 测试时,并没有发现会分配内存。(此时golang中的string和slice结构作了分析),实现上string到slice也的确可以不分配内存
于是分析string到byte转换源码。这里需注意到golang的string类型是不能被改变的,好处是并发场景下可以不加锁的多次使用同一字符串而不用担心安全问题
32字节是string和slicebyte转换的临界分配内存值。
实际到这里也已找到问题原因和解决方法,但性能测试时使用了io.WriteString(),发现即使参数是"ABC",也会分配3个字节,于是疑惑
io.WriteString() 分析结果是动态调用,显示类型转换会分配临时内存
Resources Go语言中string和[]byte的转换原理: https://segmentfault.com/a/1190000040289417 io.WriteString()探究: https://www.cxyzjd.com/article/HaoDaWang/80778008 allocation-efficiency-in-high-performance-go-service: https://segment.com/blog/allocation-efficiency-in-high-performance-go-services/ IO Go中的IO流怎么并发: https://mp.weixin.qq.com/s/wNBkC-X1FMPuHBX1P_DXbQ ]]></content></entry><entry><title>pprof in golang</title><url>/post/pprof_in_golang/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html"><![CDATA[CPU pprof package main import ( "fmt" "net/http" _ "net/http/pprof" "time" ) func main() { go func() { ticker := time.NewTicker(time.Second) for { select { case t := <- ticker.C: fmt.Println("Current time:", t) default: } } }() http.ListenAndServe(":8080", nil) } #cmdline go run main.go go tool pprof http://127.0.0.1:8080/debug/pprof/profile?seconds=10 go tool pprof -http=:8080 pprof.main.samples.cpu.001.pb.gz Resources pprof: https://go.dev/blog/pprof 使用pprof排查内存泄漏: https://www.jianshu.com/p/77cc1e350093 如何分析golang程序的内存使用情况: https://www.jianshu.com/p/43b0e12043b9 ]]></content></entry><entry><title>top command</title><url>/post/top/</url><categories><category>linux</category></categories><tags/><content type="html">top命令 进程的优先级
cpu利用率和负载
cpu利用率是对一个时间段内CPU使用情况的统计,通过这个指标可以看出某一个时间段内CPU被占用的情况
CPU负载是在一段时间内CPU正在处理以及等待CPU处理的进程数之和的统计信息,也就是CPU使用队列的长度的统计信息
Resources top命令详解: https://www.cnblogs.com/ggjucheng/archive/2012/01/08/2316399.html 深入linux进程优先级: https://linux.cn/article-7325-1.html cpu/内存使用率过高问题排查: https://www.jianshu.com/p/3674e9cb359d</content></entry><entry><title>CPU核数</title><url>/post/cpu/</url><categories><category>linux</category></categories><tags><tag/></tags><content type="html"><![CDATA[计算机系统架构 SMP, NUMA, MPP是目前主流得计算机系统架构
SMP
对称多处理架构。在这样得系统中,多个CPU对称工作,无主从关系。所有的CPU共享全部资源,如总线,内存和I/O系统等,多个CPU之间没有区别,平等地访问内存、外设。每个CPU访问内存任何地址所需时间是相同地,因此SMP也被称为一致性存储器访问架构(UMA: Uniform Memory Access)。操作系统管理着一个队列,每个处理器依次处理队列中的进程。如果两个处理器同时请求访问一个资源,由硬件、软件的锁机制去解决资源争用的问题
SMP 服务器的主要特征是共享,系统中所有资源 (CPU 、内存、 I/O 等 ) 都是共享的。也正是由于这种特征,导致了 SMP 服务器的主要问题,那就是它的扩展能力非常有限。对于 SMP 服务器而言,每一个共享的环节都可能造成 SMP 服务器扩展时的瓶颈,而最受限制的则是内存。由于每个 CPU 必须通过相同的内存总线访问相同的内存资源,因此随着 CPU 数量的增加,内存访问冲突将迅速增加,最终会造成 CPU 资源的浪费,使 CPU 性能的有效性大大降低。实验证明, SMP 服务器 CPU 利用率最好的情况是 2 至 4 个 CPU 。
NUMA
Non-Uniform Memory Access。非一致性存储访问结构.NUMA服务器的基本特征是具有多个CPU模块,每个CPU模块由多个CPU组成,并且具有独立的本地内存、I/O槽口等。其节点之间通过互联模块(crossbar switch)进行连接和信息交互,因此每个CPU可以访问整个系统的内存(这是NUMA系统和MPP系统的重要差别)。显然,访问本地内存的速度将远远高于访问remote memory的速度.为了更好的发挥系统性能,开发应用程序时需要尽量减少不同CPU模块之间的信息交互
NUMA 技术同样有一定缺陷,由于访问远地内存的延时远远超过本地内存,因此当 CPU 数量增加时,系统性能无法线性增加。
MPP
海量并行处理架构(Massive Parallel Processing).MPP提供了另外一种进行系统扩展的方式,它由多个SMP服务器通过一定的节点互联网络进行连接,协同工作,完成相同的任务,从用户的角度来看是一个服务器系统。MPP的节点互联机制是在不同的SMP服务器外部通过I/O实现的,每个节点只访问本地内存和存储,节点之间的信息交互与节点本身的处理是并行进行的。其基本特征是由多个SMP服务器通过节点互联网络连接而成,每个节点只访问自己的本地资源(内存、存储等),是一种完全无共享结构,因扩展能力最好,理论上扩展无限制
查看CPU参数 超线程: Hyper-threading就是在一个CPU Core上集成了两个逻辑处理器单元,即Logical Processor
NUMA node包括一个或多个Socket,以及与之相连的local memory
一个多核的socket有多个Core
如果CPU支持HT, OS还会把这个Core看成2个Logical Processor
lscpu
lscpu Architecture: x86_64 #处理器架构 CPU op-mode(s): 32-bit, 64-bit #CPU工作模式 Byte Order: Little Endian CPU(s): 1 #逻辑CPU核数 On-line CPU(s) list: 0 Thread(s) per core: 1 #每个逻辑核上的超线程数 Core(s) per socket: 1 #一个物理核上的逻辑核数 Socket(s): 1 #物理CPU个数 NUMA node(s): 1 #NUMA节点个数 Vendor ID: GenuineIntel CPU family: 6 Model: 85 Model name: Intel(R) Xeon(R) Platinum 8255C CPU @ 2.50GHz Stepping: 5 CPU MHz: 2494.140 BogoMIPS: 4988.28 Hypervisor vendor: KVM Virtualization type: full #虚拟化类型支持 L1d cache: 32K #1级数据缓存 L1i cache: 32K #1级指令缓存 L2 cache: 4096K L3 cache: 36608K NUMA node0 CPU(s): 0 Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single fsgsbase bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 arat avx512_vnni 查看物理CPU个数
cat /proc/cpuinfo |grep "physical id"|sort |uniq|wc -l 查看逻辑CPU个数
cat /proc/cpuinfo| grep "processor"| wc -l 查看linux系统内存信息
cat /proc/meminfo free -h Resources NUMA技术以及uumactl命令: https://www.codenong.com/cs109360467/ lscpu命令详解: https://www.cnblogs.com/machangwei-8/p/10398902.html linux查看机器cpu核数: https://www.cnblogs.com/hurry-up/p/9564064.html cpu性能逻辑优化: https://blog.csdn.net/Blues1021/article/details/44248027 ]]></content></entry><entry><title>code writing</title><url>/post/code_writing/</url><categories><category>linux</category></categories><tags/><content type="html">Resources my logging best practices: https://tuhrig.de/my-logging-best-practices/</content></entry><entry><title>container</title><url>/post/container/</url><categories><category>cloud</category></categories><tags/><content type="html"><![CDATA[ 容器和虚拟机区别
容器是将进程有效划分的一个独立空间,以便在独立的空间之间平衡资源使用冲突的技术。本质上容器是一种特殊的进程,其核心功能是通过约束和修改进程的动态表现创造出一个"边界",此外,其资源限制能力,以及基于镜像功能表现出的"强一致性",都使得容器技术成为云原生最关键的底层技术之一。
在虚拟机中,Hypevisor是最主要的部分,它通过硬件虚拟化功能,模拟出CPU、内存、I/O设备等各类硬件,之后在这些虚拟的硬件上安装了一个新的操作系统,即GuestOS,在虚拟的操作系统中运行的应用进程被相互隔离。Docker与虚拟机的差异体现在进程隔离方式不同,Docker为应用附加额外设置的Namespace参数实现进程的隔离,并没有一个真正的"Docker容器"运行在宿主机中。
]]></content></entry><entry><title>Computer Architecture</title><url>/post/computer_architecture/</url><categories><category>linux</category></categories><tags/><content type="html">Resources 计算机体系结构基础: https://foxsen.github.io/archbase/%E5%BC%95%E8%A8%80.html#%E4%BB%80%E4%B9%88%E6%98%AF%E8%AE%A1%E7%AE%97%E6%9C%BA</content></entry><entry><title>VLAN</title><url>/post/vlan/</url><categories><category>network</category></categories><tags><tag/></tags><content type="html">Resources 图文并茂VLAN详解: https://blog.51cto.com/u_6930123/2115373 VLAN详解: https://www.jb51.net/network/774813.html</content></entry><entry><title>Guessing Game</title><url>/post/guess_game/</url><categories><category>blockchain</category></categories><tags><tag>solidity</tag></tags><content type="html"><![CDATA[ Build a Failr, Oracle-Free(no oracles allowed) Guessing Game in solidity which pits two players against each other in a game where each player tries to guess whether a randomly generated number is high or low.
answer
/** ### reference - random optimize - https://www.cnblogs.com/huahuayu/p/8884165.html - https://blog.csdn.net/weixin_43587332/article/details/ 106743233 - multi people **/ pragma solidity ^0.4.0; library SafeMath { function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; assert(c / a == b); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } } contract GuessData { //address public implementation; mapping(address=>uint256) public player2balance; address[2] public playerSet; uint256[2] inputs; address public lastRoundWinner; uint256 public wager = 0; address public playerA = address(0); address public playerB = address(0); bool public isGameStarted = false; } contract GuessGame is GuessData { using SafeMath for uint256; modifier onlyPlayer() { require(playerSet[0] != address(0) && playerSet[1] != address(0)); require(msg.sender == playerSet[0] || msg.sender == playerSet[1]); _; } function registerPlayer(address player) { if (playerSet[0] == address(0)) { playerSet[0] = player; } else if (playerSet[1] == address(0)) { playerSet[1] = player; } else { revert("player is enough"); } } function deposit() external payable { player2balance[msg.sender] = player2balance[msg.sender]. add (msg.value); } function beginGame(uint256 _wager, uint256 input) external onlyPlayer payable { require(isGameStarted == false); require(player2balance[msg.sender] >= _wager); wager = _wager; inputs[0] = input; playerA = msg.sender; isGameStarted = true; } function reciveOffer(uint256 input) external onlyPlayer payable { require(isGameStarted == true); require(player2balance[msg.sender] >= wager); inputs[1] = input; playerB = msg.sender; } function endGame(uint256) external onlyPlayer payable { require(isGameStarted == true); //generate random int256 average = 1 << 255 - 1; int256 random; address winner; address loser; random = int256(sha256(abi.encodePacked(block. timestamp, block.difficulty, inputs[0], inputs[1]))); random = int256(sha256(abi.encodePacked(random))); if (random > average) { winner = playerA; loser = playerB; } else { winner = playerB; loser = playerA; } player2balance[loser] = player2balance[loser].sub(wager) ; player2balance[winner] = player2balance[winner].add (wager); lastRoundWinner = winner; clearData(); } function stopGame() external onlyPlayer payable { require(isGameStarted == true); clearData(); } function clearData() internal { isGameStarted = false; inputs[0] = 0; inputs[1] = 0; playerA = address(0); playerB = address (0); wager = 0; } function withdraw(uint256 amount) external onlyPlayer payable { require(isGameStarted == false); require(player2balance[msg.sender] >= amount); address(this).transfer(amount); } function() external payable { } } //contract Proxy is GuessData { // modifier onlyOwner() {require(msg.sender == owner); _;} // // constructor() public { // owner = msg.sender; // } // // function upgradeTo(address _newImplementation) external onlyOwner { // require(implementation != _newImplementation); // _setImplementation(_newImplementation); // // } // // function () payable public { // address imp1 = implementation; // require(imp1 != address(0)); // assembly { // let ptr := mload(0x40) // calldatacopy(ptr, 0, calldatasize) // let result := delegatecall(gas, imp1, ptr, calldatasize, 0, 0) // let size := returndatasize // returndatacopy(ptr, 0, size) // switch result // case 0 {revert(ptr, size)} // default {return(ptr, size)} // } // } // // function _setImplementation(address _newImp) internal { // implementation = _newImp; // } //} // run this solidity by brownie
GuessGame.deploy({'from': accounts[0]}) GuessGame[0].deposit({'from':accounts[1], 'amount':3000000000000000000}) GuessGame[0].deposit({'from':accounts[2], 'amount':2000000000000000000}) GuessGame[0].player2balance(accounts[1]) GuessGame[0].player2balance(accounts[2]) GuessGame[0].registerPlayer(accounts[1], {'from':accounts[0]}) GuessGame[0].registerPlayer(accounts[2], {'from':accounts[0]}) GuessGame[0].beginGame(500000000000000000, 12, {'from': accounts[1]} ) GuessGame[0].reciveOffer(34, {'from': accounts[2]}) GuessGame[0].endGame(34, {'from': accounts[2]}) GuessGame[0].player2balance(accounts[1]) GuessGame[0].player2balance(accounts[2]) GuessGame[0].isGameStarted() GuessGame[0].lastRoundWinner() other proxy:0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87 im1: 0x602C71e4DAC47a042Ee7f46E0aee17F94A3bA0B6 accounts[0]: 0x66aB6D9362d4F35596279692F0251Db635165871 owner: accounts[0] web3.eth.call({'to': '0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87', 'data': '3659cfe6000000000000000000000000602c71e4dac47a042ee7f46e0aee17f94a3ba0b6', 'from': '0x66aB6D9362d4F35596279692F0251Db635165871'}) web3.eth.call({'to': '0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87', 'data': '5c60da1b000000000000000000000000', 'from': '0x66aB6D9362d4F35596279692F0251Db635165871'}) web3.eth.call({'to': '0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87', 'data': '3659cfe6000000000000000000000000602c71e4dac47a042ee7f46e0aee17f94a3ba0b6', 'from': '0x66aB6D9362d4F35596279692F0251Db635165871'}) { 'implementation': "0x5c60da1b", 'owner': "0x8da5cb5b" } ]]></content></entry><entry><title>求解数组中唯二的两个数字</title><url>/post/xor/</url><categories><category>algorithm</category></categories><tags/><content type="html"> 一个数组中,存在两个只出现一次的数字,其余的数字均出现两次,求这两个数字是多少
#求解步骤: #假设两个数分别是a, b c = arr[0] xor arr[1] xor arr[2] xor ... xor arr[n] #获取c的二进制表示中1的最低一位,根据此位将原数组划分为两个数组,此时a,b分别在两个数组arr1, arr2中 a = arr1[0] xor ... xor arr1[n1] b = arr2[-] xor ... xor arr2[n2]</content></entry><entry><title>BTC开发资料</title><url>/post/btc/</url><categories><category>blockchain</category></categories><tags><tag/></tags><content type="html">Resources RPC API Reference: https://developer.bitcoin.org/reference/rpc/index.html btc explorer
https://www.oklink.com/btc/address/33WswvHHQMVYUQQiuoN4MYXRrjTh8zuKbq tron api doc: https://developers.tron.network/reference#tronweb-object-1 eos testnet: https://www.alohaeos.com/zh/tools/testnets sign bitcoin cash/sv/abc transaction: https://apexpl.github.io/bitcoin_cash_sv_abc_transaction_signatures.html</content></entry><entry><title>估算的一些方法</title><url>/post/some_estimate_method/</url><categories><category>system_design</category></categories><tags><tag/></tags><content type="html">Resources 估算的一些方法: https://mp.weixin.qq.com/s/fH-AJpE99ulSLbC_1jxlqw</content></entry><entry><title>Floating Point</title><url>/post/floating_point/</url><categories><category>linux</category></categories><tags/><content type="html">Resources 15张图带你深入理解浮点数: https://mp.weixin.qq.com/s/DGP4rENdy-N1VyZLS_uV4g 浮点数精度丢失问题详解: https://mp.weixin.qq.com/s/yjEkZrpIOAUAEknKTxC3YQ</content></entry><entry><title>Business</title><url>/post/business/</url><categories><category>career</category></categories><tags><tag>business</tag></tags><content type="html">Resources 对话杨浩涌: https://36kr.com/p/1440631980329603</content></entry><entry><title>shenjian channel</title><url>/post/shenjian_channel/</url><categories><category>career</category></categories><tags><tag/></tags><content type="html">xx 技术和管理如何平衡
第一,带好团队和做好技术一样,能够成为一个职场人的核心竞争力。代码写得又快又好,交付的系统没有Bug,这样的技术人产品喜欢;设计的架构高可用、易扩展,这样的架构师经理喜欢;能够达成业务目标,完成业务赋能,帮助员工成长,这样的管理老板喜欢。技术人、架构师、管理只是分工不同,这一点你认同吗。如果不认同岗位的价值,这样的管理很难做好 第二,可能你需要更换一种学习技术的学习方式,一线程序员更多是需要下沉,技术管理者则可以建立团队的学习机制。或者请教专家来学习技术,这样更高效。另外一线程序员更多是了解细节,学习是什么,要怎么样(what, how),技术管理者则更加要学习为什么,把握方向(why)。为什么要用这个组件,为什么要云原生,为什么要用这个技术栈等等。 第三,有管理能力好虚,很有可能来自于对这个岗位能力了解不够的失控感。程序员要掌握编程语言,数据结构,设计方法,我们对他的工作内容与方法非常熟悉,能掌控。成为管理者,对于角色定位,对于工作模式,工作定位不了解,突如其来的失控感是正常的。如何克服这种恐慌感是可以通过管理者的定位,熟悉工作内容,能力要求,像程序员一样对自身有准确的测量指标,减少自己的恐慌感。 Resources 其微信公众号名称: &lt;架构师之路&gt;</content></entry><entry><title>Linux内核网络</title><url>/post/linux_kernel_network/</url><categories><category>network</category></categories><tags><tag>origin</tag></tags><content type="html">讨论Linux内核网络栈地实现及其原理,深入而详尽地分析网络子系统及其架构。讲解数据包在Linux内核网络栈中的传输过程,阐述其与网络各层及各子系统之间的交互,探讨各种网络协议的实现方法。
1. Linux网络栈 1.1 网络设备
网络设备驱动程序位于数据链路层,表示网络设备的net_device结构体如下
设备的IRQ号 设备的MTU 设备的MAC地址 设备的名称, 如eth0或eth1 设备的标志,如状态为up还是down 与设备相关联的组播地址清单 promiscuity计数器 设备支持的功能,如GSO或GRO 网络设备回调函数的对象,这个对象由函数指针组成,如用于打开和停止设备、开始传输、修改网络设备MTU等的函数 ethtool回调函数对象,它支持通过运行命令行实用程序ethtool来获取有关设备的信息 发送队列和接收队列数(如果设备支持多个队列) 设备最后一次发送数据包的时间戳 如果计数器promiscuity的值大于0,网络栈就不会丢弃那些目的地并非本地主机的数据包。这样,tcpdump和wireshark等数据包分析程序(嗅探器)就能对其加以利用。嗅探器会在用户空间打开原始套接字,从而捕获此类发往别处的数据包。为了能够同时运行多个嗅探器,特将promiscuty声明成了计数器而非布尔变量。每运行一个嗅探器,计数器promicuity的值就加1;每关闭一个嗅探器,该值减1。如果这个计数器的值为0,就说明没有运行任何嗅探器,因此设备退出混杂模式.
老式设备驱动程序是在中断驱动模式下工作的。这意味着每接收一个数据包,就需要中断一次。新软件技术NAPI(New API), 如果负载很高时,网络设备驱动程序将在轮询模式,而不是中断驱动模式下运行,驱动程序会将数据包存储在缓冲区,由内核不时地向驱动程序轮询。从内核3.11起,Linux新增了频繁轮询套接字(Busy Polling Sockets)的功能,用于那些不惜以提高CPU使用率为代价而尽可能降低延迟的套接字应用程序
1.2 数据包的收发
网络设备驱动程序的主要任务是:接收目的地为当前主机的数据包,并将其传递给网络层(L3);传输当前主机生成的外出数据包或转发当前主机收到的数据包
对于每个数据包,无论是它接收到的还是发送出去的,都需要在路由子系统执行一次查找操作。根据路由子系统的查找结果,决定是否应对数据包进行转发以及该从哪个接口发送出去。决定数据包在网络栈中传输过程的因素并非只有路由子系统查找结果。在网络栈中有5个位置,Netfilter子系统在其中注册了回调函数。这些回调函数通常被称为Netfilter钩子。Linux内核中的Netfilter子系统为著名的用户空间包iptables提供了基础架构
除Netfilter钩子外,IPsec子系统也可能影响数据包的旅程。IPsec提供了一种网络层安全解决方案,它使用ESP和AH协议。IPsec在IPv6是强制执行的,而在IPv4中是可选的。IPsec有两种运行模式:传输模式和隧道模式。很多VPN(Virtual Private Network)解决方案都以IPsec为基础
套接字缓冲区SKB。从线路上收到数据包后,网络设备驱动程序会分配一个SKB,SKB包含数据包的报头(L2,L3和L4报头)和有效载荷。在数据包沿网络栈传输的过程中,可能添加或删除报头
每个第2层网络接口都由一个L2地址标识。就以太网而言,其为一个长48位的MAC地址,由制造商分配
2. IPv4 IPv4协议可在任何两台主机之间提供端到端的连接。IP层的另一项功能是转发数据包(也叫路由选择)以及管理路由选择信息表
2.1 Netlink
Netlink套接字旨在提供一种更灵活的用户空间进程与内核间通信方法,用于替代笨拙的IOCTL.
IOCTL处理程序不能从内核向用户空间发送异步消息,而Netlink套接字则可以。要使用IOCTL,还存在另一个麻烦,必须定义IOCTL号,Netlink的运行模型只需使用套接字API打开并注册一个Netlink套接字,它就会处理与内核Netlink套接字的双向通信
用于处理TCP/IP用户空间包:net-tools和iproute2。iproute2包含ip, tc, ss, lnstat, bridge。net-tool包含ifconfig, arp, route, netstat, hostname, rarp
ip route add 192.168.2.11 via 192.168.2.20 ip route del 192.168.2.11 ip monitor route 这个命令通过rtnetlink套接字,从用户空间发送一条添加路由选择条目的Netlink消息(RTM_NEWROUTE)。这条消息由rtnetlink内核套接字接收,并由方法rtnetlink_rcv()处理。最终,添加路由选择条目是通过inet_rtm_newroute()完成的,由方法fib_table_insert()完成插入转发信息库(FIB,即路由选择数据库)的工作,并通知所有注册了RTM_NEWROUTE消息的侦听者
2.2 IPv4报文
id分段标识,对SKB进行分段时,所有分段的id值都必须相同,对于分段后的数据包,则要根据各个分段的id对其进行重组
frag_off(分段偏移量,16位),后13位指出了分段的偏移量,前三位,001表示后面还有其它分段,010表示不分段, 100表示拥塞
2.3 IPv4路由转发
IPv4路由选择子系统及其使用的主要数据结构,例如路由选择表、转发信息库和FIB别名、FIB TRIE等。
两个以太网LAN1和LAN2,其中LAN1包含子网192.168.1.0/24,而LAN2包含子网192.168.2.0/24,在这两个LAN之间,有一台转发路由器,它有两个以太网接口卡,其中连接到LAN1的网络接口为eth0,其IP地址为192.168.1.200,而连接到LAN2的网络接口为eth1,其IP地址为192.168.2.200。出于简化考虑,假设转发路由器没有运行防火墙守护程序,从LAN1向LAN2发送流量,对到来的数据包进行转发的过程被成为路由选择,是根据被称为路由选择表的数据结构进行的
无类域间路由选择(Classless Inter-Domain Routing, CIDR)用0.0.0.0/0表示路由
#添加默认网关 ip route add default via 192.168.2.1 对于小型网络,管理FIB的工作可以由系统管理员手工完成,因为这种类型的网络拓扑几乎是静态的。而对于核心路由器来说,拓扑是静态的,管理FIB的工作通常由用户空间路由选择守护程序负责,这些用户空间守护程序通常用于维护独立的路由选择表,偶尔还会与内核路由选择表进行交互。
在3.6版之前的内核中,无论在接收还是传输路径中,查找都包含两个阶段:首先在路由选择缓存中查找,如果没有找到再在路由选择表中查找。查找工作方法fib_lookup(),其首先在本地FIB表中搜索,再在主FIB表查找
路由选择子系统的主数据结构是路由选择表,由结构fib_table表示。简单地说,路由选择表的每个条目都指定了前往特定子网的流量所对应的下一跳。每个路由选择条目都包含一个fib_info对象,其中存储了路由选择条目参数(出站网络设备、优先级、路由选择协议标识符)。fib_info对象包含:
fib_net: fib_info对象所属的网络命名空间 fib_protocol: 路由选择协议标识符。从用户空间添加路由选择规则时,如果没有指定路由选择协议ID,fib_protocol将被设置为RTPROT_BOOT。管理员添加路由时,可能会使用修饰符proto static,指出路由是由管理员添加的。ip route add proto static 192.168.5.3 via 192.168.2.1 fib_scope: 目标地址范围,它为地址和路由都指定了范围。简单地说,指出了主机相对于其它节点的距离 主机(RT_SCOPE_HOST): 节点无法与其它网络节点通信。环回地址的范围就是主机 全局(RT_SCOPE_UNIVERSE): 地址可用于任何地方 链路(RT_SCOPE_LINK): 地址只能从直连主机访问 场点(RT_SCOPE_SITE): 仅用于IPv6 找不到(RT_SCOPE_NOWHERE): 目的地不存在 fib_priority: 路由的优先级,默认为0,表示最高优先级。ip route add 192.168.1.10 via 192.168.2.1 metric 5, ip route add 192.168.1.10 via 192.168.2.1 priority 5, ip route add 192.168.1.10 via 192.168.2.1 preference 5。命令ip route参数的metric与fib_info对象字段fib_metrics没有任何关系 fib_dev: 将数据包传输到下一跳的网络设备 策略路由选择。使用策略路由选择时,仅根据目标地址为数据包选择路由,而不考虑其它因素(如源地址和TOS)。系统管理员可添加多达255个路由选择表。不适用策略路由选择时,将创建两个路由选择表:本地表和主表。主表的ID为254(RT_TABLE_MAIN),本地表的ID为255(RT_TABLE_LOCAL)。只有内核才能在本地表添加路由选择条目
#应用这条规则后,所有从192.168.2.103发送到192.168.1.17的数据包都将被禁止通过 ip route add prohibit 192.168.1.17 from 192.168.2.103, ip route show table local显示本地表路由规则, 默认显示主表
FIB别名。有些情况下,会针对同一个目标地址或子网创建多个路由选择条目。这些路由选择条目的唯一差别是其TOS不同,在这种情况下,将为每条路由创建一个fib_alias对象,而不是fib_info对象。fib_alias相对来说消耗资源更少
ip route add 192.168.1.10 via 192.168.2.1 tos 0x2 ip route add 192.168.1.10 via 192.168.2.1 tos 0x4 rout -n输出中标志,
U(路由处于up状态) H(目标为主机) G(使用网关) R(为动态路由选择恢复路由) D(由守护程序或重定向消息动态加入) M(被路由选择守护程序或重定向消息修改过) A(由addrconf加入) !(阻塞路由) #查看路由表 cat /etc/iproute2/rt_tables 2.4 策略路由
#所有IPv4 TOS字段为0x04的数据包都将根据表252中的路由选择规则进行处理 ip rule add tos 0x04 table 252 ip route add default via 192.168.2.10 table 252 ip rule show 3. Linux邻接子系统 邻接子系统负责发现当前链路上的节点,并将L3地址转换为L2地址。在IPv4中,实现这种转换协议为地址解析协议,而在IPv6中则为邻居发现协议(NDISC)
3.1 用户空间与邻接子系统之间的交互
ip neigh show 4. IPv6 IPv6增加的新功能,拓展报头、组播侦听者发现(MLD)协议和自动配置过程等
4.1 IPv6报头
4.2 接收IPv6数据包
5. Netfilter 5.1 Netfilter
Netfilter子系统提供了如下功能:数据包选择(iptables)、数据包过滤、网络地址转换(NAT)、数据包操纵(在路由选择之前或之后修改数据包报头的内容)、连接跟踪、网络统计信息收集
在网络栈中有5个地方设置了Netfilter挂载点
NF_INET_PRE_ROUTING: 挂载点位于ip_rcv()/ipv6_rcv(),它处于路由选择子系统查找之前 NF_INET_LOCAL_IN: 挂载点位于ip_local_deliver()/ip6_input()中,对于所有发送给当前主机的入站数据包,经过挂载点NF_INET_PRE_ROUTING并执行路由选择子系统查找后,都将到达这个挂载点 NF_INET_FORWARD: ip_forward()/ip6_forward() NF_INET_POST_ROUTING: ip_output()/ip6_finish_output2()中 NF_INET_LOCAL_OUT: __ip_local_out()/__ip6_local_out()中,当前主机生成的所有出站数据包都在经过这个挂载点后到达挂载点NF_INET_POST_ROUTING 连接跟踪能够让内核跟踪会话,主要目标是为NAT打下基础。如果没有设置CONFIG_NF_CONNTRACT_IPV4,就不能构建IPv4-NAT模块,如果没有CONFIG_NF_CONNTRACT_IPV6,就不能构建IPv6-NAT模块
5.2 iptables
#这条规则的意思是,将目标端口为5001的UDP入站数据包转存到系统日志中 iptables -A INPUT -p udp --dport=5001 -j LOG --log-level 1 5.3 NAT
网络地址转换(NAT)模块主要用于处理IP地址转换或端口操纵。NAT最常见的用途之一是,让局域网一组使用私有IP地址的主机能够通过内部网关访问internet
6. IPsec IPsec是一组协议,它们对通信会话中的每个数据包进行身份验证和加密,以确保IP流量的安全。大部分安全服务都是由两个主要的IPsec协议提供的:AH协议和ESP协议。另外,IPsec还可以防止窃听以及数据包的重新发送(重放攻击)
IPsec由多种运行模式,其中最重要的是传输模式和隧道模式。在传输模式下,对IP数据包的有效负载进行加密;而在隧道模式下,对整个IP数据包进行加密,并使用新的IP报头将其封装到新的IP数据包中。使用基于IPsec的VPN时,通常采用隧道模式。
图描述的是IPv4 ESP数据包。对于IPv4 AH数据包,将调用方法ah_input()而不是esp_input()。同理,对于IPv4 IPCOMP数据包,将调用方法ipcomp_input()而不是esp_input()
Resources net_device设备结构体详解: https://blog.csdn.net/viewsky11/article/details/53046787</content></entry><entry><title>Data Link Layer</title><url>/post/data_link_layer/</url><categories><category>network</category></categories><tags><tag>origin</tag></tags><content type="html">概念 网卡
网卡是一块被设计用来允许在计算机网络上进行通讯的计算机硬件。其拥有MAC地址。工作在数据链路层(L2), 功能一是将数据封装成帧,并通过网线或者无线网络传送到网络上;二是接受网络上的帧。网卡只接收发送到主机上的帧和广播帧,其余帧丢弃。
网卡上面装有处理器和存储器, 网卡和局域网之间的通信是通过电缆或双绞线以串行方式进行传输,而网卡和计算机之间的通信则是通过主板上的I/O总线并行传输方式运行。因此,网卡的一个重要功能就是进行串行/并行转换。由于网络上的数据率和计算机总线的数据率并不相同,因此在网卡中必须装有对数据进行缓存的存储芯片。
集线器
集线器(Hub)的主要功能是对接收到的信号进行再生整形放大,以扩大网络的传输距离,同时把所有节点集中在以它为中心的节点上。它工作于OSI参考模型第一层,即物理层。
集线器不叫网络间设备,而应该叫网络设备,主要用来连接计算机等网络终端,它不同于网关、路由器、网桥设备,它不具备协议翻译功能,只负责分配频宽。集线器为共享式带宽,连接在集线器的任何一个设备发送数据时,其它所有设备必须等待,此设备享有全部带宽,通讯完毕,再由其它设备使用带宽。正因此,集线器连接了一个冲突域的网络,所有设备相互交替使用。
从工作方式来看,HUB是一种广播模式,某个端口工作时,其它所有端口都能接收到信息
若两个接口同时有信号输入(发生碰撞),那么所有接口都将接收不正确的帧。
多级结构集线器扩大了以太网的地理范围。例如,在一个系10BASE-T以太网中,主机与集线器的最大距离是100m,因为主机之的最大距离是200m。但在通过主干集线器相连接后,不同系的主机间的距离就可扩展了,因为集线器之间的距离可以是100m或者更远
多层集线器的缺点:任一时刻,在每一个碰撞域中只能有一个站在送数据;吞吐量有短板效应;
网桥
网桥是早期的两端口二层网络设备。网桥的两个端口分别有一条独立的交换信道,不是共享一条背板总线,可隔离冲突域。网桥比集线器性能更好,集线器上各端口都是共享同一条背板总线的,网 被具有更多端口、同时也可隔离冲突域的交换机(Switch)所取代
网桥工作于数据链路层,网桥在执行转发前会先接收帧并进行缓冲,由于网桥不提供流控功能,因此在流量较大时很有可能使其过载,从而造成帧的丢失。
在过去广泛使用粗缆或细缆以太网时,常使用工作在物理层的转发器来扩展以太网的地理覆盖范围。那时,两个网段可用一个转发器连接起来(单个的网段被限制为不超过500米长)。IEEE802.3标 还规定,任意两个站之间最多可以经过三个电缆网段,但随着双绞线以太网成为以太网的主流类型,扩展以太网的覆盖范围已很少使用转发器了。
现在,扩展主机和集线器之间的距离的一种简单方法就是使用光纤(通常是一对光纤)和一对光纤调制调节器
在数据链路层扩展以太网要使用网桥。网桥工作在数据链路层,它根据MAC帧的目的地址对接收到的帧进行转发和过滤。当网桥收到一个帧时,并不是向所有的接口转发此帧,而是先检查此帧 目的MAC地址,然后再确定将该帧转发到哪一个接口,或者把它丢弃。
网桥只适合于用户数不太多(不超过几百个)和通信量不太大的以太网。否则有时还会因传播过多的广播信息而产生网络拥塞(广播风暴)
路由器
路由器工作在网络层,其主要任务是接收一个来自网络接口的数据包,根据其中所含的目的地址,决定转发的下一个目的地址。
交换机
交换机工作在数据链路层。 有系统的交换机可以记录MAC地址表,发送的数据不会再以广播方式发送到每个接口,而是直接到达目的接口。高端的交换机还可以划分VLAN来隔离广播 交换机并无准确的定义和明确的概念。而现在很多交换机已混杂了网桥和路由器的功能。以太网交换机实质上就是一个多接口的网桥。以太网交换器的每个接口都直接与一个单个主机或另一个集线器相连,并且一般都工作在全双工方式,当主机需要通信时,交换机能同时连通许多对的接口,使每一对相互通信的主机都能像独占通信媒体那样,无碰撞地传输数据 交换机每个端口都用来连接一个独立的网段,但有时为了提供更快接入速度,可以将一些重要的网络计算机直接连接到交换机端口上。 计算机网络 起源于美国的因特网现已发展成为世界最大的国际性计算机互联网。
网络: 网络由若干节点(node)和连接这些节点的链路(link)组成。网络中的节点可以是计算机、集线器、交换机或路由器等
物理层
物理层考虑的是怎样才能在连接各种计算机的传输媒体上传输数据比特流。物理层的作用正是要尽可能屏蔽掉这些差异,定义了有关传输介质的特性。
链路层
在单个链路上如何传输数据,通过各种控制协议,将有差错的物理信道变为无差错的、能可靠传输数据帧的数据链路
数据链路层协议有许多种,但有三个基本问题则是共同的。这三个基本问题是封装成帧、透明传输和差错检测。字节填充在帧界定符前面解决透明传输问题</content></entry><entry><title>interview</title><url>/post/interview/</url><categories><category>career</category></categories><tags/><content type="html">Resources 海归软件工程师起飞指南: https://www.sohu.com/a/328902066_747492</content></entry><entry><title>how to talk about your project</title><url>/post/how_to_talk_about_your_project/</url><categories><category>career</category></categories><tags/><content type="html">Resource 石墨文档基于K8S的Go微服务实践(上篇): https://mp.weixin.qq.com/s/2YrW_6My-20_DRyKRnT-tA</content></entry><entry><title>microservice</title><url>/post/microservice/</url><categories><category>system_design</category></categories><tags><tag>microservice</tag></tags><content type="html">Resources xx:
微服务解决了集中式架构的单体应用等不少问题,比如扩展性、弹性收缩能力、小规模团队的敏捷开发等待,但同时不少项目因为微服务拆分过度,导致项目复杂度过高。
在其落地实践过程中也争论不断,比如:
微服务的粒度应该多大 微服务到底应该如何拆分和设计 微服务的边界应该在哪里 用DDD来做微服务设计</content></entry><entry><title>IP</title><url>/post/ip/</url><categories><category>network</category></categories><tags/><content type="html">虚拟网桥 brctl命令
#查看网桥信息 brctl show #给网桥添加ip ifconfig brtest0 199.199.100.1 ifconfig brtest0 down brctl delbr brtest0 #不需要STP(生成树)协议 brctl stp br0 off 网桥配置: https://www.cnblogs.com/iceocean/articles/1594193.html OSI模型
OSI模型定义数据链路层/物理层和传输层/网络层有着十分相似的职责:提供数据传输的手段,即沿某条路径将数据从源点传送到目的点。不同之处在于数据链路层/网络层提供跨越物理路径的通信服务,而传输层、网络层提供跨越一连串数据链路组成的逻辑或虚拟路径的通信服务。
Resources iproute2和net-tools比较: https://www.cnblogs.com/anliven/p/6041246.html iproute2和netstat比较: https://blog.csdn.net/weixin_42767604/article/details/106251844 iptables详解: https://www.zsythink.net/archives/1199 ip route相关知识: https://blog.csdn.net/zqixiao_09/article/details/53327074 ip rule, ip route, iptables三者之间关系: https://blog.csdn.net/qq_20817327/article/details/119148298 ip address命令:
为何MTU是1500: https://blog.csdn.net/passionkk/article/details/100538418 IP寻址过程: https://blog.51cto.com/suyyzhou/279174 Linux上软件实现的&quot;交换机&quot;-Brige: https://mp.weixin.qq.com/s/ejM7OlmK-ljt_276M59UBA</content></entry><entry><title>Delay Queue</title><url>/post/delay_queue/</url><categories><category>system_design</category></categories><tags><tag/></tags><content type="html">Resources delay queue的几种实现简介: https://mp.weixin.qq.com/s/zjmVoj0nM2FZDelYaebdDw 延迟队列rabbitmq实现: https://mp.weixin.qq.com/s/I8I5PJMfDSsLkmLHiuIavw</content></entry><entry><title>panic in golang</title><url>/post/panic_in_golang/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html">Resources panic的三种触发方式: https://mp.weixin.qq.com/s/cs_jLLoaq4rweVYFbeJoFw panic实现: https://mp.weixin.qq.com/s/-tYjR9K6tWG3aNOeGFsT3Q</content></entry><entry><title>sync in golang</title><url>/post/sync_in_golang/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html">Resources sync.Once sync.Once()的妙用: https://mp.weixin.qq.com/s/7QyQS9DQj1czA6BVR6ywYA sync.Once()实现原理: https://segmentfault.com/a/1190000039294152 sync.WaitGroup()的实现原理: https://zhuanlan.zhihu.com/p/344973865</content></entry><entry><title>interface in golang</title><url>/post/interface_in_golang/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html">Resources []int能转换为[]interface吗: https://mp.weixin.qq.com/s/tqR5zMPobm20lao-2QNrVA</content></entry><entry><title>NAS</title><url>/post/nas/</url><categories><category>linux</category></categories><tags/><content type="html">Resources nas服务器搭建与配置: https://blog.csdn.net/qq_38265137/article/details/83146421</content></entry><entry><title>PA first lesson</title><url>/post/pa_first_lesson/</url><categories><category>career</category></categories><tags/><content type="html">入职第一课堂 战略执行: 战略前瞻、商业以实、规划分析、规划执行 管理决策: 管理组织、管理他人、管理自我、管理业务 科技创新: 前言创新、敏捷开发、大数据 专业技能: 销售、财务、HR、项目管理 个人成长: 沟通表达、工具方法、OKR 微课 做自己绩效的主任
作为一名普通员工,我们在职场所扮演的角色不只是一名被管理者
绩效管理
绩效目标设定: 主动了解团队的目标,结合主管的要求,设定自己的KPI指标,工作任务及期望达成的效果 绩效检视与辅导: 每月检视目标完成情况,并根据当月完成情况调整下月的计划和进度 绩效考核与反馈: 与主管深入沟通,了解自己与别人的差距 绩效结果应用: 根据结果自省 目标准绳
公司提出目标设定的SCC理念(Strategy, continous, Challenging) 与主管目标一致、目标有一定的挑战性、有明确的落实步骤 发展诀窍
月度汇报 绩效日了解你的成果,工作反馈 年度考核
摆明当前工作成绩,表明自己的想法
排名必须公平、公正,且有一个可衡量的标准
自我反思
如何获得更好的绩效: 方向正确、方法得当、提升核心竞争力、善于总结、主动沟通
新人成长心态: 空杯心态. 绩效排名结果不是由工作资历决定,而是由工作成果决定
新人成长行动: 看、信、思考、行动、分享</content></entry><entry><title>git</title><url>/post/git/</url><categories><category>linux</category></categories><tags/><content type="html">git submodule add module
git submodule add git@github.com:transmissions11/solmate.git lib/solmate cat .gitmodules cat .git/config cat .git/modules git rm --cached remove module
git submodule deinit lib/solmate git rm lib/solmate Resources 工作中Git的使用: https://mp.weixin.qq.com/s/mje4XVyuDzspttWdOFC02g git仓库操作: https://mp.weixin.qq.com/s/s_pNeT1lMlVDbjO37Fys4A git仓库过大, clone不下来解决方法: https://www.cnblogs.com/ZhaoWeiNotes/p/13920660.html git只下载最新代码: https://zhuanlan.zhihu.com/p/413110403</content></entry><entry><title>职业要求</title><url>/post/requirements/</url><categories><category>career</category></categories><tags/><content type="html"><![CDATA[P9 P5、P6被要求独立完成任务;P7要求前瞻性,能把不同点的事情串在一起,变成一条线;P8则需要有结构化的思维,能把不同的事、交错的线,变成一张网。到了P9,则需要"无中生有",自己从头拉起一个业务摊子。比如淘系的"逛逛"改版中,高层确定了电商内容化的方向,他们定规划、拉资源。分解到P9这里,就是具体的战术:内容是什么样的形式,从内容做还是从产品类型做?内容怎么来?来了以后给它什么样一个机制?
董易也是一名空降P9,他讲述了从头拉起一个业务的常规步骤。首先,作为业务的1号位要先确立终局和目标,“你能想三年就三年,能想两年就两年,尽你所能看到最远”.然后与现实情况做对照,其中的差距能指引接下来的业务路径.确定好业务路径后,接下来就是画组织的行动路径。类似地,看达成业务终局需要什么样能力结构地人,比如需要懂财务地、懂数据分析地、懂商业BD地,懂销售运营地。再对照现有组织成员地能力,缺什么补什么。
这明确对应了阿里在"管理三板斧"里对中层的要求:懂战略、搭班资、做导演.
换句话说,在组织结构的金字塔里,以阿里P9为代表的中层可以成为内部创业者,也有可能获得一定躺平的空间,这不是个体或群体的问题,而是大公司结构本身隐含的问题
多年前作为新人的他,如同白纸一张。一定阶段后,锋芒显露,自我开始膨胀、执拗、闭塞、陷入谷底,只相信自己看到的,“现在觉得是坐井观天吧”。直到面对更复杂的问题,“头破血流”—事情搞不定、业绩不达标时,才重新认识到自己的短板和缺陷,“敬畏这个词,我花了三四年的事件才理解”.此后便是涅槃,从自我打击到自我激励,开始新一轮成长.
董易羡慕这些在体系内成长的P9们,“相当于航空母舰一样把你保护得好好的,你要做得只是在这里努力工作,你不用担心失业,也不用担心企业第二天没了,其它问题不用担心,你要做得就是自己进步给企业创造价值,简直就完美”
]]></content></entry><entry><title>tcp泄漏操作系统信息</title><url>/post/get_os_by_tcp/</url><categories><category>linux</category></categories><tags/><content type="html">Resources 哈哈!TCP泄露了操作系统信息···: https://mp.weixin.qq.com/s/FIUDFL6zd3Nkf4Vvb6ajLg</content></entry><entry><title>Sync map在Go中运用</title><url>/post/sync_map_in_go/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html">Resource Go sync map 的内部实现: https://mp.weixin.qq.com/s/TGBSUYLiuBaoi3pNbN0BOQ sync map详解: https://blog.csdn.net/a348752377/article/details/104972194 RWMutex内部实现: https://sreramk.medium.com/go-sync-rwmutex-internals-and-usage-explained-9eb15865bba</content></entry><entry><title>他人面试经历</title><url>/post/interview_experience_of_others/</url><categories><category>career</category></categories><tags/><content type="html">Resources 一个学渣的阿里之路: https://mp.weixin.qq.com/s/JT9e-inlL9F1bjT40sNaLA 渣渣菜鸡的蚂蚁金服面试经历(一): https://mp.weixin.qq.com/s/bNXyjo9ywdOkAbesNTrMoA 渣渣菜鸡的蚂蚁金服面试经历(一): https://mp.weixin.qq.com/s/D-tvQBRJbYPLwzj7KuvqHg 渣渣菜鸡的有赞面试经历: https://mp.weixin.qq.com/s/wDDsa5na4b3MSdsIgh3upw 面试过阿里等互联网大公司,我知道了这些套路: https://mp.weixin.qq.com/s/4XPBYuGrOTsUv-tyR-JiyA</content></entry><entry><title>虚拟机知识</title><url>/post/lxc/</url><categories><category>linux</category></categories><tags><tag/></tags><content type="html">源码安装lxc ``` ``` lxc创建容器 lxc源码安装(./configure --prefix=/xx, 是其lxc-ls存放目录) 容器创建后存放目录 ~/.local/share/lxc/,如果是root,存放在 /var/lib/lxc ``` lxc-checkconfig lxc-create -n test -t none # 查看当前系统中可用LXC模板容器 ls /usr/share/lxc/templates/ # 会联网下载 lxc-create -n test1 -t /usr/share/lxc/templates/lxc-centos lxc-create -t centos -n test4 # 安装libvirtd, 新增网卡virbrt0 # 启动lxc(启动前先chroot) lxc-start -n test2 -d lxc-start -n test0 -f ./config --logfile=/tmp/1.log lxc-attach -n test0 ``` qemu创建镜像 ``` qemu-img create -f qcow2 fed.img 2G wget http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2.xz qemu-img info CentOS-7-x86_64-GenericCloud.qcow2 qemu-img create fed2.qcow2 2M qemu-img create -f qcow2 -b CentOS-7-x86_64-GenericCloud.qcow2 bb.qcow2 qemu-img info bb.qcow2 ``` virsh创建虚拟机 virsh list --all Resources lxc命令学习
常用命令介绍: https://www.cnblogs.com/lisperl/archive/2012/04/13/2446179.html https://blog.csdn.net/shichaog/category_2413283.html lxc创建容器步骤: https://blog.csdn.net/zhanghui962623727/article/details/98623543 virsh命令学习
常用命令: https://www.cnblogs.com/cyleon/p/9816989.html virsh创建虚拟机的两种方式区别: https://www.cnblogs.com/kouryoushine/p/8950393.html virsh创建虚拟机: https://blog.csdn.net/qq_15437629/article/details/77827033 修改qcow2镜像中文件: https://zhuanlan.zhihu.com/p/351633475 lxc安装与配置使用: https://blog.csdn.net/gzqiang08/article/details/81708656</content></entry><entry><title>Elasticsearch</title><url>/post/elasticsearch/</url><categories><category>components</category></categories><tags/><content type="html">Resources elasticsearch亿级数据检索深度优化: https://mp.weixin.qq.com/s/Vp77RZpfnB4TJIg-DmwzUg</content></entry><entry><title>ETCD</title><url>/post/etcd/</url><categories><category>components</category></categories><tags><tag>etcd</tag></tags><content type="html">Resource 基于etcd的分布式锁: https://www.jianshu.com/p/8a4dc6d900cf etcd的watch机制
https://blog.csdn.net/zl1zl2zl3/article/details/79627412 https://www.lixueduan.com/post/etcd/05-watch/ etcd读写请求流程: https://blog.csdn.net/wohu1104/article/details/116244730 只读请求优化: https://mrcroxx.github.io/posts/code-reading/etcdraft-made-simple/6-readonly/ 关于etcd的一些谣言: https://cloud.tencent.com/developer/article/1551687 蚂蚁集团万级规模k8s集群etcd高可用建设之路: https://mp.weixin.qq.com/s/1A2GFwXlk6xua7vfqAaOlg</content></entry><entry><title>Go中的map</title><url>/post/map_in_golang/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html">Resource map中的删除: https://mp.weixin.qq.com/s/fTyfcBhRGNo3Q3Gl12_XNw</content></entry><entry><title>TCP</title><url>/post/tcp/</url><categories><category>network</category></categories><tags><tag/></tags><content type="html">Resource 面试必备: https://mp.weixin.qq.com/s/iHW0hj29fBkgviINoEE4nw</content></entry><entry><title>Go语言的优势和劣势</title><url>/post/advantages_and_disadvantages_in_go/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html">Resources GopherChina 2021 Ian说完整泛型实现将在Go1.18发布(会议实录): https://mp.weixin.qq.com/s/RFfa-A0DomH2pXz4arAVnA</content></entry><entry><title>内存访问</title><url>/post/memory_access/</url><categories><category>linux</category></categories><tags><tag/></tags><content type="html"> 每个程序员都应该知道的CPU知识: https://mp.weixin.qq.com/s/fzFNRUBFlTi3p71CpTopRw 结构体字节对齐: https://mp.weixin.qq.com/s/qHeM_eJ33m-vH6CmqUYnOg</content></entry><entry><title>Scheduling in Golang</title><url>/post/schdule_in_golang/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html">gopark and goready https://blog.csdn.net/u010853261/article/details/85887948</content></entry><entry><title>Tree</title><url>/post/tree/</url><categories><category>algorithm</category></categories><tags><tag/></tags><content type="html"><![CDATA[平衡二叉树 平衡二叉树: 二叉树的每个节点的左右子树的高度差的绝对值不超过1
判断一棵树是否是平衡二叉树 例子: 输入: 给定二叉树 [3,9,20,null,null,15,7] 输出: true 输入: 给定二叉树 [1,2,2,3,3,null,null,4,4] 输出: false var heightArr = map[*TreeNode]int32{} func isBalanced(root *TreeNode) bool { if root == nil { return true } else { return math.Abs(float64(height(root.Left) - height(root.Right))) <= 1 && isBalanced(root.Left) && isBalanced(root.Right) } } func height(root *TreeNode) int32 { if h, ok := heightArr[root]; ok { return h } if root == nil { return 0 } else { return int32(math.Max(float64(height(root.Left)), float64(height(root.Right))) + 1) } } ]]></content></entry><entry><title>循环移动数组</title><url>/post/loop_move_array/</url><categories><category>algorithm</category></categories><tags><tag/></tags><content type="html">循环移动数组 给定一个数组和正整数n(n 小于数组长度), 请求此数组循环左移n个位置, 要求空间复杂度O(1)
例子:
输入: [1, 2, 3, 4, 5] 2 输出: [3, 4, 5, 1, 2] func moveArrary(arr []int, n int) []int { for i := 0; i &lt; n/2; i++ { arr[i], arr[n-i-1] = arr[n-i-1], arr[i] } fmt.Println(arr) length := len(arr) for i := n; i &lt; (n+(length-n)/2); i++ { fmt.Println(i, length-1-i+n) arr[i], arr[length-1-i+n] = arr[length-1-i+n], arr[i] } fmt.Println(arr) for i := 0; i &lt;= length/2; i++ { arr[i], arr[length-1-i] = arr[length-1-i], arr[i] } return arr } Resource 韩信点兵: https://mp.weixin.qq.com/s/v3Fw8_QRq9dKm-Q4VFuM-g</content></entry><entry><title>Linux的五种io模型</title><url>/post/io_model_in_linux/</url><categories><category>linux</category></categories><tags><tag/></tags><content type="html">Resource Linux的五种io模型: https://mp.weixin.qq.com/s/qCZKTJB25kSGVhlAnGFaEQ</content></entry><entry><title>从零开始搭建创业公司后台技术栈</title><url>/post/build_backend_from_zero/</url><categories><category>system_design</category></categories><tags><tag/></tags><content type="html">Resource 从零开始搭建创业公司后台技术栈: https://mp.weixin.qq.com/s/Ukn9AVvxVxB6phKcBykVsw 集团IT技术架构规划方案: https://mp.weixin.qq.com/s/RGvWIlm4SO24ofW5icK3iw</content></entry><entry><title>调度系统设计</title><url>/post/scheduling_system_design/</url><categories><category>system_design</category></categories><tags><tag/></tags><content type="html">Resource 调度系统设计精要: https://www.sohu.com/a/370119881_657921</content></entry><entry><title>分布式事务之底层原理揭秘</title><url>/post/distributed_transaction/</url><categories><category>system_design</category></categories><tags><tag>distributed</tag></tags><content type="html">Resource 分布式事务之底层原理揭秘: https://mp.weixin.qq.com/s/jJBlSHLTvxNPl3maYMpccQ</content></entry><entry><title>技术演讲</title><url>/post/tech_speech/</url><categories><category>career</category></categories><tags><tag/></tags><content type="html">Resource 技术演讲,如何快速做到80分: https://mp.weixin.qq.com/s/u6gtUF5yz80vRpImVOloaw 聊聊如何做技术分享: https://mp.weixin.qq.com/s/uyrQ528udyhydcB1mRMlOg</content></entry><entry><title>数据分片</title><url>/post/data_fragment/</url><categories><category>system_design</category></categories><tags><tag/></tags><content type="html">Resource 带着问题学习分布式系统之数据分片: https://mp.weixin.qq.com/s/M0QvQ6JVQVZAN8kucPyoaA</content></entry><entry><title>限流器</title><url>/post/rate_limiter/</url><categories><category>system_design</category></categories><tags><tag/></tags><content type="html">Resource 限流算法实践: https://www.infoq.cn/article/iPxNuQWU3lGwXc8J7tZW</content></entry><entry><title>虚拟内存</title><url>/post/virtual_memory/</url><categories><category>linux</category></categories><tags><tag/></tags><content type="html">Resource 虚拟内存: https://mp.weixin.qq.com/s/g5I7un3-lpffAk6UFes8DA</content></entry><entry><title>职业-他人感想与经历</title><url>/post/career/</url><categories><category>career</category></categories><tags><tag/></tags><content type="html">Resource 结束12年深漂生活!武汉,我来了!: https://mp.weixin.qq.com/s/I5WbUUA2DEZvcFftEw6GrQ 大龄码农的新西兰移民之路: https://mp.weixin.qq.com/s/H49uoTrhWrFVlsgSaPLSBA 北漂8年,撤退杭州: https://mp.weixin.qq.com/s/o9-MkdBZ-7mg3jdRvOLXPg 持炬之人要内观自我: https://mp.weixin.qq.com/s/UL1yHctmVtIvz2PnmAAzNw 你会正确与HR谈薪资吗: https://mp.weixin.qq.com/s/umlWthKW8Ij4jPy0GiCzGw Counter Offer Template: https://helloacm.com/an-example-email-to-negotiate-your-package/ 辞职卖猪肉,曾想做律师: https://mp.weixin.qq.com/s/8iHfKx_qEuR1jFzyQXvT4A 离开大厂,我去环球影城出租魔法袍: https://mp.weixin.qq.com/s/0XRu1xF6qwJXcjKpYTNoiA 我是如何晋升专家岗的: https://mp.weixin.qq.com/s/A-itUydo1fXY57uArFsjrQ 七个对我最重要的职业建议: https://www.ruanyifeng.com/blog/2015/09/career-advice.html 什么时候你该换工作了: https://www.hbrchina.org/2018-02-28/5885.html how to become a full time creator: https://blog.pragmaticengineer.com/how-to-become-a-full-time-creator/ 五十岁咸鱼翻身转码上岸: https://www.1point3acres.com/bbs/thread-918311-1-1.html</content></entry><entry><title>Golang语言遇到的bug</title><url>/post/bug_in_golang/</url><categories><category>language</category></categories><tags><tag>bug</tag></tags><content type="html">Resource 分析Go time.After 引起内存暴增OOM问题: https://mp.weixin.qq.com/s/jaIdqF0bFpHz_eyPsHjbwQ Go gctrace 引起runtime 调度阻塞: https://mp.weixin.qq.com/s/eHEvVzmpPYXAQgcdd_B55w 高并发场景下 disk io 引发的高时延问题: https://mp.weixin.qq.com/s/e3peaNGwIuNkldvnP0pDug 一个日志打点引发的OOM: https://mp.weixin.qq.com/s/8puLJSJ3GeXnmDlMxdzGgQ 为什么Go模块在下游服务抖动恢复后,CPU占用无法恢复: https://mp.weixin.qq.com/s/398lcIRUBgVcWBqJTgQs8g iptables redirect 劫持跳转引起Go服务故障: https://mp.weixin.qq.com/s/fmrw-33cbKLdAkkMHyOrbw select死锁引起的问题: https://mp.weixin.qq.com/s/ErTjq9qAufVF6yhBXfFVGw time.After死锁问题: https://blog.csdn.net/qq_39397165/article/details/108698640</content></entry><entry><title>Go中的init函数</title><url>/post/init_in_golang/</url><categories><category>language</category></categories><tags><tag/></tags><content type="html">Resource init函数: https://studygolang.com/articles/13865?fr=sidebar</content></entry><entry><title>有赞TCP网络编程最佳实践</title><url>/post/you_zan_tcpwang_luo_bian_cheng_zui_jia_shi_jian/</url><categories><category>network</category></categories><tags><tag/></tags><content type="html">Reference https://tech.youzan.com/you-zan-tcpwang-luo-bian-cheng-zui-jia-shi-jian/?hmsr=toutiao.io&utm_campaign=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io</content></entry><entry><title>brownie使用</title><url>/post/brownie/</url><categories><category>blockchain</category></categories><tags/><content type="html">Resource brownie networks add development attack20221019 cmd=ganache-cli host=&ldquo;http://127.0.0.1&rdquo; fork=&ldquo; https://mainnet.infura.io/v3/$WEB3_INFURA_PROJECT_ID@15780866" accounts=10 mnemonic=$MNEMONIC port=8545 brownie使用: https://betterprogramming.pub/part-1-brownie-smart-contracts-framework-for-ethereum-basics-5efc80205413#:~:text=Brownie%20is%20a%20robust%2C%20easy,to%20initialize%20or%20integrate%20them .</content></entry><entry><title>btc地址生成</title><url>/post/btc_address/</url><categories><category>blockchain</category></categories><tags/><content type="html">btc地址生成 Get you public key Perform Sha256 on your Public key Perform RIPEMD160 hashing on the result of Sha256 hash Add version byte in front of RIPEMD160 hash (0x00 for main net, 0x6f for testnet) Perform Sha256 hash on the extended RIPEMD160 hash Perform Sha256 hash on the result of the previous step Take the first 4 byte of the second Sha256 result, it’s the checksum Add 4 bytes of checksum in previous step, at the end of RIPEMD160 hash of step 4 (This is 25-byte bitcoin address) Convert the result from byte string to base58 string Resource 密钥生成地址: https://medium.com/swlh/create-raw-bitcoin-transaction-and-sign-it-with-golang-96b5e10c30aa 密钥衍生: https://stevenocean.github.io/2018/09/23/generate-hd-wallet-by-bip39.html</content></entry><entry><title>alloc in golang</title><url>/post/alloc_in_golang/</url><categories><category>language</category></categories><tags><tag/></tags><content type="html">Resource Go内存管理: https://mp.weixin.qq.com/s/PNRhtdS_gZVTtrkkRmx7yA Go内存管理和分配: https://mp.weixin.qq.com/s/6jAVPSnw5UPotCg40CAB8Q</content></entry><entry><title>escape in golang</title><url>/post/escape_in_golang/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html">Resource 通过Golang内存逃逸分析讨论GC的压力: https://www.jianshu.com/p/10e83dc6cd64?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io</content></entry><entry><title>System Call</title><url>/post/system_call/</url><categories><category>linux</category></categories><tags><tag/></tags><content type="html">Resource 系统调用原理和实现: https://blog.csdn.net/gatieme/article/details/50779184 用户态模式和内核态模式区分: https://blog.csdn.net/github_37382319/article/details/111833529 增加系统调用的方法: https://www.cnblogs.com/yongheng20/p/4947702.html vdso技术: https://lishiwen4.github.io/linux/vdso-and-syscall</content></entry><entry><title>microservice</title><url>/post/microservice_gateway/</url><categories><category>system_design</category></categories><tags><tag>gateway</tag></tags><content type="html">Resources https://mp.weixin.qq.com/s/So_HYu90vGcvE4qU5D9m4g</content></entry><entry><title>MySQL</title><url>/post/mysql/</url><categories><category>components</category></categories><tags><tag>mysql</tag></tags><content type="html">数据库架构 https://zhuanlan.zhihu.com/p/43736857#:~:text=MySQL%E6%98%AF%E4%B8%80%E4%B8%AA%E5%85%B8%E5%9E%8B%E7%9A%84,Client%E8%B4%9F%E8%B4%A3%E8%BF%9E%E6%8E%A5%E5%88%B0Server%E3%80%82 https://mp.weixin.qq.com/s/zL4QP15W1Zd7HUm_wTbE2Q InnoDB架构 innodb架构: https://xie.infoq.cn/article/48544a3041634ca9ce62eaff4 缓冲池(buffer pool),这次彻底懂了: https://juejin.cn/post/6844903874172551181 自适应哈希: https://www.cnblogs.com/geaozhang/p/7252389.html 写缓冲(change buffer),这次彻底懂了: https://its401.com/article/shenjian58/93691224 log buffer: https://blog.51cto.com/jyjstack/2548512 double wirte buffer: https://blog.csdn.net/shenjian58/article/details/103501888 innodb_file_per_table(表空间): https://www.jianshu.com/p/bfee26584746 mysql事务 mysql事务总结: https://zhuanlan.zhihu.com/p/29166694 InnoDB索引 InnoDB索引,终于懂了: https://mp.weixin.qq.com/s/39XgTTC2VUVGq0EIlpb3WA 数据库索引,为什么设计成这样: https://mp.weixin.qq.com/s/0x5T-wbAoNVHkKTT61lIUQ InnoDB并发为何如此高: https://mp.weixin.qq.com/s/fmzaIobOihKKZ7kyZQInTg order by: https://mp.weixin.qq.com/s/F2KkRnimpAUnQ4K8qaQJVg 联合索引: https://www.cnblogs.com/rjzheng/p/12557314.html 缓存: https://mp.weixin.qq.com/s/4oGRpNyD9ZhNvvJuvFOUXQ 数据库、缓存双写一致性: https://www.jianshu.com/p/2936a5c65e6b MySQL备份: https://mp.weixin.qq.com/s/9lmuRJY1z87t1c9mAdYqdA MyISAM与InnoDB比较: https://segmentfault.com/a/1190000019263351 mysql高可用方案: https://zhuanlan.zhihu.com/p/25960208 mysql集群方式: https://zhuanlan.zhihu.com/p/25960208 容灾 PXB热备份: https://mp.weixin.qq.com/s/HeLcuxBZr4oP_oraMjnIHQ 数据库中间件 拆分库 什么时候引入数据库中间件: https://mp.weixin.qq.com/s/fGIXDxe6fIN2bhYbwP5g_A 基因法分库: https://mp.weixin.qq.com/s/H6OENyLu6K7v3OJDWa5klw</content></entry><entry><title>system design</title><url>/post/system_design/</url><categories><category>system_design</category></categories><tags><tag/></tags><content type="html"><![CDATA[系统设计开篇 <System Design Interview: An Insider’s Guide> Resources 亿级数据库秒级扩容: https://blog.csdn.net/z50L2O08e2u4afToR9A/article/details/89839471 互联网架构高可用: https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651962050&idx=1&sn=f60b8bb833fe3425f5227da42e3b3adf&chksm=bd2d0f1e8a5a8608f81d42a16eea476d0bd4763f84f9a008ed616d1cfa050a4015780f898eb1&scene=21#wechat_redirect 业务层,到底需不需要服务化: https://mp.weixin.qq.com/s/81Phkxg13ligFEWPTwpsfg 无锁缓存,每秒10万并发: https://mp.weixin.qq.com/s/BfuRWaB7RDjpGmQbZdmMZw 第三方服务挂了,怎样解决: https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651964475&idx=1&sn=e452b648f52cb9a5deb711334b0bb354&chksm=bd2d71e78a5af8f163e7af84855a523e847732be4ba486f68a55a4a153764096b38cb60221e9&scene=21#wechat_redirect 基于DAG的任务编排框架/平台: https://mp.weixin.qq.com/s/5zsuF_EsL6tcn9uNEGpmDA 大型交易系统之高并发: https://blog.csdn.net/kaijixin/article/details/105605067 架构优化与业务迭代: https://xie.infoq.cn/article/8feef918449fd9eb1118e9d45 ]]></content></entry><entry><title>write an rpc from scratch</title><url>/post/write_an_rpc_from_scratch/</url><categories><category>components</category></categories><tags><tag>rpc</tag></tags><content type="html">Reference write an rpc from scratch: https://alexanderell.is/posts/rpc-from-scratch/</content></entry><entry><title>golang中的context</title><url>/post/context_in_golang/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html"><![CDATA[Reference: goroutine如何退出: https://geektutu.com/post/hpg-exit-goroutine.html context包解析: https://mp.weixin.qq.com/s?__biz=MzUxMDI4MDc1NA==&mid=2247486310&idx=1&sn=e1d998f35293e1bf259bf51f2f7ef6bd&scene=21#wechat_redirect ]]></content></entry><entry><title>Distributed System</title><url>/post/distributed_system/</url><categories><category>system_design</category></categories><tags><tag>redis</tag><tag>distributed</tag></tags><content type="html">分布式系统 分布式事务 大致论述: https://www.cnblogs.com/savorboard/p/distributed-system-transaction-consistency.html</content></entry><entry><title>分布式锁</title><url>/post/distributed_lock/</url><categories><category>system_design</category></categories><tags><tag>redis</tag><tag>distributed</tag></tags><content type="html">实现 分布式锁redis锁和zookeeper锁: https://mp.weixin.qq.com/s/lrSQBK-Kihkj6994kQFpUQ 未分类 企业级高可用分布式锁原理剖析及设计实践 分布式锁应用场景 掌握分布式锁设计核心要点 能够根据实际需求做出合理的设计选型</content></entry><entry><title>Redis</title><url>/post/redis/</url><categories><category>components</category></categories><tags><tag>redis</tag></tags><content type="html">Redis架构 Redis系统架构中各个处理模块是干什么的?: https://learn.lianglianglee.com/%E4%B8%93%E6%A0%8F/300%E5%88%86%E9%92%9F%E5%90%83%E9%80%8F%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98-%E5%AE%8C/19%20Redis%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84%E4%B8%AD%E5%90%84%E4%B8%AA%E5%A4%84%E7%90%86%E6%A8%A1%E5%9D%97%E6%98%AF%E5%B9%B2%E4%BB%80%E4%B9%88%E7%9A%84%EF%BC%9F.md Redis命令 redis命令目录: http://doc.redisfans.com/ 数据结构和对象 动态字符串
常数复杂度获取字符串长度 杜绝缓冲区溢出 减少修改字符串时带来的内存重新分配次数 兼容部分C字符串函数 链表
hash
rehash是渐进式的
跳跃表
每个跳跃表节点的层高都是1至32之间的随机数;在同一个跳跃表中,多个节点可以包含相同的分值,但每个节点的成员对象必须是唯一的;跳跃表中的节点按照分值大小进行排序,当分值相同时,节点按照成员对象的大小进行排序
整数集合
每当我们要将一个新元素添加到整数集合里面,并且新元素的类型比整数集合现有所有元素的类型都要长时,整数集合需要先进行升级,然后才能将新元素添加到整数集合。不支持降级
升级整数集合
根据新元素的类型,扩展整数集合底层数组的空间大小,并为新元素分配空间 将底层数组现有的所有元素都转换成新元素相同的类型,维持底层数组的有序性质不变 将新元素添加到底层数组 压缩列表
entry的组成:
previous_entry_length encoding:记录当前节点的数据类型及长度 content: 实际数据body 连锁更新问题
压缩列表是一种为解决内存而开发的顺序型数据结构
对象
redis对象实现了基于引用技术的内存回收机制;实现了基于引用技术的对象共享机制
为了确保只有指定类型的键可以执行某种特定操作,在执行一个类型特定的命令之前,会先检查输入键的类型和对应命令
字符串对象
mbstr 编码是专门用于保存短字符串的一种优化编码方式, 这种编码和 raw 编码一样, 都使用 redisObject 结构和 sdshdr 结构来表示字符串对象, 但 raw 编码会调用两次内存分配函数来分别创建 redisObject 结构和 sdshdr 结构, 而 embstr 编码则通过调用一次内存分配函数来分配一块连续的空间, 空间中依次包含 redisObject 和 sdshdr 两个结构
列表对象
哈希对象
集合对象
有序集合对象
有序集合同时使用跳跃表和字典来实现。(范围查找和等值查找)
redis对象淘汰: https://zhuanlan.zhihu.com/p/105587132 quicklist实现: https://juejin.cn/post/6844904023418486791 redis数据结构: https://i6448038.github.io/2019/12/01/redis-data-struct/ 事件循环 Redis服务器是一个事件驱动程序,服务器需要处理以下两类事件:
文件事件: Redis服务器通过套接字和客户端进行连接,而文件事件就是服务器对套接字操作的抽象
时间事件: 一些操作需要在给定时间点执行
redis服务器将所有时间事件都放在一个无序链表中,每当有时间事件执行器运行时,它遍历整个链表,查找所有已到达的时间事件,并调用相应的事件处理器。(正常情况下只有serverCron一个时间事件,benchmark也只是两个时间事件)
事件调度规则:
最早时间事件 文件事件循环,逼近时间事件when 事件处理有需要时主动让出执行权。例如写入字节数超过了一个预设值命令回复器会主动break,将余下的数据留到下次再写 Redis高可用 持久化
AOF
AOF重写
RDB
SAVE和BIGSAVE
redis持久化: https://www.jianshu.com/p/d3ba7b8ad964 主从复制
redis主从复制: https://blog.csdn.net/weixin_47067712/article/details/118569713 SYNC和PSYNC 复制积压缓冲区。当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区中 哨兵机制
Sentinel是Redis高可用解决方案;由一个或多个sentinel instance组成的系统监视任意多个主服务器及其所有从服务器
分布式集群
redis集群三种方式: https://segmentfault.com/a/1190000022808576 哨兵机制: https://zhuanlan.zhihu.com/p/95678924 redis哈希槽和一致性哈希区别: https://www.jianshu.com/p/4163916a2a8a Redis应用 分布式锁: https://www.jianshu.com/p/83224c0f3bb9 热点缓存的思考: https://blog.csdn.net/ck784101777/article/details/101367821 未分类 Redis生产架构选型解决方案: https://mp.weixin.qq.com/s/aKmagp5dyFOKgEjcZ_iwaw redis为何那么快: https://mp.weixin.qq.com/s/ZcHLSDVxdG6qXxLl5-p8qQ redis优化
缩短键值对的存储长度; 内容越大需要的持久化时间就越长,需要挂起的时间就越长 内容越大在网络传输的内容越多,需要的时间就越多 内容越大占用的内存阅读,就会更频繁地触发内存淘汰机制 使用 lazy free(延迟删除)特性; 设置键值的过期时间; 禁用长耗时的查询命令; 使用slowlog优化耗时命令; 使用pipeline批量操作数据; 避免大量数据同时失效; 客户端使用数据计算操作; 限制redis内存大小; 使用物理机而非虚拟机安装redis服务; 选择更好的数据持久化策略; 禁用THP特性; 使用分布式架构来增加读写速度</content></entry><entry><title>golang中的defer</title><url>/post/defer_in_golang/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html">Reference defer分析: https://studygolang.com/articles/742 defer实现原理: https://segmentfault.com/a/1190000020614493 defer优化: https://zhuanlan.zhihu.com/p/401339057 deferreturn函数: https://www.modb.pro/db/88823</content></entry><entry><title>golang中的error</title><url>/post/error_in_goalng/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html">Reference wrapp error解析: https://studygolang.com/articles/23346?fr=sidebar</content></entry><entry><title>golang中的select</title><url>/post/select_in_golang/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html">Reference: select关键字: https://draveness.me/golang/docs/part2-foundation/ch05-keyword/golang-select/</content></entry><entry><title>golang哈希表</title><url>/post/hashmap_in_golang/</url><categories><category>language</category></categories><tags><tag>hashmap</tag></tags><content type="html">Reference 理解Golang哈希表的原理: https://juejin.cn/post/6844903793927143438 golang哈希表: https://draveness.me/golang/docs/part2-foundation/ch03-datastructure/golang-hashmap/ golang的for-range(哈希表的遍历): https://draveness.me/golang/docs/part2-foundation/ch05-keyword/golang-for-range/</content></entry><entry><title>sort algorithm</title><url>/post/sort_algorithm/</url><categories><category>algorithm</category></categories><tags><tag/></tags><content type="html"><![CDATA[排序算法复杂度 冒泡排序 将第一个数A跟第二个数B相比较,如果A>B,那么交换A,B位置,接下来将第二个数跟第三个数比较
func bubbleSort(array []int) { length := len(array) for i := 0; i < length; i++ { for j := 0; j < length-1-i; j++ { if (array[j] > array[j+1]) { array[j], array[j+1] = array[j+1], array[j] } } } } 第一次遍历, n-1这个数已经有序,第i次遍历,n-1-i后续数已经有序。外围for循环判断式可改为 i <lenght-1,因为当i=length-1时,只剩下第0个数本身,不需要再比较 冒泡排序第一次执行,arr[n-1]为最大数值,较大的数不断往后冒泡。 第i次排序后,[n-i, n-1]有序,如果在第i次中没有发生交换,那么说明整个序列已经有序
func bubbleSort(array []int) { length := len(array) for i := 0; i < length; i++ { flag := false for j := 0; j < length-1-i; j++ { if (array[j] > array[j+1]) { array[j], array[j+1] = array[j+1], array[j] flag = true } } if !flag { break } } 第i次排序后,[n-i, n-1]有序,如果第i次排序中,在[lastPos, n-i]也没有发生交换,那么说明[lastPos, n-1]已经有序,下次循环区间可由[0, n-i-1]变为[0, n-lastPos-1],缩小了循环空间
func bubbleSort(array []int) { length := len(array) for i := 0; i < length; i++ { flag := false lastPos := i tmp := 0 for j := 0; j < length-1-lastPos; j++ { if (array[j] > array[j+1]) { array[j], array[j+1] = array[j+1], array[j] flag = true tmp = j } } lastPos = tmp if !flag { break } } } 插入排序 向一个已经排序的序列中插入一个新值
func insertSort(array []int) { length := len(array) for i := 1; i < length; i++ { j := i-1 for ; j >= 0; j-- { if array[j] > array[j+1] { array[j], array[j+1] = array[j+1], array[j] } } } } 插入排序时间复杂度: 1 + 2 + … + n = O(n^2),空间复杂度O(1)。它的平均时间复杂度、最差复杂度、最好复杂度都是一样的。 在排序过程中,如果数组元素不与排序规则一致,会导致大量元素交换。对于数组形式没有优化,如果元素以链表形式,那么可以将索引记住,在合适的地方才插入
希尔排序 将整个待排序的记录序列分割成为若干个子序列分别进行直接插入排序 错误实现:
func shellSort(array []int) { length := len(array) d := length / 2 for ; d > 0; d /= 2 { i := d for ; i < length; i += d { j := i - d for ; j >=0; j -=d { if array[j] > array[j+d] { array[j], array[j+d] = array[j+d], array[j] } } } } } 上述实现仅是在插入排序前增加了部分元素的交换,没有运用到分组思想 正确实现:
func shellSort(array []int) { length := len(array) d := length / 2 for ; d > 0; d /= 2 { i := d for ; i < length; i ++ { j := i - d for ; j >=0; j -=d { if array[j] > array[j+d] { array[j], array[j+d] = array[j+d], array[j] } } } } } 选择排序 在一个序列中搜索最小的数,在排除此数的序列中搜索最小的数,如此反复 ``` func selectSort(array []int) { length := len(array) for i := 0; i < length; i++ { minIndex := i for j := i+1; j < length; j++ { if array[minIndex] > array[j] { minIndex = j } } array[i], array[minIndex] = array[minIndex], array[i] } } ``` 选择排序是不稳定的,在交换时相对顺序会更改,例如序列(5, 5, 2, 3) 堆排序 2-路归并排序 归并排序时一个分治法典型应用,将已有序的子序列合并,得到完全有序的序列。 将长度为n的输入序列分成两个长度为n/2的子序列;对这两个子序列分别采用归并排序;将两个排序好的子序列合并成一个最终的序列 边界条件确定:当子序列有且仅有一个元素时,直接返回子序列
func merge(array []int, low, mid, high int) { var tmp []int i, j := low, mid for ; i <= mid && j <= high; { if (array[i] < array[j]) { tmp = append(tmp, array[i]) i++ } else if (array[i] > array[j]) { tmp = append(tmp, array[j]) j++ } else { tmp = append(tmp, array[i]) tmp = append(tmp, array[j]) i++ j++ } } if i > mid { tmp = append(tmp, array[j:]...) } if j > high { tmp = append(tmp, array[i:mid+1]...) } if high-low+1 != len(tmp) { printArray(array) println(low) println(mid) println(high) } for k := 0; k < len(tmp); k++ { array[low] = tmp[k] low++ } } func mergeSort(array []int, low, high int) { if low >= high { return } mid := (high + low) / 2 mergeSort(array, low, mid) mergeSort(array, mid+1, high) merge(array, low, mid, high) } 在第一遍实现时,没有注意到merge合并的条件必须时[low,mid]和[mid+1,high]
func merge(array []int, low, mid, high int) { var tmp []int i, j := low, mid+1 for ; i <= mid && j <= high; { if (array[i] < array[j]) { tmp = append(tmp, array[i]) i++ } else if (array[i] > array[j]) { tmp = append(tmp, array[j]) j++ } else { tmp = append(tmp, array[i]) tmp = append(tmp, array[j]) i++ j++ } } if i > mid && j <= high{ tmp = append(tmp, array[j:high+1]...) } if j > high && i <= mid { tmp = append(tmp, array[i:mid+1]...) } fmt.Println("--------------------------------") fmt.Println(array[low:high+1]) fmt.Println(low, mid, high) fmt.Println(tmp) fmt.Println("--------------------------------") for k := 0; k < len(tmp); k++ { array[low] = tmp[k] low++ } } func mergeSort(array []int, low, high int) { if low >= high { return } mid := (high + low) / 2 mergeSort(array, low, mid) mergeSort(array, mid+1, high) merge(array, low, mid, high) } 快速排序 快速排序同样时运用分治法来排序。从序列中挑出一个元素,称为基准;小于基准值的元素放在左边,大于则放在右边,得到两个序列和一个基准值;对子序列做同样工作;当子序列仅有1个元素或0个时,结束
func quicSort(array []int, l, r int) { if l >= r { return } i, j := l, r pviot := array[l] for ; i <= j; { for ; i < j && array[i] <= pviot; i++ { } for ; i < j && array[j] > pviot; j-- { } if i < j { array[i], array[j] = array[j], array[i] i++ j-- } else { break } } array[i], array[l] = array[l], array[i] fmt.Println("-------------------------------") printArray(array) fmt.Println(i, j, l, r) fmt.Println("-------------------------------") quicSort(array, l, i-1) quicSort(array, i+1, r) } 上述算法思路:以pviot(array[l])为基准,从左找小于或等于pviot的数,找到后记下索引i值;从右找大于pviot的数,找到后记下索引j值;如果i < j,那么交换i,j的值;如果 i找到或j找到,但另一个没有找到,此时i==j,跳出循环,将i,l对应值互换(显然,这一步时错误的,如果找到j但没找到i,互换后pviot值左边有一个比它大的数)。 那么,找到索引i值,k := l, 将arr[k] = arr[i]; k = i,留下空位k,找到索引j值,arr[k] = arr[j]; k = j, 索引k一直在区间[i, j]中变动,直到i == j,此时 k == i,将 arr[k] = pviot。之后再快排左右两个子序列。(必须从右边寻找起,因为开始k起始值在左边)
func quicSort(array []int, l, r int) { if l >= r { return } i, j := l, r k := l pviot := array[l] fmt.Println("++++++++++++++++++++++++++++++++") fmt.Println(i, j, k, pviot) for ; i < j; { for ; i < j && array[j] > pviot; j-- { } if i < j { array[k] = array[j] k = j j-- } fmt.Println(array[l:r+1]) for ; i < j && array[i] <= pviot; i++ { } if i < j { array[k] = array[i] k = i i++ } fmt.Println(array[l:r+1]) } fmt.Println("++++++++++++++++++++++++++++++++") array[k] = pviot fmt.Println("----------------------------") fmt.Println(k, i, j) fmt.Println(pviot) printArray(array) fmt.Println("----------------------------") quicSort(array, l, k-1) quicSort(array, k+1, r) } 上述算法{10, 4, 3}通过,{10, 4, 7,2, 13, 89, 4, 3}没有通。实验数据对比得出,k值变动并不会一直在区间[i,j],这是因为每次交换数值时,k值会被移动到区间外,发现i < j条件改为 i < k可能可行,理论推导不可行,k值变动中会有如下情况{14, 89, 3}这个序列中k = 0, j = 2, 于是k, j 交换对应值, k=2,此时 元素89不能被移动到k值右边。脑已经有些乱了,推倒重新开始实现
func quickSort(arr []int, left, right int) { if left >= right { return } //分子序列 pviot := arr[left] i, j := left+1, right for ;i < j; { for ; i < j && arr[i] <= pviot; i++ { } for ; i < j && arr[j] > pviot; j-- { } if i < j { arr[i], arr[j] = arr[j], arr[i] i++ j-- } } if arr[i] <= pviot { arr[left], arr[i] = arr[i], arr[left] } else { for k := left; k < i-1; k++ { arr[k] = arr[k+1] } i = i-1 arr[i] = pviot } quickSort(arr, left, i-1) quickSort(arr, i+1, right) } 上述实现正确,但i==j这个元素处理会导致过多的数组移动,能否在一开始就将数组移动,那么必须利用空位pviot
k := left for ;i < j; { for ; i < j && arr[i] <= pviot; i++ { } for ; i < j && arr[j] > pviot; j-- { } if i < j { arr[k] = arr[j] arr[j] = arr[i] k = i i++ j-- } } 使用k表示空位索引,如果从左找起,找到的i==j值远离k,一样存在区间[k, i]的移动;所以需要从右开始找起,同样会存在[k, i]区间,那么从一遍找起
func quickSort(arr []int, left, right int) { if left >= right { return } //分子序列 pviot := arr[right] i := left-1 for j := left; j <= right-1; j++ { if arr[j] < pviot { i++ arr[i], arr[j] = arr[j], arr[i] } } arr[i+1], arr[right] = arr[right], arr[i+1] quickSort(arr, left, i) quickSort(arr, i+2, right) } 上述实现如果将pviot := arr[left], j := left+1,此实现如下:
func quickSort(arr []int, left, right int) { if left >= right { return } //分子序列 pviot := arr[left] i := left for j := left+1; j <= right; j++ { if arr[j] < pviot { i++ arr[i], arr[j] = arr[j], arr[i] } } arr[i+1], arr[left] = arr[left], arr[i+1] quickSort(arr, left, i) quickSort(arr, i+2, right) } i+1 这一步有可能 i == right,导致数组溢出 请参考 https://www.cnblogs.com/sunriseblogs/p/10009890.html 20201214 插入排序
void insertSort(int arr[], int n) { for(int i = 0; i < n-1; i++) { int j = i; int tmp = arr[j+1]; for(; j >= 0; j--) { if(tmp >= arr[j]) { break; } arr[j+1] = arr[j]; } arr[j+1] = tmp; print(arr, n); } } 归并排序
void merge(int arr[], int low, int mid, int high) { int *tmp = new int[high-low+1]; int i = low, j = mid + 1; int k = 0; for(; i <= mid && j <= high;) { if(arr[i] < arr[j]) { tmp[k++] = arr[i++]; } else if(arr[i] > arr[j]) { tmp[k++] = arr[j++]; } else { tmp[k++] = arr[i++]; tmp[k++] = arr[j++]; } } while(i <= mid) { tmp[k++] = arr[i++]; } for(int i = low, j = 0; j < k; i++,j++) { arr[i] = tmp[j]; } } void mergeSort(int arr[], int low, int high) { if(low >= high) { return; } int mid = low + (high - low)/2; mergeSort(arr, low, mid); mergeSort(arr, mid+1, high); merge(arr, low, mid, high); } 快速排序
void Qsort(int arr[], int low, int high) { if(high <= low) return; int i = low, j = high + 1; int key = arr[low]; while(true) { while(arr[++i] < key) { if (i == high) { break; } } while(arr[--j] > key) { if(j == low) { break; } } if(i >= j) { break; } swap(arr[i], arr[j]); } swap(arr[j], arr[low]); Qsort(arr, low, j-1); Qsort(arr, j+1, high); } 冒泡排序 void bulbSort(int arr[], int n) { int i = 0; int len = n-1; for(; i < n; i++) { int flag = true; int tmp; for(int j = 0; j < len; j++) { if(arr[j] > arr[j+1]) { swap(arr[j], arr[j+1]); flag = false; tmp = j; } } len = tmp; if(flag) { break; } } } 基数排序 void radixSort(int *arr, int n) { int maxBit = 2; for(int i = 0; i < maxBit; i++) { int buf[10][100] = {0}; int pow = 1; for(int j = 0 ; j < i; j++) { pow *= 10; } for(int i = 0; i < n; i++) { int index = (arr[i] / pow) % 10; for (int j = 0; j < 100; j++) { if (buf[index][j] == 0) { buf[index][j] = arr[i]; break; } } } for(int i = 9, k = 0; i >= 0; i--) { for(int j = 0; j < 100; j++) { if(buf[i][j] != 0) { arr[k++] = buf[i][j]; } } } } } Reference 十大经典排序算法(动图演示): https://www.cnblogs.com/onepixel/articles/7674659.html 冒泡排序的改进: https://blog.csdn.net/tjunxin/article/details/8711389 归并排序的递归和迭代解法:https://zhuanlan.zhihu.com/p/124356219
https://www.geeksforgeeks.org/quick-sort/ 快速排序: https://mp.weixin.qq.com/s/9JScICC2Tgz8vd4OPCzFqQ 跳跃表: https://mp.weixin.qq.com/s/XY1gPvdV7hWTDYNDDW2zoQ 希尔排序: https://www.cnblogs.com/chengxiao/p/6104371.html ]]></content></entry><entry><title>二分查找</title><url>/post/binary_search/</url><categories><category>algorithm</category></categories><tags><tag/></tags><content type="html"><![CDATA[二分查找 从一个从小到大的序列中找寻数goal, 采用二分查找。
思路:选择两个变量,分别赋值区间的首尾;选取区间的中间值与goal相比较,如果goal较大,那么将begin = middle, 如果goal较小, 那么 end = middle;考虑到begin和end可能相等,goal会在边界被找到,那么循环条件为begin <= end
func binarySearch(arr []int, goal int) (index int) { begin := 0 end := len(arr)-1 for ;begin <= end; { middle := (end + begin) / 2 if arr[middle] < goal { begin = middle } else if arr[middle] > goal { end = middle } else { return middle } } return -1 } 上述思路未考虑到边界条件中这种情况。例如区间为[5, 6],goal>arr[5]的情况,此时区间此后将一直是[5,6]不会向前移动。(此种情况不会出现左边界,即goal == arr[5])
思路:当arr[middle] < goal时,begin = middle+1;当arr[middle] > goal时, end = middle or end = middle - 1。加1或减1不会使过程中出现界限被超出
func binarySearch(arr []int, goal int) (index int) { begin := 0 end := len(arr)-1 for ;begin <= end; { //"=" 展示可能出现边界情况 middle := (end + begin) / 2 if arr[middle] < goal { begin = middle+1 } else if arr[middle] > goal { end = middle-1 } else { return middle } } return -1 } 二分查找中个人认为区间应该始终为左闭右闭区间。不应该使循环代码中begin,end变量来维持区间的开闭性质
func search( nums []int , target int ) int { // write code here low, high := 0, len(nums)-1 for ; low <= high; { mid := low + (high-low) / 2 fmt.Println(low, high, mid) if nums[mid] < target { low = mid+1 } else if nums[mid] > target { high = mid-1 } else { j := mid-1 for ; j >= 0; j-- { if (nums[j] != target) { break } } return j+1 } } return -1 } 测试用例[1, 2, 2, 3, 4],2 输出结果应为1
resource http://www.cppblog.com/converse/archive/2009/10/05/97905.html ]]></content></entry><entry><title>Java线程池实现原理及其在美团业务中的实践</title><url>/post/thread_pooling_practice_in_meituan/</url><categories><category>linux</category></categories><tags><tag>thread</tag></tags><content type="html">Java线程池实现原理及其在美团业务中的实践 Portal: https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html</content></entry><entry><title>为什么TCP协议有粘包问题</title><url>/post/why_the_design_tcp_message_frame-%E5%89%AF%E6%9C%AC/</url><categories><category>network</category></categories><tags><tag>tcp</tag></tags><content type="html">为什么TCP协议有粘包问题 Portal: https://draveness.me/whys-the-design-tcp-message-frame/</content></entry><entry><title>NAT 穿透</title><url>/post/nat_in_tcp/</url><categories><category>network</category></categories><tags/><content type="html">NAT穿透在TCP中的运用 Portal: https://www.baseclass.io/huffman-coding/</content></entry><entry><title>Huffman-coding</title><url>/post/huffman_coding/</url><categories><category>algorithm</category></categories><tags/><content type="html">Huffman-coding Portal: https://www.baseclass.io/huffman-coding/</content></entry><entry><title>一道关于gorotine的死锁问题</title><url>/post/deadlock_in_goroutine/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html"><![CDATA[一道关于goroutine死锁 package main import ( "fmt" "time" ) func main() { ch := make(chan int) go fmt.Println(<-ch) ch <- 5 time.Sleep(2 * time.Second) } 上述结果我选择输出5,分析如下。go关键字启动一个协程A运行,从无缓冲通道ch中读取数据,阻塞等待chan,协程A睡眠,切换到主协程;主协程往通道发送数据,并唤醒协程A,将协程A列入下次可运行队列,协程主动陷入睡眠;golang调度程序调度到协程A,输出5
实际运行上述输出结果为死锁,修改程序
程序B
package main import ( "fmt" "time" ) func main() { ch := make(chan int) go func() { fmt.Println(<-ch) }() ch <- 5 time.Sleep(2 * time.Second) } 上述结果输出5。和原程序结果不同的原因在于协程启动时需要保存上下文参数,程序B中协程启动入参为空,并引用ch;原程序启动先计算参数,然而计算参数时需要等待结果,当主协程准备往ch发送数据,也请求锁,造成死锁。
程序C
package main import ( "fmt" "time" ) func main() { ch := make(chan int) go func() { fmt.Println(<-ch) }() time.Sleep(2 * time.Second) } ]]></content></entry><entry><title>BTC devguide(3)</title><url>/post/btc_devguide_3/</url><categories><category>blockchain</category></categories><tags><tag/></tags><content type="html">Operating Modes 比特币软件具有不同级别的安全性和权衡,以验证区块链
Introduction 当前,有两种主要的方法来验证区块链作为客户端:完整节点和SPV客户端。 由于不建议使用其他方法,例如服务器信任方法,因此不予讨论
Full Node 第一个也是最安全的模型,其次是Bitcoin Core,也就是“加厚型”或“全链”客户端。 该安全模型通过从创世区块一直下载到最新发现的区块并对其进行验证来确保区块链的有效性。 这被称为使用特定块的高度来验证客户端对网络的看法。
为了使客户蒙骗,对手将需要给出完整的替代区块链历史记录,其历史难度要大于当前的“真实”链,这在计算上是昂贵的(如果不是不可能的话),原因是带有 根据定义,最累积的工作证明是“真实”链。 由于在链的最末端生成新块所需的计算难度,在经过6次确认后,使整个节点处于愚弄状态的能力变得非常昂贵。 这种形式的验证高度抵御sybil攻击-只需一个诚实的网络对等方即可接收和验证“真实”区块链的完整状态。
Simplified Payment Verification(SPV) 原始比特币论文中详细介绍的另一种方法是,客户端仅在初始同步过程中下载块的标头,然后根据需要从完整节点请求交易。 这与块链的高度成线性关系,每个块头仅80字节,或者每年高达4.2MB(与块总大小无关)。
如白皮书中所述,区块头中的merkle根以及merkle分支可以向SPV客户端证明所涉及的事务已嵌入到区块链中的一个区块中。 这不能保证所嵌入事务的有效性。 相反,它演示了执行双花攻击所需的工作量。
区块链中区块的深度对应于在特定区块之上构建的累计难度。 SPV客户端知道merkle根和相关的交易信息,并从完整节点请求相应的merkle分支。 一旦检索到merkle分支,证明了该交易存在于区块中,SPV客户端便可以将区块深度视为交易有效性和安全性的代理。 插入无效交易的恶意节点对用户的攻击成本随着在该块之上构建的累积难度而增加,因为仅恶意节点将挖掘此伪造链
Bloom Filters 布隆过滤器是一种节省空间的概率数据结构,用于测试元素的成员资格。数据结构以规定的误报率为代价实现了很好的数据压缩。
布隆过滤器以n位均设置为0的数组开始。选择了一组k个随机哈希函数,每个函数都输出一个介于1和n之间的整数。
将元素添加到Bloom过滤器时,该元素将分别散列k次,并且对于k个输出中的每一个,该索引处的对应Bloom过滤器位都设置为1。
通过使用与以前相同的哈希函数来查询Bloom筛选器。如果将在Bloom Bloom过滤器中访问的所有k位都设置为1,则很有可能证明该元素位于该集中。显然,可以通过在域中添加其他元素的组合将k个索引设置为1,但是参数允许用户选择可接受的误报率。
元素的删除只能通过废弃Bloom Bloom过滤器并从头开始重新创建来完成
P2P Network 首次启动时,程序不知道任何活动的完整节点的IP地址。 为了发现一些IP地址,他们查询一个或多个硬编码到Bitcoin Core和BitcoinJ中的DNS名称(称为DNS种子)。 对查询的响应应包括一个或多个DNS A记录,其中包含可以接受新的传入连接的完整节点的IP地址
DNS种子由比特币社区成员维护:其中一些提供动态DNS种子服务器,该服务器通过扫描网络自动获取活动节点的IP地址。其他提供静态的DNS种子,这些种子可以手动更新,并且更有可能为非活动节点提供IP地址。无论哪种情况,如果节点在默认的比特币端口(主网络8333或测试网络18333)上运行,都将添加到DNS种子中。
DNS种子结果未经身份验证,恶意种子运营商或网络中间人攻击者只能返回由攻击者控制的节点的IP地址,从而隔离了攻击者自己网络上的程序,并允许攻击者提供虚假交易和块。因此,程序不应仅依赖DNS种子。
一旦程序连接到网络,它的对等方就可以开始向其发送addr(地址)消息以及网络上其他对等方的IP地址和端口号,从而提供了一种完全分散的对等方发现方法。 Bitcoin Core在永久的磁盘数据库中保留了已知对等项的记录,该数据库通常允许它在后续启动中直接连接到那些对等项,而不必使用DNS种子。
但是,对等方通常会离开网络或更改IP地址,因此在成功建立连接之前,程序可能需要在启动时进行几次不同的连接尝试。这会大大增加连接网络所需的时间,从而迫使用户在发送交易或检查付款状态之前要等待。
为了避免这种可能的延迟,BitcoinJ始终使用动态DNS种子来获取被认为当前处于活动状态的节点的IP地址。比特币核心还试图在最小化延迟和避免不必要的DNS种子使用之间寻求平衡:如果比特币核心在其对等数据库中有条目,则它会花费长达11秒的时间尝试连接至少其中之一,然后再返回种子。如果在此时间内建立连接,则不会查询任何种子。
比特币核心和比特币J都还包含到几十个节点的IP地址和端口号的硬编码列表,这些节点在该软件的特定版本首次发布时就处于活动状态。如果没有任何DNS种子服务器在60秒内响应查询,则Bitcoin Core将开始尝试连接到这些节点,并提供自动回退选项。
Reference https://developer.bitcoin.org/reference/index.html</content></entry><entry><title>BTC devguide(2)</title><url>/post/btc_devguide_2/</url><categories><category>blockchain</category></categories><tags><tag/></tags><content type="html"><![CDATA[Transactions 交易让用户花了satoshis。 每笔交易都由几部分组成,既可以进行简单的直接付款,也可以进行复杂的交易。
Introduction 本节将描述每个部分并演示如何一起使用它们来构建完整的交易
为简单起见,本节假装不存在coinbase交易。 Coinbase交易只能由比特币矿工创建,并且是下面列出的许多规则的例外。 我们邀请您阅读本指南的区块链部分中的coinbase交易
上图显示了比特币交易的主要部分。 每笔交易至少有一个输入和一个输出。 每个输入都会花费支付给先前输出的satoshis。 然后,每个输出都将作为未花费的交易输出(UTXO)等待,直到以后的输入将其花费为止。 当您的比特币钱包告诉您您有10,000个satoshi余额时,实际上意味着您在一个或多个UTXO中有10,000个satoshis等待。
每笔交易都以一个四字节交易版本号为前缀,该版本号告诉比特币同行和矿工用来验证它的规则集。 这使开发人员可以为将来的交易创建新规则,而不会使以前的交易无效。
根据输出在事务中的位置,输出具有隐式索引号-第一个输出的索引为零。 输出中还有一定数量的satoshi,它会支付给有条件的pubkey脚本。 可以满足该pubkey脚本条件的任何人都可以花费支付给它的satoshis数量。
输入使用事务标识符(txid)和输出索引号(通常将输出向量称为“ vout”)来标识要使用的特定输出。 它还具有一个签名脚本,该签名脚本允许它提供满足pubkey脚本中条件的数据参数。 (序列号和锁定时间是相关的,将在后面的小节中一起介绍。)
下图通过显示爱丽丝用来向鲍勃发送交易的工作流以及鲍勃后来用来花费该交易的工作流来说明如何使用这些功能。 爱丽丝(Alice)和鲍勃(Bob)都将使用标准的“付款到公共密钥哈希(P2PKH)”交易类型的最常见形式。 P2PKH让爱丽丝(Alice)在一个典型的比特币地址上花费了聪子,然后让鲍勃(Bob)使用一个简单的加密密钥对进一步花费了那些satohis。
在Alice创建第一个交易之前,Bob必须首先生成一个私钥/公钥对。比特币使用带有secp256k1曲线的椭圆曲线数字签名算法(ECDSA); secp256k1私钥是256位随机数据。该数据的副本确定地转换为secp256k1公钥。因为可以在以后可靠地重复进行转换,所以不需要存储公用密钥。
然后,对公钥(pubkey)进行加密散列。此pubkey哈希也可以在以后可靠地重复,因此也不需要存储。哈希缩短和混淆了公共密钥,使手动转录更加容易,并提供了针对未预料到的问题的安全性,这些问题可能允许稍后从公共密钥数据中重建私钥。
鲍勃将公钥哈希提供给爱丽丝。 Pubkey散列几乎总是以比特币地址编码的形式发送的,它们是base58编码的字符串,包含地址版本号,哈希值和错误检测校验和,以捕获错别字。该地址可以通过任何介质进行传输,包括阻止支出者与接收方通信的单向介质,并且可以将其进一步编码为另一种格式,例如包含“ bitcoin:” URI的QR码。
爱丽丝获得地址并将其解码回标准哈希后,便可以创建第一个交易。 她创建了一个标准的P2PKH交易输出,其中包含指令,如果任何人证明自己可以控制与鲍勃的哈希公钥相对应的私钥,则任何人都可以使用该输出。 这些指令称为pubkey脚本或scriptPubKey。
爱丽丝广播交易,并将其添加到区块链。 网络将其分类为未用交易输出(UTXO),而Bob的钱包软件将其显示为可支配余额。
稍后,当Bob决定使用UTXO时,他必须创建一个输入,该输入引用由其哈希创建的事务Alice(称为事务标识符(txid)),以及由索引号(输出索引)使用的特定输出。 。 然后,他必须创建一个签名脚本-满足爱丽丝在上一个输出的pubkey脚本中放置的条件的一组数据参数。 签名脚本也称为scriptSigs。
Pubkey脚本和签名脚本将secp256k1 pubkey和签名与条件逻辑结合在一起,从而创建了可编程的授权机制。
对于P2PKH样式的输出,Bob的签名脚本将包含以下两个数据:
他的完整(未散列)公共密钥,因此,公共密钥脚本可以检查其散列值是否与alice提供的公共密钥散列值相同 通过使用ECDSA加密公式将某些交易数据(如下所述)与Bob的私钥结合在一起而构成的secp256k1签名。 这使pubkey脚本可以验证Bob拥有创建公钥的私钥。 Bob的secp256k1签名不仅证明Bob控制了他的私钥, 它还使交易的非签名脚本部分受到防篡改,因此Bob可以在对等网络上安全地广播它们。
如上图所示,鲍勃(Bob)签名的数据包括上一个交易的txid和输出索引,上一个输出的pubkey脚本,鲍勃创建的pubkey脚本(将使下一个接收者花费此交易的输出)以及 花给下一个收件人。 本质上,除了包含完整公钥和secp256k1签名的任何签名脚本之外,整个事务都是经过签名的。
将签名和公共密钥放入签名脚本后,Bob通过对等网络将交易广播给比特币矿工。 每个对等方和矿工在进一步广播交易或尝试将其包括在新的交易块中之前独立验证交易
P2PKH Script Validation 验证过程需要评估签名脚本和pubkey脚本。在P2PKH输出中,pubkey脚本为:
OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG 将评估支出者的签名脚本,并在脚本开头添加前缀。在P2PKH事务中,签名脚本包含secp256k1签名(sig)和完整的公共密钥(pubkey),从而创建以下串联:
OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG
脚本语言是一种类似于Forth的基于堆栈的语言,故意将其设计为无状态而不是图灵完整的。无状态性确保一旦将交易添加到区块链,就不会出现使其永久无法使用的情况。图灵不完整(特别是缺少循环或混乱)使脚本语言的灵活性和可预测性降低,从而大大简化了安全模型。
为了测试交易是否有效,签名脚本和pubkey脚本操作一次执行一次,从Bob的签名脚本开始,一直到Alice的pubkey脚本结束。下图显示了对标准P2PKH公钥脚本的评估;图下方是该过程的说明。
The signature (from Bob’s signature script) is added (pushed) to an empty stack. Because it’s just data, nothing is done except adding it to the stack. The public key (also from the signature script) is pushed on top of the signature.
From Alice’s pubkey script, the “OP_DUP” operation is executed. “OP_DUP” pushes onto the stack a copy of the data currently at the top of it—in this case creating a copy of the public key Bob provided.
The operation executed next, “OP_HASH160”, pushes onto the stack a hash of the data currently on top of it—in this case, Bob’s public key. This creates a hash of Bob’s public key.
Alice’s pubkey script then pushes the pubkey hash that Bob gave her for the first transaction. At this point, there should be two copies of Bob’s pubkey hash at the top of the stack.
Now it gets interesting: Alice’s pubkey script executes “OP_EQUALVERIFY”. “OP_EQUALVERIFY” is equivalent to executing “OP_EQUAL” followed by “OP_VERIFY” (not shown).
“OP_EQUAL” (not shown) checks the two values at the top of the stack; in this case, it checks whether the pubkey hash generated from the full public key Bob provided equals the pubkey hash Alice provided when she created transaction #1. “OP_EQUAL” pops (removes from the top of the stack) the two values it compared, and replaces them with the result of that comparison: zero (false) or one (true).
“OP_VERIFY” (not shown) checks the value at the top of the stack. If the value is false it immediately terminates evaluation and the transaction validation fails. Otherwise it pops the true value off the stack
Finally, Alice’s pubkey script executes “OP_CHECKSIG”, which checks the signature Bob provided against the now-authenticated public key he also provided. If the signature matches the public key and was generated using all of the data required to be signed, “OP_CHECKSIG” pushes the value true onto the top of the stack.
P2SH Scripts Pubkey脚本是由不感兴趣该脚本功能的用户创建的。 接收者确实关心脚本条件,并且,如果需要,他们可以要求支出者使用特定的pubkey脚本。 不幸的是,自定义的pubkey脚本不如短的比特币地址方便,并且在广泛使用现已弃用的BIP70支付协议之前,还没有一种标准的方法可以在程序之间进行通信。
为了解决这些问题,2012年创建了按脚本哈希(P2SH)交易,使支出者可以创建包含第二个脚本(即赎回脚本)的哈希值的公钥脚本。
基本的P2SH工作流程,如下图所示,看起来几乎与P2PKH工作流程相同。 鲍勃使用所需的脚本创建赎回脚本,对赎回脚本进行哈希处理,然后将赎回脚本哈希提供给爱丽丝。 爱丽丝创建了一个包含鲍勃的兑换脚本哈希值的P2SH风格的输出。
Standard Transactions 当Bob想要花费输出时,他在签名脚本中提供了他的签名以及完整的(序列化)redeem脚本。 对等网络确保完整的赎回脚本哈希值与Alice在其输出中放入的脚本哈希值相同; 然后,它会完全像处理主pubkey脚本那样处理赎回脚本,如果赎回脚本未返回false,则让Bob花费输出。
在早期版本的比特币中发现了几个危险的错误之后,添加了一项测试,该测试仅在其公钥脚本和签名脚本与一小部分被认为是安全的模板匹配的情况下才接受来自网络的交易,并且该交易未违反另一套强制执行良好网络行为的规则。这是IsStandard()测试,通过它的事务称为标准事务。
不使用默认比特币核心设置的节点可以接受非标准交易(未通过测试的交易)。如果它们包含在块中,它们还将避免进行IsStandard测试并进行处理。
除了通过广播有害交易使某人更难免费攻击比特币外,标准交易测试还有助于防止用户今天创建交易,这会使将来增加新的交易功能更加困难。例如,如上所述,每个事务都包含一个版本号-如果用户开始随意更改版本号,则该版本作为引入向后不兼容功能的工具将变得毫无用处。
As of Bitcoin Core 0.9, the standard pubkey script types are:
Pay To Public Key Hash (P2PKH)
P2PKH是最常用的pubkey脚本形式,用于将交易发送到一个或多个比特币地址。
Pubkey script: OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG Signature script: <sig> <pubkey> Pay To Script Hash (P2SH)
P2SH用于将事务发送到脚本哈希。 除P2SH本身以外,每个标准的pubkey脚本都可以用作P2SH兑换脚本。 从Bitcoin Core 0.9.2开始,P2SH交易可以包含任何有效的redeemScript,从而使P2SH标准更加灵活,并可以尝试许多新颖而复杂的交易类型。 P2SH的最常见用法是标准的multisig pubkey脚本,第二最常见的用法是“开放资产协议”。
用于P2SH的另一个常见的redeemScript是将文本数据存储在区块链上。 有史以来的第一个比特币交易包括文本,P2SH是一种在区块链上存储文本的便捷方法,因为它可以存储多达1.5kb的文本数据。 在此存储库中可以找到使用P2SH在区块链上存储文本的 示例 。
Pubkey script: OP_HASH160 <Hash160(redeemScript)> OP_EQUAL Signature script: <sig> [sig] [sig...] <redeemScript> 只要脚本哈希与赎回脚本匹配,此脚本组合对于旧节点而言看起来就很好。 但是,在激活软叉之后,新节点将对赎回脚本执行进一步的验证。 他们将从签名脚本中提取赎回脚本,对其进行解码,然后使用其余的堆栈项( [sig] [sig ..]部分)执行该脚本。 因此,要赎回P2SH交易,除正确的赎回脚本外,支出者还必须提供有效的签名或答案。
Multisig
尽管现在通常将P2SH多重签名用于多重签名事务,但此基本脚本可用于在使用UTXO之前要求多个签名。
在称为m-of-n的multisig pubkey脚本中,m是必须与公钥匹配的最小签名数。 n是提供的公共密钥的数量。 m和n都应该是OP_1到OP_16的操作码,对应于所需的数字。
由于必须保留原始比特币实现中的一位错误,以确保兼容性,因此“ OP_CHECKMULTISIG”从堆栈中消耗的值比m指示的值多,因此,签名脚本中的secp256k1签名列表必须带有前缀 将会消耗但不使用的额外值(OP_0)。
签名脚本必须按照与相应的公共密钥出现在pubkey脚本或赎回脚本中相同的顺序提供签名。 有关详细信息,请参见“ OP_CHECKMULTISIG”中的描述。
Pubkey script: <m> <A pubkey> [B pubkey] [C pubkey...] <n> OP_CHECKMULTISIG Signature script: OP_0 <A sig> [B sig] [C sig...] Although it’s not a separate transaction type, this is a P2SH multisig with 2-of-3:
Pubkey script: OP_HASH160 <Hash160(redeemScript)> OP_EQUAL Redeem script: <OP_2> <A pubkey> <B pubkey> <C pubkey> <OP_3> OP_CHECKMULTISIG Signature script: OP_0 <A sig> <C sig> <redeemScript> Pubkey
公钥输出是P2PKH公钥脚本的简化形式,但是它们不如P2PKH安全,因此通常不再在新交易中使用它们。
Pubkey script: <pubkey> OP_CHECKSIG Signature script: <sig> Null Data
Signature Hash Type “OP_CHECKSIG” 从评估的每个签名提取一个non-stack 参数。允许签名者决定要在交易的哪个部分进行签名。 由于签名保护交易的这些部分免于修改,因此签名者可以有选择地选择让其他人修改其交易
用于签名的各种选项称为签名哈希类型。 当前有三种基本的SIGHASH类型:
“SIGHASH_ALL”,对所有输入和输出进行签名,保护除签名脚本以外的所有内容,防止其被修改。 “SIGHASH_NONE”,签名所有输入,但不签名任何输出,允许任何人更改satoshi的行进方向,除非使用其他签名哈希标志的其他签名保护输出。 “SIGHASH_SINGLE”, 唯一已签名的输出是与此输入相对应的输出(与该输入具有相同输出索引号的输出),以确保没有人可以更改您的交易部分,但允许其他签名者更改其交易部分。 必须存在相应的输出,否则将对值“ 1”进行签名,从而破坏了安全性方案。 此输入以及其他输入都包含在签名中。 其他输入的序列号不包括在签名中,并且可以更新。 因为每个输入都经过签名,所以具有多个输入的事务可以具有多个签名哈希类型,用于对事务的不同部分进行签名。 例如,用NONE签名的单输入交易可能会被采矿者更改其输出,并将其添加到区块链中。 另一方面,如果一个两输入交易具有一个用NONE签名的输入和一个用ALL签名的输入,则ALL签名者可以在不咨询NONE签名者的情况下选择在哪里使用satoshis,但是其他人都不能修改该交易。
BTC Locktime And Sequence Number 所有签名哈希类型都签名的一件事是交易的锁定时间。 (在比特币核心源代码中称为nLockTime。)锁定时间表示可以将事务添加到区块链的最早时间。
Locktime允许签名者创建时间锁定的交易,该交易只有在将来才有效,从而使签名者有机会改变主意。
如果任何签名者改变了主意,他们可以创建一个新的非锁定时间交易。 新交易将使用与锁定时间事务的输入相同的输出作为其输入之一。 如果在时间锁定到期之前将新交易添加到块链中,这会使锁定时间事务无效。
在时间锁定的到期时间附近必须小心。 对等网络允许区块时间比实时时间最多提前两个小时,因此可以在其时间锁定正式到期之前的两个小时内将锁定时间事务添加到区块链中。 另外,不会以保证的时间间隔创建块,因此任何取消有价值的交易的尝试都应在时间锁定到期前几个小时进行。
早期版本的Bitcoin Core提供了一项功能,该功能可防止交易签名者使用上述方法取消有时间限制的交易,但该功能的必要部分已禁用,以防止拒绝服务攻击。 该系统的传统是在每个输入中包含四个字节的序列号。 序列号旨在允许多个签名者同意更新交易。 当他们完成交易更新时,他们可以同意将每个输入的序列号设置为最大四个字节的无符号(0xffffffff),即使该交易的时间锁未过期,也可以将其添加到一个区块中。
即使在今天,将所有序列号设置为0xffffffff(Bitcoin Core中的默认值)仍然可以禁用时间锁定,因此,如果要使用锁定时间,至少一个输入的序列号必须低于最大序列号。 由于网络不会将序列号用于任何其他目的,因此将任何序列号设置为零就足以启用锁定时间。
锁定时间本身是一个无符号的4字节整数,可以通过两种方式解析:
如果小于5亿,则将锁定时间解析为块高度。 可以将交易添加到具有此高度或更高高度的任何块中。 如果大于或等于5亿,则使用Unix纪元时间格式(自1970-01-01T00:00 UTC以来经过的秒数-当前超过13.95亿)来解析锁定时间。 可以将事务添加到块时间大于锁定时间的任何块中 Transaction Fees And Change 交易根据已签名交易的总字节大小支付费用。每字节的费用是根据采矿块中当前的空间需求来计算的,费用随着需求的增加而增加。正如区块链部分中所述,交易费是提供给比特币矿工的,因此最终由每个矿工选择他们将接受的最低交易费。
还有一个所谓的“高优先级交易”的概念,它花费了很长时间没有动静的聪子。
过去,这些“优先”交易通常无需支付常规费用。在Bitcoin Core 0.12之前,将为这些高优先级事务保留每个块50 KB,但是现在默认将其设置为0 KB。在优先级区域之后,将根据每个字节的费用对所有交易进行优先级排序,并依次添加高薪交易,直到所有可用空间都被填满。
从Bitcoin Core 0.9开始,在网络上广播交易需要最低费用(目前为1,000聪)。任何仅需支付最低费用的交易,都应准备等待很长时间才能在块中有足够的可用空间来包含它。请参阅“验证付款”部分以了解为什么这很重要。
由于每笔交易都花费未使用的交易输出(UTXO),并且因为UTXO只能使用一次,所以必须花费所包含的UTXO的全部价值或将其作为交易费提供给矿工。很少有人会拥有与他们想要支付的金额完全匹配的UTXO,因此大多数交易都包含变更输出。
变更输出是常规输出,将多余的satoshis从UTXO花费回花费者。 他们可以重用与UTXO中相同的P2PKH pubkey哈希或P2SH脚本哈希,但是出于下一节所述的原因,强烈建议将更改输出发送到新的P2PKH或P2SH addres
Segwit 比特币网络区块链每秒处理7笔交易。增加TPS的方法有增大区块容量和隔离见证、侧链
隔离见证的主要思想是重组区块链数据,使签名不再与交易数据存储在一起。换句话说,Segwit升级包括将验证人(签名)与交易数据隔离,这就能够将更多交易存储在单个区块中,从而增加网络的交易TPS
事物包括两个主要部分:输入和输出。本质上,输入包含发送者的公共地址,而输出包含接收者的公共地址。但是发送者必须证明他们已经转移了资金,并且必须使用数字签名。
如果没有隔离见证,则签名数据最多可占用一个区块的65%。使用隔离见证,会把签名数据从交易的输入中移除。实际区块大小仍为1MB,但有效块大小限制为4MB
隔离见证还引入了区块权重的概念。我们可以将区块权重视为替代区块大小的概念。本质上,区块权重是一种度量单位,它包括所有的区块数据:包括交易数据(1MB)和签名数据(最大3MB)。
交易延展性 此修复程序支持在区块链社区内进行进一步的创新,包括二层协议和智能合约
]]></content></entry><entry><title>BTC devguide(1)</title><url>/post/btc_devguide_1/</url><categories><category>blockchain</category></categories><tags><tag/></tags><content type="html">Block Chain 区块链系统提供了公共分类帐,有序且带有时间戳记的交易记录。 该系统用于防止重复支出和修改以前的交易记录。
Introduction 比特币 网络 中的每个完整节点都独立存储一个区块链,其中仅包含该节点验证的区块。 当几个节点的块链中都具有相同的块时,它们被认为是一致的。 这些节点遵循以维持共识的验证规则称为 共识规则 。 本节描述了比特币核心使用的许多共识规则。
上图显示了区块链的简化版本。 一个或多个新事务的块被收集到一个块的事务数据部分中。 将每个事务的副本进行哈希处理,然后对哈希进行配对,哈希处理,再次配对和再次哈希处理,直到剩下一个哈希(即默克尔树的默克尔根)为止。
merkle根存储在块标题中。 每个块还存储前一个块头的哈希,将这些块链接在一起。 这样可确保在不修改记录该记录的块以及所有后续块的情况下,无法修改该事务。
交易也被链接在一起。 比特币钱包软件给人的印象是,satoshis是在钱包之间来回发送的,但是比特币真正地在交易之间转移。 每笔交易都花费了先前在一个或多个更早的交易中接收到的satoshis,因此一个交易的输入就是前一个交易的输出
单个交易可以创建多个输出,就像发送到多个地址时一样,但是特定事务的每个输出只能在块链中用作一次输入。 随后的任何参考都是禁止重复支出-尝试两次将相同的satoshis花费两次。
由于特定交易的每个输出只能使用一次,因此可以将包含在区块链中的所有交易的输出分类为未花费的交易输出(UTXO)或已花费的交易输出。 为了使付款有效,它只能使用UTXO作为输入。
忽略币基交易(稍后描述),如果交易的输出值超过其输入,则交易将被拒绝;但是,如果输入的值超过输出的值,则任何价值差均可被比特币要求为交易费。 创建包含该交易的区块的矿工。 例如,在上图中,每笔交易比从合并输入中获得的支出少10,000聪,有效地支付了10,000聪交易费用
Proof of Work 区块链由网络上的匿名对等方协作维护,因此比特币要求每个区块都证明在其创建上投入了大量工作,以确保想要修改过去区块的不可信对等方比仅对诚实对等方更努力。 想要向区块链添加新的区块。
将块链接在一起使得无法修改任何块中包含的事务而不修改所有后续块。 结果,每增加一个新块,修改特定块的成本就会增加,从而扩大了工作量证明的效果。
比特币中使用的工作量证明充分利用了加密散列的明显随机性。 好的加密哈希算法可将任意数据转换为看似随机的数字。 如果以任何方式修改了数据并重新运行了哈希,则会生成一个新的看似随机数,因此无法修改数据以使哈希数可预测。
为了证明您做了一些额外的工作来创建一个块,您必须创建一个不超过特定值的块头哈希。 例如,如果最大可能的哈希值是2256 -1,则可以通过产生小于2255的哈希值来证明尝试了两种组合。
在上面给出的示例中,平均每隔尝试一次,您将产生一个成功的哈希。 您甚至可以估计给定哈希尝试将生成低于目标阈值的数字的可能性。 比特币假设线性概率使得达到目标阈值的水平越低,则平均需要进行的哈希尝试越多。
如果新块的哈希值至少与共识协议预期的难度值一样具有挑战性,则仅将其添加到块链中。 网络每隔2016个块,将使用存储在每个块头中的时间戳来计算生成最后2016个块中的第一个和最后一个之间所经过的秒数。 理想值为1,209,600秒(两周)。
如果花费不到两周的时间来生成2,016块,则预期的难度值将成比例地增加(多达300%),因此,如果以相同的速率检查哈希,则接下来的2,016块应该恰好花费两周的时间来生成。
如果花了两周以上的时间来生成区块,则出于相同的原因,预期的难度值将成比例地降低(降低多达75%)。
(注意:Bitcoin Core实施中的一次性错误会导致使用时间戳(仅2,015个块)来每2,016个块更新一次难度,从而造成轻微的偏差。)
区块标头提供了几个易于修改的字段,例如专用的随机数字段,因此获取新的哈希值不需要等待新的交易。 此外,仅散列80字节的块头以进行工作量证明,因此在块中包含大量事务数据不会减慢具有额外I / O的散列,并且添加其他事务数据仅需要重新计算 merkle树中的祖先哈希。
Block Height And Forking 任何成功将区块头散列到目标阈值以下的比特币矿工都可以将整个区块添加到区块链中(假设该区块是有效的)。 这些块通常通过其块高度来解决-它们与第一个比特币块之间的块数(块0,最通常称为创世块)。 例如,方框2016是可以首先调整难度的位置。
多个块都可以具有相同的块高度,这是两个或多个矿工各自大致同时生产一个块时的常见现象。 如上图所示,这会在区块链中创建一个明显的分叉。
当矿工在区块链的末端同时生产区块时,每个节点都会分别选择要接受的区块。 在没有其他考虑的情况下(如下所述),节点通常使用它们看到的第一个块。
最终,一个矿工生产了另一个块,该块仅附着在竞争的同时开采的一个块上。 这使叉子的那一侧比另一侧更坚固。 假设一个分叉仅包含有效块,普通对等方总是遵循最困难的链来重新创建并丢弃属于较短分叉的陈旧块。 (过时的块有时也称为孤立块或孤立块,但这些术语也用于没有已知父块的真实孤立块。)
如果不同的矿工可以跨部门工作,则可以进行长期分叉,例如,一些矿工努力工作以扩展区块链,而其他矿工则试图发起51%的攻击来修改交易历史。
由于在块链叉期间多个块可以具有相同的高度,因此不应将块高度用作全局唯一标识符。 取而代之的是,块通常由其标题的哈希引用(通常以相反的字节顺序并以十六进制表示)。
Transaction Data 每个区块必须包含一个或多个交易。 这些交易中的第一个必须是coinbase交易,也称为generation交易,该交易应收集并使用大宗交易奖励(包括大宗交易补贴和该大宗交易中所含交易支付的任何交易费用)。
Coinbase交易的UTXO具有特殊条件,即至少100个块不能花费(用作输入)。 这暂时阻止了矿工在区块链分叉之后花费交易费用和来自某个区块的区块奖励,该区块后来被确定为陈旧(因此销毁了coinbase交易)。
区块不需要包含任何非Coinbase交易,但是矿工几乎总是包含其他交易以收取交易费用。
所有交易,包括coinbase交易,都以二进制原始交易格式编码为块。
原始交易格式被散列以创建交易标识符(txid)。 根据这些txid,通过将每个txid与另一个txid配对,然后将它们散列在一起,来构建merkle树。 如果txid的数量为奇数,则不带伙伴的txid将以其自身的副本进行哈希处理。
产生的散列本身各自与另一个散列配对并散列在一起。 没有伙伴的任何哈希都将自身哈希。 重复该过程,直到仅保留一个散列(merkle根)为止。
例如,如果仅连接事务(不进行哈希处理),则five-transaction merkle树将类似于以下文本图:
如简化付款验证(SPV)小节中所述,merkle树允许客户通过从区块头获取merkle根以及从完整对等方获得中间哈希的列表,来自己验证交易是否包含在区块中。 完整的对等点不需要被信任:伪造块头是昂贵的,并且中间的哈希不能被伪造,否则验证将失败。
例如,要验证是否已将交易D添加到块中,SPV客户端除了需要添加Merkle根以外,还仅需要C,AB和EEEE哈希的副本。 客户不需要了解其他任何交易。 如果此块中的五个事务全部达到最大大小,则下载整个块将需要超过500,000字节-但是下载三个散列和块头仅需要140字节。
Consensus Rule Changes 为了保持共识,所有完整节点都使用相同的共识规则来验证块。 但是,有时会更改共识规则以引入新功能或防止网络滥用。 实施新规则后,未升级的节点将遵循旧规则,而升级后的节点将遵循新规则,这可能会产生一段时间,从而形成可能破坏共识的两种方式:
A block following the new consensus rules is accepted by upgraded nodes but rejected by non-upgraded nodes. For example, a new transaction feature is used within a block: upgraded nodes understand the feature and accept it, but non-upgraded nodes reject it because it violates the old rules
A block violating the new consensus rules is rejected by upgraded nodes but accepted by non-upgraded nodes. For example, an abusive transaction feature is used within a block: upgraded nodes reject it because it violates the new rules, but non-upgraded nodes accept it because it follows the old rules.
在第一种情况下,被未升级的节点拒绝,从那些未升级的节点获取块链数据的挖掘软件拒绝与从升级的节点获取数据的挖掘软件在同一链上构建。 这将创建永久性的分歧链(一个用于非升级节点,一个用于升级节点),称为hard fork。
尽管分叉实际上是区块链中的分歧,但是共识规则的更改通常通过其创建硬分叉或软分叉的潜力来描述。 例如,“将块大小增加到1 MB以上需要硬分叉。” 在此示例中,不需要实际的区块链分叉,但这是可能的结果。
Detecting Forks 未升级的节点可能会在两种类型的分叉期间使用和分发不正确的信息,从而造成可能导致财务损失的多种情况。 特别是,未升级的节点可能会中继并接受被升级的节点视为无效的交易,因此永远不会成为公认的最佳区块链的一部分。 未升级的节点也可能拒绝中继已经添加到最佳区块链或将很快添加到最佳区块链的区块或事务,因此提供的信息不完整。
Bitcoin Core包含通过查看区块链工作证明来检测硬分叉的代码。 如果未升级的节点接收到的块链头比其认为有效的最佳链至少证明了六个块的工作量证明,则该节点将在“ getnetworkinfo” RPC结果中报告警告,并运行-alertnotify命令(如果设置)。 这会警告操作员,未升级的节点无法切换到可能最好的区块链。
完整节点还可以检查区块和交易版本号。 如果最近几个区块中看到的区块或交易版本号高于节点使用的版本号,则可以假定它未使用当前的共识规则。 Bitcoin Core通过“ getnetworkinfo” RPC和-alertnotify命令(如果设置)报告这种情况。
无论哪种情况,如果区块和交易数据来自显然没有使用当前共识规则的节点,都不应依赖该数据。
连接到完整节点的SPV客户端可以通过连接到多个完整节点并确保它们全部位于同一链上且具有相同的块高(加上或减去几个块)来考虑传输延迟和陈旧的块,从而检测出可能的硬分叉。 如果存在分歧,客户端可以从链数较弱的节点断开连接。
SPV客户还应该监视阻止和交易版本号的增加,以确保他们处理接收到的交易并使用当前的共识规则创建新的交易。</content></entry><entry><title>Base58Check 编码</title><url>/post/base58check_encoding/</url><categories><category>blockchain</category></categories><tags><tag>encoding</tag></tags><content type="html"><![CDATA[ 本文章翻译自 Base58Check encoding ,如有侵权,请联系 作者 进行删除
Background Base58Check 是一种被用来编码比特币地址的协议
这原始比特币源码解释了为何使用base58而不是base64
不想在某些字体中看起来相同的0OIl字符,可用于创建外观相同的帐号 具有非字母数字字符的字符串不像帐号那样容易被接受 如果没有标点符号中断,电子邮件通常不会换行 如果全为字母数字,则双击将整个数字选择为一个单词 Features of Base58Check Base58Check有如下特点:
一个任意大小的payload 一组58个字母数字符号,由容易区分的大写和小写字母组成 比特币地址使用一个字节保存版本信息, 0x00 基于SHA256的错误检查代码的四个字节(32位)。此代码可用于自动检测并可能纠正印刷错误 一个额外的步骤用来保存数据中的先导零 Creating a Base58Check string 一个base58Check字符串从版本号和附加字段被创建:
带有版本字节和payload字节,级联在一起 得到sha256(result of step 1)的前四个字节 将step1和step2的结果级联在一起 将step3结果作为一个大端大整数转换为base58字符串 前导零被保存为字符1, 级联step5和step4的结果,This is the Base58Check Encoding a Bitcoin address 比特币地址被实现为Base58Check编码
Pay-to-script-hash(p2sh): payload is:RIPEMD160(SHA256(redeemScript)) where redeemScript is a script the wallet knows how to spend; version 0x05(these addresses begin with the digit ‘3’) Pay-to-pubkey-hash (p2pkh): payload is RIPEMD160(SHA256(ECDSA_publicKey)) where ECDSA_publicKey is a public key the wallet knows the private key for; version 0x00 (these addresses begin with the digit ‘1’) 这结果一直是20个字节
code_string = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" x = convert_bytes_to_big_integer(hash_result) output_string = "" while(x > 0) { x, remainder = divide(x, 58) output_string.append(code_string[remainder]) } repeat(number_of_leading_zero_bytes_in_hash) { output_string.append(code_string[0]) } output_string.reverse() Version bytes Reference ]]></content></entry><entry><title>Cloud Storage</title><url>/post/cloud_storage/</url><categories><category>cloud</category></categories><tags/><content type="html"><![CDATA[unkonw 云原生与微服务
云原生本质上是一套知道软件架构涉及的思想,建立在"未来的软件一定生长于云"的核心假设之上。
云原生包括容器化封装、自动化管理、面向微服务、服务网格、声明式API。符合云原生架构的应用程序应该是:采用开源堆栈进行容器化,基于微服务架构提高灵活性和可维护性,借助敏捷方法,DevOps支持持续迭代和运维自动化,利用云平台设施实现弹性伸缩、动态调度、优化资源利用率。
云原生帮助企业将应用和业务"云化",解耦业务和基础设施,只关注业务员逻辑和价值,将非业务逻辑的复杂性下沉到基础设施,使基础设施更高效、更高性能、更稳定可靠,从而充分释放云的价值。
基于kubermetes的云原生架构技术平台实现企业内各类业务的统一管理
微服务架构是解决单体复杂度问题的首选方式,却带来整个系统的整体复杂度大幅增加,容器技术和kubemetes分别解决了微服务架构下大量应用的部署,以及容器的管理和调度问题,同时,kubemetes也为service mesh提供了更好的底层支撑,也带来了底层基础设施的serverless云原生化和中间件能力的进一步下沉
微服务架构本质上是通过承受更高的运维复杂度来换取更好的敏捷性,其优势在于小而冶之,去中心化,但也导致基础架构的需求、成本和复杂性激增。
微服务架构是一种架构模式/架构风格,将单独的应用程序开发为一套小服务并独立运行在自己的进程中,相互之间使用HTTP API等轻量级机制通信。这些服务围绕着具体业务进行构建,通过完全自动化部署机制来独立部署,并可用不同的编程语言书写,以及不同数据存储技术,并保持最低限度的集中式管理。
上云经验
某视频平台视频转码业务在华为云云原生高性能解决方案的过程中,实现了极速的弹性能力、海量算力场景下高效的调度能力、跨客户IDC与华为云的混合调度能力、极大提升了业务的上线效率和资源利用率,部署时长由原来的2天下降到30分钟、CPU整体利用率由54%提升到65%,资源扩容速度由5分钟提升到秒级扩容。此外,该方案在互联网、生物制药、医疗、汽车行业,以及多个省级超算中心、科研机构均得到深度使用(技术得到强有力的验证)。
集群规模超过百万容器,单容器集群节点数量过万,数据库的峰值超过54万笔每秒,对应8700万查询每秒,而实时计算每秒峰值处理消息超过25亿条,消息系统RocketMQ峰值处理了超过每秒1.5亿条消息
在实现基础设施自助化的前提下,其智能化也成为重要演进方向、通过监控数据、历史数据驱动的智能化基础设施会在未来成为可能
高负载压力下的电商应用,实现30%的QPS上升,而rt也有明显下降,长尾rt下降尤其明显。云化促进了运维管理的自动化
存储计算分离即业务逻辑执行在计算集群上面,存储部署在存储集群上面。计算和存储集群之间通过告诉网络连接。随着数据处理对存储需求和计算需求在规模、速度、容量和成本等维度的不断变化,计算和存储分离可以最大限度地解耦并使这两类不同的关键资源相对独立地扩展和演进,获得更好的弹性、资源效率,同时可以让应用更容易的获得分布式存储的可靠性。
上云过程中,盘古2.0的升级也带来更好的io长尾延迟的稳定性,通过慢盘黑名单、backup read, 动态timeout等关键技术大幅度改进了长尾延迟。
原有的集团安全域,由现在的集团自建网络为主体逐渐转变为以云上集团的虚拟网络为主体,以VPC的方式实现网络隔离混合云网络:为了实现集团网络与云上VPC内业务单元的互通,采用了云专线产品方案,组成了混合云网络。云专线方案中的虚拟网关(xGW集群),采用硬件化HGW集群。
一开始就把安全策略、对安全的考量、安全配置作为应用的一部分,而不是在应用交付后再进行事后的安全审计和管理
云化存储计算分离带来应用架构变化
]]></content></entry><entry><title>Channel在Go中运用</title><url>/post/the_usage_of_channel_in_go/</url><categories><category>language</category></categories><tags><tag>golang</tag></tags><content type="html"><![CDATA[basic usage of channel 使用channel发送接收
package main import "fmt" func main() { jobs := make(chan int, 5) done := make(chan bool) go func() { for { j, more := <-jobs if more { fmt.Println("received job", j) } else { fmt.Println("received all jobs") done <- true return } } }() for j := 1; j <= 3; j++ { jobs <- j fmt.Println("sent job", j) } fmt.Println("sent all jobs") close(jobs) <-done } 程序结果
sent job 1 sent job 2 sent job 3 sent all jobs received job 1 received job 2 received job 3 received all jobs 往一个已关闭的channel中发送数据
package main import ( "log" ) func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) defer func() { err := recover() if err != nil { log.Println(err) } }() done := make(chan bool) close(done) done <- true } 往一个已经关闭的channel中发送数据会panic
往一个已关闭的channel中读取数据
package main import ( "log" ) func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) defer func() { err := recover() if err != nil { log.Println(err) } }() done := make(chan bool) close(done) ret := <-done log.Println(ret) } 向一个已关闭的channel中读取数据,会马上返回channel中的值(如果没有即类型初值),不会panic
package main import ( "log" ) func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) defer func() { err := recover() if err != nil { log.Println(err) } }() done := make(chan bool, 3) done <- true close(done) ret := <-done log.Println(ret) } chan原理实现 chan结构
src/runtime/chan.go
type hchan struct { qcount uint // total data in the queue dataqsiz uint // size of the circular queue buf unsafe.Pointer // points to an array of dataqsiz elements elemsize uint16 closed uint32 elemtype *_type // element type sendx uint // send index recvx uint // receive index recvq waitq // list of recv waiters sendq waitq // list of send waiters // lock protects all fields in hchan, as well as several // fields in sudogs blocked on this channel. // // Do not change another G's status while holding this lock // (in particular, do not ready a G), as this can deadlock // with stack shrinking. lock mutex } type waitq struct { first *sudog last *sudog } buf指向dataqsiz元素数组,指向环形队列;qcount是队列中的总数据;datasiz是环形队列的大小;如果是带缓冲区的chan,则缓冲区实际是紧跟着hchan结构体分配的
创建chan
chan由make创建,Go语言在编译期间的类型检查阶段将代表make关键字的OMAKE节点根据参数类型的不同转换成了OMAKESLICE、OMAKEMAP和OMAKECHAN三种不同类型的节点,这些节点会调用不同的运行时函数来初始化相应的数据结构。而chan类型则调用 makechan() ,如下(src/runtime/chan.go)
func makechan(t *chantype, size int) *hchan { elem := t.elem // compiler checks this but be safe. if elem.size >= 1<<16 { throw("makechan: invalid channel element type") } if hchanSize%maxAlign != 0 || elem.align > maxAlign { throw("makechan: bad alignment") } mem, overflow := math.MulUintptr(elem.size, uintptr(size)) if overflow || mem > maxAlloc-hchanSize || size < 0 { panic(plainError("makechan: size out of range")) } // Hchan does not contain pointers interesting for GC when elements stored in buf do not contain pointers. // buf points into the same allocation, elemtype is persistent. // SudoG's are referenced from their owning thread so they can't be collected. // TODO(dvyukov,rlh): Rethink when collector can move allocated objects. var c *hchan switch { case mem == 0: // Queue or element size is zero. c = (*hchan)(mallocgc(hchanSize, nil, true)) // Race detector uses this location for synchronization. c.buf = c.raceaddr() case elem.ptrdata == 0: // Elements do not contain pointers. // Allocate hchan and buf in one call. c = (*hchan)(mallocgc(hchanSize+mem, nil, true)) c.buf = add(unsafe.Pointer(c), hchanSize) default: // Elements contain pointers. c = new(hchan) c.buf = mallocgc(mem, elem, true) } c.elemsize = uint16(elem.size) c.elemtype = elem c.dataqsiz = uint(size) lockInit(&c.lock, lockRankHchan) if debugChan { print("makechan: chan=", c, "; elemsize=", elem.size, "; dataqsiz=", size, "\n") } return c } 声明一个hchan指针,如果是无缓冲chan,使用mallocgc分配;如果chan元素类型为指针,同时为chan和chan元素分配一块连续的内存;默认使用new分配chan,用mallocgc分配chan元素
发送chan
func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool { if c == nil { if !block { return false } gopark(nil, nil, waitReasonChanSendNilChan, traceEvGoStop, 2) throw("unreachable") } if debugChan { print("chansend: chan=", c, "\n") } if raceenabled { racereadpc(c.raceaddr(), callerpc, funcPC(chansend)) } // Fast path: check for failed non-blocking operation without acquiring the lock. // // After observing that the channel is not closed, we observe that the channel is // not ready for sending. Each of these observations is a single word-sized read // (first c.closed and second full()). // Because a closed channel cannot transition from 'ready for sending' to // 'not ready for sending', even if the channel is closed between the two observations, // they imply a moment between the two when the channel was both not yet closed // and not ready for sending. We behave as if we observed the channel at that moment, // and report that the send cannot proceed. // // It is okay if the reads are reordered here: if we observe that the channel is not // ready for sending and then observe that it is not closed, that implies that the // channel wasn't closed during the first observation. However, nothing here // guarantees forward progress. We rely on the side effects of lock release in // chanrecv() and closechan() to update this thread's view of c.closed and full(). if !block && c.closed == 0 && full(c) { return false } var t0 int64 if blockprofilerate > 0 { t0 = cputicks() } lock(&c.lock) if c.closed != 0 { unlock(&c.lock) panic(plainError("send on closed channel")) } if sg := c.recvq.dequeue(); sg != nil { // Found a waiting receiver. We pass the value we want to send // directly to the receiver, bypassing the channel buffer (if any). send(c, sg, ep, func() { unlock(&c.lock) }, 3) return true } if c.qcount < c.dataqsiz { // Space is available in the channel buffer. Enqueue the element to send. qp := chanbuf(c, c.sendx) if raceenabled { raceacquire(qp) racerelease(qp) } typedmemmove(c.elemtype, qp, ep) c.sendx++ if c.sendx == c.dataqsiz { c.sendx = 0 } c.qcount++ unlock(&c.lock) return true } if !block { unlock(&c.lock) return false } // Block on the channel. Some receiver will complete our operation for us. gp := getg() mysg := acquireSudog() mysg.releasetime = 0 if t0 != 0 { mysg.releasetime = -1 } // No stack splits between assigning elem and enqueuing mysg // on gp.waiting where copystack can find it. mysg.elem = ep mysg.waitlink = nil mysg.g = gp mysg.isSelect = false mysg.c = c gp.waiting = mysg gp.param = nil c.sendq.enqueue(mysg) gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanSend, traceEvGoBlockSend, 2) // Ensure the value being sent is kept alive until the // receiver copies it out. The sudog has a pointer to the // stack object, but sudogs aren't considered as roots of the // stack tracer. KeepAlive(ep) // someone woke us up. if mysg != gp.waiting { throw("G waiting list is corrupted") } gp.waiting = nil gp.activeStackChans = false if gp.param == nil { if c.closed == 0 { throw("chansend: spurious wakeup") } panic(plainError("send on closed channel")) } gp.param = nil if mysg.releasetime > 0 { blockevent(mysg.releasetime-t0, 2) } mysg.c = nil releaseSudog(mysg) return true } 发送时,先进行加锁,判断recvq队列是否有receivers,如果有则直接将元素拷贝到接收者的go协程的mem中,使用goready唤醒接收者所在协程
如果是recvq队列没有接收者,有缓存队列首先计算一个可以放置待处理变量的位置,然后通过typedmemmove将元素拷贝到所在位置,更新sendx和qcount,释放锁
无缓冲队列阻塞发送操作: 调用getg获取发送操作的协程;执行acquireSudog获取一个sudog结构体并设置这一次阻塞发送的相关信息;在当前channel的sendq队列中将刚刚创建并初始化的sudog结构体加入等待队列;调用goparkunlock函数将当前的goroutine更新成gwaiting状态并解锁,该goroutine可以被调用goready再次唤醒;当前的gouroutine在此陷入阻塞状态
梳理下向channel发送数据时遇到的几种情况:
如果当前channel的recvq上已存在被阻塞的goroutine,那么会直接将数据发送给当前的goroutine并将其设置成下一个运行的协程; 如果channel存在缓冲区并且其中还有空闲的容量,我们就会直接将数据存储到当前缓冲区sendx所在位置 如果都不满足上面的两种情况,就会创建一个sudog结构并加入channel的sendq队列,同时当前的goroutine就会陷入阻塞等待其它的协程向channel中发送数据 chan接收
func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) { // raceenabled: don't need to check ep, as it is always on the stack // or is new memory allocated by reflect. if debugChan { print("chanrecv: chan=", c, "\n") } if c == nil { if !block { return } gopark(nil, nil, waitReasonChanReceiveNilChan, traceEvGoStop, 2) throw("unreachable") } // Fast path: check for failed non-blocking operation without acquiring the lock. if !block && empty(c) { // After observing that the channel is not ready for receiving, we observe whether the // channel is closed. // // Reordering of these checks could lead to incorrect behavior when racing with a close. // For example, if the channel was open and not empty, was closed, and then drained, // reordered reads could incorrectly indicate "open and empty". To prevent reordering, // we use atomic loads for both checks, and rely on emptying and closing to happen in // separate critical sections under the same lock. This assumption fails when closing // an unbuffered channel with a blocked send, but that is an error condition anyway. if atomic.Load(&c.closed) == 0 { // Because a channel cannot be reopened, the later observation of the channel // being not closed implies that it was also not closed at the moment of the // first observation. We behave as if we observed the channel at that moment // and report that the receive cannot proceed. return } // The channel is irreversibly closed. Re-check whether the channel has any pending data // to receive, which could have arrived between the empty and closed checks above. // Sequential consistency is also required here, when racing with such a send. if empty(c) { // The channel is irreversibly closed and empty. if raceenabled { raceacquire(c.raceaddr()) } if ep != nil { typedmemclr(c.elemtype, ep) } return true, false } } var t0 int64 if blockprofilerate > 0 { t0 = cputicks() } lock(&c.lock) if c.closed != 0 && c.qcount == 0 { if raceenabled { raceacquire(c.raceaddr()) } unlock(&c.lock) if ep != nil { typedmemclr(c.elemtype, ep) } return true, false } if sg := c.sendq.dequeue(); sg != nil { // Found a waiting sender. If buffer is size 0, receive value // directly from sender. Otherwise, receive from head of queue // and add sender's value to the tail of the queue (both map to // the same buffer slot because the queue is full). recv(c, sg, ep, func() { unlock(&c.lock) }, 3) return true, true } if c.qcount > 0 { // Receive directly from queue qp := chanbuf(c, c.recvx) if raceenabled { raceacquire(qp) racerelease(qp) } if ep != nil { typedmemmove(c.elemtype, ep, qp) } typedmemclr(c.elemtype, qp) c.recvx++ if c.recvx == c.dataqsiz { c.recvx = 0 } c.qcount-- unlock(&c.lock) return true, true } if !block { unlock(&c.lock) return false, false } // no sender available: block on this channel. gp := getg() mysg := acquireSudog() mysg.releasetime = 0 if t0 != 0 { mysg.releasetime = -1 } // No stack splits between assigning elem and enqueuing mysg // on gp.waiting where copystack can find it. mysg.elem = ep mysg.waitlink = nil gp.waiting = mysg mysg.g = gp mysg.isSelect = false mysg.c = c gp.param = nil c.recvq.enqueue(mysg) gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanReceive, traceEvGoBlockRecv, 2) // someone woke us up if mysg != gp.waiting { throw("G waiting list is corrupted") } gp.waiting = nil gp.activeStackChans = false if mysg.releasetime > 0 { blockevent(mysg.releasetime-t0, 2) } closed := gp.param == nil gp.param = nil mysg.c = nil releaseSudog(mysg) return true, !closed } 接收时,先进行枷锁,判断sendq队列是否有senders,如果有则直接将send队列中的拷贝到接收者目标内存地址中(如果当前channel已经满了,通过typedmemmove将队列中的数据拷贝到接收方的内存地址中并将发送方的数据拷贝到队列中,释放一个发送方协程)。
与select语句结合使用时可能会使用到非阻塞block=false的接收操作
梳理下向channel接收数据时遇到的几种情况:
如果channel是空的,那么会直接调用gopark挂起当前的goroutine 如果channel已经关闭并且缓冲区没有任何数据,chanrecv函数就会直接返回 如果channel的sendq队列中存在观其的goroutine,就会将recvx索引所在数据拷贝到接收变量所在的内存空间上并将sendq队列中goroutine的数据拷贝到缓冲区中 如果channel的缓冲区包含数据就会直接从recvx所在索引上进行读取 在默认情况下会直接挂起当前的goroutine,将sudog结构加入recvq队列并等待调度器唤醒 problem chan结构为何是环形队列而不是队列,有什么优势?
循环队列的出队操作时间为O(1),而队列为O(n)
reference https://draveness.me/golang/docs/part1-prerequisite/ch02-compile/golang-typecheck/ https://juejin.cn/post/6844903806082220046 调试+图解 channel 的内部实现: https://mp.weixin.qq.com/s/01Hl_eOAP_k_YDTNFErTJQ ]]></content></entry><entry><title>Celery笔记</title><url>/post/celerynote/</url><categories><category>language</category></categories><tags><tag>celery</tag></tags><content type="html"><![CDATA[Getting Started celery is flexible. Almost every part of Celry can be extended or used on its own, Custom pool implementations, serializers, compression schemes, logging, schedulers, consumers, producers, broker transports, and much more.
celery中current_app的指向
# main.py from celery import current_app from celery import Celery cel = Celery("lll") cel2 = Celery("bbb") cel2 = Celery("ddd") cel2 = Celery("fff") print(current_app.main) cel2 = Celery("ddd") print(current_app.main) pr # result of running main.py fff ddd Running the Celery worker server
demo1.py
from celery import Celery from celery.utils.log import get_task_logger logger = get_task_logger(__name__) app = Celery('hello', broker='amqp://guest:guest@127.0.0.1:5672//', backend='redis://127.0.0.1:6379/0') @app.task def hello(): logger.info("task hello") return 'hello' 运行 demo1.py 命令:
celery -A main worker 默认celery队列,exchange为direct类型的celery, binding_key为celery
Calling the task
from main import hello a = hello.delay() print(a.id) a = a.get() print(a) delay函数发送任务,a.id获取其任务ID,a.get阻塞获取异步任务结果
消息发送到rabbitmq中,rabbitmq消息内容为:
如果以redis作为broker,其消息内容为:
{\"body\": \"W1sxLCAyXSwge30sIHsiY2FsbGJhY2tzIjogbnVsbCwgImVycmJhY2tzIjogbnVsbCwgImNoYWluIjogbnVsbCwgImNob3JkIjogbnVsbH1d\", \"content-encoding\": \"utf-8\", \"content-type\": \"application/json\", \"headers\": {\"lang\": \"py\", \"task\": \"main.add\", \"id\": \"95eb2c01-437e-40e6-9b15-e9b6c9218e99\", \"shadow\": null, \"eta\": null, \"expires\": null, \"group\": null, \"group_index\": null, \"retries\": 0, \"timelimit\": [null, null], \"root_id\": \"95eb2c01-437e-40e6-9b15-e9b6c9218e99\", \"parent_id\": null, \"argsrepr\": \"[1, 2]\", \"kwargsrepr\": \"{}\", \"origin\": \"gen226466@iZ8vb4rhbik3h9nvaj42m2Z\"}, \"properties\": {\"correlation_id\": \"95eb2c01-437e-40e6-9b15-e9b6c9218e99\", \"reply_to\": \"e227c785-cc00-35d9-b52a-5062bfcf22ce\", \"delivery_mode\": 2, \"delivery_info\": {\"exchange\": \"\", \"routing_key\": \"celery\"}, \"priority\": 0, \"body_encoding\": \"base64\", \"delivery_tag\": \"9a140915-c69d-40b7-a895-7978a8cc077a\"}} get函数获取任务执行结果,以redis为backend,其结果如下:
Configuration
有多种配置方式,本配置介绍采用app.conf.update(**kwargs),格式为 celery configuration 配置参数在不同版本有不同的key值。例如CELERY_ACCEPT_CONTENT –> accept_content,请参考 Configration and defaults Basic configuration
# task serializer (json, pickle, yaml, msgpack) task_serailizer = ['json'] # using serializer name, default['json'] accept_content = ['json'] # using seralizer name result_accept_content = ['json'] # enable_utc enable_utc = True # pytz timezone = "UTC" # consumer rate limit task_annotations = {'tasks.add': {'rate_limit': '10/s'}} task_annotations = {'*': {'rate_limit': '10/s'}} # If this is True, all tasks will be executed locally by blocking until the task returns task_always_eager = False task_ignore_result = False task_store_errors_even_if_ignored = False # task_acks_late = False # default 1 day, unit in seconds result_expires = 86400 result_persistent = False Task failure handling
def my_on_failure(self, exc, task_id, args, kwargs, einfo): print('Oh no! Task failed: {0!r}'.format(exc)) task_annotations = {'*': {'on_failure': my_on_failure}} Message routing
celery任务可以通过delay(tuple, **kwargs)和apply_async(tuple, **kwargs)进行。apply_async中kwargs支持更多的参数
任务投放
按routing_key投放
from main import hello, add from kombu import Exchange a = hello.apply_async((), exchange=Exchange('celery'), routing_key='celery') print(a.id) 按队列投放,如果队列不存在将建立exchange和queue并绑定routing_key
from main import hello, add from kombu import Exchange a = hello.apply_async((), queue='celery') print(a.id) task_routes配置用于任务投放,当delay()和applay_async()没有描述相关参数时,按照task_routes投放至队列
app.conf.update(task_routes=[ {'main.hello': 'hello'}, {'*': 'celery'} ]) celery定时任务, celery -A main beat
from celery import Celery from kombu import Queue, Exchange from datetime import timedelta from celery.utils.log import get_task_logger logger = get_task_logger(__name__) app = Celery('hello', broker='amqp://guest:guest@127.0.0.1:5672//', backend='redis://127.0.0.1:6379/0') app.conf.update(task_routes=[ {'main.hello': 'hello'}, {'*': 'celery'} ]) app.conf.update(beat_schedule={ 'bbb': { 'task': 'main.add', 'schedule': timedelta(seconds=1), 'args': [1, 2] } }) @app.task(name='main.hello') def hello(): logger.info("task hello") return 'hello' @app.task(name='main.add') def add(x, y :int): return x + y 任务消费
task_queues参数负责worker启动时消费的队列,如未指定Exchange,key均使用celery
# demo2.py from celery import Celery from kombu import Queue, Exchange from celery.utils.log import get_task_logger logger = get_task_logger(__name__) app = Celery('hello', broker='amqp://guest:guest@127.0.0.1:5672//', backend='redis://127.0.0.1:6379/0') app.conf.update(task_routes=[ {'main.hello': 'hello'}, {'*': 'celery'} ]) app.conf.update(task_queues=[ Queue('normal', Exchange('normal'), routing_key='normal'), Queue('exception', Exchange('exception'), routing_key='exception') ]) @app.task(name='main.hello') def hello(): logger.info("task hello") return 'hello' @app.task def add(x, y :int): return x + y celelry -A main worker -Q normal中 -Q 参数指定worker消费队列
A worker instance can consume from any number of queues. By default it will consume from all queues defined in the task_queues setting (that if not specified falls back to the default queue named celery).
celery中使用logging记录日志
from celery.utils.log import get_task_logger celery进阶 celery定时任务的实现
celery任务消费速度限制
celery工作流实现
Extensions and Bootsteps
https://docs.celeryproject.org/en/stable/userguide/extending.html celery源码解析 附录 celery configuration
app.conf.update( task_serializer='json', task_annotations = {'consumer.hello': {'rate_limit': '100/s'}}, timezone='UTC', result_persistent=False, celery_default_queue='bbb', celery_default_routing_key='default', task_routes = [ {'consumer.hello': {'queue': 'hello_celec'}}, ], # 一个用语描述task.name与队列关系的字典 task_queues = { Queue('hello'), Queue('hello_celec', exchange=Exchange('hello_celec'), routing_key='hello_celec') }, beat_schedule={ 'bbb': { 'task': 'consumer.hello', 'schedule': timedelta(seconds=1), 'args': [1, 2] } } ) Reference: https://www.jianshu.com/p/db4fead7a56c https://www.jianshu.com/p/581fd8f92509 celery源码解析: https://liqiang.io/post/kombu-source-code-analysis-part-2?lang=ZH_CN Problem: celery为何是高可用的?
worker和beat具有重试机制当连接断开或丢失时,borker本身高可用,具有主从复制或双主可用
]]></content></entry><entry><title>Linux线程</title><url>/post/linux_thread/</url><categories><category>linux</category></categories><tags><tag>thread</tag></tags><content type="html">Linux线程 Linux线程的发展
1991年1月发布的Linux2.2内核中,进程是通过系统调用fork创建的,新的进程是原来进程的子进程。在2.2x版本中,不存在真正意义上的线程。Linux中常用的线程Pthread实际是通过进程来模拟的,即LWP。Linux2.2仅默认允许4096个进程/线程同时运行。
2001年1月发布的Linux2.4内核消除了这个限制,并且允许在系统运行中动态调整进程数上限。因此进程数只受制于物理内存的多少。
2003年12月发布的Linux2.6内核,NPTL线程模型出现。线程框架的改变包含Linux线程空间中许多新的概念,包括线程组、线程各自的本地存储区、POSIX风格信号
什么是Linux线程
线程是在共享内存空间中并发的多道执行路径,是linux系统中的最小执行单元。
线程的标识
和进程一样,每个线程也都有属于自己的ID,称为TID,在进程内唯一。Linux系统的线程实现确保了每个TID在系统范围内唯一,并且当线程不复存在后,其线程ID可以被其它线程复用
线程管理
线程管理包括创建线程(pthread_create)、终止线程(pthread_exit\pthread_cancel)、连接已终止的线程(pthread_join)、分离线程(pthread_detach)
在主线程中调用pthread_exit函数,那么只有主线程自己会被终止,而其它线程仍然会照常运行。此时父进程是僵尸进程,当其它线程退出后,僵尸进程被回收。
线程调度
线程的动态优先级是可以被调度器实时调整的,而与之对应的线程静态优先级则只能由应用程序指定,如果应用程序没有显示指定一个线程的静态优先级,那么它将被设定为0.调度器并不会改变线程的静态优先级,线程的动态优先级是调度器在其静态优先级的基础上调整得出的,它在线程的运行顺序上起到了关键作用。而线程的静态优先级则决定了线程单次在CPU上运行的最长时间,也就是调度器分配给它的时间片大小。
所有等待CPU的线程按照动态优先级从高到低顺序排列。每一个CPU的运行队列中都包含两个优先级阵列:其中一个用于存放正在等待运行的线程(激活的优先级阵列);而另一个则用于存放已经运行过但还未完成的线程(过期的优先级阵列)。下一个运行的线程总是从激活的优先级阵列中选出。如果调度器发现某个线程已经占用了CPU很长时间(该时间只会小于或等于给予该线程的时间片),并且激活的优先级阵列中还有优先级与它相同的线程在等待运行,那么调度器就会让那个等待的线程在CPU上运行,而被换下的线程会被排入过期的优先级阵列。当激活的优先级阵列中没有待运行的线程时,调度器会把这两个优先级阵列的身份互换,即之前的激活的优先级阵列成为新的过期的优先级阵列
线程和进程中文件描述符的影响
一个线程拥有自己的线程栈,并以此存储自己的私有数据。一个进程中的代码段、数据段、堆、信号处理函数,以及当前进程所持有的文件描述符
线程模型
线程实现模型主要有3个,分别是:用户级线程模型、内核级线程模型和两级线程模型。它们之间最大的差异在于线程与内核调度实体(Kernel Scheduling Entity)
查看当前系统线程模型: getconf GNU_LIBPTHREAD_VERSION
NPTL
NPTL是一个内核级线程模型线程库,当使用pthread_create创建一个线程,在内核相应创建一个调度实体
NPTL没有使用管理线程
多线程下的编程经验
同步 过载保护:多线程场景处理任务,常用到队列,即多线程从任务队列中取出任务,然后并发处理。当任务量大,线程来不及处理,需要及时丢弃队列中的任务,以保证高吞吐和低延迟。 公平调度:使得某个线程负载过高 析构出core: 线程退出时析构出core的问题,一个线程释放了还在被使用的资源 Reference:
&lt;Go并发编程实战&gt;:105 pages http://blog.chinaunix.net/uid-25909722-id-3026451.html</content></entry><entry><title>哈希算法和密钥保存</title><url>/post/hashalgorithmandkeypreservation/</url><categories><category>blockchain</category></categories><tags><tag/></tags><content type="html"> 本文章翻译自 Pradeep Loganathan ,如有侵权,请联系 作者 进行删除
什么是哈希?哈希算法和密钥保存 哈希
哈希是一个单向函数,它将任意长度的数据映射到固定长度的输出摘要,在这种情况下,从输出中查找输入在计算上是不可行的。哈希函数返回的值通常被称为消息摘要、哈希值、哈希代码、哈希和、校验和,或者简单地称为哈希。
散列函数是无密钥的,提供数据完整性服务。它们通常使用迭代和专用的哈希函数构造技术来构建。哈希函数将数据压缩到固定大小,这可以看作是对原始数据的缩短引用。散列函数应该易于计算,但是很难为压缩而反转,哈希函数通常使用数论的单向函数,因此它们是不可逆的。因此,当哈希值已知时,重构特定数据是不可行的。
散列是单向函数,因为它不能反转。我们可以把散列看作是输入其中的数据的数字指纹。这些类型的散列函数有很多种用法。它们可以用于身份验证、将数据索引到哈希表、校验和和和数字签名。SHA256散列的一个示例如下所示:4usow2gkuwzwpy2uh8cdnfmoqhm7gv5zbfxdnv4ww。
最常用的两个加密哈希函数是MD5和SHA。每种算法在一个或多个参数方面与其他算法不同
MD5
MD5于1991年由罗纳德·里维斯特创建。MD5使用128位散列值。起初它被认为是安全的,但现在大多数专家建议不要使用MD5进行身份验证,因为多年来发现了许多漏洞。MD5的工作原理是获取可变长度的数据并将其转换为128位的固定长度哈希字符串。
SHA
SHA比MD5更安全。SHA的创造者是Guido Bertoni、Joan Daemen、Michael Peeters和Gilles Van Assche,安全哈希算法,或SHA哈希,由美国国家标准与技术研究所(NIST)发布,作为美国联邦信息处理标准——FIPS PUB 180-3,其中规定了SHA算法的三种风格:
SHA-0: 不再被使用 SHA-1: 最广泛使用的版本 SHA-2: 有四种不同的变体:SHA-224, SHA-256, SHA-384和SHA-512 当长度小于264位(SHA-1、SHA-224和SHA-256)或小于2128位(SHA-384和SHA-512)的消息输入哈希算法时,结果是一个称为消息摘要的输出。消息摘要的长度范围从160到512位不等,具体取决于算法。
本标准中指定的五种哈希算法称为安全算法,因为对于给定算法,查找与给定消息摘要相对应的消息或查找生成同一消息摘要的两个不同消息在计算上是不可行的。对消息的任何更改都很可能导致不同的消息摘要。当安全哈希算法与数字签名算法或密钥哈希消息验证算法一起使用时,这将导致验证失败。
SHA-1
该算法的原始规范于1993年在FIPS PUB 180-1中发布。这是现有的SHA哈希函数中使 用最广泛的一种,并被应用于一些广泛使用的安全应用程序和协议中,例如传输层安全 性(TLS)、安全套接字层(SSL)、相当好的隐私性(PGP)、安全外壳(SSH)、安 全/多用途互联网邮件扩展(S/MIME)和互联网协议安全性(IPSEC)。SHA-1哈希还用 于分布式修订控制系统,如Arch、Mercurial、Monotone和BitKeeper,以识别修订 并检测数据损坏或篡改。而且,是的,即使你在家里享受着通过任天堂在互联网上杀死 一个完全陌生的人或者用你的Wii来保持健康的快乐,SHA-1散列也被用来在你启动过程 中进行签名验证。SHA-1于2005年被破解。