-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.xml
More file actions
138 lines (138 loc) · 160 KB
/
index.xml
File metadata and controls
138 lines (138 loc) · 160 KB
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
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Home on Ween's Blog</title>
<link>https://weenable.github.io/</link>
<description>Recent content in Home on Ween's Blog</description>
<generator>Hugo</generator>
<language>en-us</language>
<lastBuildDate>Tue, 06 May 2025 19:14:03 +0800</lastBuildDate>
<atom:link href="https://weenable.github.io/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>传统merkle树和utreexo(rolling merkle tree)</title>
<link>https://weenable.github.io/posts/web3/%E4%BC%A0%E7%BB%9Fmerkle%E6%A0%91%E5%92%8Cutreexorolling-merkle-tree/</link>
<pubDate>Tue, 06 May 2025 19:14:03 +0800</pubDate>
<guid>https://weenable.github.io/posts/web3/%E4%BC%A0%E7%BB%9Fmerkle%E6%A0%91%E5%92%8Cutreexorolling-merkle-tree/</guid>
<description><h3 class="heading" id="传统merkle树实现">
 传统Merkle树实现
 <a class="anchor" href="#%e4%bc%a0%e7%bb%9fmerkle%e6%a0%91%e5%ae%9e%e7%8e%b0">#</a>
</h3>
<pre tabindex="0"><code>package main 
 
import ( 
 &#34;crypto/sha256&#34; 
 &#34;encoding/hex&#34;
 &#34;fmt&#34;
) 
 
func hashPair(a, b []byte) []byte { 
 h := sha256.New() 
 h.Write([]byte{0x01}) // 内部节点前缀 
 if hex.EncodeToString(a) &lt; hex.EncodeToString(b) { 
 h.Write(a) 
 h.Write(b) 
 } else { 
 h.Write(b) 
 h.Write(a) 
 } 
 return h.Sum(nil) 
} 
 
type MerkleTree struct { 
 nodes [][]byte 
} 
 
func NewMerkleTree() *MerkleTree { 
 return &amp;MerkleTree{nodes: make([][]byte, 0)} 
} 
 
func (mt *MerkleTree) Add(data []byte) { 
 mt.nodes = append(mt.nodes, data) 
} 
 
func (mt *MerkleTree) Root() []byte { 
 if len(mt.nodes) == 0 { 
 return nil 
 } 
 level := make([][]byte, len(mt.nodes)) 
 copy(level, mt.nodes) 
 
 for len(level) &gt; 1 { 
 var nextLevel [][]byte 
 for i := 0; i &lt; len(level); i += 2 { 
 if i+1 &gt;= len(level) { 
 nextLevel = append(nextLevel, hashPair(level[i], level[i])) 
 } else { 
 nextLevel = append(nextLevel, hashPair(level[i], level[i+1])) 
 } 
 } 
 level = nextLevel 
 } 
 
 return level[0] 
} 
 
func main() { 
 testData := [][]byte{ 
 []byte(&#34;Transaction1&#34;), 
 []byte(&#34;Transaction2&#34;), 
 []byte(&#34;Transaction3&#34;), 
 []byte(&#34;Transaction4&#34;), 
 } 
 fmt.Println(&#34;=== 传统Merkle树测试 ===&#34;) 
 mt := NewMerkleTree() 
 for i, data := range testData { 
 mt.Add(data) 
 fmt.Printf(&#34;添加第%d个交易后的根哈希: %x\n&#34;, i+1, mt.Root()) 
 } 
}


=== 传统Merkle树测试 ===
添加第1个交易后的根哈希: 5472616e73616374696f6e31
添加第2个交易后的根哈希: ae8f5f113879432c68c9aa01a45e2cf0b3da92243277f0db78cf3a868141f73e
添加第3个交易后的根哈希: a76764553968d075e0a82751ec1dbced37b5be3ef29bb3765867da9a5178e81d
添加第4个交易后的根哈希: 170d56828552760c53ae90f4241b6fdd8a2d85c783264e92d887557527802169
</code></pre><h3 class="heading" id="utreexorolling-merkle-tree实现">
 UTreexo(rolling merkle tree)实现
 <a class="anchor" href="#utreexorolling-merkle-tree%e5%ae%9e%e7%8e%b0">#</a>
</h3>
<p>和传统merkle相比,主要差异点在于add节点的时候,rolling merkle只需要存储最终的合并根哈希</p></description>
</item>
<item>
<title>P2P核心技术: 以太坊P2P连接</title>
<link>https://weenable.github.io/posts/web3/p2p%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF-%E4%BB%A5%E5%A4%AA%E5%9D%8Ap2p%E8%BF%9E%E6%8E%A5/</link>
<pubDate>Wed, 30 Apr 2025 11:16:08 +0800</pubDate>
<guid>https://weenable.github.io/posts/web3/p2p%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF-%E4%BB%A5%E5%A4%AA%E5%9D%8Ap2p%E8%BF%9E%E6%8E%A5/</guid>
<description><h3 class="heading" id="详细逻辑">
 详细逻辑
 <a class="anchor" href="#%e8%af%a6%e7%bb%86%e9%80%bb%e8%be%91">#</a>
</h3>
<ol>
<li>建立网络连接</li>
<li>ECDH握手</li>
<li>消息加密和签名</li>
</ol>
<h3 class="heading" id="代码实现">
 代码实现
 <a class="anchor" href="#%e4%bb%a3%e7%a0%81%e5%ae%9e%e7%8e%b0">#</a>
</h3>
<p>服务端实现</p>
<pre tabindex="0"><code>package main 
 
import ( 
 &#34;bytes&#34; 
 &#34;crypto/aes&#34; &#34;crypto/cipher&#34; &#34;crypto/ecdsa&#34; &#34;crypto/elliptic&#34; &#34;crypto/rand&#34; &#34;crypto/sha256&#34; &#34;errors&#34; &#34;fmt&#34; &#34;github.com/ethereum/go-ethereum/crypto&#34; &#34;github.com/ethereum/go-ethereum/crypto/ecies&#34; &#34;github.com/ethereum/go-ethereum/p2p&#34; &#34;github.com/ethereum/go-ethereum/p2p/discover&#34; &#34;github.com/ethereum/go-ethereum/p2p/enode&#34; &#34;github.com/ethereum/go-ethereum/p2p/nat&#34; &#34;github.com/ethereum/go-ethereum/rlp&#34; &#34;log&#34; &#34;math/big&#34; &#34;net&#34;) 
 
const ( 
 MsgTypeHandshake = 0 
 MsgTypeData = 1 
) 
 
type SimpleProtocol struct { 
 peers map[*p2p.Peer]*peerSession 
 privateKey *ecies.PrivateKey 
 ecdsaPrv *ecdsa.PrivateKey 
 localPubKey []byte 
} 
 
type peerSession struct { 
 rw p2p.MsgReadWriter 
 sharedKey []byte 
 peerPubKey *ecies.PublicKey 
 peerECDSAPub *ecdsa.PublicKey 
 aesgcm cipher.AEAD 
} 
 
func (ps *peerSession) decrypt(encrypted []byte) ([]byte, error) { 
 nonceSize := ps.aesgcm.NonceSize() 
 nonce, ct := encrypted[:nonceSize], encrypted[nonceSize:] 
 
 plaintext, err := ps.aesgcm.Open(nil, nonce, ct, nil) 
 if err != nil { 
 return nil, fmt.Errorf(&#34;解密失败: %v&#34;, err) 
 } 
 
 return plaintext, nil 
} 
 
func (sp *SimpleProtocol) Name() string { return &#34;SimpleProtocol&#34; } 
func (sp *SimpleProtocol) Version() uint { return 1 } 
func (sp *SimpleProtocol) Length() uint64 { return 16 } 
 
func (sp *SimpleProtocol) generateSharedSecret(peerPubKey []byte) ([]byte, error) { 
 pubKey, err := crypto.DecompressPubkey(peerPubKey) 
 if err != nil { 
 return nil, err 
 } 
 peerEciesPub := ecies.ImportECDSAPublic(pubKey) 
 localEciesPub := sp.privateKey.PublicKey 
 
 x, _ := localEciesPub.Curve.ScalarMult(peerEciesPub.X, peerEciesPub.Y, sp.privateKey.D.Bytes()) 
 if x == nil { 
 return nil, errors.New(&#34;failed to generate shared secret&#34;) 
 } 
 
 shared := sha256.Sum256(x.Bytes()) 
 return shared[:], nil 
} 
func (sp *SimpleProtocol) handshake(peer *p2p.Peer, session *peerSession) error { 
 // 发送公钥 
 if err := p2p.Send(session.rw, MsgTypeHandshake, sp.localPubKey); err != nil { 
 return err 
 } 
 
 // 接受对方公钥 
 msg, err := session.rw.ReadMsg() 
 if err != nil { 
 return err 
 } 
 if msg.Code != MsgTypeHandshake { 
 return errors.New(&#34;unexpected message type&#34;) 
 } 
 
 var peerPubKey []byte 
 if err = msg.Decode(&amp;peerPubKey); err != nil { 
 return err 
 } 
 
 // 生成共享密钥 
 shared, err := sp.generateSharedSecret(peerPubKey) 
 if err != nil { 
 return err 
 } 
 
 block, err := aes.NewCipher(shared) 
 if err != nil { 
 return err 
 } 
 
 aesgcm, err := cipher.NewGCM(block) 
 if err != nil { 
 return err 
 } 
 session.aesgcm = aesgcm 
 
 pubKey, err := crypto.DecompressPubkey(peerPubKey) 
 if err != nil { 
 return fmt.Errorf(&#34;解压公钥失败: %v&#34;, err) 
 } 
 session.peerECDSAPub = pubKey 
 
 log.Printf(&#34;Handshake completed with %v&#34;, peer) 
 return nil 
} 
 
func (sp *SimpleProtocol) handleMessages(peer *p2p.Peer, session *peerSession) error { 
 for { 
 msg, err := session.rw.ReadMsg() 
 if err != nil { 
 log.Printf(&#34;Read error: %v&#34;, err) 
 return nil 
 } 
 var ciphertext []byte 
 msg.Decode(&amp;ciphertext) 
 
 pt, err := session.decrypt(ciphertext) 
 var signedMsg struct { 
 Data []byte 
 Signature []byte 
 } 
 s := rlp.NewStream(bytes.NewReader(pt), 0) 
 if err := s.Decode(&amp;signedMsg); err != nil { 
 log.Printf(&#34;解析签名消息失败: %v&#34;, err) 
 continue 
 } 
 hash := crypto.Keccak256Hash(signedMsg.Data) 
 sig := signedMsg.Signature 
 
 r := new(big.Int).SetBytes(sig[:32]) 
 ss := new(big.Int).SetBytes(sig[32:64]) 
 valid := ecdsa.Verify(session.peerECDSAPub, hash.Bytes(), r, ss) 
 if !valid { 
 log.Printf(&#34;签名验证失败&#34;) 
 continue 
 } 
 
 log.Printf(&#34;receive msg: %s&#34;, string(signedMsg.Data)) 
 } 
} 
func (sp *SimpleProtocol) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error { 
 session := &amp;peerSession{rw: rw} 
 sp.peers[peer] = session 
 
 if err := sp.handshake(peer, session); err != nil { 
 return fmt.Errorf(&#34;handshake failed: %v&#34;, err) 
 } 
 
 defer delete(sp.peers, peer) 
 
 err := sp.handleMessages(peer, session) 
 if err != nil { 
 return fmt.Errorf(&#34;handlemessage failed: %v&#34;, err) 
 } 
 
 return nil 
} 
 
func (sp *SimpleProtocol) SendMessage(peer *p2p.Peer, message string) error { 
 session, ok := sp.peers[peer] 
 if !ok { 
 return errors.New(&#34;peer not connected&#34;) 
 } 
 
 hash := crypto.Keccak256Hash([]byte(message)) 
 signature, err := crypto.Sign(hash.Bytes(), sp.ecdsaPrv) 
 if err != nil { 
 return fmt.Errorf(&#34;签名失败: %v&#34;, err) 
 } 
 signedMsg := struct { 
 Data []byte 
 Signature []byte 
 }{ 
 Data: []byte(message), 
 Signature: signature, 
 } 
 
 // RLP编码 
 var buffer bytes.Buffer 
 if err := rlp.Encode(&amp;buffer, signedMsg); err != nil { 
 return fmt.Errorf(&#34;RLP编码失败: %v&#34;, err) 
 } 
 encodedMsg := buffer.Bytes() 
 
 // 加密 
 nonce := make([]byte, session.aesgcm.NonceSize()) 
 if _, err := rand.Read(nonce); err != nil { 
 return fmt.Errorf(&#34;生成nonce失败: %v&#34;, err) 
 } 
 ciphertext := session.aesgcm.Seal(nil, nonce, encodedMsg, nil) 
 
 // 合并nonce和密文 
 encryptedMsg := append(nonce, ciphertext...) 
 
 // 发送加密消息 
 return p2p.Send(session.rw, MsgTypeData, encryptedMsg) 
} 
 
func NewSimpleProtocol(prv *ecdsa.PrivateKey) *SimpleProtocol { 
 eciesPrv := ecies.ImportECDSA(prv) 
 pubKey := elliptic.MarshalCompressed(prv.Curve, prv.PublicKey.X, prv.PublicKey.Y) 
 return &amp;SimpleProtocol{ 
 peers: make(map[*p2p.Peer]*peerSession), 
 privateKey: eciesPrv, 
 localPubKey: pubKey, 
 } 
} 
 
func main() { 
 privKey, _ := crypto.GenerateKey() 
 proto := NewSimpleProtocol(privKey) 
 
 config := p2p.Config{ 
 PrivateKey: privKey, 
 Name: &#34;SimpleP2PNode&#34;, 
 ListenAddr: &#34;:30303&#34;, // 固定端口 
 Protocols: []p2p.Protocol{{ 
 Name: proto.Name(), 
 Version: proto.Version(), 
 Length: proto.Length(), 
 Run: proto.Run, 
 }}, 
 NAT: nat.Any(), 
 MaxPeers: 10, 
 EnableMsgEvents: true, 
 } 
 
 srv := &amp;p2p.Server{Config: config} 
 if err := srv.Start(); err != nil { 
 log.Fatalf(&#34;Failed to start server: %v&#34;, err) 
 } 
 defer srv.Stop() 
 
 log.Printf(&#34;Server enode: %s&#34;, srv.Self().URLv4()) 
 
 // 初始化发现协议 
 tcpAddr, _ := net.ResolveTCPAddr(&#34;tcp&#34;, srv.ListenAddr) 
 udpPort := tcpAddr.Port + 1 
 udpAddr := fmt.Sprintf(&#34;:%d&#34;, udpPort) 
 udpConn, err := net.ListenPacket(&#34;udp&#34;, udpAddr) 
 if err != nil { 
 log.Fatalf(&#34;Failed to listen UDP: %v&#34;, err) 
 } 
 
 discoverDB, _ := enode.OpenDB(&#34;&#34;) 
 localNode := enode.NewLocalNode(discoverDB, privKey) 
 localNode.SetFallbackIP(net.IP{127, 0, 0, 1}) 
 localNode.SetFallbackUDP(udpPort) 
 
 discv5, err := discover.ListenV5(udpConn.(*net.UDPConn), localNode, discover.Config{ 
 PrivateKey: privKey, 
 }) 
 if err != nil { 
 log.Fatalf(&#34;Failed to start discovery: %v&#34;, err) 
 } 
 defer discv5.Close() 
 
 select {} 
}
</code></pre><p>客户端实现</p></description>
</item>
<item>
<title>排序算法汇总</title>
<link>https://weenable.github.io/posts/%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E6%B1%87%E6%80%BB/</link>
<pubDate>Fri, 28 Feb 2025 19:24:41 +0800</pubDate>
<guid>https://weenable.github.io/posts/%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E6%B1%87%E6%80%BB/</guid>
<description><h6 class="heading" id="冒泡排序n2">
 冒泡排序n^2
 <a class="anchor" href="#%e5%86%92%e6%b3%a1%e6%8e%92%e5%ba%8fn2">#</a>
</h6>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> <span style="color:#666;font-weight:bold;font-style:italic">bubbleSort</span>(list []<span style="font-weight:bold;text-decoration:underline">int</span>) []<span style="font-weight:bold;text-decoration:underline">int</span> {
</span></span><span style="display:flex;"><span>	done := <span style="font-weight:bold;text-decoration:underline">false</span>
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">for</span> i := <span style="font-weight:bold;font-style:italic">len</span>(list) - 1; i &gt;= 0; i-- {
</span></span><span style="display:flex;"><span>		done = <span style="font-weight:bold;text-decoration:underline">true</span>
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">for</span> j := 0; j &lt; i; i++ {
</span></span><span style="display:flex;"><span>			<span style="font-weight:bold;text-decoration:underline">if</span> list[j] &gt; list[j+1] {
</span></span><span style="display:flex;"><span>				list[j], list[j+1] = list[j], list[j+1]
</span></span><span style="display:flex;"><span>				done = <span style="font-weight:bold;text-decoration:underline">false</span>
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">if</span> done {
</span></span><span style="display:flex;"><span>			<span style="font-weight:bold;text-decoration:underline">break</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">return</span> list
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h6 class="heading" id="选择排序n2">
 选择排序n^2
 <a class="anchor" href="#%e9%80%89%e6%8b%a9%e6%8e%92%e5%ba%8fn2">#</a>
</h6>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> <span style="color:#666;font-weight:bold;font-style:italic">selectSort</span>(list []<span style="font-weight:bold;text-decoration:underline">int</span>) []<span style="font-weight:bold;text-decoration:underline">int</span> {
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">for</span> i := <span style="font-weight:bold;font-style:italic">len</span>(list) - 1; i &gt;= 0; i-- {
</span></span><span style="display:flex;"><span>		maxIdx := 0
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">for</span> j := 1; j &lt;= i; i++ {
</span></span><span style="display:flex;"><span>			<span style="font-weight:bold;text-decoration:underline">if</span> list[j] &gt; list[maxIdx] {
</span></span><span style="display:flex;"><span>				maxIdx = j
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		list[maxIdx], list[i] = list[i], list[maxIdx]
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">return</span> list
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h6 class="heading" id="插入排序n2">
 插入排序n^2
 <a class="anchor" href="#%e6%8f%92%e5%85%a5%e6%8e%92%e5%ba%8fn2">#</a>
</h6>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> <span style="color:#666;font-weight:bold;font-style:italic">insertSort</span>(list []<span style="font-weight:bold;text-decoration:underline">int</span>) []<span style="font-weight:bold;text-decoration:underline">int</span> {
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">for</span> i := 0; i &lt; <span style="font-weight:bold;font-style:italic">len</span>(list); i++ {
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">for</span> j := i; j &gt; 0; j-- {
</span></span><span style="display:flex;"><span>			<span style="font-weight:bold;text-decoration:underline">if</span> list[j] &lt; list[j-1] {
</span></span><span style="display:flex;"><span>				list[j], list[j-1] = list[j-1], list[j]
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">return</span> list
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h6 class="heading" id="归并排序nlgn">
 归并排序nlgn
 <a class="anchor" href="#%e5%bd%92%e5%b9%b6%e6%8e%92%e5%ba%8fnlgn">#</a>
</h6>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> <span style="color:#666;font-weight:bold;font-style:italic">mergeSort</span>(list []<span style="font-weight:bold;text-decoration:underline">int</span>) []<span style="font-weight:bold;text-decoration:underline">int</span> {
</span></span><span style="display:flex;"><span>	merge := <span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span>(l, r []<span style="font-weight:bold;text-decoration:underline">int</span>) []<span style="font-weight:bold;text-decoration:underline">int</span> {
</span></span><span style="display:flex;"><span>		ret := <span style="font-weight:bold;font-style:italic">make</span>([]<span style="font-weight:bold;text-decoration:underline">int</span>, 0)
</span></span><span style="display:flex;"><span>		i, j := 0, 0
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">for</span> i &lt; <span style="font-weight:bold;font-style:italic">len</span>(l) &amp;&amp; j &lt; <span style="font-weight:bold;font-style:italic">len</span>(r) {
</span></span><span style="display:flex;"><span>			<span style="font-weight:bold;text-decoration:underline">if</span> l[i] &lt; r[j] {
</span></span><span style="display:flex;"><span>				ret = <span style="font-weight:bold;font-style:italic">append</span>(ret, l[i])
</span></span><span style="display:flex;"><span>				i++
</span></span><span style="display:flex;"><span>			} <span style="font-weight:bold;text-decoration:underline">else</span> {
</span></span><span style="display:flex;"><span>				ret = <span style="font-weight:bold;font-style:italic">append</span>(ret, r[j])
</span></span><span style="display:flex;"><span>				j++
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">if</span> i &lt; <span style="font-weight:bold;font-style:italic">len</span>(l) {
</span></span><span style="display:flex;"><span>			ret = <span style="font-weight:bold;font-style:italic">append</span>(ret, l[i:]...)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">if</span> j &lt; <span style="font-weight:bold;font-style:italic">len</span>(r) {
</span></span><span style="display:flex;"><span>			ret = <span style="font-weight:bold;font-style:italic">append</span>(ret, r[j:]...)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">return</span> ret
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;font-style:italic;text-decoration:underline">var</span> dm <span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span>([]<span style="font-weight:bold;text-decoration:underline">int</span>) []<span style="font-weight:bold;text-decoration:underline">int</span>
</span></span><span style="display:flex;"><span>	dm = <span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span>(ls []<span style="font-weight:bold;text-decoration:underline">int</span>) []<span style="font-weight:bold;text-decoration:underline">int</span> {
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">if</span> <span style="font-weight:bold;font-style:italic">len</span>(ls) == 1 {
</span></span><span style="display:flex;"><span>			<span style="font-weight:bold;text-decoration:underline">return</span> ls
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		ll := <span style="color:#666;font-weight:bold;font-style:italic">dm</span>(ls[:<span style="font-weight:bold;font-style:italic">len</span>(ls)/2])
</span></span><span style="display:flex;"><span>		lr := <span style="color:#666;font-weight:bold;font-style:italic">dm</span>(ls[<span style="font-weight:bold;font-style:italic">len</span>(ls)/2:])
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">return</span> <span style="color:#666;font-weight:bold;font-style:italic">merge</span>(ll, lr)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">return</span> <span style="color:#666;font-weight:bold;font-style:italic">dm</span>(list)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h6 class="heading" id="快速排序nlgn">
 快速排序nlgn
 <a class="anchor" href="#%e5%bf%ab%e9%80%9f%e6%8e%92%e5%ba%8fnlgn">#</a>
</h6>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> <span style="color:#666;font-weight:bold;font-style:italic">quickSort</span>(list []<span style="font-weight:bold;text-decoration:underline">int</span>) []<span style="font-weight:bold;text-decoration:underline">int</span> {
</span></span><span style="display:flex;"><span>	part := <span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span>(l, r <span style="font-weight:bold;text-decoration:underline">int</span>) <span style="font-weight:bold;text-decoration:underline">int</span> {
</span></span><span style="display:flex;"><span>		pv := list[r]
</span></span><span style="display:flex;"><span>		i := l
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">for</span> j := l; j &lt; r; j++ {
</span></span><span style="display:flex;"><span>			<span style="font-weight:bold;text-decoration:underline">if</span> list[j] &lt; pv {
</span></span><span style="display:flex;"><span>				list[i], list[j] = list[j], list[i]
</span></span><span style="display:flex;"><span>				i++
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		list[i], list[r] = list[r], list[i]
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">return</span> i
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;font-style:italic;text-decoration:underline">var</span> df <span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span>(<span style="font-weight:bold;text-decoration:underline">int</span>, <span style="font-weight:bold;text-decoration:underline">int</span>)
</span></span><span style="display:flex;"><span>	df = <span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span>(l, r <span style="font-weight:bold;text-decoration:underline">int</span>) {
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">if</span> l &gt;= r {
</span></span><span style="display:flex;"><span>			<span style="font-weight:bold;text-decoration:underline">return</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		p := <span style="color:#666;font-weight:bold;font-style:italic">part</span>(l, r)
</span></span><span style="display:flex;"><span>		<span style="color:#666;font-weight:bold;font-style:italic">df</span>(l, p-1)
</span></span><span style="display:flex;"><span>		<span style="color:#666;font-weight:bold;font-style:italic">df</span>(p+1, r)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#666;font-weight:bold;font-style:italic">df</span>(0, <span style="font-weight:bold;font-style:italic">len</span>(list)-1)
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">return</span> list
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h6 class="heading" id="堆排序nlgn">
 堆排序nlgn
 <a class="anchor" href="#%e5%a0%86%e6%8e%92%e5%ba%8fnlgn">#</a>
</h6>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> <span style="color:#666;font-weight:bold;font-style:italic">heapSort</span>(list []<span style="font-weight:bold;text-decoration:underline">int</span>) []<span style="font-weight:bold;text-decoration:underline">int</span> {
</span></span><span style="display:flex;"><span>	minHeap := <span style="font-weight:bold;font-style:italic">make</span>([]<span style="font-weight:bold;text-decoration:underline">int</span>, 0)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#888;font-style:italic">// 建堆,上浮</span>
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">for</span> _, v := <span style="font-weight:bold;text-decoration:underline">range</span> list {
</span></span><span style="display:flex;"><span>		minHeap = <span style="font-weight:bold;font-style:italic">append</span>(minHeap, v)
</span></span><span style="display:flex;"><span>		j := <span style="font-weight:bold;font-style:italic">len</span>(minHeap) - 1
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">for</span> j &gt; 0 {
</span></span><span style="display:flex;"><span>			parent := (j - 1) / 2
</span></span><span style="display:flex;"><span>			<span style="font-weight:bold;text-decoration:underline">if</span> v &gt;= minHeap[parent] {
</span></span><span style="display:flex;"><span>				<span style="font-weight:bold;text-decoration:underline">break</span>
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>			minHeap[j] = minHeap[parent]
</span></span><span style="display:flex;"><span>			j = parent
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#888;font-style:italic">// 取值,下沉</span>
</span></span><span style="display:flex;"><span>	ret := <span style="font-weight:bold;font-style:italic">make</span>([]<span style="font-weight:bold;text-decoration:underline">int</span>, 0)
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">for</span> <span style="font-weight:bold;text-decoration:underline">range</span> minHeap {
</span></span><span style="display:flex;"><span>		<span style="color:#888;font-style:italic">// 取堆顶</span>
</span></span><span style="display:flex;"><span>		ret = <span style="font-weight:bold;font-style:italic">append</span>(ret, minHeap[0])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#888;font-style:italic">// 获取最后一个元素,并且挪到堆顶</span>
</span></span><span style="display:flex;"><span>		x := minHeap[<span style="font-weight:bold;font-style:italic">len</span>(minHeap)-1]
</span></span><span style="display:flex;"><span>		minHeap = minHeap[:<span style="font-weight:bold;font-style:italic">len</span>(minHeap)-1]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#888;font-style:italic">// 没有元素了</span>
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">if</span> <span style="font-weight:bold;font-style:italic">len</span>(minHeap) == 0 {
</span></span><span style="display:flex;"><span>			<span style="font-weight:bold;text-decoration:underline">break</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#888;font-style:italic">// 对堆顶元素做下沉操作</span>
</span></span><span style="display:flex;"><span>		i := 0
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">for</span> {
</span></span><span style="display:flex;"><span>			l := 2*i + 1 <span style="color:#888;font-style:italic">// 左孩子</span>
</span></span><span style="display:flex;"><span>			r := 2*i + 2 <span style="color:#888;font-style:italic">// 右孩子</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>			<span style="color:#888;font-style:italic">// 如果没有左孩子,则停止下沉</span>
</span></span><span style="display:flex;"><span>			<span style="font-weight:bold;text-decoration:underline">if</span> l &gt;= <span style="font-weight:bold;font-style:italic">len</span>(minHeap) {
</span></span><span style="display:flex;"><span>				<span style="font-weight:bold;text-decoration:underline">break</span>
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>			<span style="color:#888;font-style:italic">// 如果有右孩子,则比较左右孩子得到比较小的孩子下标赋值给l</span>
</span></span><span style="display:flex;"><span>			<span style="font-weight:bold;text-decoration:underline">if</span> r &lt; <span style="font-weight:bold;font-style:italic">len</span>(minHeap) &amp;&amp; minHeap[l] &gt; minHeap[r] {
</span></span><span style="display:flex;"><span>				l = r
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>			<span style="color:#888;font-style:italic">// 如果比最小的孩子还要小,则停止下沉</span>
</span></span><span style="display:flex;"><span>			<span style="font-weight:bold;text-decoration:underline">if</span> x &lt;= minHeap[l] {
</span></span><span style="display:flex;"><span>				<span style="font-weight:bold;text-decoration:underline">break</span>
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>			<span style="color:#888;font-style:italic">// 将较小的孩子提升上来</span>
</span></span><span style="display:flex;"><span>			minHeap[i] = minHeap[l]
</span></span><span style="display:flex;"><span>			i = l
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		minHeap[i] = x
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">return</span> ret
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h6 class="heading" id="计数排序n">
 计数排序n
 <a class="anchor" href="#%e8%ae%a1%e6%95%b0%e6%8e%92%e5%ba%8fn">#</a>
</h6>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> <span style="color:#666;font-weight:bold;font-style:italic">countSort</span>(list []<span style="font-weight:bold;text-decoration:underline">int</span>) []<span style="font-weight:bold;text-decoration:underline">int</span> {
</span></span><span style="display:flex;"><span>	maxNum := list[0]
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">for</span> i := 1; i &lt; <span style="font-weight:bold;font-style:italic">len</span>(list)-1; i++ {
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">if</span> list[i] &gt; maxNum {
</span></span><span style="display:flex;"><span>			maxNum = list[i]
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#888;font-style:italic">// 记录每个数的数量</span>
</span></span><span style="display:flex;"><span>	count := <span style="font-weight:bold;font-style:italic">make</span>([]<span style="font-weight:bold;text-decoration:underline">int</span>, maxNum+1)
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">for</span> _, v := <span style="font-weight:bold;text-decoration:underline">range</span> list {
</span></span><span style="display:flex;"><span>		count[v]++
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#888;font-style:italic">// 记录每个数的位置</span>
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">for</span> i := 1; i &lt; <span style="font-weight:bold;font-style:italic">len</span>(count); i++ {
</span></span><span style="display:flex;"><span>		count[i] += count[i-1]
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#888;font-style:italic">// 从后往前遍历,保证稳定性</span>
</span></span><span style="display:flex;"><span>	ret := <span style="font-weight:bold;font-style:italic">make</span>([]<span style="font-weight:bold;text-decoration:underline">int</span>, <span style="font-weight:bold;font-style:italic">len</span>(list))
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">for</span> i := <span style="font-weight:bold;font-style:italic">len</span>(list) - 1; i &gt;= 0; i-- {
</span></span><span style="display:flex;"><span>		idx := count[list[i]] - 1
</span></span><span style="display:flex;"><span>		ret[idx] = list[i]
</span></span><span style="display:flex;"><span>		count[list[i]]--
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">return</span> ret
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></description>
</item>
<item>
<title>二分查找算法汇总</title>
<link>https://weenable.github.io/posts/%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE%E7%AE%97%E6%B3%95%E6%B1%87%E6%80%BB/</link>
<pubDate>Fri, 28 Feb 2025 19:23:48 +0800</pubDate>
<guid>https://weenable.github.io/posts/%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE%E7%AE%97%E6%B3%95%E6%B1%87%E6%80%BB/</guid>
<description><h5 class="heading" id="二分查找原题">
 二分查找原题
 <a class="anchor" href="#%e4%ba%8c%e5%88%86%e6%9f%a5%e6%89%be%e5%8e%9f%e9%a2%98">#</a>
</h5>
<p><a href="https://leetcode.cn/problems/binary-search/">https://leetcode.cn/problems/binary-search/</a></p>
<p>注意计算mid的时候不要用(left+right) / 2,因为可能会溢出</p>
<pre tabindex="0"><code>func search(nums []int, target int) int {
 n := len(nums)

 left := 0
 right := n-1
 for left &lt;= right {
 mid := left + ((right - left) / 2)
 if nums[mid] == target {
 return mid
 }

 if nums[mid] &gt; target {
 right = mid-1
 } else {
 left = mid+1
 }
 }
 return -1
}
</code></pre><h5 class="heading" id="搜索插入位置">
 搜索插入位置
 <a class="anchor" href="#%e6%90%9c%e7%b4%a2%e6%8f%92%e5%85%a5%e4%bd%8d%e7%bd%ae">#</a>
</h5>
<p><a href="https://leetcode.cn/problems/search-insert-position/">https://leetcode.cn/problems/search-insert-position/</a></p>
<ol>
<li>通过二分查找值</li>
<li>插入位置默认为数组末尾,如果mid所在的数比target大,则更新插入位置为mid</li>
</ol>
<pre tabindex="0"><code>func searchInsert(nums []int, target int) int {
 n := len(nums)
 left, right := 0, n-1
 ret := n

 for left &lt;= right {
 mid := left + ((right - left) / 2)
 if nums[mid] == target {
 return mid
 }

		// 如果mid所在的数比target大,则更新插入位置为mid
 if nums[mid] &gt; target {
 right = mid-1
 ret = mid
 } else {
 left = mid + 1
 }

 }
 return ret
}
</code></pre><h5 class="heading" id="查找元素出现的第一个和最后一个位置">
 查找元素出现的第一个和最后一个位置
 <a class="anchor" href="#%e6%9f%a5%e6%89%be%e5%85%83%e7%b4%a0%e5%87%ba%e7%8e%b0%e7%9a%84%e7%ac%ac%e4%b8%80%e4%b8%aa%e5%92%8c%e6%9c%80%e5%90%8e%e4%b8%80%e4%b8%aa%e4%bd%8d%e7%bd%ae">#</a>
</h5>
<p><a href="https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/description/">https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/description/</a></p></description>
</item>
<item>
<title>游戏服务器架构演进</title>
<link>https://weenable.github.io/posts/game/%E6%B8%B8%E6%88%8F%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9E%B6%E6%9E%84%E6%BC%94%E8%BF%9B/</link>
<pubDate>Fri, 28 Feb 2025 19:21:10 +0800</pubDate>
<guid>https://weenable.github.io/posts/game/%E6%B8%B8%E6%88%8F%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9E%B6%E6%9E%84%E6%BC%94%E8%BF%9B/</guid>
<description><h3 class="heading" id="游戏服务器要素">
 游戏服务器要素
 <a class="anchor" href="#%e6%b8%b8%e6%88%8f%e6%9c%8d%e5%8a%a1%e5%99%a8%e8%a6%81%e7%b4%a0">#</a>
</h3>
<p>对于游戏服务器,三个比较重要的部份就是:CPU、内存、带宽的设计</p>
<ul>
<li>逻辑架构:合适的进程、线程、协程的CPU调度方案,合适的同步或异步编程模型,分区分服或世界服的方式来提高服务器的承载量和稳定性</li>
<li>内存架构:合理使用内存,提高承载量,降低服务延迟</li>
<li>通信架构:通信协议如UDP/TCP</li>
</ul>
<h3 class="heading" id="游戏服务器演进">
 游戏服务器演进
 <a class="anchor" href="#%e6%b8%b8%e6%88%8f%e6%9c%8d%e5%8a%a1%e5%99%a8%e6%bc%94%e8%bf%9b">#</a>
</h3>
<h4 class="heading" id="一弱交互游戏服务器">
 一、弱交互游戏服务器
 <a class="anchor" href="#%e4%b8%80%e5%bc%b1%e4%ba%a4%e4%ba%92%e6%b8%b8%e6%88%8f%e6%9c%8d%e5%8a%a1%e5%99%a8">#</a>
</h4>
<p>基于http通信模式的服务器,服务器架构和web差不多</p>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-2025228214121.png" src="https://weenable.github.io/images/%E6%B8%B8%E6%88%8F%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9E%B6%E6%9E%84%E6%BC%94%E8%BF%9B/image-2025228214121.png" >
 </div>

 
</figure>
</p>
<h4 class="heading" id="二长连接游戏服务器">
 二、长连接游戏服务器
 <a class="anchor" href="#%e4%ba%8c%e9%95%bf%e8%bf%9e%e6%8e%a5%e6%b8%b8%e6%88%8f%e6%9c%8d%e5%8a%a1%e5%99%a8">#</a>
</h4>
<p>在长连接中玩家是有状态的,消息传送的频率以及速度都快于弱联网游戏</p>
<h5 class="heading" id="第一代游戏服务器单线程无阻塞">
 第一代游戏服务器,单线程无阻塞
 <a class="anchor" href="#%e7%ac%ac%e4%b8%80%e4%bb%a3%e6%b8%b8%e6%88%8f%e6%9c%8d%e5%8a%a1%e5%99%a8%e5%8d%95%e7%ba%bf%e7%a8%8b%e6%97%a0%e9%98%bb%e5%a1%9e">#</a>
</h5>
<p>所有玩家的请求都发到一个无阻塞的消息队列中,由单线程处理消息</p>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252282212369.png" src="https://weenable.github.io/images/%E6%B8%B8%E6%88%8F%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9E%B6%E6%9E%84%E6%BC%94%E8%BF%9B/image-20252282212369.png" >
 </div>

 
</figure>
</p>
<p>线程模型如下:</p>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252282223840.png" src="https://weenable.github.io/images/%E6%B8%B8%E6%88%8F%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9E%B6%E6%9E%84%E6%BC%94%E8%BF%9B/image-20252282223840.png" >
 </div>

 
</figure>
</p>
<h5 class="heading" id="第二代游戏服务器分区分服">
 第二代游戏服务器,分区分服
 <a class="anchor" href="#%e7%ac%ac%e4%ba%8c%e4%bb%a3%e6%b8%b8%e6%88%8f%e6%9c%8d%e5%8a%a1%e5%99%a8%e5%88%86%e5%8c%ba%e5%88%86%e6%9c%8d">#</a>
</h5>
<p>单游戏服务器承载用户客户端有限,于是出现分区分服的概念,这种模型中一个游戏服务器就是一个平行世界,在第二代游戏服务器中同时也对进程和线程模型做了升级</p>
<ul>
<li>多线程:每个线程处理一个特定的场景内的tick事件,如果玩家跨场景则通过消息投递的方式通知另一个场景线程来进行玩家数据同步</li>
<li>多进程:将网络、数据库的操作单独使用进程来处理,逻辑进程专心处理逻辑任务</li>
</ul>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252282244672.png" src="https://weenable.github.io/images/%E6%B8%B8%E6%88%8F%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9E%B6%E6%9E%84%E6%BC%94%E8%BF%9B/image-20252282244672.png" >
 </div>

 
</figure>
</p>
<h5 class="heading" id="第三代游戏服务器世界服">
 第三代游戏服务器,世界服
 <a class="anchor" href="#%e7%ac%ac%e4%b8%89%e4%bb%a3%e6%b8%b8%e6%88%8f%e6%9c%8d%e5%8a%a1%e5%99%a8%e4%b8%96%e7%95%8c%e6%9c%8d">#</a>
</h5>
<p>第三代游戏服务器主要是世界服,分为以下几种演化</p>
<ul>
<li>三层架构:包含网关服务器、游戏逻辑服务器、DB服务器</li>
</ul>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252282257901.png" src="https://weenable.github.io/images/%E6%B8%B8%E6%88%8F%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9E%B6%E6%9E%84%E6%BC%94%E8%BF%9B/image-20252282257901.png" >
 </div>

 
</figure>
</p>
<ul>
<li>分片架构:将游戏逻辑服务器再进行分片,分离出如场景服务器、非场景服务器等</li>
</ul>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252282311243.png" src="https://weenable.github.io/images/%E6%B8%B8%E6%88%8F%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9E%B6%E6%9E%84%E6%BC%94%E8%BF%9B/image-20252282311243.png" >
 </div>

 
</figure>
</p>
<ul>
<li>大世界架构:将世界按区块划分,各场景服务器管理不同区块</li>
</ul>
<h4 class="heading" id="三房间服务器游戏大厅">
 三、房间服务器,游戏大厅
 <a class="anchor" href="#%e4%b8%89%e6%88%bf%e9%97%b4%e6%9c%8d%e5%8a%a1%e5%99%a8%e6%b8%b8%e6%88%8f%e5%a4%a7%e5%8e%85">#</a>
</h4>
<p>主要分为大厅服务器、匹配服务器、对战服务器</p>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252282323894.png" src="https://weenable.github.io/images/%E6%B8%B8%E6%88%8F%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9E%B6%E6%9E%84%E6%BC%94%E8%BF%9B/image-20252282323894.png" >
 </div>

 
</figure>
</p></description>
</item>
<item>
<title>基于redis的分布式锁实现</title>
<link>https://weenable.github.io/posts/%E6%9E%B6%E6%9E%84/%E5%9F%BA%E4%BA%8Eredis%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%AE%9E%E7%8E%B0/</link>
<pubDate>Fri, 28 Feb 2025 19:19:48 +0800</pubDate>
<guid>https://weenable.github.io/posts/%E6%9E%B6%E6%9E%84/%E5%9F%BA%E4%BA%8Eredis%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%AE%9E%E7%8E%B0/</guid>
<description><h4 class="heading" id="分布式锁安全和失效保障">
 分布式锁安全和失效保障
 <a class="anchor" href="#%e5%88%86%e5%b8%83%e5%bc%8f%e9%94%81%e5%ae%89%e5%85%a8%e5%92%8c%e5%a4%b1%e6%95%88%e4%bf%9d%e9%9a%9c">#</a>
</h4>
<p>算法只需要具备3个特性就可以实现最低保障的分布式锁</p>
<ol>
<li>安全属性:独享,任意时刻只有一个客户端持有锁</li>
<li>活性A:无死锁,即便持有锁客户端崩溃或网络分裂,锁仍然可以被获取</li>
<li>活性B:容错,只要大部分redis节点存活,客户端即可获取和释放锁</li>
</ol>
<h4 class="heading" id="单redis实例实现">
 单redis实例实现
 <a class="anchor" href="#%e5%8d%95redis%e5%ae%9e%e4%be%8b%e5%ae%9e%e7%8e%b0">#</a>
</h4>
<ul>
<li>使用锁命令
<ul>
<li>NX 不存在key才执行</li>
<li>PX超时</li>
<li>random_value随机值,确保可以安全释放</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code>SET name random_value NX PX 3000
</code></pre><ul>
<li>释放锁</li>
</ul>
<pre tabindex="0"><code>if redis.call(&#34;get&#34;,KEYS[1]) == ARGV[1] then
 return redis.call(&#34;del&#34;,KEYS[1])
else
 return 0
end
</code></pre><h4 class="heading" id="单实例配合故障转移实现">
 单实例配合故障转移实现
 <a class="anchor" href="#%e5%8d%95%e5%ae%9e%e4%be%8b%e9%85%8d%e5%90%88%e6%95%85%e9%9a%9c%e8%bd%ac%e7%a7%bb%e5%ae%9e%e7%8e%b0">#</a>
</h4>
<p>在单实例实现中存在单点失败问题,如果增加salve形成主从结构,当主从存在延迟时会导致从并没同步到锁的状态导致安全失效</p>
<h4 class="heading" id="redlock实现">
 RedLock实现
 <a class="anchor" href="#redlock%e5%ae%9e%e7%8e%b0">#</a>
</h4>
<p>假设有N个redis master节点,在这种算法情况下客户端获取锁</p>
<ol>
<li>获取当前unix时间戳,单位为毫秒</li>
<li>依次尝试从N个实例使用相同的key和随机值获取锁,并且获取锁时设置一个网络超时时间,并且这个时间&lt;锁失效时间</li>
<li>客户端使用当前时间减去获取锁时刻时间得到获取锁耗时,只有从大多数节点获取到锁并且使用时间小于锁失效时间,锁才算获取成功</li>
<li>锁的真正有效时间是有效时间减去获取锁所使用的时间</li>
<li>如果获取锁失败,则应尽快在所有实例上释放锁</li>
</ol>
<h4 class="heading" id="代码实现">
 代码实现
 <a class="anchor" href="#%e4%bb%a3%e7%a0%81%e5%ae%9e%e7%8e%b0">#</a>
</h4>
<h5 class="heading" id="redsyncgo">
 redsync.go
 <a class="anchor" href="#redsyncgo">#</a>
</h5>
<pre tabindex="0"><code>// redsync.go
package distributedlock

import (
	&#34;math/rand&#34;
	&#34;time&#34;
)

// 最小最大延迟重试时间
const (
	minRetryDelayMilliSec = 50
	maxRetryDelayMilliSec = 250
)

type RedSync struct {
	pools []*Pool
}

func New(pool ...*Pool) *RedSync {
	return &amp;RedSync{pools: pool}
}

func (r *RedSync) NewMutex(name string, options ...MutexOption) *Mutex {
	m := &amp;Mutex{
		name: name,
		expiry: 8 * time.Second, // 8s过期
		tries: 32, // 最大重试32次
		delayFunc: func(tries int) time.Duration {
			return time.Duration(rand.Intn(maxRetryDelayMilliSec-minRetryDelayMilliSec)+minRetryDelayMilliSec) * time.Millisecond
		}, // 延迟数值计算函数
		genValueFunc: genValue, // 随机值生成函数
		driftFactor: 0.01, // 到达过期时间漂移因子
		timeoutFactor: 0.05, // 获取锁超时时间漂移因子
		quorum: len(r.pools)/2 + 1, // 成功获取到锁的节点数
		pools: r.pools, // redis节点池
	}

	for _, o := range options {
		o(m)
	}

	return m
}
</code></pre><h5 class="heading" id="mutexgo">
 mutex.go
 <a class="anchor" href="#mutexgo">#</a>
</h5>
<pre tabindex="0"><code>// mutex.go
package distributedlock

import (
	&#34;context&#34;
	&#34;crypto/rand&#34;
	&#34;encoding/base64&#34;
	&#34;errors&#34;
	&#34;fmt&#34;
	&#34;github.com/hashicorp/go-multierror&#34;
	&#34;time&#34;
)

type Mutex struct {
	expiry time.Duration
	name string
	timeoutFactor float64
	driftFactor float64
	tries int
	pools []*Pool
	failFast bool
	quorum int
	value string
	until time.Time
	delayFunc func(int) time.Duration
	genValueFunc func() (string, error)
}

func (m *Mutex) Unlock() (bool, error) {
	return m.UnlockContext(context.Background())
}

func (m *Mutex) UnlockContext(ctx context.Context) (bool, error) {
	n, err := m.actOnPoolAsync(ctx, m.release, m.value)
	if n &lt; m.quorum {
		return false, err
	}
	return true, nil
}

func (m *Mutex) Lock() error {
	return m.LockContext(context.Background())
}

func (m *Mutex) LockContext(ctx context.Context) error {
	value, err := m.genValueFunc()
	if err != nil {
		return err
	}

	var timer *time.Timer
	for i := 0; i &lt; m.tries; i++ {
		// 重试延迟
		if i != 0 {
			if timer == nil {
				timer = time.NewTimer(m.delayFunc(i))
			} else {
				timer.Reset(m.delayFunc(i))
			}
			select {
			case &lt;-ctx.Done():
				timer.Stop()
				return ErrFailed
			case &lt;-timer.C:

			}
		}

		start := time.Now()

		n, err := m.actOnPoolAsync(ctx, m.acquire, value)

		now := time.Now()
		until := now.Add(m.expiry - now.Sub(start) - time.Duration(int64(float64(m.expiry)*m.driftFactor)))
		if n &gt;= m.quorum &amp;&amp; now.Before(until) {
			m.value = value
			m.until = until
			return nil
		}

		// 如果没有最终获取锁成功,快速释放掉已经获取的子锁
		m.actOnPoolAsync(ctx, m.release, value)

		// 达到最大尝试次数,并且有报错,直接返回
		if i == m.tries-1 &amp;&amp; err != nil {
			return err
		}
	}

	return ErrFailed
}

// 获取锁
func (m *Mutex) acquire(ctx context.Context, pool *Pool, value string) (bool, error) {
	conn, err := pool.Get(ctx)
	if err != nil {
		return false, err
	}
	defer conn.Close()
	reply, err := conn.SetNX(m.name, value, m.expiry)
	if err != nil {
		return false, err
	}
	return reply, nil
}

var deleteScript = NewScript(1, `
	local val = redis.call(&#34;GET&#34;, KEYS[1])
	if val == ARGV[1] then
		return redis.call(&#34;DEL&#34;, KEYS[1])
	elseif val == false then
		return -1
	else
		return 0
	end
`)

// 释放锁,通过脚本执行
func (m *Mutex) release(ctx context.Context, pool *Pool, value string) (bool, error) {
	conn, err := pool.Get(ctx)
	if err != nil {
		return false, err
	}
	defer conn.Close()
	status, err := conn.Eval(deleteScript, m.name, value)
	if err != nil {
		return false, err
	}

	if status == int64(-1) {
		return false, ErrLockAlreadyExpired
	}

	return status != int64(0), nil
}

// 异步获取各节点的锁
func (m *Mutex) actOnPoolAsync(ctx context.Context, actFn func(context.Context, *Pool, string) (bool, error), value string) (int, error) {

	ctx, cancel := context.WithTimeout(ctx, time.Duration(int64(float64(m.expiry)*m.timeoutFactor)))
	defer cancel()

	type result struct {
		node int
		statusOK bool
		err error
	}
	ch := make(chan result, len(m.pools))
	for node, pool := range m.pools {
		go func(node int, pool *Pool) {
			r := result{node: node}
			r.statusOK, r.err = actFn(ctx, pool, value)
			ch &lt;- r
		}(node, pool)
	}

	n := 0
	var err error
	taken := make([]int, 0)

	for range m.pools {
		r := &lt;-ch
		if r.statusOK {
			n++
		} else if r.err == ErrLockAlreadyExpired {
			err = multierror.Append(err, ErrLockAlreadyExpired)
		} else if r.err != nil {
			err = multierror.Append(err, errors.New(fmt.Sprintf(&#34;redis error, node: %d err: %v&#34;, r.node, r.err)))
		} else {
			taken = append(taken, r.node)
			err = multierror.Append(err, errors.New(fmt.Sprintf(&#34;taken error, node: %d err: %v&#34;, r.node, r.err)))
		}

		if m.failFast {
			if n &gt;= m.quorum {
				return n, err
			}

			if len(taken) &gt;= m.quorum {
				return n, &amp;ErrTaken{Nodes: taken}
			}
		}
	}

	if len(taken) &gt;= m.quorum {
		return n, &amp;ErrTaken{Nodes: taken}
	}

	return n, err
}

func genValue() (string, error) {
	b := make([]byte, 16)
	_, err := rand.Read(b)
	if err != nil {
		return &#34;&#34;, err
	}

	return base64.StdEncoding.EncodeToString(b), nil
}

type MutexOption func(*Mutex)

func WithExpiry(expiry time.Duration) MutexOption {
	return func(m *Mutex) {
		m.expiry = expiry
	}
}
</code></pre><h5 class="heading" id="poolgo">
 pool.go
 <a class="anchor" href="#poolgo">#</a>
</h5>
<pre tabindex="0"><code>// pool.go
package distributedlock

import (
	&#34;context&#34;
	&#34;github.com/go-redis/redis&#34;
)

type Pool struct {
	redisClient *redis.Client
}

func NewPool(redisClient *redis.Client) *Pool {
	return &amp;Pool{redisClient: redisClient}
}

func (p *Pool) Get(ctx context.Context) (*Conn, error) {
	c := p.redisClient
	if ctx != nil {
		c = c.WithContext(ctx)
	}

	return &amp;Conn{c}, nil
}
</code></pre><h5 class="heading" id="conngo">
 conn.go
 <a class="anchor" href="#conngo">#</a>
</h5>
<pre tabindex="0"><code>// conn.go
package distributedlock

import (
	&#34;crypto/sha1&#34;
	&#34;encoding/hex&#34;
	&#34;github.com/go-redis/redis&#34;
	&#34;io&#34;
	&#34;strings&#34;
	&#34;time&#34;
)

type Conn struct {
	redisClient *redis.Client
}

func (c *Conn) Close() error {
	return nil
}

type Script struct {
	KeyCount int
	Src string
	Hash string
}

func NewScript(keyCount int, src string) *Script {
	h := sha1.New()
	_, _ = io.WriteString(h, src)
	return &amp;Script{
		KeyCount: keyCount,
		Src: src,
		Hash: hex.EncodeToString(h.Sum(nil)),
	}
}

func (c *Conn) SetNX(name string, value string, expiry time.Duration) (bool, error) {
	ok, err := c.redisClient.SetNX(name, value, expiry).Result()
	if err != redis.Nil {
		return false, err
	}

	return ok, nil
}

// 执行脚本
func (c *Conn) Eval(script *Script, keysAndArgs ...interface{}) (interface{}, error) {
	keys := make([]string, script.KeyCount)
	args := keysAndArgs

	if script.KeyCount &gt; 0 {
		for i := 0; i &lt; script.KeyCount; i++ {
			keys[i] = keysAndArgs[i].(string)
		}
		args = keysAndArgs[script.KeyCount:]
	}
	v, err := c.redisClient.EvalSha(script.Hash, keys, args...).Result()
	if err != nil &amp;&amp; strings.HasPrefix(err.Error(), &#34;NOSCRIPT&#34;) {
		v, err = c.redisClient.Eval(script.Src, keys, args...).Result()
	}

	if err != nil {
		return false, err
	}

	return v, nil
}
</code></pre><h5 class="heading" id="errorgo">
 error.go
 <a class="anchor" href="#errorgo">#</a>
</h5>
<pre tabindex="0"><code>// error.go
package distributedlock

import (
	&#34;errors&#34;
	&#34;fmt&#34;
)

var ErrFailed = errors.New(&#34;redsync: failed to acquire lock&#34;)

var ErrLockAlreadyExpired = errors.New(&#34;redsync: failed to unlock, lock already expired&#34;)

type ErrTaken struct {
	Nodes []int
}

func (e *ErrTaken) Error() string {
	return fmt.Sprintf(&#34;lock already taken, locked nodes: %v&#34;, e.Nodes)
}
</code></pre></description>
</item>
<item>
<title>分布式脑裂</title>
<link>https://weenable.github.io/posts/%E6%9E%B6%E6%9E%84/%E5%88%86%E5%B8%83%E5%BC%8F%E8%84%91%E8%A3%82/</link>
<pubDate>Fri, 28 Feb 2025 19:18:43 +0800</pubDate>
<guid>https://weenable.github.io/posts/%E6%9E%B6%E6%9E%84/%E5%88%86%E5%B8%83%E5%BC%8F%E8%84%91%E8%A3%82/</guid>
<description><h4 class="heading" id="脑裂定义">
 脑裂定义
 <a class="anchor" href="#%e8%84%91%e8%a3%82%e5%ae%9a%e4%b9%89">#</a>
</h4>
<p>一个整体的系统分裂成两个独立的节点,争夺共享资源造成系统混乱</p>
<h4 class="heading" id="出现脑裂的原因">
 出现脑裂的原因
 <a class="anchor" href="#%e5%87%ba%e7%8e%b0%e8%84%91%e8%a3%82%e7%9a%84%e5%8e%9f%e5%9b%a0">#</a>
</h4>
<p>主心跳检测超时,导致主备切换,备提供服务后,主又恢复正常,出现双主</p>
<h4 class="heading" id="解决思路">
 解决思路
 <a class="anchor" href="#%e8%a7%a3%e5%86%b3%e6%80%9d%e8%b7%af">#</a>
</h4>
<ul>
<li>
<p>仲裁机制
通过设置一个仲裁角色,定时探活主备,但是仲裁者会存在高可用和性能瓶颈</p>
</li>
<li>
<p>授权机制
假设slave已经提供服务,会有颁发的授权lease,假设master还在提供服务则lease必然是失效的,请求应当是失败的</p>
</li>
<li>
<p>隔离机制</p>
<ul>
<li>共享资源fencing:确保只有一个master往共享存储提供写数据</li>
<li>客户端fencing:确保只有一个master可以响应客户端请求</li>
<li>slave fencing:确保只有一个master可以向slave下发命令</li>
</ul>
</li>
</ul></description>
</item>
<item>
<title>在线支付系统设计</title>
<link>https://weenable.github.io/posts/%E6%9E%B6%E6%9E%84/%E5%9C%A8%E7%BA%BF%E6%94%AF%E4%BB%98%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/</link>
<pubDate>Fri, 28 Feb 2025 19:14:00 +0800</pubDate>
<guid>https://weenable.github.io/posts/%E6%9E%B6%E6%9E%84/%E5%9C%A8%E7%BA%BF%E6%94%AF%E4%BB%98%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/</guid>
<description><h3 class="heading" id="支付系统基本概念">
 支付系统基本概念
 <a class="anchor" href="#%e6%94%af%e4%bb%98%e7%b3%bb%e7%bb%9f%e5%9f%ba%e6%9c%ac%e6%a6%82%e5%bf%b5">#</a>
</h3>
<h4 class="heading" id="简单支付流程">
 简单支付流程
 <a class="anchor" href="#%e7%ae%80%e5%8d%95%e6%94%af%e4%bb%98%e6%b5%81%e7%a8%8b">#</a>
</h4>
<p>最简单的支付流程只展示了正向支付的流程,当然还有退款流程、撤销流程等












<figure class="">

 <div>
 <img loading="lazy" alt="image-20252281435321.png" src="https://weenable.github.io/images/%E5%9C%A8%E7%BA%BF%E6%94%AF%E4%BB%98%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/image-20252281435321.png" >
 </div>

 
</figure>
</p>
<h4 class="heading" id="简单清结算流程">
 简单清结算流程
 <a class="anchor" href="#%e7%ae%80%e5%8d%95%e6%b8%85%e7%bb%93%e7%ae%97%e6%b5%81%e7%a8%8b">#</a>
</h4>
<ol>
<li>支付系统和上游(如银行)是机构对机构的关系,通常使用清算概念,并且金融机构之间大部份情况下会有独立的清算机构做清算任务</li>
<li>支付系统和商户之间通常使用结算概念,由支付系统直接大款给商户</li>
<li>清算主要是把钱算清楚,结算主要是真实打款</li>
</ol>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252281457938.png" src="https://weenable.github.io/images/%E5%9C%A8%E7%BA%BF%E6%94%AF%E4%BB%98%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/image-20252281457938.png" >
 </div>

 
</figure>
</p>
<h4 class="heading" id="简单本对本收单流程">
 简单本对本收单流程
 <a class="anchor" href="#%e7%ae%80%e5%8d%95%e6%9c%ac%e5%af%b9%e6%9c%ac%e6%94%b6%e5%8d%95%e6%b5%81%e7%a8%8b">#</a>
</h4>
<ol>
<li>所谓本对本收单,就是指商户的商品标价币种、向支付系统下单的币种、用户支付的币种、商户结算币种都是同一个币种,不涉及到外汇交易</li>
</ol>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252281522725.png" src="https://weenable.github.io/images/%E5%9C%A8%E7%BA%BF%E6%94%AF%E4%BB%98%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/image-20252281522725.png" >
 </div>

 
</figure>
</p>
<h4 class="heading" id="简单跨境收单流程">
 简单跨境收单流程
 <a class="anchor" href="#%e7%ae%80%e5%8d%95%e8%b7%a8%e5%a2%83%e6%94%b6%e5%8d%95%e6%b5%81%e7%a8%8b">#</a>
</h4>
<ol>
<li>跨境收单就是结算给商户的币种和用户支付的币种不一样,需要经过外汇机构换汇</li>
<li>在扣款EUR之后,支付系统会调用外汇机构进行锁定汇率(HA)</li>
<li>在银行清算之后,支付平台再调用外汇机构进行换汇(TA)</li>
<li>最后支付系统结算给商户</li>
</ol>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252281556354.png" src="https://weenable.github.io/images/%E5%9C%A8%E7%BA%BF%E6%94%AF%E4%BB%98%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/image-20252281556354.png" >
 </div>

 
</figure>
</p>
<p>整个时序图如下:</p>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252281613881.png" src="https://weenable.github.io/images/%E5%9C%A8%E7%BA%BF%E6%94%AF%E4%BB%98%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/image-20252281613881.png" >
 </div>

 
</figure>
</p>
<h3 class="heading" id="概要设计">
 概要设计
 <a class="anchor" href="#%e6%a6%82%e8%a6%81%e8%ae%be%e8%ae%a1">#</a>
</h3>
<h4 class="heading" id="简单产品架构图">
 简单产品架构图
 <a class="anchor" href="#%e7%ae%80%e5%8d%95%e4%ba%a7%e5%93%81%e6%9e%b6%e6%9e%84%e5%9b%be">#</a>
</h4>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252281633583.png" src="https://weenable.github.io/images/%E5%9C%A8%E7%BA%BF%E6%94%AF%E4%BB%98%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/image-20252281633583.png" >
 </div>

 
</figure>
</p>
<h4 class="heading" id="简单系统架构图">
 简单系统架构图
 <a class="anchor" href="#%e7%ae%80%e5%8d%95%e7%b3%bb%e7%bb%9f%e6%9e%b6%e6%9e%84%e5%9b%be">#</a>
</h4>
<ol>
<li>较简单的一个本队本交易的支付系统架构</li>
<li>复杂的支付系统可能还有外汇等子系统,甚至一个子系统可能会分为多个应用来部署,比如收单结算子系统拆分为收单、结算应用</li>
</ol>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252281651782.png" src="https://weenable.github.io/images/%E5%9C%A8%E7%BA%BF%E6%94%AF%E4%BB%98%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/image-20252281651782.png" >
 </div>

 
</figure>
</p>
<h4 class="heading" id="复杂系统架构">
 复杂系统架构
 <a class="anchor" href="#%e5%a4%8d%e6%9d%82%e7%b3%bb%e7%bb%9f%e6%9e%b6%e6%9e%84">#</a>
</h4>
<ol>
<li>比较完整的一个系统架构,里面划分了比较清楚的子域或模块</li>
</ol>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252281711947.png" src="https://weenable.github.io/images/%E5%9C%A8%E7%BA%BF%E6%94%AF%E4%BB%98%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/image-20252281711947.png" >
 </div>

 
</figure>
</p>
<h4 class="heading" id="系统依赖图">
 系统依赖图
 <a class="anchor" href="#%e7%b3%bb%e7%bb%9f%e4%be%9d%e8%b5%96%e5%9b%be">#</a>
</h4>
<ol>
<li>红色链路为主要支付链路</li>
</ol>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252281731832.png" src="https://weenable.github.io/images/%E5%9C%A8%E7%BA%BF%E6%94%AF%E4%BB%98%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/image-20252281731832.png" >
 </div>

 
</figure>
</p>
<h3 class="heading" id="常见术语">
 常见术语
 <a class="anchor" href="#%e5%b8%b8%e8%a7%81%e6%9c%af%e8%af%ad">#</a>
</h3>
<h4 class="heading" id="通用">
 通用
 <a class="anchor" href="#%e9%80%9a%e7%94%a8">#</a>
</h4>
<ul>
<li>支付服务提供商:PSP,Payment Service Provider。为商户提供支付解决方案的公司。银行、第三方支付公司都属于支付服务提供商</li>
<li>收单机构:Acuquiring Institution/Acquirer。负责处理和清算商户交易的金融机构或支付服务提供商(PSP)</li>
<li>拍照:License。由监管机构或政府授予的官方许可,允许持牌者在特定的法律框架和规定内经营某种金融服务或支付业务。支付相关的牌照主要有:
<ul>
<li>银行牌照:允许公司提供传统银行服务,例如接受存款、提供贷款等</li>
<li>支付机构牌照:允许公司提供支付服务,如支付处理、资金转账、电子钱包服务等</li>
<li>电子货币机构牌照:允许公司发行和管理电子货币,如预付卡、电子钱包中的资金等</li>
<li>PA牌照:Payment Account,也就是储值牌照,用户可以先充值,后使用余额进行支付</li>
<li>PG牌照:Payment Gateway。也就是支付网关牌照,只能调用外部渠道支付,不能有余额</li>
</ul>
</li>
<li>展业:在指定的区域开展业务。通常和牌照申请、监管合规等放在一起</li>
<li>收单产品:支付服务提供商为商户提供的支付服务</li>
<li>担保交易:用户先把钱给到支付平台,用户确认收货再给钱到商家</li>
<li>即时到帐:付款资金直接转移到收款账户,不需要用户二次确认</li>
<li>资金产品:通常指与处理客户资金流动相关的服务,包括充值、转账、提现、代发等功能</li>
<li>代发:公司或组织通过在线支付系统将资金直接转入个人账户。有代发到余额和代发到卡</li>
<li>退款:Refund,一般指支付第二天的逆向行为,有部份退款和全额退款</li>
<li>撤销:Cancel,一般指支付当天的逆向行为</li>
<li>冲正:与撤销类似。来源于POS机时代,在交易发生当天用户申请退货退款时,或POS机支付超时的情况下,操作员发起一笔冲正,收单机构如果已经扣款就会退回</li>
<li>T日/T+N日:T日:交易实际发生的日期,T+N:从交易日发生之后N个工作日。比如T+1清算,就是指交易完成后,第二天进行清算</li>
<li>风控:交易的风险控制,欺诈检测、信用评估、合规检查</li>
</ul>
<h4 class="heading" id="外汇">
 外汇
 <a class="anchor" href="#%e5%a4%96%e6%b1%87">#</a>
</h4>
<ul>
<li>锁汇:锁定汇率。在电商场景下,电商平台不愿意承担汇损风险,就直接在支付成功后,使用支付金额向外汇机构锁定一个固定汇率</li>
<li>换汇:实际购汇。在电商场景下,电商平台不愿意承担汇损风险,在支付渠道清算后,使用原锁汇时的汇率,正式向外汇机构购汇,完成交割</li>
<li>即期:按当前市场汇率立即交换两种货币。一般是2个工作日内交割。一般是场内交易</li>
<li>远期:双方约定以当前确定汇率(远期汇率)在将来某天交换货币。一般是场外交易</li>
<li>掉期:双方先通过即期交易,然后约定在远期时再换回来。一般是场外交易</li>
<li>结汇入境:以入境中国为例。先在境外兑换成离岸人民币,再结算到境内银行账户或金融机构账户</li>
<li>入境结汇:以入境中国为例。先以外币入境,在境内再兑换成人民币</li>
</ul>
<h4 class="heading" id="资金账务">
 资金账务
 <a class="anchor" href="#%e8%b5%84%e9%87%91%e8%b4%a6%e5%8a%a1">#</a>
</h4>
<ul>
<li>会计日:标识一笔交易在会计层面的日期。交易需要跨多个子系统,完成一笔交易的时间在不同子系统中是不一样的。与自然日可能有差异,特别是交易在零点附近的时候,所以统一使用会计日来明确交易应该计入哪一天。有可能一笔交易发生在2023.12.23这天,但是交易记录在2023.12.24这个会计日内</li>
<li>日切:会计日切换到下一天。在这个点之后,所有的交易会被记录到下一个会计日。日切之后,通常需要进行批处理,包括交易清算、账户余额更新、试算平衡等</li>
<li>记账:交易记录到会计科目中</li>
<li>复试记账:一种会计准则,要求每笔交易都要有两个或以上的账目变动来记录,使得借方和贷方的总金额相等</li>
<li>头寸:通俗地说,就是余额情况。头寸不够,就是余额不够</li>
<li>流动性调拨:在多个银行备付金账户中转账,以确保符合业务需求。比如因为一些特殊原因,所有用户当天通过CMB渠道只支付了400万,但是当天所有用户合计要在CMB渠道退款500万</li>
<li>结算:收单机构把交易资金结转给商户。通常有结算到余额到结算到银行卡</li>
<li>清算:机构之间进行交易资金的转移。通常会由专门的清算机构负责清算。本质和结算是一样的,只是结算通常用在收单机构与商户之间,清算用在持牌的金融机构之间</li>
<li>轧差:清分过程中把当天应收和应付金额相互抵消,最终只有净额需要结算或清算,注意这个字读(gá)</li>
<li>净清算额:轧差之后,各参与方需要转移的资金总额</li>
<li>对账:比对交易双方的记账或资金。比如和渠道的对账。通常有明细对账和资金对账。前者就是对交易数据,按笔核对,后者对真实打款情况,当天交易100万,是否真实打款100万</li>
<li>长款/短款:对账过程中发现实际的金额高于或低于账面金额</li>
<li>计收费:支付平台针对手续费的记录和汇总。一般有商户计费和渠道计费</li>
</ul></description>
</item>
<item>
<title>网络游戏同步机制</title>
<link>https://weenable.github.io/posts/game/%E7%BD%91%E7%BB%9C%E6%B8%B8%E6%88%8F%E5%90%8C%E6%AD%A5%E6%9C%BA%E5%88%B6/</link>
<pubDate>Fri, 28 Feb 2025 19:08:15 +0800</pubDate>
<guid>https://weenable.github.io/posts/game/%E7%BD%91%E7%BB%9C%E6%B8%B8%E6%88%8F%E5%90%8C%E6%AD%A5%E6%9C%BA%E5%88%B6/</guid>
<description><h3 class="heading" id="网络同步">
 网络同步
 <a class="anchor" href="#%e7%bd%91%e7%bb%9c%e5%90%8c%e6%ad%a5">#</a>
</h3>
<p>网络同步 = 数据同步 + 表现同步
网络同步一般分为帧同步和状态同步</p>
<h4 class="heading" id="帧同步">
 帧同步
 <a class="anchor" href="#%e5%b8%a7%e5%90%8c%e6%ad%a5">#</a>
</h4>
<h5 class="heading" id="基本原理">
 基本原理
 <a class="anchor" href="#%e5%9f%ba%e6%9c%ac%e5%8e%9f%e7%90%86">#</a>
</h5>
<p>计算逻辑在客户端,按照一定的帧速率(逻辑帧而非渲染帧),服务端只转发</p>
<h5 class="heading" id="帧同步缺陷">
 帧同步缺陷
 <a class="anchor" href="#%e5%b8%a7%e5%90%8c%e6%ad%a5%e7%bc%ba%e9%99%b7">#</a>
</h5>
<ul>
<li>计算逻辑不在服务端,容易出现外挂</li>
<li>严格帧锁定同步网络差的客户端会影响其他玩家体验(乐观帧锁定同步)</li>
<li>不同客户端浮点数精度问题、随机值不统一</li>
</ul>
<h5 class="heading" id="乐观帧锁定">
 乐观帧锁定
 <a class="anchor" href="#%e4%b9%90%e8%a7%82%e5%b8%a7%e9%94%81%e5%ae%9a">#</a>
</h5>
<p>传统严格帧锁定算法会出现慢网影响其他人的情况,此时可以采用定时不等待的方式,服务端以固定帧率同步数据,不依赖每个玩家是否有操作</p>
<h4 class="heading" id="状态同步">
 状态同步
 <a class="anchor" href="#%e7%8a%b6%e6%80%81%e5%90%8c%e6%ad%a5">#</a>
</h4>
<h5 class="heading" id="基本原理-1">
 基本原理
 <a class="anchor" href="#%e5%9f%ba%e6%9c%ac%e5%8e%9f%e7%90%86-1">#</a>
</h5>
<p>计算逻辑在服务端,客户端只表现。状态同步一般都应用RPC调用和增量同步技术</p>
<h5 class="heading" id="状态同步缺陷">
 状态同步缺陷
 <a class="anchor" href="#%e7%8a%b6%e6%80%81%e5%90%8c%e6%ad%a5%e7%bc%ba%e9%99%b7">#</a>
</h5>
<ul>
<li>延迟过大、服务端压力大</li>
<li>状态同步做回放系统较麻烦</li>
</ul>
<h4 class="heading" id="网络协议">
 网络协议
 <a class="anchor" href="#%e7%bd%91%e7%bb%9c%e5%8d%8f%e8%ae%ae">#</a>
</h4>
<h5 class="heading" id="一些基础">
 一些基础
 <a class="anchor" href="#%e4%b8%80%e4%ba%9b%e5%9f%ba%e7%a1%80">#</a>
</h5>
<ul>
<li>TCP是流式传输没有固定内容边界,会出现拆包和沾包问题,UDP基于报文有消息边界,不存在拆包沾包问题</li>
<li>TCP默认情况下使用Nagle算法,可以优化传输减少网络里的小包数量,传输时协议栈会累积数据直到满足以下条件之一
<ul>
<li>积累的数量达到最大MSS</li>
<li>收到一个ACK</li>
</ul>
</li>
<li>TCp默认情况下使用延迟累积ACK发送机制,传输时协议栈会合并多个ACK发送,提高网络性能,直到满足一下条件之一
<ul>
<li>累积多个ACK或有数据传输时</li>
<li>ACK超时(40ms)</li>
</ul>
</li>
<li>TCP可以使用<strong>TCP_NODELAY</strong>来关闭Nagle算法</li>
</ul>
<h5 class="heading" id="网络协议方案">
 网络协议方案
 <a class="anchor" href="#%e7%bd%91%e7%bb%9c%e5%8d%8f%e8%ae%ae%e6%96%b9%e6%a1%88">#</a>
</h5>
<p>可以默认使用TCP,然后再打开使用KCP
在网络状态较好时可以使用KCP,当出现大量丢包时切换使用TCP</p>
<h3 class="heading" id="同步优化方案">
 同步优化方案
 <a class="anchor" href="#%e5%90%8c%e6%ad%a5%e4%bc%98%e5%8c%96%e6%96%b9%e6%a1%88">#</a>
</h3>
<h4 class="heading" id="表现优化">
 表现优化
 <a class="anchor" href="#%e8%a1%a8%e7%8e%b0%e4%bc%98%e5%8c%96">#</a>
</h4>
<p>表现优化主要是想弱化玩家对于延迟的感受,主要分为以下一些优化技术</p>
<h5 class="heading" id="1插值优化">
 1.插值优化
 <a class="anchor" href="#1%e6%8f%92%e5%80%bc%e4%bc%98%e5%8c%96">#</a>
</h5>
<p><a href="https://zhuanlan.zhihu.com/p/631954987?utm_id=0">https://zhuanlan.zhihu.com/p/631954987?utm_id=0</a>
状态同步中,由于客户端每次收到都是其他角色的位置信息,为了避免位置突变,客户端会使用插值技术而不是跳帧</p>
<ul>
<li>内插值目的是解决客户端离散信息更新导致的突变问题,通过线性插值Lerp插入过渡数据
<ul>
<li>收到数据包时不能直接应用,必须等到下一个数据包到来才能开始插值,延迟增加</li>
<li>运动状态发生剧烈变化时,会丢失部份运动状态</li>
</ul>
</li>
</ul>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-2025228102431.png" src="https://pic4.zhimg.com/v2-3fea56f86c6bffa3d32ae1681fe9ad73_b.webp" >
 </div>

 
</figure>
</p></description>
</item>
<item>
<title>跳表和实现</title>
<link>https://weenable.github.io/posts/%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E8%B7%B3%E8%A1%A8%E5%92%8C%E5%AE%9E%E7%8E%B0/</link>
<pubDate>Fri, 28 Feb 2025 19:07:07 +0800</pubDate>
<guid>https://weenable.github.io/posts/%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E8%B7%B3%E8%A1%A8%E5%92%8C%E5%AE%9E%E7%8E%B0/</guid>
<description><h5 class="heading" id="数据结构示意图">
 数据结构示意图
 <a class="anchor" href="#%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e7%a4%ba%e6%84%8f%e5%9b%be">#</a>
</h5>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-202543009608.png" src="https://weenable.github.io/images/image-202543009608.png" >
 </div>

 
</figure>
</p>
<h5 class="heading" id="代码实现">
 代码实现
 <a class="anchor" href="#%e4%bb%a3%e7%a0%81%e5%ae%9e%e7%8e%b0">#</a>
</h5>
<pre tabindex="0"><code>package main

import &#34;math/rand&#34;

const (
	MaxLevel = 16 // 跳表的最大层数
	Probability = 0.5 // 随机提升层数的概率
)

type SkipList struct {
	head *Node
	level int
}

type Node struct {
	key int
	value string
	next []*Node
}

func randomLevel() int {
	level := 1
	for rand.Float64() &lt; Probability &amp;&amp; level &lt; MaxLevel {
		level++
	}
	return level
}

func NewSkipList() *SkipList {
	return &amp;SkipList{
		head: NewNode(0, &#34;&#34;, MaxLevel),
		level: 1,
	}
}

func NewNode(key int, value string, level int) *Node {
	return &amp;Node{
		key: key,
		value: value,
		next: make([]*Node, level),
	}
}

// 插入元素
func (sl *SkipList) Insert(key int, value string) {
	update := make([]*Node, MaxLevel)
	node := sl.head

	// 找到每一层最后一个&lt;key的节点放入update中
	for i := sl.level - 1; i &gt;= 0; i-- {
		for node.next[i] != nil &amp;&amp; node.next[i].key &lt; key {
			node = node.next[i]
		}
		update[i] = node
	}

	// 随机层数
	level := randomLevel()
	// 将&gt;sl.level的层,sl.head加入update中
	if level &gt; sl.level {
		for i := sl.level; i &lt; level; i++ {
			update[i] = sl.head
		}
		sl.level = level
	}

	// 将新节点加入跳表
	newNode := NewNode(key, value, level)
	for i := 0; i &lt; level; i++ {
		newNode.next[i] = update[i].next[i]
		update[i].next[i] = newNode
	}
}

// 查找元素
func (sl *SkipList) Search(key int) (string, bool) {
	// 从最高层开始往下,找到最后一个比key小的节点
	node := sl.head
	for i := sl.level - 1; i &gt;= 0; i-- {
		for node.next[i] != nil &amp;&amp; node.next[i].key &lt; key {
			node = node.next[i]
		}
	}

	node = node.next[0]
	if node != nil &amp;&amp; node.key == key {
		return node.value, true
	}

	return &#34;&#34;, false
}

// 删除元素
func (sl *SkipList) Delete(key int) bool {
	update := make([]*Node, MaxLevel)

	// 从最高层开始往下,找到最后一个比key小的节点
	node := sl.head
	for i := sl.level; i &gt;= 0; i-- {
		for node.next[i] != nil &amp;&amp; node.next[i].key &lt; key {
			node = node.next[i]
		}
		update[i] = node
	}

	// 判断元素是否存在
	node = node.next[0]
	if node == nil || node.key != key {
		return false
	}

	// 从第1层开始往上,剔除目标节点
	for i := 0; i &lt; sl.level; i++ {
		if update[i].next[i] != node {
			break
		}
		update[i].next[i] = node.next[i]
	}

	// 如果高层除了头节点没有多余节点,则降层
	for sl.level &gt; 1 &amp;&amp; sl.head.next[sl.level-1] == nil {
		sl.level--
	}

	return true
}

func main() {
	sl := skiplist.NewSkipList()
	sl.Insert(1, &#34;Hello&#34;)
	sl.Insert(2, &#34;World&#34;)
	sl.Insert(3, &#34;SkipList&#34;)

	if value, found := sl.Search(2); found {
		fmt.Println(&#34;Found key 2 with value:&#34;, value)
	} else {
		fmt.Println(&#34;Key 2 not found&#34;)
	}

	sl.Delete(2)
	if value, found := sl.Search(2); found {
		fmt.Println(&#34;Found key 2 with value:&#34;, value)
	} else {
		fmt.Println(&#34;Key 2 not found&#34;)
	}

}
</code></pre></description>
</item>
<item>
<title>哈希表渐进式Rehash实现</title>
<link>https://weenable.github.io/posts/%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E5%93%88%E5%B8%8C%E8%A1%A8%E6%B8%90%E8%BF%9B%E5%BC%8Frehash%E5%AE%9E%E7%8E%B0/</link>
<pubDate>Fri, 28 Feb 2025 19:06:04 +0800</pubDate>
<guid>https://weenable.github.io/posts/%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E5%93%88%E5%B8%8C%E8%A1%A8%E6%B8%90%E8%BF%9B%E5%BC%8Frehash%E5%AE%9E%E7%8E%B0/</guid>
<description><h5 class="heading" id="说明">
 说明
 <a class="anchor" href="#%e8%af%b4%e6%98%8e">#</a>
</h5>
<ol>
<li>哈希冲突采用链地址法</li>
<li>在操作中进行渐进式扩容迁移</li>
</ol>
<h5 class="heading" id="代码实现">
 代码实现
 <a class="anchor" href="#%e4%bb%a3%e7%a0%81%e5%ae%9e%e7%8e%b0">#</a>
</h5>
<pre tabindex="0"><code>package main

// 初始哈希表大小
const initialSize = 4

// 哈希表元素
type entry struct {
	key string
	value interface{}
	next *entry
}

type dict struct {
	ht [2][]*entry
	rehashIndex int
}

func NewDict() *dict {
	d := &amp;dict{
		ht: [2][]*entry{make([]*entry, initialSize), nil},
		rehashIndex: -1,
	}

	return d
}

// 实现一个哈希函数
func (d *dict) hash(key string) int {
	h := 0
	for i := 0; i &lt; len(key); i++ {
		h = 31*h + int(key[i])
	}

	return h
}

// 插入元素
func (d *dict) Add(key string, value interface{}) {
	if d.rehashIndex != -1 {
		d.rehash()
	}

	// 如果此刻没有在渐进扩容期间,并且当前元素个数&gt;=容量一半,触发渐进扩容
	if d.ht[1] == nil &amp;&amp; len(d.ht[0]) &gt; 0 &amp;&amp; len(d.ht[0]) &gt;= cap(d.ht[0])/2 {
		d.ht[1] = make([]*entry, len(d.ht[0])*2) //扩容一倍
		d.rehashIndex = 0
	}

	index := d.hash(key) % len(d.ht[0])
	newEntry := &amp;entry{key: key, value: value, next: d.ht[0][index]}
	d.ht[0][index] = newEntry
}

// 获取元素
func (d *dict) Get(key string) (interface{}, bool) {
	if d.rehashIndex != -1 {
		d.rehash()
	}

	// 在h[0]中找
	index := d.hash(key) % len(d.ht[0])
	for curEntry := d.ht[0][index]; curEntry != nil; curEntry = curEntry.next {
		if curEntry.key == key {
			return curEntry.value, true
		}
	}

	// 在h[1]中找,已经迁移
	if d.ht[1] != nil {
		index = d.hash(key) % len(d.ht[1])
		for entry := d.ht[1][index]; entry != nil; entry = entry.next {
			if entry.key == key {
				return entry.value, true
			}
		}
	}

	return nil, false
}

// 渐进rehash执行的函数
func (d *dict) rehash() {
	if d.rehashIndex == -1 {
		return
	}

	// 渐进移动一个桶的数据
	for i := 0; i &lt; 1 &amp;&amp; d.rehashIndex &lt; len(d.ht[0]); i++ {
		if d.ht[0][d.rehashIndex] != nil {
			curEntry := d.ht[0][d.rehashIndex]
			for curEntry != nil {
				nextEntry := curEntry.next
				index := d.hash(curEntry.key) % len(d.ht[1])
				curEntry.next = d.ht[1][index]
				d.ht[1][index] = curEntry
				curEntry = nextEntry
			}

			d.ht[0][d.rehashIndex] = nil
		}
		d.rehashIndex++
	}

	if d.rehashIndex &gt;= len(d.ht[0]) {
		d.ht[0] = d.ht[1]
		d.ht[1] = nil
		d.rehashIndex = -1
	}
}


func main() {
	d := rehash.NewDict()
	d.Add(&#34;foo&#34;, &#34;bar&#34;)
	d.Add(&#34;baz&#34;, &#34;qux&#34;)

	val, found := d.Get(&#34;foo&#34;)
	if found {
		fmt.Println(&#34;Found foo:&#34;, val)
	} else {
		fmt.Println(&#34;foo not found&#34;)
	}

	val, found = d.Get(&#34;baz&#34;)
	if found {
		fmt.Println(&#34;Found baz:&#34;, val)
	} else {
		fmt.Println(&#34;baz not found&#34;)
	}

	// 加这两个元素前已经触发扩容,因为设定元素个数&gt;=容量一半则扩容
	d.Add(&#34;new&#34;, &#34;value&#34;)
	d.Add(&#34;another&#34;, &#34;entry&#34;)

	val, found = d.Get(&#34;new&#34;)
	if found {
		fmt.Println(&#34;Found new:&#34;, val)
	} else {
		fmt.Println(&#34;new not found&#34;)
	}

}
</code></pre></description>
</item>
<item>
<title>go-redis哨兵模式主从切换源码分析</title>
<link>https://weenable.github.io/posts/golang/go-redis%E5%93%A8%E5%85%B5%E6%A8%A1%E5%BC%8F%E4%B8%BB%E4%BB%8E%E5%88%87%E6%8D%A2%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/</link>
<pubDate>Fri, 28 Feb 2025 19:04:12 +0800</pubDate>
<guid>https://weenable.github.io/posts/golang/go-redis%E5%93%A8%E5%85%B5%E6%A8%A1%E5%BC%8F%E4%B8%BB%E4%BB%8E%E5%88%87%E6%8D%A2%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/</guid>
<description><h5 class="heading" id="实现原理">
 实现原理
 <a class="anchor" href="#%e5%ae%9e%e7%8e%b0%e5%8e%9f%e7%90%86">#</a>
</h5>
<ol>
<li>通过哨兵获取主节点信息:在连接 Redis 集群时,<code>go-redis</code> 客户端会连接到一个或多个哨兵节点,获取当前的主节点信息。如果主节点发生故障,哨兵会进行故障转移,并通知客户端新的主节点信息。</li>
<li>周期性地从哨兵获取主节点信息:<code>go-redis</code> 客户端会周期性地向哨兵节点请求主节点信息,以确保它始终连接到当前的主节点。这个机制可以帮助客户端快速感知主从切换。</li>
<li>在操作失败时重新获取主节点信息:如果客户端在执行操作时遇到连接错误或其他错误,可能会重新从哨兵节点获取主节点的信息,并重试操作。</li>
</ol>
<h5 class="heading" id="源码分析">
 源码分析
 <a class="anchor" href="#%e6%ba%90%e7%a0%81%e5%88%86%e6%9e%90">#</a>
</h5>
<ul>
<li>初始化</li>
</ul>
<pre tabindex="0"><code>//rdb := redis.NewFailoverClient(&amp;redis.FailoverOptions{
// MasterName: &#34;mymaster&#34;, // 哨兵配置中主节点的名字
// SentinelAddrs: []string{&#34;127.0.0.1:26379&#34;, &#34;127.0.0.1:26380&#34;, &#34;127.0.0.1:26381&#34;},
//})


type FailoverOptions struct {
 MasterName string
 SentinelAddrs []string
 // ... other options
}

func NewFailoverClient(opt *FailoverOptions) *Client {
 sentinel := newSentinel(opt)
 return NewClient(&amp;Options{
 Addr: sentinel.masterAddr(),
 // ... other options
 })
}

func newSentinel(opt *FailoverOptions) *sentinel {
 // Connect to sentinel nodes and get master address
 return &amp;sentinel{
 masterName: opt.MasterName,
 addrs: opt.SentinelAddrs,
 }
}

func (s *sentinel) masterAddr() string {
 // Get master address from sentinel nodes
}
</code></pre><ul>
<li>周期性刷新master地址</li>
</ul>
<pre tabindex="0"><code>func (s *sentinel) periodicallyRefreshMasterAddr() {
 ticker := time.NewTicker(time.Minute)
 for range ticker.C {
 addr := s.masterAddr()
 // Update client&#39;s master address
 }
}
</code></pre><ul>
<li>失败时重试和刷新master</li>
</ul>
<pre tabindex="0"><code>func (c *Client) doWithRetry(fn func() error) error {
 for i := 0; i &lt; maxRetries; i++ {
 err := fn()
 if err == nil {
 return nil
 }
 if isConnectionError(err) {
 c.refreshMasterAddr()
 }
 time.Sleep(retryBackoff)
 }
 return fmt.Errorf(&#34;after %d retries, last error: %v&#34;, maxRetries, err)
}

func (c *Client) refreshMasterAddr() {
 addr := c.sentinel.masterAddr()
 c.Options().Addr = addr
}
</code></pre></description>
</item>
<item>
<title>实现redis客户端一致性哈希分片</title>
<link>https://weenable.github.io/posts/golang/%E5%AE%9E%E7%8E%B0redis%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%B8%80%E8%87%B4%E6%80%A7%E5%93%88%E5%B8%8C%E5%88%86%E7%89%87/</link>
<pubDate>Fri, 28 Feb 2025 19:03:16 +0800</pubDate>
<guid>https://weenable.github.io/posts/golang/%E5%AE%9E%E7%8E%B0redis%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%B8%80%E8%87%B4%E6%80%A7%E5%93%88%E5%B8%8C%E5%88%86%E7%89%87/</guid>
<description><h5 class="heading" id="实现原理">
 实现原理
 <a class="anchor" href="#%e5%ae%9e%e7%8e%b0%e5%8e%9f%e7%90%86">#</a>
</h5>
<p>哈希方案有以下几种:</p>
<ul>
<li>普通哈希分片</li>
<li>一致性哈希分片</li>
<li>范围哈希分片</li>
</ul>
<p>分片有以下几种方案:</p>
<ul>
<li>redis官方哈希槽分片方案(属于服务端sharding,使用范围哈希)</li>
<li>客户端sharding,可以使用普通哈希、一致性哈希</li>
<li>代理sharding,使用代理器进行分片,有性能损耗</li>
</ul>
<h5 class="heading" id="客户端通过一致性哈希实现分片">
 客户端通过一致性哈希实现分片
 <a class="anchor" href="#%e5%ae%a2%e6%88%b7%e7%ab%af%e9%80%9a%e8%bf%87%e4%b8%80%e8%87%b4%e6%80%a7%e5%93%88%e5%b8%8c%e5%ae%9e%e7%8e%b0%e5%88%86%e7%89%87">#</a>
</h5>
<pre tabindex="0"><code>package main

import (
	&#34;context&#34;
	&#34;github.com/go-redis/redis/v8&#34;
	&#34;github.com/stathat/consistent&#34;
)

// 初始化支持一致性哈希的客户端结构
type ShardingClient struct {
	consistentHash *consistent.Consistent
	clients map[string]*redis.Client
}

func NewShardingClient(addrs []string) *ShardingClient {
	ch := consistent.New()
	clients := make(map[string]*redis.Client)

	for _, addr := range addrs {
		ch.Add(addr)
		clients[addr] = redis.NewClient(&amp;redis.Options{
			Addr: addr,
		})
	}

	return &amp;ShardingClient{
		consistentHash: ch,
		clients: clients,
	}
}

// getClient 根据键获取相应的 Redis 客户端
func (sc *ShardingClient) getClient(key string) (*redis.Client, error) {
	addr, err := sc.consistentHash.Get(key)
	if err != nil {
		return nil, err
	}
	return sc.clients[addr], nil
}

// Set 在相应的 Redis 实例上设置键值对
func (sc *ShardingClient) Set(ctx context.Context, key, value string) error {
	client, err := sc.getClient(key)
	if err != nil {
		return err
	}
	return client.Set(ctx, key, value, 0).Err()
}

// Get 在相应的 Redis 实例上获取键值对
func (sc *ShardingClient) Get(ctx context.Context, key string) (string, error) {
	client, err := sc.getClient(key)
	if err != nil {
		return &#34;&#34;, err
	}
	return client.Get(ctx, key).Result()
}


func main() {
	addrs := []string{
		&#34;127.0.0.1:6379&#34;,
		&#34;127.0.0.1:6380&#34;,
		&#34;127.0.0.1:6381&#34;,
	}

	// 初始化 Sharding 客户端
	shardingClient := NewShardingClient(addrs)

	ctx := context.Background()

	// 测试连接和操作
	err := shardingClient.Set(ctx, &#34;key&#34;, &#34;value&#34;)
	if err != nil {
		log.Fatalf(&#34;Failed to set key: %v&#34;, err)
	}

	val, err := shardingClient.Get(ctx, &#34;key&#34;)
	if err != nil {
		log.Fatalf(&#34;Failed to get key: %v&#34;, err)
	}

	fmt.Printf(&#34;key: %s\n&#34;, val)
}
</code></pre></description>
</item>
<item>
<title>布隆过滤器算法实现</title>
<link>https://weenable.github.io/posts/%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E7%AE%97%E6%B3%95%E5%AE%9E%E7%8E%B0/</link>
<pubDate>Fri, 28 Feb 2025 19:01:03 +0800</pubDate>
<guid>https://weenable.github.io/posts/%E7%AE%97%E6%B3%95%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E7%AE%97%E6%B3%95%E5%AE%9E%E7%8E%B0/</guid>
<description><h5 class="heading" id="实现原理">
 实现原理
 <a class="anchor" href="#%e5%ae%9e%e7%8e%b0%e5%8e%9f%e7%90%86">#</a>
</h5>
<p>布隆过滤器由一个位数组(bit array)和多个哈希函数(hash functions)组成。其工作原理如下:</p>
<ol>
<li><strong>初始化</strong>:
<ul>
<li>创建一个长度为 <code>m</code> 的位数组,将所有位初始化为 0。</li>
<li>选择 <code>k</code> 个独立的哈希函数,每个哈希函数将输入映射到 <code>[0, m-1]</code> 范围内的一个位置。</li>
</ul>
</li>
<li><strong>插入元素</strong>:
<ul>
<li>对于要插入的元素,通过 <code>k</code> 个哈希函数分别计算出 <code>k</code> 个哈希值。</li>
<li>将位数组中这 <code>k</code> 个位置的值设置为 1。</li>
</ul>
</li>
<li><strong>查询元素</strong>:
<ul>
<li>对于要查询的元素,通过 <code>k</code> 个哈希函数分别计算出 <code>k</code> 个哈希值。</li>
<li>检查位数组中这 <code>k</code> 个位置的值,如果所有位置的值都为 1,则认为该元素可能在集合中;如果有任何一个位置的值为 0,则认为该元素不在集合中。</li>
</ul>
</li>
</ol>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-2025228218354.png" src="https://weenable.github.io/images/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E7%AE%97%E6%B3%95%E5%AE%9E%E7%8E%B0/image-2025228218354.png" >
 </div>

 
</figure>
</p>
<h5 class="heading" id="代码实现">
 代码实现
 <a class="anchor" href="#%e4%bb%a3%e7%a0%81%e5%ae%9e%e7%8e%b0">#</a>
</h5>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="font-weight:bold;text-decoration:underline">package</span> bloomfilter
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="font-weight:bold;text-decoration:underline">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#666;font-style:italic">&#34;hash/fnv&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#666;font-style:italic">&#34;math&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">type</span> BloomFilter <span style="font-weight:bold;font-style:italic;text-decoration:underline">struct</span> {
</span></span><span style="display:flex;"><span>	bitset []<span style="font-weight:bold;text-decoration:underline">bool</span> <span style="color:#888;font-style:italic">//比特数组</span>
</span></span><span style="display:flex;"><span>	m <span style="font-weight:bold;text-decoration:underline">int</span> <span style="color:#888;font-style:italic">//比特数组长度</span>
</span></span><span style="display:flex;"><span>	k <span style="font-weight:bold;text-decoration:underline">int</span> <span style="color:#888;font-style:italic">//哈希函数数量</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> <span style="color:#666;font-weight:bold;font-style:italic">NewBloomFilter</span>(n <span style="font-weight:bold;text-decoration:underline">int</span>, p <span style="font-weight:bold;text-decoration:underline">float64</span>) *BloomFilter {
</span></span><span style="display:flex;"><span>	m := <span style="font-weight:bold;font-style:italic">int</span>(math.<span style="color:#666;font-weight:bold;font-style:italic">Ceil</span>(<span style="font-weight:bold;font-style:italic">float64</span>(-n) * math.<span style="color:#666;font-weight:bold;font-style:italic">Log</span>(p) / math.<span style="color:#666;font-weight:bold;font-style:italic">Pow</span>(math.<span style="color:#666;font-weight:bold;font-style:italic">Log</span>(2), 2)))
</span></span><span style="display:flex;"><span>	k := <span style="font-weight:bold;font-style:italic">int</span>(math.<span style="color:#666;font-weight:bold;font-style:italic">Ceil</span>(math.<span style="color:#666;font-weight:bold;font-style:italic">Log</span>(2) * <span style="font-weight:bold;font-style:italic">float64</span>(m) / <span style="font-weight:bold;font-style:italic">float64</span>(n)))
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">return</span> &amp;BloomFilter{
</span></span><span style="display:flex;"><span>		bitset: <span style="font-weight:bold;font-style:italic">make</span>([]<span style="font-weight:bold;text-decoration:underline">bool</span>, m),
</span></span><span style="display:flex;"><span>		m: m,
</span></span><span style="display:flex;"><span>		k: k,
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#888;font-style:italic">// 哈希函数</span>
</span></span><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> (bf *BloomFilter) <span style="color:#666;font-weight:bold;font-style:italic">hash</span>(data []<span style="font-weight:bold;text-decoration:underline">byte</span>, seed <span style="font-weight:bold;text-decoration:underline">uint32</span>) <span style="font-weight:bold;text-decoration:underline">int</span> {
</span></span><span style="display:flex;"><span>	h := fnv.<span style="color:#666;font-weight:bold;font-style:italic">New32a</span>()
</span></span><span style="display:flex;"><span>	h.<span style="color:#666;font-weight:bold;font-style:italic">Write</span>(data)
</span></span><span style="display:flex;"><span>	hash := h.<span style="color:#666;font-weight:bold;font-style:italic">Sum32</span>()
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">return</span> <span style="font-weight:bold;font-style:italic">int</span>((hash + seed) % <span style="font-weight:bold;font-style:italic">uint32</span>(bf.m))
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#888;font-style:italic">// 添加元素</span>
</span></span><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> (bf *BloomFilter) <span style="color:#666;font-weight:bold;font-style:italic">Add</span>(item <span style="font-weight:bold;text-decoration:underline">string</span>) {
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">for</span> i := 0; i &lt; bf.k; i++ {
</span></span><span style="display:flex;"><span>		pos := bf.<span style="color:#666;font-weight:bold;font-style:italic">hash</span>([]<span style="font-weight:bold;font-style:italic">byte</span>(item), <span style="font-weight:bold;font-style:italic">uint32</span>(i))
</span></span><span style="display:flex;"><span>		bf.bitset[pos] = <span style="font-weight:bold;text-decoration:underline">true</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#888;font-style:italic">// 查找元素</span>
</span></span><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> (bf *BloomFilter) <span style="color:#666;font-weight:bold;font-style:italic">Contains</span>(item <span style="font-weight:bold;text-decoration:underline">string</span>) <span style="font-weight:bold;text-decoration:underline">bool</span> {
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">for</span> i := 0; i &lt; bf.k; i++ {
</span></span><span style="display:flex;"><span>		pos := bf.<span style="color:#666;font-weight:bold;font-style:italic">hash</span>([]<span style="font-weight:bold;font-style:italic">byte</span>(item), <span style="font-weight:bold;font-style:italic">uint32</span>(i))
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">if</span> !bf.bitset[pos] {
</span></span><span style="display:flex;"><span>			<span style="font-weight:bold;text-decoration:underline">return</span> <span style="font-weight:bold;text-decoration:underline">false</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">return</span> <span style="font-weight:bold;text-decoration:underline">true</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></description>
</item>
<item>
<title>区块链相关定义和概念</title>
<link>https://weenable.github.io/posts/web3/%E5%8C%BA%E5%9D%97%E9%93%BE%E7%9B%B8%E5%85%B3%E5%AE%9A%E4%B9%89%E5%92%8C%E6%A6%82%E5%BF%B5/</link>
<pubDate>Fri, 28 Feb 2025 18:59:06 +0800</pubDate>
<guid>https://weenable.github.io/posts/web3/%E5%8C%BA%E5%9D%97%E9%93%BE%E7%9B%B8%E5%85%B3%E5%AE%9A%E4%B9%89%E5%92%8C%E6%A6%82%E5%BF%B5/</guid>
<description><h4 class="heading" id="1区块链基本架构">
 1.区块链基本架构
 <a class="anchor" href="#1%e5%8c%ba%e5%9d%97%e9%93%be%e5%9f%ba%e6%9c%ac%e6%9e%b6%e6%9e%84">#</a>
</h4>
<ul>
<li>数据层:描述区块链物理结构</li>
<li>网络层:提供节点间数据通信和数据校验</li>
<li>共识层:提供节点间达成共识的算法</li>
<li>激励层:提供激励措施</li>
<li>合约层:提供可编程能力</li>
<li>应用层:提供封装区块链技术的应用</li>
</ul>
<h4 class="heading" id="2拜占庭问题和拜占庭容错">
 2.拜占庭问题和拜占庭容错
 <a class="anchor" href="#2%e6%8b%9c%e5%8d%a0%e5%ba%ad%e9%97%ae%e9%a2%98%e5%92%8c%e6%8b%9c%e5%8d%a0%e5%ba%ad%e5%ae%b9%e9%94%99">#</a>
</h4>
<p>拜占庭问题:存在少数不良节点场景下的一致性达成问题
拜占庭算法(BFT):N为节点总数,F为其中不良节点数,当N&gt;=3F+1时问题才有解
实用拜占庭算法(PBFT):是第一个广泛采用的BFT算法,采用密码学技术(RSA签名算法、信息摘要算法、信息验证算法)</p>
<h4 class="heading" id="3区块链常见共识算法">
 3.区块链常见共识算法
 <a class="anchor" href="#3%e5%8c%ba%e5%9d%97%e9%93%be%e5%b8%b8%e8%a7%81%e5%85%b1%e8%af%86%e7%ae%97%e6%b3%95">#</a>
</h4>
<ul>
<li>工作量证明POW:通过耗费电力阻止不良行为</li>
<li>权益证明POS:代币质押者为验证者,通过绑定验证者和网络的利益来阻止不良行为</li>
<li>委托权益证明DPOS:代币持有者对交易验证者进行投票</li>
</ul>
<h4 class="heading" id="4身份认证">
 4.身份认证
 <a class="anchor" href="#4%e8%ba%ab%e4%bb%bd%e8%ae%a4%e8%af%81">#</a>
</h4>
<p>摘要算法+签名算法
A:对交易信息进行Hash取摘要,通过私钥加密生成签名
广播:广播交易信息+A公钥+A生成的签名
B:对交易信息Hash取摘要,通过A公钥解密签名得到摘要,比对两个摘要</p>
<h4 class="heading" id="5双重支付问题">
 5.双重支付问题
 <a class="anchor" href="#5%e5%8f%8c%e9%87%8d%e6%94%af%e4%bb%98%e9%97%ae%e9%a2%98">#</a>
</h4>
<p>区块链存在51%攻击的问题,依靠强大的算力篡改区块进行强行分叉。
可以通过等待若干新块产生后再进行正式交付,来降低51%攻击的可能性</p>
<h4 class="heading" id="6闪电网络">
 6.闪电网络
 <a class="anchor" href="#6%e9%97%aa%e7%94%b5%e7%bd%91%e7%bb%9c">#</a>
</h4>
<p>比特币交易性能差(全网tps=7左右),同时还需要等待6个块的可行确认(抵御双重支付攻击),导致一笔交易可能要1小时的最终确认</p>
<ul>
<li>RSMC(序列到期可撤销合约):链下通道+链上更新+违约惩罚</li>
<li>HTLC(哈希时间锁定合约):通过合约双方约定接收方提供一个哈希值,在一定时间内有人能提出一个字符串使得哈希后的值与已知值匹配,则转账方将这笔钱转给回答正确的人</li>
</ul>
<h4 class="heading" id="7软分叉">
 7.软分叉
 <a class="anchor" href="#7%e8%bd%af%e5%88%86%e5%8f%89">#</a>
</h4>
<h5 class="heading" id="定义">
 定义
 <a class="anchor" href="#%e5%ae%9a%e4%b9%89">#</a>
</h5>
<p>在区块链协议中,软分叉(Soft Fork)是对现有协议做向后兼容的更改。软分叉的特点是,新规则是现有规则的一个子集,也就是说,遵循旧规则的节点仍然可以理解并处理遵循新规则的区块和交易,但不能创建不符合新规则的区块和交易</p>
<h5 class="heading" id="软分叉状态">
 软分叉状态
 <a class="anchor" href="#%e8%bd%af%e5%88%86%e5%8f%89%e7%8a%b6%e6%80%81">#</a>
</h5>
<p>软分叉状态指的是区块链网络中软分叉的激活和部署状态。通常,软分叉的激活分为以下几个阶段</p>
<ol>
<li>定义阶段(Defined):
<ul>
<li>软分叉的代码已经被写入客户端软件,但还没有开始正式监控。</li>
</ul>
</li>
<li>投票阶段(Started):
<ul>
<li>节点开始在区块头中嵌入信号(通过特定的区块版本位)来表示它们是否支持该软分叉。</li>
<li>矿工通过在区块头中设置特定的比特位来投票支持或反对这个软分叉。</li>
</ul>
</li>
<li>锁定阶段(Locked In):
<ul>
<li>如果在一个特定的周期内(例如 2016 个区块)达到了一定的支持率(通常是 95%),软分叉就会进入锁定阶段。</li>
<li>在锁定阶段结束后,软分叉将会被激活。</li>
</ul>
</li>
<li>激活阶段(Active):
<ul>
<li>软分叉的规则开始正式生效,所有新创建的区块和交易都必须遵循这些新规则。</li>
</ul>
</li>
</ol>
<p>软分叉状态表示区块链网络中软分叉的不同阶段,包括定义、投票、锁定和激活阶段。检查软分叉状态有助于节点决定如何与其他节点进行交互,以确保遵循最新的协议规则,从而提高网络的安全性和数据一致性。例如,在 SegWit 激活后,节点需要与支持 SegWit 的对等节点进行同步,以确保能够完整地验证所有区块链数据</p></description>
</item>
<item>
<title>go内存池实现</title>
<link>https://weenable.github.io/posts/golang/go%E5%86%85%E5%AD%98%E6%B1%A0%E5%AE%9E%E7%8E%B0/</link>
<pubDate>Fri, 28 Feb 2025 18:57:27 +0800</pubDate>
<guid>https://weenable.github.io/posts/golang/go%E5%86%85%E5%AD%98%E6%B1%A0%E5%AE%9E%E7%8E%B0/</guid>
<description><h5 class="heading" id="实现">
 实现
 <a class="anchor" href="#%e5%ae%9e%e7%8e%b0">#</a>
</h5>
<p>用于管理固定大小的字节切片(<code>[]byte</code>)。内存池的目的在于减少内存分配和垃圾回收的开销,通过重用已经分配的内存块来提高性能</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="font-weight:bold;text-decoration:underline">package</span> main
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">type</span> MemoryPool <span style="font-weight:bold;font-style:italic;text-decoration:underline">chan</span> []<span style="font-weight:bold;text-decoration:underline">byte</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#888;font-style:italic">// 从内存池中返回长度为8的字节切片,如果内存池中没有可用字节切片则新分配</span>
</span></span><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> (l MemoryPool) <span style="color:#666;font-weight:bold;font-style:italic">Borrow</span>() []<span style="font-weight:bold;text-decoration:underline">byte</span> {
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;font-style:italic;text-decoration:underline">var</span> buf []<span style="font-weight:bold;text-decoration:underline">byte</span>
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">select</span> {
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">case</span> buf = &lt;-l:
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">default</span>:
</span></span><span style="display:flex;"><span>		buf = <span style="font-weight:bold;font-style:italic">make</span>([]<span style="font-weight:bold;text-decoration:underline">byte</span>, 8)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">return</span> buf[:8]
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#888;font-style:italic">// 将字节切片放回内存池</span>
</span></span><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> (l MemoryPool) <span style="color:#666;font-weight:bold;font-style:italic">Return</span>(buf []<span style="font-weight:bold;text-decoration:underline">byte</span>) {
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">select</span> {
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">case</span> l &lt;- buf:
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">default</span>:
</span></span><span style="display:flex;"><span>		<span style="color:#888;font-style:italic">// 垃圾回收</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> <span style="color:#666;font-weight:bold;font-style:italic">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;font-style:italic;text-decoration:underline">var</span> mp MemoryPool = <span style="font-weight:bold;font-style:italic">make</span>(<span style="font-weight:bold;font-style:italic;text-decoration:underline">chan</span> []<span style="font-weight:bold;text-decoration:underline">byte</span>, 1024)
</span></span><span style="display:flex;"><span>	sl := mp.<span style="color:#666;font-weight:bold;font-style:italic">Borrow</span>()
</span></span><span style="display:flex;"><span>	mp.<span style="color:#666;font-weight:bold;font-style:italic">Return</span>(sl)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></description>
</item>
<item>
<title>以太坊消息签名</title>
<link>https://weenable.github.io/posts/web3/%E4%BB%A5%E5%A4%AA%E5%9D%8A%E6%B6%88%E6%81%AF%E7%AD%BE%E5%90%8D/</link>
<pubDate>Fri, 28 Feb 2025 18:55:39 +0800</pubDate>
<guid>https://weenable.github.io/posts/web3/%E4%BB%A5%E5%A4%AA%E5%9D%8A%E6%B6%88%E6%81%AF%E7%AD%BE%E5%90%8D/</guid>
<description><h3 class="heading" id="签名算法和原理">
 签名算法和原理
 <a class="anchor" href="#%e7%ad%be%e5%90%8d%e7%ae%97%e6%b3%95%e5%92%8c%e5%8e%9f%e7%90%86">#</a>
</h3>
<p>以太坊使用椭圆曲线加密算法ECDSA来进行交易签名</p>
<ul>
<li>密钥生成:生成私钥和公钥,私钥用于签名,公钥用于验证签名</li>
<li>交易签名:使用私钥对原始消息进行签名
<ul>
<li>对交易使用RLP(递归长度前缀编码)编码序列化</li>
<li>使用keccka256哈希函数对编码后的交易进行计算</li>
<li>使用私钥对哈希值进行签名,生成r、s、v值</li>
</ul>
</li>
<li>签名验证:使用公钥以及原始消息来验证签名的有效性</li>
</ul>
<p>验证签名代码:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="font-weight:bold;font-style:italic;text-decoration:underline">func</span> <span style="color:#666;font-weight:bold;font-style:italic">IsValidOrderSignature</span>(address <span style="font-weight:bold;text-decoration:underline">string</span>, message <span style="font-weight:bold;text-decoration:underline">string</span>, sign <span style="font-weight:bold;text-decoration:underline">string</span>) <span style="font-weight:bold;text-decoration:underline">bool</span> {
</span></span><span style="display:flex;"><span>	ethMessage := []<span style="font-weight:bold;font-style:italic">byte</span>(<span style="color:#666;font-style:italic">&#34;\x19Ethereum Signed Message:\n&#34;</span> + strconv.<span style="color:#666;font-weight:bold;font-style:italic">Itoa</span>(<span style="font-weight:bold;font-style:italic">len</span>(message)) + message)
</span></span><span style="display:flex;"><span>	hash := crypto.<span style="color:#666;font-weight:bold;font-style:italic">Keccak256</span>(ethMessage)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	sigBytes := hexutil.<span style="color:#666;font-weight:bold;font-style:italic">MustDecode</span>(sign)
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">if</span> sigBytes[64] == 27 || sigBytes[64] == 28 {
</span></span><span style="display:flex;"><span>		sigBytes[64] -= 27
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	pubKey, err := crypto.<span style="color:#666;font-weight:bold;font-style:italic">SigToPub</span>(hash, sigBytes)
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">if</span> err != <span style="font-weight:bold;text-decoration:underline">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="font-weight:bold;text-decoration:underline">return</span> <span style="font-weight:bold;text-decoration:underline">false</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	recAddress := crypto.<span style="color:#666;font-weight:bold;font-style:italic">PubkeyToAddress</span>(*pubKey)
</span></span><span style="display:flex;"><span>	recStr := strings.<span style="color:#666;font-weight:bold;font-style:italic">ToLower</span>(recAddress.<span style="color:#666;font-weight:bold;font-style:italic">String</span>())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="font-weight:bold;text-decoration:underline">return</span> recStr == strings.<span style="color:#666;font-weight:bold;font-style:italic">ToLower</span>(address)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></description>
</item>
<item>
<title>P2P核心技术:Kad分布式路由表算法</title>
<link>https://weenable.github.io/posts/web3/p2p%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AFkad%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%AF%E7%94%B1%E8%A1%A8%E7%AE%97%E6%B3%95/</link>
<pubDate>Fri, 28 Feb 2025 18:51:48 +0800</pubDate>
<guid>https://weenable.github.io/posts/web3/p2p%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AFkad%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%AF%E7%94%B1%E8%A1%A8%E7%AE%97%E6%B3%95/</guid>
<description><h3 class="heading" id="简介">
 简介
 <a class="anchor" href="#%e7%ae%80%e4%bb%8b">#</a>
</h3>
<p>Kademlia(Kad)是分布式散列表(DHT)算法的一种,是去中心化P2P网络最核心的一种路由寻址技术,可以在无中心服务器(trackerless)的情况下,在网络中快速找到目标节点</p>
<p>早期中心化服务器BtTorrent网络,需要种子服务器来帮助节点之间发现












<figure class="">

 <div>
 <img loading="lazy" alt="image-20252285241628.png" src="https://weenable.github.io/images/P2P%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF%EF%BC%9AKad%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%AF%E7%94%B1%E8%A1%A8%E7%AE%97%E6%B3%95/image-20252285241628.png" >
 </div>

 
</figure>
</p>
<p>实现Kad协议的P2P网络,每个节点维护一个路由表,仅记录离自己最近的一些节点信息,通过迭代查询来发现其他节点</p>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-2025228534197.png" src="https://weenable.github.io/images/P2P%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF%EF%BC%9AKad%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%AF%E7%94%B1%E8%A1%A8%E7%AE%97%E6%B3%95/image-2025228534197.png" >
 </div>

 
</figure>
</p>
<h3 class="heading" id="核心内容">
 核心内容
 <a class="anchor" href="#%e6%a0%b8%e5%bf%83%e5%86%85%e5%ae%b9">#</a>
</h3>
<ul>
<li>Node ID:P2P网络中,节点通过唯一的ID来进行标识,在原始Kad算法中,使用160bit哈希空间来作为Node ID</li>
<li>Node Distance:每个节点保存自己附近的节点信息,是通过计算得到的逻辑距离来判断的(通过把两个节点的Node ID进行XOR运算,结果越小距离越近)</li>
<li>K-Bucket:用一个Bucket来保存与当前节点距离在某个范围内的所有节点列表</li>
<li>Bucket分裂:如果原始Bucket数量不够,需要进行分裂</li>
<li>Routing Table:记录所有Bucket,每个bucket限制最多k个节点,如下图所示</li>
</ul>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252285349459.png" src="https://weenable.github.io/images/P2P%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF%EF%BC%9AKad%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%AF%E7%94%B1%E8%A1%A8%E7%AE%97%E6%B3%95/image-20252285349459.png" >
 </div>

 
</figure>
</p>
<ul>
<li>Update:在节点bootstrap时,需要把连接上的节点更新到自己的routing table中</li>
<li>LookUp:查找节点,找到与目标节点最近的bucket,如果目标节点在bucket中则直接范围,否则往bucket中节点发送查询请求,这些节点继续迭代查询</li>
</ul>
<h3 class="heading" id="详细内容">
 详细内容
 <a class="anchor" href="#%e8%af%a6%e7%bb%86%e5%86%85%e5%ae%b9">#</a>
</h3>
<h5 class="heading" id="1node-id">
 1.Node ID
 <a class="anchor" href="#1node-id">#</a>
</h5>
<p>Kad使用SHA1哈希来计算Node ID,SHA1是一个160bit(20字节)的哈希空间
IPFS使用SHA256哈希来计算Node ID,256bit(32字节)的哈希空间
eth使用SHA3,也是256bit哈希空间</p>
<h5 class="heading" id="2node-distance和xor">
 2.Node Distance和XOR
 <a class="anchor" href="#2node-distance%e5%92%8cxor">#</a>
</h5>
<p>对两个Node ID进行XOR运算,可以得出他们之间的距离
Kad中,根据当前节点和其他节点的Node ID匹配的最多的bit个数来构建一棵二叉树,这里匹配的bit数也叫LCP(longest common prefix),按照LCP来划分子树</p>
<p>











<figure class="">

 <div>
 <img loading="lazy" alt="image-20252285458639.png" src="https://weenable.github.io/images/P2P%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF%EF%BC%9AKad%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%AF%E7%94%B1%E8%A1%A8%E7%AE%97%E6%B3%95/image-20252285458639.png" >
 </div>

 
</figure>
</p>
<p>对于160bit空间的Node ID来说,一共会有160颗子树,也就是160个bucket
Kad要求每个节点知道其各子树的至少一个节点</p></description>
</item>
</channel>
</rss>