Plokiahela arendamine tööstusele Go kasutades. 1. osa

Olen nüüdseks neli kuud tegelenud projektiga "Andmekaitse- ja haldustööriistade arendamine valitsus- ja tööstussektoris, mis põhinevad plokiahelal."
Nüüd tahaksin teile rääkida, kuidas ma selle projektiga alustasin, ja nüüd kirjeldan üksikasjalikult programmi koodi.

Plokiahela arendamine tööstusele Go kasutades. 1. osa

See on esimene artikkel artiklite sarjas. Siin kirjeldan serverit ja protokolli. Tegelikult saab lugeja nendest plokiahela elementidest isegi oma versioonid kirjutada.

Ja siin on teine ​​osa — plokiahela ja tehingute andmestruktuuride kohta, samuti paketi kohta, mis teostab interaktsiooni andmebaasiga.

Eelmisel aastal tuli neil Digital Breakthrough häkatonil idee teha hajutatud pearaamatutehnoloogia abil tööstusele ja digimajandusele kasulik süsteem, arenduseks anti välja ka Innovatsiooniabi fond (peaksin kirjutama eraldi artikkel toetuse kohta neile, kes alles alustavad idufirmasid ) ja nüüd järjekorras.

Arendus toimub Go keeles ja andmebaas, kuhu plokid salvestatakse, on LevelDB.
Peamised osad on protokoll, server (mis jookseb TCP-d ja WebSocketit – esimene plokiahela sünkroonimiseks, teine ​​klientide ühendamiseks, tehingute ja käskude saatmiseks näiteks JavaScriptist.

Nagu öeldud, on seda plokiahelat vaja eelkõige tarnijate ja klientide või mõlema ühes isikus kaubavahetuse automatiseerimiseks ja kaitsmiseks. Need inimesed ei kiirusta üksteist usaldama. Kuid ülesandeks pole mitte ainult sisseehitatud kalkulaatoriga "tšekiraamatu" loomine, vaid süsteem, mis automatiseerib enamiku toote elutsükliga töötamisel tekkivatest rutiinsetest ülesannetest. Selle asja eest vastutav baitkood, nagu plokiahelate puhul tavaks, salvestatakse tehingute sisenditesse ja väljunditesse (tehingud ise salvestatakse plokkidesse, LevelDB plokid on eelkodeeritud GOB-vormingus). Kõigepealt räägime protokollist ja serverist (aka node).

Protokoll ei ole keeruline, selle mõte on lülituda teatud andmete, tavaliselt ploki või tehingu, laadimise režiimile vastuseks spetsiaalsele käsureale ja seda on vaja ka laoseisu vahetamiseks, et sõlm teaks, kes seda teeb. on ühendatud ja kuidas neil on asju ajada (sünkroonimisseansi jaoks ühendatud sõlme nimetatakse ka "naabersõlmedeks", kuna nende IP on teada ja nende olekuandmed salvestatakse mällu).

Go programmeerijate mõistes nimetatakse kaustu (katalooge, nagu Linux neid nimetab) pakettideks, seega kirjutavad nad iga selle kataloogi Go koodiga faili algusesse paketi kausta_nimi_kus_see_fail asub. Vastasel juhul ei saa te paketti kompilaatorile edastada. Noh, see pole saladus neile, kes seda keelt oskavad. Need on paketid:

  • Võrguside (server, klient, protokoll)
  • Salvestatud ja edastatavate andmete struktuurid (plokk, tehing)
  • Andmebaas (plokiahel)
  • Konsensus
  • Virnastatud virtuaalmasin (xvm)
  • Abi (krüpto, tüübid) on selleks korraks kõik.

Siin on link githubile

See on hariv versioon, sellel puudub protsessidevaheline interaktsioon ja mitmed eksperimentaalsed komponendid, kuid struktuur vastab sellele, millel arendust teostatakse. Kui teil on kommentaarides midagi soovitada, siis võtan seda hea meelega edasi arendamisel arvesse. Ja nüüd selgituseks serveri ja protokoll.

Vaatame kõigepealt serverit.

Serveri alamprogramm toimib andmeserverina, mis töötab TCP-protokolli peal, kasutades protokollipaketi andmestruktuure.

Rutiin kasutab järgmisi pakette: server, protokoll, liigid. Pakendis endas tcp_server.go sisaldab andmestruktuuri Serveeri.

type Serve struct {
	Port string
	BufSize int
	ST *types.Settings
}

See võib aktsepteerida järgmisi parameetreid:

  • Võrguport, mille kaudu andmeid vahetatakse
  • Serveri konfiguratsioonifail JSON-vormingus
  • Silumisrežiimis töötamise märk (privaatne plokiahel)

Edusammud:

  • Loeb konfiguratsiooni JSON-failist
  • Silumisrežiimi lipp on kontrollitud: kui see on määratud, siis võrgu sünkroonimise ajakava ei käivitu ja plokiahelat ei laadita
  • Konfiguratsiooniandmete struktuuri lähtestamine ja serveri käivitamine

server

  • Teostab TCP-serveri käivitamise ja võrgu interaktsiooni vastavalt protokollile.
  • Sellel on serveerimisandmestruktuur, mis koosneb pordi numbrist, puhvri suurusest ja struktuuri osutajast tüübid. Seaded
  • Käivita meetod käivitab võrgu interaktsiooni (antud pordi sissetulevate ühenduste kuulamine, uue ühenduse vastuvõtmisel viiakse selle töötlemine üle privaatkäepideme meetodile uues lõimes)
  • В käepide ühenduse andmed loetakse puhvrisse, teisendatakse stringi esituseks ja edastatakse protokoll.Valik
  • protokoll.Valik naaseb kaasa või põhjustab tõrke. kaasa seejärel üle kantud protokoll.Tõlgendadamis naaseb intrpr - tüüpi objekt InterpreteDatavõi põhjustab valiku tulemuse töötlemisel vea
  • Seejärel käivitatakse lüliti intrpr.Commands[0] mis kontrollib ühte järgmistest: tulemus, arv, viga ja seal on sektsioon vaikimisi
  • Jaotises kaasa lüliti leitakse väärtuse järgi intrpr.Commands[1] mis kontrollib väärtusi puhverpikkus и versioon (igal juhul kutsutakse välja vastav funktsioon)

Funktsioonid GetVersion и Puhvri pikkus on failis srvlib.go serveripakett

GetVersion(conn net.Conn, version string)

see lihtsalt prindib konsooli ja saadab parameetris edastatud versiooni kliendile:

conn.Write([]byte("result:" + version))

.
Funktsioon

BufferLength(conn net.Conn, intrpr *protocol.InterpreteData)

laadib ploki, tehingu või muud spetsiifilised andmed järgmiselt:

  • Prindib konsooli protokollis määratud andmete tüübi, mida tuleb aktsepteerida:
    fmt.Println("DataType:", intrpr.Commands[2])
  • Loeb väärtust intrpr.Keha numbrilisele muutujale buf_len
  • Loob puhvri newbuf määratud suurus:
    make([]byte, buf_len)
  • Saadab ok vastuse:
    conn.Write([]byte("result:ok"))
  • Täidab lugemisvoo puhvri täielikult:
    io.ReadFull(conn, newbuf)

    .

  • Prindib puhvri sisu konsooli
    fmt.Println(string(newbuf))

    ja loetud baitide arv

    fmt.Println("Bytes length:", n)
  • Saadab ok vastuse:
    conn.Write([]byte("result:ok"))

Serveripaketi meetodid on konfigureeritud töötlema vastuvõetud andmeid, kasutades paketi funktsioone protokoll.

Protokoll

Protokoll on vahend, mis esindab andmeid võrguvahetuses.

Valik(string) (string, viga) teostab serveri poolt vastuvõetud andmete esmast töötlemist, võtab sisendiks vastu andmete stringi ja tagastab Tõlk:

  • Sisendstring jagatakse pea ja keha abil ReqParseN2(str)
  • pea jagatakse elementideks ja asetatakse käsuviiluks, kasutades ReqParseHead(head)
  • В lüliti (käsud[0]) vali vastuvõetud käsk (cmd, võti, aadress või sektsioon käivitub vaikimisi)
  • cmd-s on kontrollitud 2 käsku lüliti(käsud[1]) — pikkus и saada versioon.
  • pikkus kontrollib sisestatud andmetüüpi käsud[2] ja salvestab selle sisse andmetüüp
  • Kontrollib seda keha sisaldab stringi väärtust
    len(body) < 1
  • Tagastab vastuse stringi:
    "result:bufferlength:" + datatype + "/" + body
  • saada versioon tagastab stringi
    return "result:version/auto"

Tõlk

Sisaldab InterpreteData struktuuri ja teostab tagastatud andmete sekundaarset töötlemist Valik stringid ja objektide moodustamine InterpreteData.

type InterpreteData struct {
	Head string
	Commands []string
	Body string
	IsErr bool
	ErrCode int 
	ErrMessage string
}

Funktsioon

Interprete(str string) (*InterpreteData, error)

võtab stringi vastu kaasa ning loob ja tagastab viite objektile InterpreteData.

Edusammud:

  • Samamoodi Valik pea ja keha ekstraheeritakse kasutades ReqParseN2(str)
  • pea jagatakse elementideks kasutades ReqParseHead(pea)
  • Objekt initsialiseeritakse InterpreteData ja sellele tagastatakse osuti:

res := &InterpreteData{
	Head: head,
	Commands: commands,
	Body: body,
}
return res, nil

Seda objekti kasutatakse server.go pakett peamine.

klient

Kliendipakett sisaldab funktsioone TCPConnect и TCPResponseData.

Funktsioon

TCPConnect(s *types.Settings, data []byte, payload []byte)

töötab järgmiselt:

  • Ühendus luuakse läbitud sätete objektis määratud ühendusega
    net.Dial("tcp", s.Host + ":" + s.Port)
  • Andmeparameetris edastatud andmed edastatakse:
    conn.Write(data)
  • Vastus loetakse
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    ja prinditakse konsoolile

    fmt.Println(string(resp[:n]))
  • Kui üle antakse kasulik koormus siis annab selle edasi
    conn.Write(payload)

    ja loeb ka serveri vastuse, printides selle konsooli

Funktsioon

 TCPResponseData(conn net.Conn, bufsiz int) ([]byte, int, error)

loob määratud suurusega puhvri, loeb seal serveri vastuse ja tagastab selle puhvri ja loetud baitide arvu ning veaobjekti.

Kliendi alamprogramm

Teenib käskude saatmiseks sõlmeserveritele, samuti lühikese statistika ja testimise hankimiseks.

Saab aktsepteerida järgmisi parameetreid: JSON-vormingus konfiguratsioonifail, stringina serverisse saadetavad andmed, kasulikku koormusse saadetava faili tee, sõlmede ajakava emuleerimise lipp, numbrilise väärtusena edastatavate andmete tüüp.

  • Konfiguratsiooni hankimine
    st := types.ParseConfig(*config)
  • Kui emu lipp on möödas, käivitub sheduler
  • Kui faili teed näitav lipp f on esitatud, laadime selle andmed sisse fdb ja sisu saadetakse serverisse
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • Kui faili pole määratud, saadetakse lipu andmed lihtsalt ära -d:
    client.TCPConnect(st, []byte(*data), nil)

Kõik see on lihtsustatud esitus, mis näitab protokolli struktuuri. Arenduse käigus lisatakse selle struktuuri vajalik funktsionaalsus.

Teises osas räägin plokkide ja tehingute andmestruktuuridest, 3. osas WebSocket serverist JavaScriptist ühenduse loomiseks, 4. osas vaatan sünkroonimisplaanijat, seejärel pinu masinat, mis töötleb baitkoodi sisenditest ja väljunditest, krüptograafiat ja basseinid väljundite jaoks.

Allikas: www.habr.com

Lisa kommentaar