DHT, atau jadual cincang teragih, ialah protokol teragih yang digunakan untuk melaksanakan storan teragih dan pengkomputeran teragih. Dalam persekitaran rangkaian peer-to-peer, DHT amat penting kerana ia boleh berfungsi sebagai protokol penghalaan dan fungsi utama untuk mengatur data. Artikel ini akan memperkenalkan cara melaksanakan DHT menggunakan bahasa Golang.
1. Prinsip DHT
Algoritma teras yang digunakan oleh DHT ialah algoritma jadual cincang. DHT memetakan data dan nod masing-masing ke dalam ruang cincang, dan nilai cincang nod dan data menentukan kedudukan mereka dalam ruang cincang. Setiap nod mengekalkan nilai cincangnya sendiri dan nilai cincang nod bersebelahannya, dengan itu membentuk cincin cincang.
Apabila nod bergabung dengan DHT, ia perlu menghubungi nod yang diketahui, mencari kedudukan di mana ia sepatutnya berada pada gelang cincang dan menjadi nod pengganti kedudukan itu. Pada masa ini, nod boleh menerima permintaan daripada nod lain, menyimpan data yang perlu disimpan di lokasinya sendiri, dan pada masa yang sama menghantar nilai hash sendiri dan nilai hash nod pengganti kepada nod yang diketahui. Apabila nod meninggalkan DHT, ia memerlukan nod penggantinya untuk menyambung semula bagi memastikan operasi normal rangkaian DHT.
Kedudukan nod DHT dalam gelang cincang bukan sahaja menentukan kedudukannya dalam rangkaian DHT, tetapi juga menentukan data yang perlu disimpan dan permintaan yang perlu diproses. Sebagai contoh, apabila nod perlu mencari nilai, ia boleh melawati nod yang lebih dekat dengan nilai itu daripada dalam gelang cincang. Nod ini memajukan permintaan langkah demi langkah sehingga nod ditemui yang menyimpan nilai. Begitu juga, apabila nod perlu menyimpan nilai, ia perlu menyimpannya pada nod yang lebih dekat dengan nilai itu daripada dalam gelang cincang.
2. Melaksanakan DHT di Golang
Sangat mudah untuk melaksanakan DHT di Golang. Pertama, kita perlu menggunakan fungsi cincang untuk menukar identiti nod dan data kepada nilai cincang. Golang menyediakan pelbagai fungsi cincang, termasuk MD5, SHA-1, SHA-256, dll. Kita boleh memilih mana-mana daripada mereka.
import (
"crypto/sha1"
)
func hash(rentetan data) []bait {
h := sha1.New() h.Write([]byte(data)) return h.Sum(nil)
}
Seterusnya, kami A jenis nod perlu ditakrifkan untuk menyimpan identiti nod, nilai hash dan nilai hash nod pengganti.
taip struct Nod {
ID string Hash []byte Successor []byte
}
type DHT struct {
Nodes map[string]*Node
}
Struktur DHT mengandungi Jadual pemetaan nod Nod, menyimpan semua nod yang diketahui. Kita boleh menggunakan peta untuk melaksanakan jadual pemetaan ini.
Sebelum melaksanakan algoritma DHT, kita perlu melaksanakan beberapa fungsi tambahan, seperti mencari nod pengganti yang sepadan dengan nilai utama dalam gelang cincang, menambah nod baharu, dsb.
func (dht *DHT) findSuccessor(key []bait) []bait {
for _, node := range dht.Nodes { if bytes.Compare(key, node.Hash) == -1 || bytes.Equal(key, node.Hash) { return node.Hash } } return dht.Nodes[dht.minNode()].Hash
}
func (dht DHT) addNode(nod Nod) ralat {
if _, ok := dht.Nodes[node.ID]; ok { return errors.New("Node already exists") } dht.Nodes[node.ID] = node dht.fixSuccessorList() return nil
}
Dalam fungsi findSuccessor, kami merentasi jadual pemetaan nod Nod untuk mencari nod pengganti yang paling hampir dengan kunci nilai cincang yang diberikan. Apabila kunci kurang daripada atau sama dengan nilai cincang nod atau semua nod telah dilalui, fungsi mengembalikan nod pengganti terdekat. Dalam fungsi addNode, kami mula-mula menyemak sama ada nod sudah wujud dalam jadual pemetaan nod Nod, dan mengembalikan ralat jika wujud. Jika tidak, kami menambah nod baharu pada Nod dan kemudian memanggil fungsi fixSuccessorList untuk melaraskan senarai pengganti nod.
func (dht *DHT) fixSuccessorList() {
ids := make([]string, 0, len(dht.Nodes)) for id := range dht.Nodes { ids = append(ids, id) } sort.Slice(ids, func(i, j int) bool { return bytes.Compare(dht.Nodes[ids[i]].Hash, dht.Nodes[ids[j]].Hash) == -1 }) for i, id := range ids { prev := ids[(i+len(ids)-1)%len(ids)] next := ids[(i+1)%len(ids)] dht.Nodes[id].Successor = dht.Nodes[next].Hash dht.Nodes[id].Predecessor = dht.Nodes[prev].Hash }
}
Dalam fungsi fixSuccessorList, kami mengisih jadual pemetaan nod Nod dan kemudian menetapkan nod Predecessor dan nod pengganti . Nod sebelumnya ialah nod sebelum nod semasa selepas mengisih, dan nod seterusnya ialah nod selepas nod semasa selepas mengisih. Sila ambil perhatian bahawa kami menggunakan operator % untuk memastikan sambungan ke peta nod adalah bulat.
Akhir sekali, kita boleh melaksanakan algoritma DHT. Apabila nod perlu mencari nilai, ia menghantar permintaan kepada nod penggantinya. Jika nod pengganti tidak mempunyai nilai ini, ia memajukan permintaan kepada nod penggantinya. Begitu juga, apabila nod perlu menyimpan nilai, ia akan menyimpan nilai di lokasinya sendiri dan menghantar cincangnya dan cincang nod pengganti ke nod yang diketahui. Nod ini akan memajukan permintaan langkah demi langkah sehingga nod yang menyimpan nilai ditemui.
func (dht *DHT) findValue(rentetan kunci) (rentetan, ralat) {
hash := hash(key) successor := dht.findSuccessor(hash) if bytes.Equal(successor, hash) { return dht.Nodes[string(successor)].Value, nil } addr := dht.getNodeAddr(successor) conn, err := net.Dial("tcp", addr) if err != nil { return "", err } defer conn.Close() request := &FindValueRequest{Key: key} response := &FindValueResponse{} if err := sendRequest(conn, request); err != nil { return "", err } if err := receiveResponse(conn, response); err != nil { return "", err } if len(response.Value) == 0 { return "", errors.New("Key not found") } return response.Value, nil
}
func (dht *DHT) storeValue(kunci, rentetan nilai) ralat {
hash := hash(key) successor := dht.findSuccessor(hash) if bytes.Equal(successor, hash) { dht.Nodes[string(hash)].Value = value return nil } addr := dht.getNodeAddr(successor) conn, err := net.Dial("tcp", addr) if err != nil { return err } defer conn.Close() request := &StoreValueRequest{Key: key, Value: value} response := &StoreValueResponse{} if err := sendRequest(conn, request); err != nil { return err } if err := receiveResponse(conn, response); err != nil { return err } return nil
}
Dalam fungsi findValue, kita mula-mula menggunakan fungsi cincang untuk menukar nilai kunci kepada cincang nilai cincang dan cari nod pengganti yang sepadan dengan nilai cincang. Jika nod pengganti adalah sama dengan nilai cincang, kami telah menemui nilai yang sepadan dan mengembalikannya. Jika tidak, kami menghantar permintaan kepada nod pengganti dan secara rekursif memanggil fungsi yang mengendalikan permintaan itu. Dalam fungsi storeValue, kami menggunakan fungsi cincang untuk menukar nilai kunci kepada nilai cincang dan mencari nod pengganti yang sepadan dengan nilai cincang. Jika nod pengganti adalah sama dengan nilai hash, kami menyimpan nilai pada nod tersebut dan mengembalikannya. Jika tidak, kami menghantar permintaan kepada nod pengganti dan secara rekursif memanggil fungsi yang mengendalikan permintaan itu.
3. Ringkasan
Artikel ini memperkenalkan cara melaksanakan algoritma DHT menggunakan bahasa Golang. DHT ialah protokol teragih berasaskan jadual cincang yang digunakan untuk melaksanakan storan teragih dan pengkomputeran teragih. Artikel ini membolehkan kami memahami dengan lebih baik prinsip dan pelaksanaan protokol ini dengan melaksanakan algoritma DHT yang mudah. DHT digunakan secara meluas dalam banyak bidang, seperti perkongsian fail BitTorrent, pengesahan transaksi mata wang kripto seperti Bitcoin, dsb.
Atas ialah kandungan terperinci Cara membina dht di golang. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!