Go'nun en güçlü özellikleri goroutine'ler ve channel'lar ile eşzamanlı (concurrent) programlama yapabilmesidir. Bu derste Go'nun concurrency modelini anlıyor ve net/http ile REST API geliştiriyoruz.
Goroutine Temelleri
// GO //
package main
import (
"fmt"
"sync"
"time"
)
func main() {
// Goroutine — "go" anahtar kelimesiyle başlatılır
go func() {
fmt.Println("Ben goroutine'yim")
}()
// WaitGroup — goroutine'lerin bitmesini bekle
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
time.Sleep(time.Duration(id) * 100 * time.Millisecond)
fmt.Printf("Goroutine %d tamamlandı\n", id)
}(i)
}
wg.Wait() // Hepsi bitene dek bekle
fmt.Println("Tümü tamamlandı")
}Channel — Goroutine İletişimi
// GO //
// Unbuffered channel — senkron iletişim
ch := make(chan int)
go func() {
ch <- 42 // Gönder (alıcı hazır olana dek bekler)
}()
deger := <-ch // Al (gönderici hazır olana dek bekler)
fmt.Println(deger)
// Buffered channel — asenkron, kapasiteye kadar beklemez
bufferedCh := make(chan string, 3)
bufferedCh <- "birinci"
bufferedCh <- "ikinci"
bufferedCh <- "üçüncü"
// bufferedCh <- "dördüncü" // BLOKLAR — kapasite dolu
// Range ile channel'dan okuma
sayilar := make(chan int, 5)
go func() {
for i := 0; i < 5; i++ {
sayilar <- i
}
close(sayilar) // Kapanmazsa range sonsuza döner!
}()
for sayi := range sayilar {
fmt.Println(sayi)
}Select — Çoklu Channel
// GO //
func parallelVeriCek(url1, url2 string) (string, string) {
ch1 := make(chan string, 1)
ch2 := make(chan string, 1)
go func() { ch1 <- httpGet(url1) }()
go func() { ch2 <- httpGet(url2) }()
var sonuc1, sonuc2 string
for i := 0; i < 2; i++ {
select {
case s := <-ch1:
sonuc1 = s
case s := <-ch2:
sonuc2 = s
case <-time.After(5 * time.Second):
// Timeout
return sonuc1, sonuc2
}
}
return sonuc1, sonuc2
}
// Fan-out / Fan-in deseni
func fanOut(is <-chan int, isciSayisi int) []<-chan int {
cikislar := make([]<-chan int, isciSayisi)
for i := 0; i < isciSayisi; i++ {
cikislar[i] = isci(is)
}
return cikislar
}
func isci(giris <-chan int) <-chan int {
cikis := make(chan int)
go func() {
defer close(cikis)
for n := range giris {
cikis <- n * n // Karesini al
}
}()
return cikis
}Mutex — Paylaşılan Veri Koruması
// GO //
type GuvenliSayac struct {
mu sync.RWMutex
deger int
}
func (s *GuvenliSayac) Arttir() {
s.mu.Lock()
defer s.mu.Unlock()
s.deger++
}
func (s *GuvenliSayac) Deger() int {
s.mu.RLock()
defer s.mu.RUnlock()
return s.deger
}
// sync.Map — concurrent-safe map
var onbellek sync.Map
func veriGetir(anahtar string) (interface{}, bool) {
return onbellek.Load(anahtar)
}
func veriKaydet(anahtar string, deger interface{}) {
onbellek.Store(anahtar, deger)
}net/http ile REST API
// GO //
// main.go
package main
import (
"encoding/json"
"log"
"net/http"
"time"
)
type Sunucu struct {
router *http.ServeMux
db *VeritabaniIstemcisi
}
func YeniSunucu(db *VeritabaniIstemcisi) *Sunucu {
s := &Sunucu{router: http.NewServeMux(), db: db}
s.rotaKur()
return s
}
func (s *Sunucu) rotaKur() {
s.router.HandleFunc("GET /api/makaleler", s.makaleListele)
s.router.HandleFunc("GET /api/makaleler/{id}", s.makaleGetir)
s.router.HandleFunc("POST /api/makaleler", s.kimlikDogrula(s.makaleOlustur))
}
// JSON yardımcı fonksiyonlar
func jsonCevap(w http.ResponseWriter, durum int, veri any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(durum)
json.NewEncoder(w).Encode(veri)
}
func jsonHata(w http.ResponseWriter, durum int, mesaj string) {
jsonCevap(w, durum, map[string]string{"hata": mesaj})
}
// Handler
func (s *Sunucu) makaleListele(w http.ResponseWriter, r *http.Request) {
makaleler, err := s.db.MakaleListele(r.Context())
if err != nil {
jsonHata(w, http.StatusInternalServerError, "Sunucu hatası")
return
}
jsonCevap(w, http.StatusOK, makaleler)
}
func (s *Sunucu) makaleGetir(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id") // Go 1.22+
makale, err := s.db.MakaleGetir(r.Context(), id)
if err != nil {
jsonHata(w, http.StatusNotFound, "Makale bulunamadı")
return
}
jsonCevap(w, http.StatusOK, makale)
}
// Middleware — kimlik doğrulama
func (s *Sunucu) kimlikDogrula(sonraki http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
jsonHata(w, http.StatusUnauthorized, "Token gerekli")
return
}
sonraki(w, r)
}
}
func main() {
db := VeritabaniBaglan()
sunucu := YeniSunucu(db)
srv := &http.Server{
Addr: ":8080",
Handler: sunucu.router,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
}
log.Println("Sunucu :8080 portunda başlatıldı")
log.Fatal(srv.ListenAndServe())
}Context ile İptal Yönetimi
// GO //
func (s *Sunucu) agirIslem(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() // HTTP isteği iptal edilirse context iptal olur
sonuc, err := s.veritabaniSorgu(ctx)
if err != nil {
select {
case <-ctx.Done():
// İstemci bağlantıyı kesti
return
default:
jsonHata(w, 500, "Veritabanı hatası")
}
return
}
jsonCevap(w, 200, sonuc)
}
// Timeout ile context
func veritabaniSorgu(parent context.Context) ([]Makale, error) {
ctx, iptal := context.WithTimeout(parent, 5*time.Second)
defer iptal()
return db.QueryContext(ctx, "SELECT * FROM makaleler WHERE yayinlandi = true")
}Hata Yönetimi
// GO //
// Go'da hata, dönüş değeridir — exception yoktur
type BulunamadiHatasi struct {
Kaynak string
ID string
}
func (h BulunamadiHatasi) Error() string {
return fmt.Sprintf("%s bulunamadı: %s", h.Kaynak, h.ID)
}
func makaleGetir(id string) (*Makale, error) {
makale, err := db.FindByID(id)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, BulunamadiHatasi{Kaynak: "Makale", ID: id}
}
return nil, fmt.Errorf("makale getirme hatası: %w", err) // Wrap
}
return makale, nil
}
// Çağıran taraf
makale, err := makaleGetir("abc-123")
if err != nil {
var bulunamadi BulunamadiHatasi
if errors.As(err, &bulunamadi) {
jsonHata(w, 404, bulunamadi.Error())
return
}
jsonHata(w, 500, "Sunucu hatası")
return
}Sonuç
Go'nun goroutine ve channel modeli, geleneksel thread'lere kıyasla çok daha hafif ve verimli concurrent programlama sağlar. net/http ile minimal ve hızlı REST API'ler, context.Context ile iptal yönetimi ve Go'nun hata-dönüş-değeri yaklaşımı birlikte güvenilir sunucu uygulamaları oluşturmanıza olanak tanır. Bir sonraki derste Go ile PostgreSQL entegrasyonunu ve sqlc kullanımını inceleyeceğiz.