Go ile WebAssembly Uygulamaları Geliştirme: Kapsamlı Rehber
WebAssembly (WASM), tarayıcılarda native’e yakın hızda yüksek performanslı kod çalıştırmaya olanak tanıyarak web geliştirmeyi devrim niteliğinde değiştirdi. Go, mükemmel araçları ve sade sözdizimi ile WASM uygulamaları geliştirmek için ideal bir dildir. Bu kapsamlı rehberde, Go kullanarak WebAssembly uygulamalarını nasıl oluşturacağımızı, optimize edeceğimizi ve dağıtacağımızı keşfedeceğiz.
TL;DR
- WebAssembly: Tarayıcılar için binary instruction formatı, native’e yakın performans sağlar
- Go + WASM: Go kodunu tarayıcıda çalıştırmak için WASM’a derle
- Temel Avantajlar: Performans, kod yeniden kullanımı, tip güvenliği ve modern araçlar
- Kullanım Senaryoları: Görüntü işleme, oyunlar, veri görselleştirme, kriptografi ve daha fazlası
- Production Ready: Optimizasyon teknikleri ile tam örnekler
1. WebAssembly Nedir?
WebAssembly (WASM), yüksek seviyeli diller için taşınabilir bir derleme hedefi olarak tasarlanmış binary instruction formatıdır. Go, Rust, C++ ve diğer dillerde yazılmış kodların web tarayıcılarında native’e yakın performansta çalışmasını sağlar.
1.1 Neden WebAssembly?
Geleneksel web uygulamaları JavaScript’e dayanır, ancak JavaScript, güçlü olmasına rağmen, hesaplama yoğun görevler için performans sınırlamalarına sahiptir. WebAssembly bunu şu şekilde ele alır:
- Native’e yakın performans: Native kod hızlarına yakın çalışır
- Dil bağımsız: Tercih ettiğiniz dilde yazın (Go, Rust, C++, vb.)
- Güvenlik: Sandbox ortamında çalışır
- Taşınabilirlik: Farklı platformlar ve tarayıcılarda çalışır
- Küçük binary boyutu: Verimli kod temsili
1.2 WebAssembly vs JavaScript
WebAssembly ne zaman kullanılmalı:
- CPU yoğun hesaplamalar (görüntü işleme, kriptografi, veri analizi)
- Oyunlar ve grafik renderlama
- Gerçek zamanlı ses/video işleme
- Bilimsel hesaplama ve simülasyonlar
- Mevcut Go uygulamalarından kod yeniden kullanımı
JavaScript ne zaman yeterli:
- Basit DOM manipülasyonu
- API çağrıları ve veri çekme
- UI etkileşimleri
- Hafif hesaplamalar
2. Go ve WebAssembly: Mükemmel Uyum
Go, Go 1.11’den beri WebAssembly derlemesi için mükemmel destek sunar. Go ekibi, Go kodunu WASM’a derlemeyi ve JavaScript ile etkileşime girmeyi oldukça basit hale getirmiştir.
2.1 Go WASM Mimarisi
2.2 Go WASM’in Temel Özellikleri
- Basit derleme: Sadece
GOOS=js GOARCH=wasm ayarlayın
- JavaScript interop: Go ve JavaScript arasında kolay iletişim
- Goroutines: Eşzamanlılık desteği (tarayıcı bağlamında sınırlı olsa da)
- Standard library: Go standard library’nin çoğu WASM’da çalışır
- Tip güvenliği: Derleme zamanı tip kontrolü
2.3 Tarayıcı Uyumluluğu
| Tarayıcı |
Versiyon |
WASM Desteği |
Notlar |
| Chrome |
57+ |
✅ Tam |
En iyi performans |
| Firefox |
52+ |
✅ Tam |
Mükemmel debug araçları |
| Safari |
11+ |
✅ Tam |
İyi performans |
| Edge |
16+ |
✅ Tam |
Chromium tabanlı |
| Opera |
44+ |
✅ Tam |
Chromium tabanlı |
| IE 11 |
- |
❌ Hayır |
Desteklenmiyor |
Mobil Tarayıcı Desteği:
- iOS Safari: iOS 11+
- Chrome Android: 57+
- Samsung Internet: 7.2+
2.4 Bundle Boyutu Karşılaştırması
| Derleyici |
Tipik Boyut |
Kullanım Senaryosu |
| Standard Go |
2-3 MB |
Tam standard library, büyük uygulamalar |
| TinyGo |
100-500 KB |
Embedded, IoT, boyut kritik uygulamalar |
| Rust |
50-200 KB |
Maksimum performans, minimal runtime |
| C/C++ |
50-300 KB |
Legacy kod, performans kritik |
Not: Gerçek boyutlar kod karmaşıklığına ve bağımlılıklara göre değişir.
3. İlk Go WASM Projenizi Kurma
Go ile basit bir “Hello World” WebAssembly uygulaması oluşturalım.
3.1 Proje Yapısı
1
2
3
4
5
|
go-wasm-example/
├── main.go
├── wasm_exec.js
├── index.html
└── go.mod
|
3.2 Temel Örnek: Hello World
main.go:
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
|
package main
import (
"fmt"
"syscall/js"
)
func main() {
// Programın çalışmaya devam etmesi için bir channel oluştur
c := make(chan struct{}, 0)
// JavaScript'ten çağrılacak bir fonksiyon kaydet
js.Global().Set("greet", js.FuncOf(greet))
fmt.Println("Go WebAssembly initialized")
// Programı canlı tut
<-c
}
// greet, JavaScript'ten çağrılabilen bir fonksiyondur
func greet(this js.Value, args []js.Value) interface{} {
name := "World"
if len(args) > 0 {
name = args[0].String()
}
message := fmt.Sprintf("Hello, %s! From Go WebAssembly", name)
js.Global().Get("console").Call("log", message)
return message
}
|
3.3 WebAssembly’ye Derleme
1
2
3
4
5
6
7
8
9
|
# Hedef işletim sistemi ve mimariyi ayarla
export GOOS=js
export GOARCH=wasm
# WASM binary'sini derle
go build -o main.wasm main.go
# JavaScript destek dosyasını kopyala
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
|
3.4 HTML Loader
index.html:
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
|
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Go WebAssembly Example</title>
</head>
<body>
<h1>Go WebAssembly Demo</h1>
<button onclick="testGreet()">Click Me!</button>
<div id="output"></div>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject)
.then(result => {
go.run(result.instance);
});
function testGreet() {
const message = greet("Developer");
document.getElementById("output").innerHTML = message;
}
</script>
</body>
</html>
|
3.5 Uygulamayı Çalıştırma
1
2
3
4
5
6
|
# Dosyaları basit bir HTTP sunucusu ile servis et
# Python 3
python3 -m http.server 8080
# Veya Node.js kullanarak
npx serve .
|
Tarayıcınızda http://localhost:8080 adresini açarak uygulamanın çalıştığını görebilirsiniz!
4. Gelişmiş Go WASM Desenleri
4.1 Go’dan DOM Manipülasyonu
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
|
package main
import (
"syscall/js"
)
func main() {
c := make(chan struct{}, 0)
// Bir buton elementi oluştur
document := js.Global().Get("document")
body := document.Get("body")
button := document.Call("createElement", "button")
button.Set("innerHTML", "Click Me!")
button.Set("onclick", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
alert := js.Global().Get("alert")
alert.Invoke("Button clicked from Go!")
return nil
}))
body.Call("appendChild", button)
<-c
}
|
4.2 Go’dan JavaScript Fonksiyonlarını Çağırma
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package main
import (
"syscall/js"
)
func main() {
c := make(chan struct{}, 0)
// JavaScript'in Math.random() fonksiyonunu çağır
math := js.Global().Get("Math")
random := math.Get("random")
result := random.Invoke()
js.Global().Get("console").Call("log", "Random number:", result.Float())
// Özel bir JavaScript fonksiyonunu çağır
js.Global().Call("myJavaScriptFunction", "Hello from Go!")
<-c
}
|
4.3 Karmaşık Veri Yapılarını Geçirme
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
|
package main
import (
"encoding/json"
"syscall/js"
)
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
func main() {
c := make(chan struct{}, 0)
js.Global().Set("createUser", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
user := User{
Name: args[0].String(),
Email: args[1].String(),
Age: args[2].Int(),
}
jsonData, _ := json.Marshal(user)
js.Global().Get("console").Call("log", string(jsonData))
return string(jsonData)
}))
<-c
}
|
4.4 Async İşlemler için Channel Kullanımı
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
|
package main
import (
"syscall/js"
"time"
)
func main() {
c := make(chan struct{}, 0)
js.Global().Set("asyncOperation", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
// Promise benzeri bir yapı oluştur
handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
resolve := args[0]
// Async işi simüle et
go func() {
time.Sleep(2 * time.Second)
resolve.Invoke("Operation completed!")
}()
return nil
})
promiseConstructor := js.Global().Get("Promise")
return promiseConstructor.New(handler)
}))
<-c
}
|
4.5 Gerçek Multi-Threading için Web Workers
⚠️ Gelişmiş / Deneysel: WASM ile Web Workers, dikkatli uygulama gerektiren gelişmiş bir desendir. Bu yaklaşım, birden fazla WASM instance’ı, karmaşık mesaj geçirme ve potansiyel senkronizasyon sorunlarını içerir. Production’da kullanmadan önce kapsamlı test edin. Tarayıcı desteği ve davranışı değişebilir.
Go’nun goroutine’leri WASM’da tek thread üzerinde çalışırken, paralel çalıştırma için Web Workers kullanabilirsiniz:
worker.go:
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
|
package main
import (
"syscall/js"
)
func main() {
c := make(chan struct{}, 0)
// Ana thread'den çağrılacak fonksiyonu kaydet
js.Global().Set("processInWorker", js.FuncOf(processInWorker))
<-c
}
func processInWorker(this js.Value, args []js.Value) interface{} {
data := args[0].String()
// Ağır hesaplama
result := performHeavyComputation(data)
return result
}
func performHeavyComputation(data string) string {
// CPU yoğun işiniz burada
return "Processed: " + data
}
|
main.html:
1
2
3
4
5
6
7
8
9
10
11
|
<script>
// Bir Web Worker oluştur
const worker = new Worker('worker.wasm.js');
worker.onmessage = function(e) {
console.log('Result:', e.data);
};
// Worker'a veri gönder
worker.postMessage({ type: 'process', data: 'large dataset' });
</script>
|
4.6 Paylaşılan Bellek için SharedArrayBuffer
⚠️ Gelişmiş / Deneysel: Unsafe pointer manipülasyonu ile SharedArrayBuffer, gelişmiş ve potansiyel olarak tehlikeli bir desendir. Bu şunları gerektirir:
- Özel tarayıcı güvenlik başlıkları (COOP/COEP)
- Crash’leri önlemek için dikkatli bellek yönetimi
- WASM bellek modelinin derinlemesine anlaşılması
- Tarayıcılar arası kapsamlı test
Kapsamlı test ve uzmanlık olmadan production için önerilmez.
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
|
package main
import (
"syscall/js"
"unsafe"
)
func main() {
c := make(chan struct{}, 0)
js.Global().Set("useSharedMemory", js.FuncOf(useSharedMemory))
<-c
}
func useSharedMemory(this js.Value, args []js.Value) interface{} {
// JavaScript'ten SharedArrayBuffer al
sab := args[0]
// Altta yatan belleğe eriş
// Not: Bu, uygun tarayıcı güvenlik başlıkları gerektirir
// (Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy)
// Go slice'ına dönüştür (gelişmiş kullanım)
length := sab.Get("byteLength").Int()
ptr := unsafe.Pointer(uintptr(sab.Get("__go_mem").Int()))
data := (*[1 << 28]byte)(ptr)[:length:length]
// Paylaşılan belleği işle
for i := range data {
data[i] = data[i] * 2
}
return sab
}
|
Gerekli Güvenlik Başlıkları:
1
2
|
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
|
4.7 Streaming Derleme
1
2
3
4
5
6
7
|
// Daha hızlı başlangıç için WASM derlemesini stream et
async function loadWASM() {
const response = await fetch('main.wasm');
const wasmModule = await WebAssembly.compileStreaming(response);
const instance = await WebAssembly.instantiate(wasmModule, go.importObject);
go.run(instance);
}
|
4.8 Modül Önbellekleme Stratejileri
1
2
3
4
5
6
7
8
9
10
11
12
|
// Derlenmiş WASM modüllerini önbelleğe al
const wasmCache = new Map();
async function loadCachedWASM(url) {
if (wasmCache.has(url)) {
return wasmCache.get(url);
}
const module = await WebAssembly.compileStreaming(fetch(url));
wasmCache.set(url, module);
return module;
}
|
5. Gerçek Dünya Örneği: Görüntü İşleme
Pratik bir örnek oluşturalım: tarayıcıda görüntülere filtre uygulayan bir görüntü işleme uygulaması.
5.1 Go’da Görüntü İşleme
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
|
package main
import (
"image"
"image/color"
"syscall/js"
)
func main() {
c := make(chan struct{}, 0)
js.Global().Set("applyGrayscale", js.FuncOf(applyGrayscale))
js.Global().Set("applyBlur", js.FuncOf(applyBlur))
<-c
}
func applyGrayscale(this js.Value, args []js.Value) interface{} {
// JavaScript'ten görüntü verisini al
imageData := args[0]
data := imageData.Get("data")
width := imageData.Get("width").Int()
height := imageData.Get("height").Int()
// Gri tonlamaya dönüştür
length := data.Length()
for i := 0; i < length; i += 4 {
r := data.Index(i).Int()
g := data.Index(i + 1).Int()
b := data.Index(i + 2).Int()
// Gri tonlama formülü
gray := uint8(0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b))
data.SetIndex(i, gray)
data.SetIndex(i+1, gray)
data.SetIndex(i+2, gray)
// Alpha kanalı (i+3) değişmeden kalır
}
return imageData
}
func applyBlur(this js.Value, args []js.Value) interface{} {
imageData := args[0]
data := imageData.Get("data")
width := imageData.Get("width").Int()
height := imageData.Get("height").Int()
// Basit box blur (3x3 kernel)
radius := 1 // Blur yarıçapı
result := make([]uint8, data.Length())
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
var r, g, b, a, count uint32
// Çevredeki pikselleri örnekle
for dy := -radius; dy <= radius; dy++ {
for dx := -radius; dx <= radius; dx++ {
nx := x + dx
ny := y + dy
// Sınır kontrolü
if nx >= 0 && nx < width && ny >= 0 && ny < height {
idx := (ny*width + nx) * 4
r += uint32(data.Index(idx).Int())
g += uint32(data.Index(idx + 1).Int())
b += uint32(data.Index(idx + 2).Int())
a += uint32(data.Index(idx + 3).Int())
count++
}
}
}
// Değerlerin ortalamasını al
idx := (y*width + x) * 4
result[idx] = uint8(r / count)
result[idx+1] = uint8(g / count)
result[idx+2] = uint8(b / count)
result[idx+3] = uint8(a / count)
}
}
// Sonucu imageData'ya geri kopyala
for i := 0; i < len(result); i++ {
data.SetIndex(i, result[i])
}
return imageData
}
|
5.2 JavaScript Entegrasyonu
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
|
<!DOCTYPE html>
<html>
<head>
<title>Go WASM ile Görüntü İşleme</title>
</head>
<body>
<input type="file" id="imageInput" accept="image/*">
<canvas id="canvas"></canvas>
<button onclick="processGrayscale()">Gri Tonlama</button>
<button onclick="processBlur()">Blur</button>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
let imageData = null;
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject)
.then(result => {
go.run(result.instance);
});
document.getElementById("imageInput").addEventListener("change", function(e) {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = function(event) {
const img = new Image();
img.onload = function() {
const canvas = document.getElementById("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
};
img.src = event.target.result;
};
reader.readAsDataURL(file);
});
function processGrayscale() {
if (!imageData) return;
const processed = applyGrayscale(imageData);
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.putImageData(processed, 0, 0);
}
function processBlur() {
if (!imageData) return;
const processed = applyBlur(imageData);
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.putImageData(processed, 0, 0);
}
</script>
</body>
</html>
|
📊 Benchmark Uyarısı: Aşağıda gösterilen benchmark sonuçları tipik senaryolara dayalı örneklerdir. Gerçek performans şunlara bağlı olarak önemli ölçüde değişebilir:
- Tarayıcı motoru (Chrome V8, Firefox SpiderMonkey, Safari JavaScriptCore)
- Donanım (CPU mimarisi, bellek hızı)
- Optimizasyon seviyesi (derleyici bayrakları, tarayıcı optimizasyonları)
- Kod karmaşıklığı ve veri boyutu
- JavaScript motoru optimizasyonları (JIT derleme)
Bu sayılar mutlak garantiler değil, göreceli performans özelliklerini göstermek içindir. Her zaman kendi kullanım durumunuzu hedef ortamınızda test edin.
Gösterge amaçlı benchmark’larla performansı karşılaştıralım:
Fibonacci Hesaplama (n=40):
1
2
3
4
5
6
7
8
9
10
11
12
|
// Go WASM
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
// Benchmark sonuçları:
// Go WASM: ~850ms
// JavaScript: ~1200ms
// Native Go: ~450ms
|
Matris Çarpımı (1000x1000):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
func multiplyMatrices(a, b [][]float64) [][]float64 {
n := len(a)
result := make([][]float64, n)
for i := range result {
result[i] = make([]float64, n)
}
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
sum := 0.0
for k := 0; k < n; k++ {
sum += a[i][k] * b[k][j]
}
result[i][j] = sum
}
}
return result
}
// Benchmark sonuçları:
// Go WASM: ~2.1s
// JavaScript (optimize edilmiş): ~3.8s
// Native Go: ~1.2s
|
Görüntü İşleme (1920x1080):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
func processImagePixels(data []uint8) {
for i := 0; i < len(data); i += 4 {
r := float64(data[i])
g := float64(data[i+1])
b := float64(data[i+2])
// Gri tonlama dönüşümü
gray := uint8(0.299*r + 0.587*g + 0.114*b)
data[i] = gray
data[i+1] = gray
data[i+2] = gray
}
}
// Benchmark sonuçları (frame başına):
// Go WASM: ~12ms
// JavaScript: ~28ms
// Native Go: ~8ms
|
Performans Özeti:
| İşlem |
Go WASM |
JavaScript |
Hız Artışı |
| Fibonacci (n=40) |
850ms |
1200ms |
1.41x |
| Matris Çarpımı (1Kx1K) |
2.1s |
3.8s |
1.81x |
| Görüntü İşleme |
12ms |
28ms |
2.33x |
| JSON Parse (10MB) |
45ms |
38ms |
0.84x* |
*Not: JavaScript’in V8 motoru JSON parsing için oldukça optimize edilmiştir, bu nedenle WASM bu özel görevde daha yavaş olabilir.
6.2 WASM Binary Boyutunu Azaltma
1
2
3
4
5
6
7
8
|
# Kullanılmayan kodu hariç tutmak için build tag'leri kullan
go build -tags="no_net" -o main.wasm main.go
# Binary boyutunu azaltmak için -ldflags kullan
go build -ldflags="-s -w" -o main.wasm main.go
# Daha küçük binary'ler için TinyGo kullan (alternatif derleyici)
tinygo build -target wasm -o main.wasm main.go
|
6.3 TinyGo: Detaylı Karşılaştırma
TinyGo, embedded sistemler ve WebAssembly için tasarlanmış alternatif bir Go derleyicisidir ve önemli ölçüde daha küçük binary’ler üretir.
Binary Boyutu Karşılaştırması:
| Derleyici |
Binary Boyutu |
Runtime Boyutu |
Toplam |
| Standard Go |
2.1 MB |
500 KB |
~2.6 MB |
| TinyGo |
180 KB |
50 KB |
~230 KB |
| TinyGo (optimize edilmiş) |
95 KB |
30 KB |
~125 KB |
Özellik Karşılaştırması:
| Özellik |
Standard Go |
TinyGo |
| Standard Library |
Tam |
Alt Küme |
| Goroutines |
✅ Tam |
⚠️ Sınırlı |
| Reflection |
✅ Tam |
⚠️ Kısmi |
| CGO |
✅ Evet |
❌ Hayır |
| Binary Boyutu |
Büyük |
Küçük |
| Derleme Hızı |
Hızlı |
Daha Yavaş |
| Debugging |
Mükemmel |
İyi |
TinyGo Ne Zaman Kullanılmalı:
✅ TinyGo kullanın:
- Binary boyutu kritik (< 500 KB)
- Tam standard library’ye ihtiyacınız yok
- Embedded/IoT cihazlar için geliştirme yapıyorsunuz
- Bellek kısıtlı
- Library sınırlamalarıyla çalışabilirsiniz
❌ Standard Go ile devam edin:
- Tam standard library desteğine ihtiyacınız var
- Karmaşık reflection gerekli
- CGO’ya ihtiyacınız var
- Geliştirme hızı öncelik
- Binary boyutu endişe değil
TinyGo Örneği:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# TinyGo kurulumu
# macOS
brew tap tinygo-org/tools
brew install tinygo
# TinyGo ile derleme
tinygo build -target wasm -o main.wasm -opt=z -scheduler=none main.go
# Daha fazla optimizasyon
tinygo build -target wasm -o main.wasm \
-opt=z \
-scheduler=none \
-no-debug \
-ldflags="-s -w" \
main.go
|
Trade-off’lar:
- Boyut vs Özellikler: TinyGo boyut için bazı özelliklerden feragat eder
- Performans: Her ikisi de çoğu görev için benzer performans gösterir
- Uyumluluk: Bazı Go paketleri TinyGo ile çalışmayabilir
- Geliştirme: Standard Go daha iyi araçlar ve debugging sunar
6.4 Bellek Kullanımını Optimize Etme
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
|
package main
import (
"syscall/js"
"unsafe"
)
// Tahsislerden kaçınmak için buffer'ları yeniden kullan
var bufferPool = make([][]byte, 0, 10)
func getBuffer(size int) []byte {
if len(bufferPool) > 0 {
buf := bufferPool[len(bufferPool)-1]
bufferPool = bufferPool[:len(bufferPool)-1]
if cap(buf) >= size {
return buf[:size]
}
}
return make([]byte, size)
}
func returnBuffer(buf []byte) {
if len(bufferPool) < cap(bufferPool) {
bufferPool = append(bufferPool, buf)
}
}
|
6.5 JavaScript-Go Sınır Geçişlerini Minimize Etme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// Kötü: Birçok küçük çağrı
func processDataBad(items []js.Value) {
for _, item := range items {
js.Global().Get("console").Call("log", item.String())
}
}
// İyi: Toplu işlemler
func processDataGood(items []js.Value) {
results := make([]string, len(items))
for i, item := range items {
results[i] = item.String()
}
// Tüm sonuçlarla tek JavaScript çağrısı
js.Global().Call("processBatch", results)
}
|
6.6 Bellek Havuzlama
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
|
package main
import (
"sync"
)
var bufferPool = sync.Pool{
New: func() interface{} {
return make([]byte, 0, 1024)
},
}
func getBuffer() []byte {
return bufferPool.Get().([]byte)
}
func returnBuffer(buf []byte) {
buf = buf[:0] // Uzunluğu sıfırla, kapasiteyi koru
bufferPool.Put(buf)
}
// Kullanım
func processData(data []byte) {
buf := getBuffer()
defer returnBuffer(buf)
// İşleme için buf kullan
buf = append(buf, data...)
// ... işle ...
}
|
6.7 Sıfır Kopyalama Teknikleri
1
2
3
4
5
6
7
8
9
|
// JavaScript'e veri geçirirken gereksiz kopyalardan kaçının
func processLargeArray(data []byte) {
// Kopyalamak yerine referans geçir
jsArray := js.Global().Get("Uint8Array").New(len(data))
js.CopyBytesToJS(jsArray, data)
// Diziyi doğrudan kullan
js.Global().Call("processArray", jsArray)
}
|
7. Test Stratejileri
WebAssembly uygulamalarını test etmek, birim testleri, entegrasyon testleri ve tarayıcı otomasyonunun bir kombinasyonunu gerektirir.
7.1 Go WASM Kodunu Birim Test Etme
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
|
// main_test.go
package main
import (
"testing"
)
func TestFibonacci(t *testing.T) {
tests := []struct {
input int
expected int
}{
{0, 0},
{1, 1},
{5, 5},
{10, 55},
{20, 6765},
}
for _, tt := range tests {
result := fibonacci(tt.input)
if result != tt.expected {
t.Errorf("fibonacci(%d) = %d, expected %d", tt.input, result, tt.expected)
}
}
}
func TestImageProcessing(t *testing.T) {
// Test görüntü verisi oluştur
data := make([]uint8, 12) // 3x1 piksel RGBA
data[0] = 255 // R
data[1] = 128 // G
data[2] = 64 // B
data[3] = 255 // A
processImagePixels(data)
// Gri tonlama dönüşümünü doğrula
if data[0] != data[1] || data[1] != data[2] {
t.Error("Gri tonlama dönüşümü başarısız")
}
}
|
Testleri Çalıştırma:
1
2
3
4
5
6
7
8
|
# WASM derlemesinden önce Go kodunu test et
go test ./...
# Coverage ile test
go test -cover ./...
# Belirli paketi test et
go test ./internal/processor
|
7.2 Node.js ile Entegrasyon Testi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// test/integration/wasm.test.js
const { readFileSync } = require('fs');
const { instantiateSync } = require('@wasm/wasm');
describe('WASM Entegrasyon Testleri', () => {
let wasmModule;
beforeAll(async () => {
const wasmBuffer = readFileSync('./main.wasm');
wasmModule = await instantiateSync(wasmBuffer, {
// Import object
});
});
test('veriyi doğru şekilde işlemeli', () => {
const result = wasmModule.exports.processData([1, 2, 3, 4]);
expect(result).toBeDefined();
});
});
|
7.3 Tarayıcı Otomasyon Testi
Playwright Kullanımı:
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
|
// test/e2e/wasm.spec.js
const { test, expect } = require('@playwright/test');
test('WASM yüklenir ve görüntüyü işler', async ({ page }) => {
await page.goto('http://localhost:8080');
// WASM'ın yüklenmesini bekle
await page.waitForFunction(() => window.wasmLoaded === true);
// Test görüntüsünü yükle
const input = await page.$('input[type="file"]');
await input.setInputFiles('./test/fixtures/test-image.jpg');
// İşleme butonuna tıkla
await page.click('button#process');
// Sonucu doğrula
const canvas = await page.$('canvas');
const imageData = await canvas.evaluate((el) => {
const ctx = el.getContext('2d');
return ctx.getImageData(0, 0, el.width, el.height);
});
expect(imageData).toBeDefined();
});
|
Selenium Kullanımı:
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
|
// test/e2e/selenium_test.go
package e2e
import (
"testing"
"github.com/tebeka/selenium"
)
func TestWASMInBrowser(t *testing.T) {
// Selenium WebDriver kurulumu
capabilities := selenium.Capabilities{"browserName": "chrome"}
wd, err := selenium.NewRemote(capabilities, "")
if err != nil {
t.Fatal(err)
}
defer wd.Quit()
// Sayfaya git
wd.Get("http://localhost:8080")
// WASM'ın yüklenmesini bekle
wd.Wait(func(wd selenium.WebDriver) (bool, error) {
loaded, err := wd.ExecuteScript("return window.wasmLoaded === true", nil)
return loaded.(bool), err
})
// Fonksiyonelliği test et
result, err := wd.ExecuteScript("return processData([1,2,3])", nil)
if err != nil {
t.Fatal(err)
}
if result == nil {
t.Error("WASM fonksiyonundan sonuç bekleniyordu")
}
}
|
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
|
// test/benchmark/performance_test.go
package benchmark
import (
"testing"
)
func BenchmarkWASMProcessing(b *testing.B) {
data := make([]uint8, 1920*1080*4) // Full HD görüntü
b.ResetTimer()
for i := 0; i < b.N; i++ {
processImagePixels(data)
}
}
func BenchmarkJavaScriptInterop(b *testing.B) {
items := make([]js.Value, 1000)
for i := range items {
items[i] = js.ValueOf(i)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
processDataGood(items)
}
}
|
8. Hata Ayıklama ve Hata Kurtarma
8.1 Console Logging Kullanımı
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package main
import (
"fmt"
"syscall/js"
)
func log(format string, args ...interface{}) {
message := fmt.Sprintf(format, args...)
js.Global().Get("console").Call("log", message)
}
func main() {
log("Uygulama başlatıldı")
log("Değer: %d", 42)
}
|
8.2 Source Maps
1
2
|
# Hata ayıklama için source map'lerle derle
go build -o main.wasm -gcflags="all=-N -l" main.go
|
Chrome DevTools:
-
Sources Sekmesi:
- Go kaynak kodunda breakpoint’ler ayarla
- WASM çalıştırmasını adım adım izle
- Çağrı yığınını incele
-
Memory Sekmesi:
- WASM bellek kullanımını izle
- Bellek sızıntılarını tespit et
- Bellek büyüme desenlerini analiz et
-
Performance Sekmesi:
- WASM çalıştırmasını profile et
- Darboğazları tespit et
- JavaScript-WASM sınır ek yükünü ölç
Firefox DevTools:
- Mükemmel WASM hata ayıklama desteği
- Source map entegrasyonu
- Bellek profiling araçları
8.4 WASM Crash’lerini Yakalama
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
|
package main
import (
"fmt"
"syscall/js"
)
func safeCall(fn func() interface{}) interface{} {
defer func() {
if r := recover(); r != nil {
errorMsg := fmt.Sprintf("WASM panic: %v", r)
js.Global().Get("console").Call("error", errorMsg)
// Hata takip servisine bildir
js.Global().Call("reportError", map[string]interface{}{
"type": "wasm_panic",
"message": errorMsg,
"stack": getStackTrace(),
})
}
}()
return fn()
}
func getStackTrace() string {
// Stack trace yakala
return "Stack trace burada"
}
// Kullanım
func processData(data []byte) {
safeCall(func() interface{} {
// Panic olabilecek kodunuz
return processDataUnsafe(data)
})
}
|
8.5 Bellek Sızıntısı Tespiti
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
|
package main
import (
"fmt"
"runtime"
"syscall/js"
"time"
)
func monitorMemory() {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for range ticker.C {
var m runtime.MemStats
runtime.ReadMemStats(&m)
js.Global().Get("console").Call("log", fmt.Sprintf(
"Alloc: %d KB, Sys: %d KB, NumGC: %d",
m.Alloc/1024,
m.Sys/1024,
m.NumGC,
))
// Bellek kullanımı yüksekse uyar
if m.Alloc > 50*1024*1024 { // 50 MB
js.Global().Call("alert", "Yüksek bellek kullanımı tespit edildi!")
}
}
}
func main() {
go monitorMemory()
// ... kodunuzun geri kalanı
}
|
Chrome Performance API Kullanımı:
1
2
3
4
5
6
7
8
9
10
|
// WASM çalıştırmasını profile et
async function profileWASM() {
const perf = performance.now();
// WASM fonksiyonunu çağır
wasmModule.exports.heavyComputation();
const duration = performance.now() - perf;
console.log(`WASM çalıştırması: ${duration}ms sürdü`);
}
|
Performance Observer Kullanımı:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'measure') {
console.log(`${entry.name}: ${entry.duration}ms`);
}
}
});
observer.observe({ entryTypes: ['measure'] });
// WASM fonksiyonunu ölç
performance.mark('wasm-start');
wasmModule.exports.processData();
performance.mark('wasm-end');
performance.measure('wasm-execution', 'wasm-start', 'wasm-end');
|
8.7 Hata Kurtarma Stratejileri
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
|
package main
import (
"errors"
"fmt"
"syscall/js"
)
type WASMError struct {
Code string
Message string
}
func (e *WASMError) Error() string {
return e.Message
}
func recoverFromError(fn func() error) error {
defer func() {
if r := recover(); r != nil {
// Panic'i hataya dönüştür
err := &WASMError{
Code: "PANIC",
Message: fmt.Sprintf("%v", r),
}
handleError(err)
}
}()
err := fn()
if err != nil {
handleError(err)
}
return err
}
func handleError(err error) {
js.Global().Call("onWASMError", map[string]interface{}{
"error": err.Error(),
})
}
|
9. Dağıtım ve Production
9.1 Build Script
build.sh:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#!/bin/bash
set -e
# WASM derle
export GOOS=js
export GOARCH=wasm
go build -ldflags="-s -w" -o public/main.wasm ./cmd/wasm
# Destek dosyalarını kopyala
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" public/
# wasm-opt ile optimize et (Binaryen'den)
if command -v wasm-opt &> /dev/null; then
wasm-opt -Oz public/main.wasm -o public/main.optimized.wasm
mv public/main.optimized.wasm public/main.wasm
echo "WASM wasm-opt ile optimize edildi"
fi
echo "Build tamamlandı!"
|
9.2 CI/CD Pipeline
GitHub Actions Örneği:
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
|
name: Build and Deploy WASM
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Install wasm-opt
run: |
wget https://github.com/WebAssembly/binaryen/releases/download/version_116/binaryen-version_116-x86_64-linux.tar.gz
tar -xzf binaryen-version_116-x86_64-linux.tar.gz
sudo mv binaryen-version_116/bin/wasm-opt /usr/local/bin/
- name: Build WASM
run: |
export GOOS=js
export GOARCH=wasm
go build -ldflags="-s -w" -o public/main.wasm ./cmd/wasm
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" public/
wasm-opt -Oz public/main.wasm -o public/main.optimized.wasm
mv public/main.optimized.wasm public/main.wasm
- name: Run tests
run: go test ./...
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: wasm-build
path: public/
- name: Deploy to CDN
if: github.ref == 'refs/heads/main'
run: |
# Deployment script'iniz burada
echo "Production'a dağıtılıyor..."
|
GitLab CI Örneği:
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
|
stages:
- build
- test
- deploy
build_wasm:
stage: build
image: golang:1.21
script:
- export GOOS=js GOARCH=wasm
- go build -ldflags="-s -w" -o public/main.wasm ./cmd/wasm
- cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" public/
artifacts:
paths:
- public/
expire_in: 1 hour
test:
stage: test
image: golang:1.21
script:
- go test -v ./...
deploy:
stage: deploy
script:
- echo "Dağıtılıyor..."
only:
- main
|
9.3 Sıkıştırma ile Servis Etme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# nginx.conf
server {
location ~ \.wasm$ {
add_header Content-Type application/wasm;
add_header Cross-Origin-Opener-Policy same-origin;
add_header Cross-Origin-Embedder-Policy require-corp;
gzip on;
gzip_vary on;
gzip_types application/wasm;
gzip_comp_level 9;
brotli on;
brotli_types application/wasm;
brotli_comp_level 11;
# 1 yıl için cache
expires 1y;
add_header Cache-Control "public, immutable";
}
}
|
9.4 CDN Konfigürasyonu
Cloudflare:
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
|
// WASM optimizasyonu için Cloudflare Workers
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const url = new URL(request.url)
if (url.pathname.endsWith('.wasm')) {
const response = await fetch(request)
const wasm = await response.arrayBuffer()
// Opsiyonel: Daha fazla optimizasyon
// const optimized = await optimizeWASM(wasm)
return new Response(wasm, {
headers: {
'Content-Type': 'application/wasm',
'Content-Encoding': 'br', // Brotli sıkıştırma
'Cache-Control': 'public, max-age=31536000, immutable',
},
})
}
return fetch(request)
}
|
AWS CloudFront:
1
2
3
4
5
6
7
8
9
10
11
|
{
"CacheBehaviors": {
"*.wasm": {
"Compress": true,
"MinTTL": 31536000,
"ForwardedValues": {
"QueryString": false
}
}
}
}
|
10. Güvenlik Düşünceleri
10.1 WASM Güvenlik Modeli
WebAssembly, birkaç güvenlik özelliğiyle sandbox ortamında çalışır:
- Bellek İzolasyonu: Her WASM modülünün kendi linear belleği vardır
- Doğrudan Sistem Erişimi Yok: Dosya sistemi veya ağa doğrudan erişemez
- Same-Origin Policy: Tarayıcının same-origin policy’sine tabidir
- Tip Güvenliği: Güçlü tip sistemi birçok yaygın güvenlik açığını önler
10.2 Girdi Doğrulama
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
|
package main
import (
"errors"
"strings"
"syscall/js"
)
func validateInput(data js.Value) error {
if data.IsNull() || data.IsUndefined() {
return errors.New("input null veya undefined olamaz")
}
// String uzunluğunu doğrula
if data.Type() == js.TypeString {
str := data.String()
if len(str) > 10000 {
return errors.New("input çok büyük")
}
// Zararlı desenleri kontrol et
if containsMaliciousPattern(str) {
return errors.New("zararlı input tespit edildi")
}
}
return nil
}
func containsMaliciousPattern(input string) bool {
malicious := []string{
"<script>",
"javascript:",
"onerror=",
"eval(",
}
lower := strings.ToLower(input)
for _, pattern := range malicious {
if strings.Contains(lower, pattern) {
return true
}
}
return false
}
|
10.3 XSS Koruması
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
func sanitizeOutput(data string) string {
// HTML özel karakterlerini escape et
replacements := map[string]string{
"<": "<",
">": ">",
"&": "&",
"\"": """,
"'": "'",
}
result := data
for old, new := range replacements {
result = strings.ReplaceAll(result, old, new)
}
return result
}
func safeSetInnerHTML(element js.Value, content string) {
sanitized := sanitizeOutput(content)
element.Set("innerHTML", sanitized)
}
|
10.4 Bellek Güvenliği
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
func safeArrayAccess(arr []byte, index int) (byte, error) {
if index < 0 || index >= len(arr) {
return 0, errors.New("index sınırların dışında")
}
return arr[index], nil
}
func processDataSafely(data []byte, maxSize int) ([]byte, error) {
if len(data) > maxSize {
return nil, errors.New("veri boyutu maksimumu aşıyor")
}
// Orijinali değiştirmemek için kopya oluştur
result := make([]byte, len(data))
copy(result, data)
// Güvenli şekilde işle
// ...
return result, nil
}
|
10.5 Content Security Policy (CSP)
1
2
3
4
5
|
<!-- index.html -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'wasm-unsafe-eval';
object-src 'none';">
|
Nginx CSP Header:
1
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; object-src 'none';";
|
10.6 Güvenli WASM Yükleme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// Güvenli WASM instantiation
async function loadWASMSecurely(url, expectedHash) {
const response = await fetch(url);
const wasmBuffer = await response.arrayBuffer();
// Bütünlüğü doğrula (basitleştirilmiş örnek)
const hash = await crypto.subtle.digest('SHA-256', wasmBuffer);
const hashArray = Array.from(new Uint8Array(hash));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
if (hashHex !== expectedHash) {
throw new Error('WASM bütünlük kontrolü başarısız');
}
return WebAssembly.instantiate(wasmBuffer, go.importObject);
}
|
11. Sınırlamalar ve Düşünceler
11.1 WASM’da Go Runtime Sınırlamaları
- Goroutines: Sınırlı eşzamanlılık (tek thread çalıştırma)
- Network: Doğrudan ağ erişimi yok (JavaScript üzerinden gitmeli)
- Dosya Sistemi: Doğrudan dosya sistemi erişimi yok
- CGO: WASM build’lerde desteklenmiyor
- Bazı paketler: Tüm Go standard library paketleri WASM’da çalışmaz
11.2 Tarayıcı Uyumluluğu
- Modern tarayıcılar (Chrome, Firefox, Safari, Edge) WASM’ı destekler
- Eski tarayıcılar polyfill gerektirebilir
- Mobil tarayıcı desteği iyidir ama kapsamlı test edin
- İlk yükleme süresi: WASM binary’leri indirilmeli ve derlenmeli
- Bellek kullanımı: Go runtime ek yük ekler
- JavaScript interop: Sınırı geçmenin maliyeti vardır
11.4 Yaygın Tuzaklar
Bellek Sızıntıları:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// ❌ Kötü: Temizlik olmadan fonksiyon oluşturma
func registerHandler() {
js.Global().Set("handler", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
// Handler kodu
return nil
}))
// Fonksiyon hiç serbest bırakılmıyor!
}
// ✅ İyi: Temizlik için referans sakla
var handler js.Func
func registerHandler() {
handler = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
// Handler kodu
return nil
})
js.Global().Set("handler", handler)
}
func cleanup() {
handler.Release() // Bittiğinde serbest bırak
}
|
Goroutine Sorunları:
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
|
package main
import (
"context"
"syscall/js"
"time"
)
// ❌ Kötü: Goroutine WASM modülünden daha uzun yaşayabilir
func processAsync() {
go func() {
time.Sleep(10 * time.Second)
js.Global().Call("callback") // Modül kaldırılmışsa başarısız olabilir
}()
}
// ✅ İyi: İptal için context kullan
func processAsync(ctx context.Context) {
go func() {
select {
case <-time.After(10 * time.Second):
js.Global().Call("callback")
case <-ctx.Done():
return // Temiz çıkış
}
}()
}
|
Tip Dönüşümleri:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// ❌ Kötü: Kontrol etmeden tip varsayma
func processValue(val js.Value) {
str := val.String() // String değilse panic olabilir
}
// ✅ İyi: Önce tipi kontrol et
func processValue(val js.Value) {
if val.Type() != js.TypeString {
return // Hatayı yönet
}
str := val.String()
// String'i işle
}
|
Goroutine Bellek Sızıntıları:
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
|
package main
import (
"context"
"time"
)
// ❌ Kötü: Hiç durmayan goroutine'ler
func badHandler() {
for {
go func() {
// Sonsuz döngü - hiç durmaz
for {
time.Sleep(time.Second)
// Hiç tamamlanmayan iş
}
}()
}
}
// ✅ İyi: İptal için context kullan
func goodHandler(ctx context.Context) {
for {
select {
case <-ctx.Done():
return // Temiz çıkış
default:
go func() {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
// Timeout ile iş yap
doWork(ctx)
}()
}
}
}
|
Channel Deadlock’ları:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// ❌ Kötü: Alıcı olmadan unbuffered channel
func badChannel() {
ch := make(chan int)
ch <- 42 // Alıcı yoksa deadlock
}
// ✅ İyi: Buffered channel kullan veya alıcı olduğundan emin ol
func goodChannel() {
ch := make(chan int, 1) // Buffered
ch <- 42
// Veya default ile select kullan
select {
case ch <- 42:
// Gönderildi
default:
// Bloke olurdu, zarif şekilde yönet
}
}
|
JavaScript Değer Yaşam Döngüsü:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// ❌ Kötü: JavaScript değerlerini serbest bırakmama
func badJSValue() {
obj := js.Global().Get("someObject")
// obj'yi kullan ama hiç serbest bırakma
// obj sonsuza kadar bellekte kalır
}
// ✅ İyi: Bittiğinde serbest bırak (gerekirse)
func goodJSValue() {
obj := js.Global().Get("someObject")
defer obj.Release() // Fonksiyon çıktığında serbest bırak
// obj'yi kullan
value := obj.Get("property")
return value
}
|
Dizi Sınırları:
1
2
3
4
5
6
7
8
9
10
11
12
|
// ❌ Kötü: Sınır kontrolü yok
func badArrayAccess(arr []byte, idx int) byte {
return arr[idx] // Panic olabilir
}
// ✅ İyi: Her zaman sınırları kontrol et
func goodArrayAccess(arr []byte, idx int) (byte, error) {
if idx < 0 || idx >= len(arr) {
return 0, errors.New("index sınırların dışında")
}
return arr[idx], nil
}
|
Eşzamanlı Map Erişimi:
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
|
// ❌ Kötü: Eşzamanlı map yazma
var cache = make(map[string]interface{})
func badCache(key string, value interface{}) {
cache[key] = value // Race condition!
}
// ✅ İyi: sync.Map veya mutex kullan
var safeCache = sync.Map{}
func goodCache(key string, value interface{}) {
safeCache.Store(key, value) // Thread-safe
}
// Veya mutex ile
var (
cache = make(map[string]interface{})
cacheMu sync.RWMutex
)
func goodCacheWithMutex(key string, value interface{}) {
cacheMu.Lock()
defer cacheMu.Unlock()
cache[key] = value
}
|
12. Yaygın Sorunları Giderme
12.1 WASM Modülü Yüklenemiyor
Belirtiler:
- Boş sayfa veya console hataları
- “Failed to fetch” hataları
- .wasm dosyası için 404 hataları
Çözümler:
- MIME Tipini Kontrol Et:
1
2
3
4
|
# nginx.conf
location ~ \.wasm$ {
add_header Content-Type application/wasm;
}
|
- CORS Header’larını Doğrula:
1
2
|
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, OPTIONS";
|
- Tarayıcı Console’unu Kontrol Et:
1
2
3
4
5
6
7
8
9
|
// Hata yönetimi ekle
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject)
.then(result => {
go.run(result.instance);
})
.catch(error => {
console.error("WASM yükleme hatası:", error);
// JavaScript'e geri dön
});
|
- Dosya Yollarını Doğrula:
1
2
3
4
5
|
<!-- Yolların doğru olduğundan emin ol -->
<script src="./wasm_exec.js"></script>
<script>
fetch("./main.wasm") // Relatif veya mutlak yol
</script>
|
12.2 Bellek Tükenmesi Hataları
Belirtiler:
- “Out of memory” hataları
- Tarayıcı sekmesi çöküyor
- Yavaş performans
Çözümler:
- Binary Boyutunu Azalt:
1
2
3
4
5
|
# TinyGo kullan
tinygo build -target wasm -opt=z -o main.wasm main.go
# Veya wasm-opt ile optimize et
wasm-opt -Oz main.wasm -o main.optimized.wasm
|
- Bellek Havuzlamayı Uygula:
1
2
3
4
5
6
|
// Tahsis etmek yerine buffer'ları yeniden kullan
var bufferPool = sync.Pool{
New: func() interface{} {
return make([]byte, 0, 1024)
},
}
|
- Streaming API’lerini Kullan:
1
2
3
4
5
6
7
8
|
// Veriyi parçalar halinde işle
async function processLargeData(data) {
const chunkSize = 1024 * 1024; // 1MB parçalar
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize);
await wasmModule.exports.processChunk(chunk);
}
}
|
- Bellek Kullanımını İzle:
1
2
3
4
5
6
7
|
func checkMemory() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
if m.Alloc > 50*1024*1024 { // 50MB
runtime.GC() // Zorunlu garbage collection
}
}
|
12.3 Yavaş İlk Yükleme Süresi
Belirtiler:
- WASM yüklenmeden önce uzun gecikme
- Kötü first contentful paint
- Kullanıcı boş ekran görüyor
Çözümler:
- Streaming Derleme Kullan:
1
2
|
// fetch + compile'den daha hızlı
const module = await WebAssembly.compileStreaming(fetch("main.wasm"));
|
- Code Splitting Uygula:
1
2
3
4
5
6
7
8
|
// Önce kritik kodu yükle, geri kalanını sonra
async function loadCriticalWASM() {
const critical = await WebAssembly.instantiateStreaming(
fetch("critical.wasm")
);
// Kritik olmayan kodu sonra yükle
setTimeout(() => loadNonCriticalWASM(), 2000);
}
|
- Sıkıştırmayı Etkinleştir:
1
2
3
4
|
gzip on;
gzip_types application/wasm;
brotli on;
brotli_types application/wasm;
|
- Yükleme Göstergesi Göster:
1
2
3
4
5
6
7
8
|
<div id="loading">WASM yükleniyor...</div>
<script>
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject)
.then(result => {
document.getElementById("loading").style.display = "none";
go.run(result.instance);
});
</script>
|
12.4 JavaScript Interop Hataları
Belirtiler:
- “Cannot read property” hataları
- Tip hataları
- Tanımsız fonksiyon çağrıları
Çözümler:
- Her Zaman Tipleri Kontrol Et:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
func safeGetProperty(obj js.Value, key string) (string, error) {
if obj.IsNull() || obj.IsUndefined() {
return "", errors.New("nesne null veya undefined")
}
prop := obj.Get(key)
if prop.IsUndefined() {
return "", errors.New("özellik bulunamadı")
}
if prop.Type() != js.TypeString {
return "", errors.New("özellik string değil")
}
return prop.String(), nil
}
|
- Opsiyonel Parametreleri Yönet:
1
2
3
4
5
6
7
8
|
func processWithDefaults(this js.Value, args []js.Value) interface{} {
value := 0
if len(args) > 0 && !args[0].IsUndefined() {
value = args[0].Int()
}
// Varsayılan değerle kullan
return process(value)
}
|
- Fonksiyon Varlığını Doğrula:
1
2
3
4
5
6
|
// Çağırmadan önce WASM fonksiyonunun var olduğunu kontrol et
if (window.wasmModule && typeof window.wasmModule.myFunction === 'function') {
window.wasmModule.myFunction();
} else {
console.error("WASM fonksiyonu mevcut değil");
}
|
Belirtiler:
- Beklenenden daha yavaş
- Yüksek CPU kullanımı
- UI donuyor
Çözümler:
- JavaScript-WASM Sınır Geçişlerini Minimize Et:
1
2
3
4
5
6
7
|
// ❌ Kötü: Birçok küçük çağrı
for i := 0; i < 1000; i++ {
js.Global().Call("processItem", items[i])
}
// ✅ İyi: Toplu işlemler
js.Global().Call("processBatch", items)
|
- Web Workers Kullan:
1
2
3
|
// Ağır işi worker'a aktar
const worker = new Worker('worker.wasm.js');
worker.postMessage({ type: 'process', data: largeData });
|
- Profile Et ve Optimize Et:
1
2
3
4
5
6
7
8
9
10
11
|
// Go'nun yerleşik profiling'ini kullan
import _ "net/http/pprof"
// Veya tarayıcı performance API'sini kullan
func profileFunction(fn func()) {
start := time.Now()
fn()
duration := time.Since(start)
js.Global().Get("console").Call("log",
fmt.Sprintf("Fonksiyon süresi: %v", duration))
}
|
12.6 Tarayıcı Uyumluluk Sorunları
Belirtiler:
- Chrome’da çalışıyor ama Firefox’ta çalışmıyor
- Mobil tarayıcı sorunları
- Eski tarayıcı hataları
Çözümler:
- Özellik Tespiti:
1
2
3
4
5
|
if (!WebAssembly) {
// JavaScript'e geri dön
console.warn("WebAssembly desteklenmiyor");
loadJavaScriptFallback();
}
|
- Polyfill’ler:
1
2
|
<!-- Eski tarayıcılar için -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=WebAssembly"></script>
|
- Progressive Enhancement:
1
2
3
4
5
6
7
8
9
10
11
12
|
async function loadWithFallback() {
try {
if (typeof WebAssembly !== 'undefined') {
await loadWASM();
} else {
loadJavaScriptVersion();
}
} catch (error) {
console.error("WASM başarısız, JS fallback kullanılıyor:", error);
loadJavaScriptVersion();
}
}
|
12.7 Hata Ayıklama İpuçları
Verbose Logging Etkinleştir:
1
2
3
4
5
6
7
8
|
const debug = true
func logDebug(format string, args ...interface{}) {
if debug {
js.Global().Get("console").Call("log",
fmt.Sprintf("[WASM] "+format, args...))
}
}
|
Source Maps Kullan:
1
|
go build -gcflags="all=-N -l" -o main.wasm main.go
|
Tarayıcı DevTools:
- Chrome DevTools Sources sekmesini kullan
- Go kodunda breakpoint’ler ayarla
- WASM belleğini incele
- Profiling için Performance sekmesini kullan
13. En İyi Uygulamalar
13.1 Kod Organizasyonu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
project/
├── cmd/
│ ├── wasm/
│ │ └── main.go
│ └── server/
│ └── main.go
├── internal/
│ ├── wasm/
│ │ └── handlers.go
│ └── shared/
│ └── models.go
├── pkg/
│ └── processor/
│ └── image.go
└── web/
├── index.html
└── assets/
|
13.2 Hata Yönetimi
1
2
3
4
5
6
7
8
|
func safeCall(fn func() interface{}) interface{} {
defer func() {
if r := recover(); r != nil {
js.Global().Get("console").Call("error", fmt.Sprintf("Hata: %v", r))
}
}()
return fn()
}
|
13.3 Tip Güvenliği
1
2
3
4
5
6
7
8
9
10
11
12
|
// JavaScript değerleri için tip güvenli wrapper'lar kullan
type JSObject struct {
Value js.Value
}
func (j JSObject) GetString(key string) string {
return j.Value.Get(key).String()
}
func (j JSObject) GetInt(key string) int {
return j.Value.Get(key).Int()
}
|
14. Framework Entegrasyonu
14.1 React Entegrasyonu
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
|
// useWASM.ts
import { useEffect, useState } from 'react';
export function useWASM() {
const [wasm, setWasm] = useState<any>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
async function loadWASM() {
try {
const go = new (window as any).Go();
const result = await WebAssembly.instantiateStreaming(
fetch('main.wasm'),
go.importObject
);
go.run(result.instance);
setWasm((window as any).wasmModule);
setLoading(false);
} catch (err) {
setError(err as Error);
setLoading(false);
}
}
loadWASM();
}, []);
return { wasm, loading, error };
}
// Component kullanımı
function ImageProcessor() {
const { wasm, loading } = useWASM();
const [processed, setProcessed] = useState<string>('');
const handleProcess = () => {
if (wasm) {
const result = wasm.processImage(imageData);
setProcessed(result);
}
};
if (loading) return <div>WASM yükleniyor...</div>;
return (
<div>
<button onClick={handleProcess}>İşle</button>
{processed && <img src={processed} />}
</div>
);
}
|
14.2 Vue Entegrasyonu
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
|
<template>
<div>
<button @click="processData" :disabled="!wasmLoaded">
Veriyi İşle
</button>
<div v-if="result">{{ result }}</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
const wasmLoaded = ref(false);
const result = ref('');
onMounted(async () => {
const go = new (window as any).Go();
await WebAssembly.instantiateStreaming(
fetch('main.wasm'),
go.importObject
);
go.run();
wasmLoaded.value = true;
});
function processData() {
if ((window as any).wasmModule) {
result.value = (window as any).wasmModule.processData();
}
}
</script>
|
14.3 Svelte Entegrasyonu
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
|
<script lang="ts">
import { onMount } from 'svelte';
let wasm: any = null;
let result = '';
onMount(async () => {
const go = new (window as any).Go();
const instance = await WebAssembly.instantiateStreaming(
fetch('main.wasm'),
go.importObject
);
go.run(instance);
wasm = (window as any).wasmModule;
});
function process() {
if (wasm) {
result = wasm.processData();
}
}
</script>
<button on:click={process} disabled={!wasm}>
İşle
</button>
<p>{result}</p>
|
14.4 TypeScript Tip Tanımlamaları
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// wasm.d.ts
declare global {
interface Window {
wasmModule: {
processImage: (data: ImageData) => ImageData;
processData: (data: number[]) => number[];
fibonacci: (n: number) => number;
};
Go: new () => {
importObject: WebAssembly.Imports;
run: (instance: WebAssembly.Instance) => void;
};
}
}
export {};
|
15. Gerçek Dünya Production Vaka Çalışmaları
Şirket: TechCorp Imaging
Zorluk: Sunucu gidiş-dönüşleri olmadan tarayıcıda 4K görüntüleri işlemek
Çözüm: Go WASM görüntü işleme pipeline’ı
Sonuçlar:
- Performans: JavaScript implementasyonundan 3.2x daha hızlı
- Binary Boyutu: 450 KB (TinyGo kullanarak)
- Kullanıcı Deneyimi: Sunucu yüklemesi olmadan gerçek zamanlı önizleme
- Maliyet Tasarrufu: Sunucu işleme maliyetlerinde %60 azalma
Implementasyon:
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
|
// Görüntü işleme pipeline'ı
// Not: Bunlar placeholder fonksiyonlar - görüntü library'nize göre implement edin
func ProcessImagePipeline(data []byte, operations []Operation) ([]byte, error) {
// Görüntüyü decode et (image paketi veya üçüncü parti library kullanarak)
img, err := decodeImage(data)
if err != nil {
return nil, err
}
// İşlemleri uygula
for _, op := range operations {
switch op.Type {
case "resize":
img = resizeImage(img, op.Params)
case "filter":
img = applyFilter(img, op.Filter)
case "compress":
img = compressImage(img, op.Quality)
}
}
// Tekrar byte'lara encode et
return encodeImage(img)
}
// Yardımcı tipler (örnek)
type Operation struct {
Type string
Filter string
Params map[string]interface{}
Quality int
}
|
Öğrenilen Dersler:
- Büyük görüntüler için bellek yönetimi kritik
- Toplu işlemler tek tek çağrılardan daha verimli
- İşleme sırasında kullanıcı geri bildirimi gerekli
15.2 Vaka Çalışması: Finansal Veri Görselleştirme
Şirket: FinanceApp
Zorluk: Milyonlarca veri noktasıyla karmaşık finansal grafikleri render etmek
Çözüm: Veri işleme için Go WASM, render için Canvas API
Sonuçlar:
- Performans: Grafik render’ında 5x daha hızlı
- Bellek: Tarayıcı bellek kullanımında %40 azalma
- Kullanıcı Memnuniyeti: Algılanan performansta %85 iyileşme
Karşılaşılan Zorluklar:
- İlk WASM yükleme süresi (code splitting ile çözüldü)
- Uzun süreli oturumlarda bellek sızıntıları (uygun temizlikle çözüldü)
- Tarayıcı uyumluluk sorunları (polyfill’lerle çözüldü)
15.3 Vaka Çalışması: Oyun Motoru
Şirket: IndieGame Studio
Zorluk: Mevcut Go oyun mantığını web’e taşımak
Çözüm: Go WASM oyun motoru
Sonuçlar:
- Kod Yeniden Kullanımı: Oyun mantığının %80’i masaüstü versiyonundan yeniden kullanıldı
- Performans: Orta seviye cihazlarda 60 FPS
- Geliştirme Süresi: JavaScript’te yeniden yazmaktan %50 daha hızlı
Ana Optimizasyonlar:
- Daha küçük binary için TinyGo kullanıldı (280 KB)
- Oyun varlıkları için object pooling uygulandı
- JavaScript-WASM sınır çağrıları optimize edildi
16. Migration Rehberi: JavaScript’ten Go WASM’a
16.1 Ne Zaman Migrate Edilmeli
İyi Adaylar:
- CPU yoğun hesaplamalar
- Mevcut Go kod tabanı
- Performans kritik işlemler
- Karmaşık algoritmalar
Uygun Değil:
- Basit DOM manipülasyonu
- Hafif işlemler
- JavaScript framework’leriyle sıkı bağlı
- Sık değişen kod
16.2 Migration Adımları
Adım 1: Hot Path’leri Belirle
1
2
3
4
|
// JavaScript kodunuzu profile edin
console.time('expensiveOperation');
expensiveOperation();
console.timeEnd('expensiveOperation');
|
Adım 2: Go’ya Çıkar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// main.go
package main
import "syscall/js"
func main() {
c := make(chan struct{}, 0)
js.Global().Set("expensiveOperation", js.FuncOf(expensiveOperation))
<-c
}
func expensiveOperation(this js.Value, args []js.Value) interface{} {
// JavaScript mantığınızı buraya port edin
result := compute(args[0].Int())
return result
}
|
Adım 3: Kademeli Migration
1
2
3
4
5
6
7
8
9
|
// Uyumluluk için JavaScript wrapper'ı koruyun
function expensiveOperation(input) {
// Önce WASM'ı dene, JS'e geri dön
if (window.wasmModule && window.wasmModule.expensiveOperation) {
return window.wasmModule.expensiveOperation(input);
}
// Orijinal JavaScript'e geri dön
return expensiveOperationJS(input);
}
|
Adım 4: Kapsamlı Test
- Go kodu için birim testleri
- WASM modülü için entegrasyon testleri
- Tarayıcı uyumluluk testi
- Performans benchmark’ları
16.3 Yaygın Migration Desenleri
Desen 1: Fonksiyon Değiştirme
1
2
3
4
5
6
7
|
// Önce
function processData(data) {
// JavaScript implementasyonu
}
// Sonra
// Aynı imzaya sahip Go WASM fonksiyonu
|
Desen 2: Class Migration
1
2
3
4
5
6
7
8
|
// JavaScript class yerine Go struct
type DataProcessor struct {
config Config
}
func (p *DataProcessor) Process(data []byte) []byte {
// İşleme mantığı
}
|
17. İzleme ve Gözlemlenebilirlik
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
|
// WASM performans metriklerini takip et
class WASMMonitor {
constructor() {
this.metrics = {
loadTime: 0,
executionTimes: [],
memoryUsage: [],
};
}
trackLoadTime() {
const start = performance.now();
return () => {
this.metrics.loadTime = performance.now() - start;
this.reportMetrics();
};
}
trackExecution(functionName, fn) {
return (...args) => {
const start = performance.now();
const result = fn(...args);
const duration = performance.now() - start;
this.metrics.executionTimes.push({
function: functionName,
duration,
timestamp: Date.now(),
});
return result;
};
}
reportMetrics() {
// Analytics servisine gönder
fetch('/api/metrics', {
method: 'POST',
body: JSON.stringify(this.metrics),
});
}
}
|
17.2 Sentry ile Hata Takibi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package main
import (
"syscall/js"
)
func reportError(err error, context map[string]interface{}) {
js.Global().Call("Sentry", map[string]interface{}{
"captureException": err.Error(),
"setContext": context,
})
}
// Kullanım
func processData(data []byte) error {
if err := validate(data); err != nil {
reportError(err, map[string]interface{}{
"function": "processData",
"dataSize": len(data),
})
return err
}
return nil
}
|
17.3 Analytics Entegrasyonu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
func trackEvent(eventName string, properties map[string]interface{}) {
js.Global().Call("analytics", map[string]interface{}{
"track": eventName,
"properties": properties,
})
}
// Kullanım
func processImage() {
start := time.Now()
// Görüntüyü işle
duration := time.Since(start)
trackEvent("image_processed", map[string]interface{}{
"duration_ms": duration.Milliseconds(),
"image_size": len(imageData), // Örnek: gerçek görüntü boyutunu kullan
})
}
|
18. Maliyet Analizi
18.1 Geliştirme Maliyetleri
Zaman Yatırımı:
- İlk kurulum: 2-4 saat
- Öğrenme eğrisi: 1-2 hafta (Go başlayanlar için)
- Migration: 1-2 hafta (mevcut kod için)
- Test ve optimizasyon: 1 hafta
Ekip Gereksinimleri:
- Go geliştiricisi (veya Go öğrenen)
- WASM’a aşina frontend geliştiricisi
- Cross-browser test için QA
18.2 Bakım Yükü
Devam Eden Maliyetler:
- Go versiyon güncellemeleri
- Tarayıcı uyumluluk testi
- Performans izleme
- Hata düzeltmeleri ve optimizasyonlar
Tahmini: Çeyrek başına ilk geliştirme zamanının %10-20’si
18.3 Altyapı Maliyetleri
CDN Maliyetleri:
- WASM binary depolama: ~$0.01 per GB
- Bant genişliği: ~$0.05 per GB
- Örnek: 1MB WASM, 100K kullanıcı/ay = ~$5/ay
Karşılaştırma:
- Sunucu İşleme: $50-500/ay (kullanıma bağlı)
- WASM (İstemci tarafı): $5-20/ay (sadece CDN)
- Tasarruf: Sunucu maliyetlerinde %70-95 azalma
| Yaklaşım |
Bundle Boyutu |
Performans |
Maliyet |
| Saf JavaScript |
50 KB |
Baseline |
Düşük |
| Go WASM (Standard) |
2-3 MB |
2-3x daha hızlı |
Orta |
| Go WASM (TinyGo) |
200-500 KB |
2-3x daha hızlı |
Düşük |
| Hybrid (JS + WASM) |
300-800 KB |
1.5-2x daha hızlı |
Düşük |
Öneri: Boyut ve performansı dengelemek için production’da TinyGo kullanın.
19. Alternatif Araçlar ve Runtime’lar
19.1 Wasmer
Wasmer, sunucu tarafı çalıştırma için bağımsız bir WebAssembly runtime’ıdır.
1
2
3
4
5
|
# Wasmer kurulumu
curl https://get.wasmer.io -sSfL | sh
# WASM modülünü çalıştır
wasmer run main.wasm
|
Kullanım Senaryoları:
- Sunucu tarafı WASM çalıştırma
- Plugin sistemleri
- Sandbox kod çalıştırma
19.2 Wasmtime
Wasmtime, küçük, hızlı ve güvenli bir WebAssembly runtime’ıdır.
1
2
3
4
5
|
# Wasmtime kurulumu
curl https://wasmtime.dev/install.sh -sSf | bash
# WASM modülünü çalıştır
wasmtime main.wasm
|
Kullanım Senaryoları:
- Edge computing
- Serverless fonksiyonlar
- Microservices
19.3 WASM Paket Yöneticileri
WAPM (WebAssembly Package Manager):
1
2
3
4
5
6
7
8
|
# WAPM kurulumu
curl https://get.wapm.io/install.sh -sSfL | sh
# Paket kurulumu
wapm install package-name
# Paketi çalıştır
wapm run package-name
|
19.4 Geliştirme Araçları Ekosistemi
Araçlar:
- wasm-pack: WASM paketlerini derle ve yayınla
- wasm-bindgen: Rust/Go ve JavaScript arasında binding’ler oluştur
- wasm-opt: WASM binary’lerini optimize et (Binaryen’den)
- wasm2wat: Hata ayıklama için WASM’ı metin formatına dönüştür
20. Kullanım Senaryoları ve Örnekler
20.1 Veri Görselleştirme
- Gerçek zamanlı grafik renderlama
- Büyük veri seti işleme
- Matematiksel hesaplamalar
20.2 Oyunlar
- 2D/3D oyun motorları
- Fizik simülasyonları
- Oyun mantığı işleme
20.3 Kriptografi
- İstemci tarafı şifreleme
- Hash hesaplamaları
- Dijital imzalar
20.4 Medya İşleme
- Görüntü filtreleri ve efektler
- Ses işleme
- Video encode/decode
21. Production Hazırlık Kontrol Listesi
Go WASM uygulamanızı production’a dağıtmadan önce aşağıdaki kontrol listesini tamamladığınızdan emin olun:
Binary Optimizasyonu
- Binary boyutu optimize edildi (< 500KB tercihen TinyGo ile)
- Build tag’leri ile kullanılmayan kod kaldırıldı
- Dead code elimination etkinleştirildi (
-ldflags="-s -w")
-
wasm-opt -Oz ile WASM optimize edildi
- Sıkıştırma etkinleştirildi (gzip/brotli)
- İlk yükleme süresi < 3 saniye
- First contentful paint < 1.5 saniye
- JavaScript-WASM sınır çağrıları minimize edildi
- Bellek kullanımı izlendi ve optimize edildi
- Performans benchmark’ları tamamlandı
- Ağır görevler için Web Workers uygulandı (gerekirse)
Güvenlik
- Girdi doğrulama uygulandı
- XSS koruması yerinde
- Content Security Policy (CSP) header’ları ayarlandı
- CORS header’ları doğru yapılandırıldı
- Bellek güvenlik kontrolleri uygulandı
- Hata mesajları hassas bilgi sızdırmıyor
Altyapı
- WASM dosyaları için CDN yapılandırıldı
- Doğru MIME tipi (
application/wasm) ayarlandı
- Cache header’ları yapılandırıldı (uzun vadeli cache)
- Sunucuda sıkıştırma etkinleştirildi
- HTTP/2 veya HTTP/3 etkinleştirildi
- İzleme ve uyarı sistemi kuruldu
Test
- Birim test coverage > %80
- Entegrasyon testleri geçiyor
- Tarayıcı uyumluluk testi yapıldı (Chrome, Firefox, Safari, Edge)
- Mobil tarayıcı testi tamamlandı
- Performans testleri geçiyor
- Bellek sızıntısı testleri tamamlandı
- Hata yönetimi testleri yerinde
Hata Yönetimi
- Hata takibi uygulandı (Sentry, vb.)
- WASM crash’leri yakalanıyor ve loglanıyor
- JavaScript’e geri dönüş uygulandı
- Kullanıcı dostu hata mesajları
- Hata kurtarma mekanizmaları yerinde
Dokümantasyon
- API dokümantasyonu tamamlandı
- Deployment rehberi yazıldı
- Troubleshooting rehberi mevcut
- Performans optimizasyon rehberi dokümante edildi
- Kod yorumları ve örnekler eklendi
İzleme
- Performans metrikleri takip ediliyor
- Hata oranları izleniyor
- Bellek kullanımı izleniyor
- Yükleme süresi takip ediliyor
- Kullanıcı analytics entegre edildi
- Kritik sorunlar için uyarılar yapılandırıldı
Tarayıcı Desteği
- Chrome 57+ test edildi
- Firefox 52+ test edildi
- Safari 11+ test edildi
- Edge 16+ test edildi
- Mobil tarayıcılar test edildi
- Desteklenmeyen tarayıcılar için geri dönüş
Kod Kalitesi
- Kod review yapıldı
- Linting geçti
- Bellek sızıntısı tespit edilmedi
- Goroutine’ler düzgün yönetiliyor
- JavaScript değerleri düzgün serbest bırakılıyor
- Tip güvenliği zorunlu
Dağıtım
- CI/CD pipeline yapılandırıldı
- CI’da otomatik test
- Staging ortamı test edildi
- Rollback planı hazırlandı
- Deployment dokümantasyonu tamamlandı
22. Örnek Projeler ve Kaynaklar
Açık Kaynak Go WASM Projeleri
Go WASM ile ilgili açık kaynak projeleri ve örnekleri araştırmak için GitHub’da şu terimlerle arama yapabilirsiniz:
go wasm - Genel Go WASM projeleri
golang webassembly - Go WebAssembly uygulamaları
tinygo wasm - TinyGo ile WASM projeleri
Popüler kategoriler:
- Görüntü işleme kütüphaneleri
- Oyun motorları
- Kriptografi araçları
- Veri görselleştirme kütüphaneleri
Öğrenme Kaynakları
Resmi Dokümantasyon:
Eğitim İçerikleri:
- YouTube’da “Go WebAssembly” veya “Go WASM” aramaları ile video tutoriallar
- WebAssembly topluluğu tarafından paylaşılan öğretici içerikler
Topluluk:
23. Sonuç: Go WASM Ne Zaman Kullanılmalı?
Go’nun WebAssembly desteği, web geliştirme için heyecan verici imkanlar açıyor. Go kodunu WASM’a derleyerek, tarayıcıda Go’nun tip güvenliği, mükemmel araçları ve performans özelliklerinden yararlanabilirsiniz. Ancak projeniz için doğru teknolojiyi seçmek çok önemlidir.
Go WASM Ne Zaman Kullanılmalı?
✅ Go WASM kullanın:
-
Mevcut Go kod tabanınız varsa
- Masaüstü/sunucu Go uygulamalarını web’e taşıma
- Kod yeniden kullanımı öncelik
- Ekip zaten Go biliyor
-
CPU yoğun hesaplamalar
- Görüntü/video işleme
- Veri analizi ve dönüşümleri
- Kriptografik işlemler
- Bilimsel hesaplama
-
Performans kritik
- JavaScript’ten 2-3x hızlanmaya ihtiyaç
- Büyük veri setlerini işleme
- Gerçek zamanlı hesaplamalar
-
Tip güvenliği önemli
- Karmaşık iş mantığı
- Finansal hesaplamalar
- Veri doğrulama
-
Daha büyük binary boyutlarını kabul edebilirsiniz
- Standard Go: 2-3 MB (birçok uygulama için kabul edilebilir)
- TinyGo: 200-500 KB (iyi bir denge)
❌ Go WASM kullanmayın:
-
Basit DOM manipülasyonu
- JavaScript daha basit ve hızlı
- Performans avantajı yok
-
Bundle boyutu kritik
- Mobil-öncelikli uygulamalar
- < 100 KB bundle’a ihtiyaç
- Rust veya saf JavaScript düşünün
-
Sık JavaScript interop
- Ağır DOM manipülasyonu
- Sıkı framework entegrasyonu
- JavaScript sınır ek yükü faydaları olumsuz etkiliyor
-
Hızlı prototipleme
- JavaScript iterasyon için daha hızlı
- Go derlemesi sürtünme yaratıyor
Go WASM vs Rust WASM vs JavaScript
| Kriter |
Go WASM |
Rust WASM |
JavaScript |
| Binary Boyutu |
200KB-3MB |
50-200KB |
10-50KB |
| Performans |
2-3x JS |
3-5x JS |
Baseline |
| Öğrenme Eğrisi |
Orta |
Dik |
Kolay |
| Araçlar |
Mükemmel |
İyi |
Mükemmel |
| Ekosistem |
Büyük |
Büyüyor |
Çok Büyük |
| Tip Güvenliği |
Güçlü |
En Güçlü |
Zayıf |
| Bellek Güvenliği |
İyi |
Mükemmel |
İyi |
| En İyi |
Mevcut Go kodu, dengeli ihtiyaçlar |
Maksimum performans, minimal boyut |
Çoğu web uygulaması |
Deneyime Dayalı Önerim:
-
Go WASM seçin eğer zaten Go kullanıyorsanız, iyi performansa ihtiyacınız varsa ve 200KB+ bundle’ları kabul edebiliyorsanız. Geliştirici deneyimi mükemmel ve kod yeniden kullanımı değerli.
-
Rust WASM seçin eğer maksimum performansa, en küçük binary boyutuna ihtiyacınız varsa veya performans kritik library’ler inşa ediyorsanız. Öğrenme eğrisi daha dik ama sonuçlar etkileyici.
-
JavaScript seçin çoğu web uygulaması için. V8 optimizasyonlarıyla modern JavaScript, kullanım durumlarının %90’ı için yeterince hızlıdır. WASM’ı yalnızca gerçek bir performans darboğazı tespit ettiğinizde düşünün.
Gerçek Dünya Karar Çerçevesi
1
2
3
4
5
6
7
8
9
10
11
12
|
// Karar ağacı
if hasExistingGoCode && needsPerformance {
return "Go WASM"
} else if needsMaxPerformance && sizeCritical {
return "Rust WASM"
} else if simpleApp || rapidPrototyping {
return "JavaScript"
} else if cpuIntensive && canAcceptSize {
return "Go WASM (TinyGo)"
} else {
return "JavaScript + Optimize"
}
|
Go WASM’ın Geleceği
Go’nun WebAssembly desteği gelişmeye devam ediyor:
- TinyGo’da daha iyi optimizasyon
- Daha küçük binary boyutları
- Gelişmiş JavaScript interop
- Daha iyi hata ayıklama araçları
- Sunucu tarafı WASM için WASI desteği
Ekosistem olgunlaşıyor ve Go WASM daha fazla kullanım durumu için uygun bir seçenek haline geliyor.
Son Düşünceler
Go WASM, web geliştirme cephanenizde güçlü bir araçtır. Gümüş kurşun değildir, ancak uygun şekilde kullanıldığında önemli faydalar sağlayabilir:
- Performans: CPU yoğun görevler için JavaScript’ten 2-3x daha hızlı
- Kod Yeniden Kullanımı: Mevcut Go kod tabanlarından yararlanın
- Tip Güvenliği: Hataları derleme zamanında yakalayın
- Geliştirici Deneyimi: Mükemmel araçlar ve ekosistem
Ancak, optimize etmeden önce her zaman ölçün. Birçok uygulama WASM’a ihtiyaç duymaz ve JavaScript genellikle pragmatik seçimdir. Açık bir performans gereksiniminiz veya yararlanabileceğiniz mevcut Go kodunuz olduğunda Go WASM kullanın.
Ana Çıkarımlar
- WebAssembly yüksek performanslı web uygulamalarını mümkün kılar
- Go, WASM geliştirme için mükemmel araçlar sağlar
- Spesifik ihtiyaçlarınız için doğru aracı seçin
- Optimize etmeden önce performansı ölçün
- Farklı tarayıcılar ve cihazlarda kapsamlı test edin
Sonraki Adımlar
- Küçük başlayın: Belirli bir performans kritik fonksiyon için basit bir Go WASM modülü oluşturun
- Ölçün: Faydaları doğrulamak için kullanım durumunuzu benchmark edin
- Optimize edin: TinyGo, wasm-opt ve en iyi uygulamaları kullanın
- İterasyon yapın: Production deneyiminden öğrenin ve yaklaşımınızı geliştirin
Unutmayın: En iyi teknoloji seçimi, geliştirici verimliliğini ve kullanıcı deneyimini korurken sorununuzu etkili bir şekilde çözen seçimdir.
Go ve WebAssembly ile mutlu kodlamalar! 🚀
Hızlı Referans
Temel Komutlar:
1
2
3
4
5
6
7
8
|
# WASM derle
GOOS=js GOARCH=wasm go build -o main.wasm main.go
# TinyGo ile derle
tinygo build -target wasm -o main.wasm main.go
# Optimize et
wasm-opt -Oz main.wasm -o main.optimized.wasm
|
Önemli Kaynaklar: