paint-brush
Web IDE honek zure kodea exekutatzen du hodeian, ordenagailu eramangarria urtu gabearabera@oleksiijko
Historia berria

Web IDE honek zure kodea exekutatzen du hodeian, ordenagailu eramangarria urtu gabe

arabera Oleksii Bondar12m2025/02/21
Read on Terminal Reader

Luzeegia; Irakurri

Proiektua mikrozerbitzuen arkitekturaren printzipioan oinarritzen da, funtzionaltasuna zerbitzu independenteetan banatzeko aukera ematen duena. Osagai bakoitza zeregin oso espezializatu baten arduraduna da, eta sistemaren malgutasuna, eskalagarritasuna eta akatsen tolerantzia bermatzen ditu. Proiektua Go programazio lengoaian oinarritzen da.
featured image - Web IDE honek zure kodea exekutatzen du hodeian, ordenagailu eramangarria urtu gabe
Oleksii Bondar HackerNoon profile picture
0-item
1-item

Hodeiko informatikaren eta mikrozerbitzuen arkitekturaren garapen azkarraren testuinguruan, gero eta beharra handiagoa dago hainbat programazio-lengoaietarako kodea dinamikoki exekutatzeko gaitasuna eskaintzeko, segurtasun, eskalagarritasun eta errendimendu handiko bermearekin. Artikulu honek ingurune isolatu batean kodearen exekuzioa inplementatzen duen proiektu bat deskribatzen du eta WEB IDE moderno baterako aukeratutako irtenbide arkitektonikoaren abantailak eztabaidatzen ditu. Sistema eraikita dago Zoaz , erabilerak gRPC zerbitzuen arteko elkarrekintza eraginkorra lortzeko, Redis mezu-artekari gisa eta Docker exekuzio ingurunea isolatzeko. A WebSocket zerbitzaria emaitzak denbora errealean bistaratzeko erabiltzen da.

Xehetasunez deskribatuko dugu nola egituratzen diren sistemaren osagai nagusiak, nola desberdintzen diren irtenbide alternatiboetatik eta zergatik teknologia hauek aukeratzeak errendimendu eta segurtasun handia lortzea ahalbidetzen duen.


1. Arkitekturaren ikuspegi orokorra eta osagai nagusiak

Proiektua mikrozerbitzuen arkitekturaren printzipioan oinarritzen da, funtzionaltasuna zerbitzu independenteetan banatzeko aukera ematen duena. Osagai bakoitza zeregin oso espezializatu baten arduraduna da, eta sistemaren malgutasuna, eskalagarritasuna eta akatsen tolerantzia bermatzen ditu.


Osagai nagusiak:


  • gRPC zerbitzuen arteko komunikaziorako erabiltzen da. Aproposa da mikrozerbitzuen artean datuak transferitzeko:
    • Protokolo bitarra (Protocol Buffers): datuen transferentzia azkarra eta trinkoa bermatzen du.
    • Idazketa zorrotza: datuak transferitzeko eta prozesatzeko akatsak saihesten laguntzen du.
    • Latentzia baxua: ezinbestekoa da zerbitzuen arteko barne deietarako (adibidez, gRPC zerbitzari baten eta Redis ilara baten artean).
  • WebSocket Server: bezeroarekin bi norabideko komunikazioa eskaintzen du exekuzio emaitzak denbora errealean transmititzeko. Emaitzekin ilara batera harpidetzen da eta datuak bezeroari birbidaltzen dizkio, konpilazio eta exekuzio erregistroen berehalako bistaratzea eskainiz.
  • Langilea: zereginak ilara batetik ateratzen dituen zerbitzu independentea, aldi baterako lan-ingurune bat sortzen duena, kodea balioztatu eta exekutatzen duena Docker edukiontzi isolatu batean, eta, ondoren, exekuzioaren emaitzak ilaran argitaratzen dituena.
  • Redis: mezu-artekari gisa erabiltzen da zereginak gRPC zerbitzaritik Langilera eta emaitzak Langiletik WebSocket zerbitzarira transferitzeko. Redis-en abantailak abiadura handia, Pub/Sub laguntza eta eskalatze erraza dira.
  • Barne moduluak:
    • Konpilatzailea eta Docker Runner: Docker komandoak korronteen erregistroarekin exekutatzeko ardura duen modulua, konpilazio- eta exekuzio-prozesuaren denbora errealeko jarraipena ahalbidetuz.
    • Language Runners: konbinatu logika hainbat hizkuntzatarako (C, C++, C#, Python, JavaScript, TypeScript) kodea balioztatzeko, konpilatzeko eta exekutatzeko. Korrikalari bakoitzak interfaze bakarra ezartzen du, eta horrek hizkuntza berrietarako funtzionalitateen hedapena errazten du.


Beheko diagramak bezerotik langile prozesura eta atzera gRPC, Redis eta WebSocket erabiliz datu-fluxua erakusten du.


2. Teknologiak eta aukeraketaren arrazoia

Zoaz

Go-ren abantailak:

  • Errendimendua eta eskalagarritasuna: Go-k exekuzio abiadura handia du, eta hori bereziki garrantzitsua da eskaera paralelo ugari kudeatzeko.

  • Aldiberekotasun-laguntza integratua: goroutine eta kanalen mekanismoek osagaien arteko elkarrekintza asinkronoa ezartzea ahalbidetzen dute hari anitzeko eredu konplexurik gabe.


gRPC

gRPCren abantailak:

  • Datuen transferentzia eraginkorra: transferentzia protokolo bitarrari esker (Protocol Buffers), gRPC-k latentzia txikia eta sareko karga baxua eskaintzen ditu.
  • Idazketa sendoa: mikrozerbitzuen arteko datuen interpretazio okerrekin lotutako erroreen kopurua murrizten da.
  • Norabide biko erreprodukziorako laguntza: hau bereziki erabilgarria da erregistroak eta exekuzio emaitzak denbora errealean trukatzeko.

Konparazioa: REST APIa ez bezala, gRPC-k zerbitzuen arteko komunikazio eraginkorragoa eta fidagarriagoa eskaintzen du, eta hori funtsezkoa da sistema oso aldiberekoentzat.


Redis

Zergatik Redis?

  • Errendimendu handia: Redis-ek segundoko eragiketa ugari kudeatu ditzake, eta horrek zeregin eta emaitzen ilaretarako aproposa da.

  • Pub/Sub eta Zerrenden laguntza: ilarak eta harpidetza-mekanismoak ezartzearen sinpletasunak errazten du zerbitzuen arteko interakzio asinkronoak antolatzea.

  • Beste mezu-artekari batzuekin alderatzea: RabbitMQ edo Kafka ez bezala, Redis-ek konfigurazio gutxiago behar du eta denbora errealeko sistemetarako nahikoa errendimendu eskaintzen du.


Docker

Dockerren rola:

  • Ingurunearen isolamendua: Docker edukiorrek kodea guztiz isolatu batean exekutatzeko aukera ematen dute, eta horrek exekuzioaren segurtasuna areagotzen du eta sistema nagusiarekin gatazkak izateko arriskua murrizten du.

  • Kudeatzeko gaitasuna eta koherentzia: Docker erabiltzeak ingurune bera eskaintzen du kodea konpilatzeko eta exekutatzeko, ostalari-sistema edozein dela ere.

  • Konparazioa: Ostalari zuzenean kodea exekutatzeak segurtasun arriskua sor dezake eta menpekotasun-gatazkak sor ditzake, Docker-ek arazo horiek konpontzeko aukera ematen dizu.


WebSocket

  • Denbora errealean: bezeroarekiko konexio iraunkorrak datuak (erregistroak, exekuzio-emaitzak) berehala transferitzeko aukera ematen du.
  • Erabiltzaileen esperientzia hobetua: WebSocket-ekin, IDEak dinamikoki bistaratu ditzake kodearen emaitzak.


3. Mikrozerbitzuen arkitekturaren abantailak

Proiektu honek mikrozerbitzuen ikuspegia erabiltzen du, eta horrek abantaila esanguratsu batzuk ditu:


  • Eskalatze independentea: Zerbitzu bakoitza (gRPC zerbitzaria, Worker, WebSocket zerbitzaria, Redis) bereizita eskalatu daiteke kargaren arabera. Horri esker, baliabideen erabilera eraginkorra eta eskaera kopuruaren hazkundera azkar egokitzea ahalbidetzen da.
  • Akatsen tolerantzia: sistema modulu independenteetan banatzeak esan nahi du mikrozerbitzu baten hutsegiteak ez duela sistema osoaren hutsegiterik ekartzen. Horrek egonkortasun orokorra areagotzen du eta akatsetatik berreskuratzea errazten du.
  • Garapenaren eta hedapenaren malgutasuna: mikrozerbitzuak modu independentean garatzen eta zabaltzen dira, eta horrek funtzio eta eguneratze berriak sartzea errazten du. Horrek, gainera, zerbitzu zehatz bakoitzerako teknologiarik egokienak erabiltzeko aukera ematen du.
  • Integratzeko erraztasuna: argi definitutako interfazeek (adibidez, gRPC bidez) zerbitzu berriak konektatzea errazten dute lehendik dagoen arkitekturan aldaketa handirik gabe.
  • Isolamendua eta segurtasuna: mikrozerbitzu bakoitza bere edukiontzian exekutatu daiteke, eta horrek kode ez-segurua exekutatzeko arriskuak gutxitzen ditu eta babes-geruza gehigarria eskaintzen du.


4. Planteamendu arkitektonikoen analisi konparatiboa

Urrutiko kodea exekutatzeko WEB IDE modernoak eraikitzean, hainbat irtenbide arkitektoniko alderatu ohi dira. Azter ditzagun bi ikuspegi:


A ikuspegia: mikrozerbitzuen arkitektura (gRPC + Redis + Docker)


  • Latentzia: 40 ms
  • Errendimendua: 90 unitate
  • Segurtasuna: 85 unitate
  • Eskalagarritasuna: 90 unitate


Ezaugarriak:
Ikuspegi honek zerbitzuen arteko komunikazio azkarra eta fidagarria eskaintzen du, kodearen exekuzioaren isolamendu handia eta eskalatze malgua eskaintzen du edukiontziaren ondorioz. Ezin hobea da WEB IDE modernoetarako, non sentikortasuna eta segurtasuna garrantzitsuak diren.


B Planteamendua: Arkitektura Monolitiko Tradizionala (HTTP REST + Exekuzio Zentralizatua)


  • Latentzia: 70 ms
  • Errendimendua: 65 unitate
  • Segurtasuna: 60 unitate
  • Eskalagarritasuna: 70 unitate


Ezaugarriak:
Irtenbide monolitikoak, sarritan web IDEen lehen bertsioetan erabiliak, HTTP REST eta kode zentralizatuan exekutatzeko oinarritzen dira. Sistema horiek eskalatzeko arazoak, latentzia handiagoa eta segurtasuna bermatzeko zailtasunak dituzte beste norbaiten kodea exekutatzean.


Oharra: WEB IDE garapenaren testuinguru modernoan, HTTP REST eta exekuzio zentralizatuaren ikuspegia mikrozerbitzuen arkitekturaren abantailen aldean txikiagoa da, ez baitu beharrezko malgutasuna eta eskalagarritasuna ematen.


Metriko konparatiboak bistaratzea

Grafikoak argi erakusten du mikrozerbitzuen arkitekturak (A Planteamendua) latentzia txikiagoa, errendimendu handiagoa, segurtasun eta eskalagarritasun hobea eskaintzen duela soluzio monolitikoarekin alderatuta (B Planteamendua).


5. Docker arkitektura: isolamendua eta eskalagarritasuna

Sistemaren segurtasunaren eta egonkortasunaren funtsezko elementuetako bat Docker-en erabilera da. Gure soluzioan, zerbitzu guztiak edukiontzi bereizietan zabaltzen dira, eta horrek bermatzen du:


  • Exekuzio-ingurunearen isolamendua: Zerbitzu bakoitza (gRPC zerbitzaria, Worker, WebSocket zerbitzaria) eta mezu-artekaria (Redis) bere edukiontzian exekutatzen da, eta horrek sistema nagusian kode seguruak izateko arriskua gutxitzen du. Aldi berean, erabiltzaileak arakatzailean exekutatzen duen kodea (adibidez, WEB IDEaren bidez) zeregin bakoitzerako Docker edukiontzi batean sortzen eta exekutatzen da. Ikuspegi honek ziurtatzen du potentzialki ez-seguruak edo akatsak diren kodeak ezin duela eraginik azpiegitura nagusiaren funtzionamenduan.
  • Ingurunearen koherentzia: Docker erabiltzeak ezarpenak garapen, proba eta produkzio inguruneetan berdinak izaten jarraitzen duela ziurtatzen du, eta horrek asko errazten du arazketa eta kodearen exekuzioaren aurreikuspena ziurtatzen du.
  • Eskalagarritasun malgutasuna: osagai bakoitza modu independentean eskalatu daiteke, eta horrek karga aldakorretara eraginkortasunez egokitzeko aukera ematen du. Adibidez, eskaera kopurua handitzen den heinean, Worker edukiontzi gehigarriak abiarazi ditzakezu, eta horietako bakoitzak edukiontzi bereiziak sortuko ditu erabiltzaile-kodea exekutatzeko.

Eskema honetan, Worker-ek Redis-en zereginak jasotzeaz gain, edukiontzi bereizia ere sortzen du (Edukiontzia: Code Execution) eskaera bakoitzeko erabiltzaile-kodea modu isolatuan exekutatzeko.


6. Kodearen atal txikiak

Jarraian, sistema nola erakusten duen kodearen atal nagusien bertsio txikitua dago:

  1. Korrikalarien erregistro globala erabiliz zein hizkuntza exekutatu behar den zehazten du.
  2. Docker edukiontzi bat abiarazten du erabiltzailearen kodea RunInDockerStreaming funtzioa erabiliz exekutatzeko.



1. Hizkuntza antzematea korrikalarien erregistroaren bidez

Sistemak erregistro global bat erabiltzen du, non hizkuntza bakoitzak bere korrikalaria duen. Honek hizkuntza berrietarako laguntza erraz gehitzeko aukera ematen du, nahikoa da korrikalariaren interfazea ezartzea eta erregistratzea:


 package languages import ( "errors" "sync" ) var ( registry = make(map[string]Runner) registryMu sync.RWMutex ) type Runner interface { Validate(projectDir string) error Compile(ctx context.Context, projectDir string) (<-chan string, error) Run(ctx context.Context, projectDir string) (<-chan string, error) } func Register(language string, runner Runner) { registryMu.Lock() defer registryMu.Unlock() registry[language] = runner } func GetRunner(language string) (Runner, error) { registryMu.RLock() defer registryMu.RUnlock() if runner, exists := registry[language]; exists { return runner, nil } return nil, errors.New("unsupported language") }


Hizkuntza berri bat erregistratzeko adibidea:


 func init() { languages.Register("python", NewGenericRunner("python")) languages.Register("javascript", NewGenericRunner("javascript")) }


Horrela, eskaera bat jasotzean, sistemak dei egiten du:


 runner, err := languages.GetRunner(req.Language)


eta dagokion korrikalaria jasotzen du kodea exekutatzeko.


2. Docker edukiontzi bat abiarazi kodea exekutatzeko

Erabiltzaile-kode eskaera bakoitzerako, Docker edukiontzi bereizi bat sortzen da. Hau korrikalari metodoen barruan egiten da (adibidez, Run-en). Edukiontzia exekutatzeko logika nagusia RunInDockerStreaming funtzioan dago:


 package compiler import ( "bufio" "fmt" "io" "log" "os/exec" "time" ) func RunInDockerStreaming(image, dir, cmdStr string, logCh chan < -string) error { timeout: = 50 * time.Second cmd: = exec.Command("docker", "run", "--memory=256m", "--cpus=0.5", "--network=none", "-v", fmt.Sprintf("%s:/app", dir), "-w", "/app", image, "sh", "-c", cmdStr) cmd.Stdin = nil stdoutPipe, err: = cmd.StdoutPipe() if err != nil { return fmt.Errorf("error getting stdout: %v", err) } stderrPipe, err: = cmd.StderrPipe() if err != nil { return fmt.Errorf("error getting stderr: %v", err) } if err: = cmd.Start();err != nil { return fmt.Errorf("Error starting command: %v", err) } // Reading logs from the container go func() { reader: = bufio.NewReader(io.MultiReader(stdoutPipe, stderrPipe)) for { line, isPrefix, err: = reader.ReadLine() if err != nil { if err != io.EOF { logCh < -fmt.Sprintf("[Error reading logs: %v]", err) } break } msg: = string(line) for isPrefix { more, morePrefix, err: = reader.ReadLine() if err != nil { break } msg += string(more) isPrefix = morePrefix } logCh < -msg } close(logCh) }() doneCh: = make(chan error, 1) go func() { doneCh < -cmd.Wait() }() select { case err: = < -doneCh: return err case <-time.After(timeout): if cmd.Process != nil { cmd.Process.Kill() } return fmt.Errorf("Execution timed out") } }


Funtzio honek docker run komandoa sortzen du, non:


  • irudia hizkuntza jakin baterako hautatutako Docker-eko irudia da (korrikalariaren konfigurazioak definituta).
  • dir eskaera honetarako sortutako kodea duen direktorioa da.
  • cmdStr kodea konpilatzeko edo exekutatzeko komandoa da.


Horrela, korrikalariaren Run metodoa deitzean, honako hau gertatzen da:


  • RunInDockerStreaming funtzioak kodea exekutatzen den Docker edukiontzia abiarazten du.
  • Exekuzio-erregistroak logCh kanalera igortzen dira, exekuzio-prozesuari buruzko informazioa denbora errealean transmititzeko aukera ematen duena.


3. Exekuzio prozesu integratua

Kode exekutatzeko logika nagusiaren zati minimoa (executor.ExecuteCode):


 func ExecuteCode(ctx context.Context, req CodeRequest, logCh chan string) CodeResponse { // Create a temporary directory and write files projectDir, err: = util.CreateTempProjectDir() if err != nil { return CodeResponse { "", fmt.Sprintf("Error: %v", err) } } defer os.RemoveAll(projectDir) for fileName, content: = range req.Files { util.WriteFileRecursive(filepath.Join(projectDir, fileName), [] byte(content)) } // Get a runner for the selected language runner, err: = languages.GetRunner(req.Language) if err != nil { return CodeResponse { "", err.Error() } } if err: = runner.Validate(projectDir); err != nil { return CodeResponse { "", fmt.Sprintf("Validation error: %v", err) } } // Compile (if needed) and run code in Docker container compileCh, _: = runner.Compile(ctx, projectDir) for msg: = range compileCh { logCh < -"[Compilation]: " + msg } runCh, _: = runner.Run(ctx, projectDir) var output string for msg: = range runCh​​ { logCh < -"[Run]: " + msg output += msg + "\n" } return CodeResponse { Output: output } }


Adibide minimo honetan:


  • Hizkuntza detektatzeko languages.GetRunner(req.Language) dei baten bidez egiten da, eta horrek hizkuntza berri baterako laguntza erraz gehitzeko aukera ematen du.
  • Docker edukiontziaren abiarazte Konpilatu/Exekutatu metodoetan inplementatzen da, RunInDockerStreaming erabiltzen dutenak kodea isolatuta exekutatzeko.


Gako-zati hauek sistemak hedagarritasuna (hizkuntza berriak gehitzea erraza) onartzen duen erakusten dute eta isolamendua eskaintzen du eskaera bakoitzerako Docker edukiontzi bat sortuz. Ikuspegi honek plataformaren segurtasuna, egonkortasuna eta eskalagarritasuna hobetzen ditu, eta hori bereziki garrantzitsua da WEB IDE modernoentzat.

7. Ondorioa

Artikulu honek gRPC + Redis + Docker pila erabiliz mikrozerbitzuen arkitekturan eraikitako urruneko kodea exekutatzeko plataforma bat aztertzen du. Ikuspegi honek aukera ematen dizu:


  • Murriztu latentzia eta bermatu errendimendu handia zerbitzuen arteko komunikazio eraginkorra dela eta.
  • Bermatu segurtasuna kodearen exekuzioa Docker edukiontzi bereizietan isolatuta, non erabiltzaileen eskaera bakoitzerako edukiontzi bereizia sortzen den.
  • Sistema malgutasunez eskalatu mikrozerbitzuen eskalatze independenteari esker.
  • Eman emaitzak denbora errealean WebSocket bidez, eta hori bereziki garrantzitsua da WEB IDE modernoentzat.


Analisi konparatibo batek erakusten du mikrozerbitzuen arkitekturak soluzio monolitiko tradizionalak nabarmen gainditzen dituela funtsezko metrika guztietan. Ikuspegi honen abantailak datu errealek berresten dituzte, eta horrek irtenbide erakargarria bihurtzen du errendimendu handiko eta akatsekiko tolerantzia duten sistemak sortzeko.



Egilea: Oleksii Bondar
Data: 2025-02-07