Go ile Mikroservis Mimarisi: Kurulum ve Best Practices
Go (Golang), Google tarafından geliştirilen, yüksek performanslı ve concurrent yapısına sahip modern bir programlama dilidir. Mikroservis mimarisi için idealdir çünkü hızlı derleme, iyi concurrency desteği ve küçük binary size gibi avantajlara sahiptir.
Neden Go Mikroservis İçin?
Avantajlar
- Hızlı Derleme: Büyük projelerde bile hızlı build süreleri
- Küçük Binary Size: Container deployment için ideal
- Built-in Concurrency: Goroutines ve channels ile kolay concurrent programming
- Static Typing: Compile-time error catching
- Strong Standard Library: HTTP, JSON, encoding dahil
- Cross-platform: Windows, macOS, Linux için native binary
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Merhaba, Mikroservis!")
})
fmt.Println("Server 8080 portunda çalışıyor...")
http.ListenAndServe(":8080", nil)
}
Basit Mikroservis Kurulumu
Proje Yapısı
my-microservice/
├── go.mod
├── go.sum
├── cmd/
│ └── server/
│ └── main.go
├── internal/
│ ├── handlers/
│ ├── models/
│ └── services/
├── pkg/
│ └── utils/
└── Dockerfile
main.go
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
// Route tanımlamaları
r.HandleFunc("/api/users", getUsersHandler).Methods("GET")
r.HandleFunc("/api/users/{id}", getUserHandler).Methods("GET")
// Middleware
r.Use(loggingMiddleware)
r.Use(authMiddleware)
// Server
port := ":8080"
log.Printf("Server %s portunda çalışıyor...", port)
log.Fatal(http.ListenAndServe(port, r))
}
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s %s", r.Method, r.RequestURI, r.RemoteAddr)
next.ServeHTTP(w, r)
})
}
gRPC ile Inter-Service Communication
gRPC, Google’ın geliştirdiği bir RPC framework’tür. HTTP/2, Protocol Buffers ve streaming’i destekler.
Proto Dosyası
syntax = "proto3";
package user;
service UserService {
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
}
message GetUserRequest {
string id = 1;
}
message GetUserResponse {
User user = 1;
}
message User {
string id = 1;
string name = 2;
string email = 3;
}
Go Server Implementation
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
)
type server struct {
pb.UnimplementedUserServiceServer
}
func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
// Veritabanından kullanıcı çek
user := &pb.User{
Id: req.Id,
Name: "John Doe",
Email: "john@example.com",
}
return &pb.GetUserResponse{User: user}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterUserServiceServer(s, &server{})
log.Println("gRPC server 50051 portunda çalışıyor...")
if err := s.Serve(lis); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}
Concurrency and Performance
Goroutines
func processOrders() {
orders := []int{1, 2, 3, 4, 5}
var wg sync.WaitGroup
for _, orderID := range orders {
wg.Add(1)
go func(id int) {
defer wg.Done()
// Siparişi işle
log.Printf("Sipariş %d işleniyor", id)
}(orderID)
}
wg.Wait()
}
Channels
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
log.Printf("Worker %d işliyor: %d", id, j)
results <- j * 2
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
// Worker'ları başlat
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// İşleri gönder
for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)
// Sonuçları al
for a := 1; a <= 5; a++ {
<-results
}
}
Best Practices
Error Handling
func processRequest(r *http.Request) (Response, error) {
user, err := getUserFromRequest(r)
if err != nil {
return Response{}, fmt.Errorf("kullanıcı alınamadı: %w", err)
}
data, err := processData(user)
if err != nil {
return Response{}, fmt.Errorf("veri işlenemedi: %w", err)
}
return Response{Data: data}, nil
}
Configuration Management
type Config struct {
ServerPort string `env:"SERVER_PORT" envDefault:"8080"`
DatabaseURL string `env:"DATABASE_URL" envDefault:"postgres://localhost"`
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
}
func LoadConfig() (*Config, error) {
cfg := &Config{}
if err := env.Parse(cfg); err != nil {
return nil, err
}
return cfg, nil
}
Logging
import (
"go.uber.org/zap"
)
var logger *zap.Logger
func InitLogger() error {
var err error
logger, err = zap.NewProduction()
if err != nil {
return err
}
return nil
}
func LogRequest(r *http.Request) {
logger.Info("gelen istek",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("ip", r.RemoteAddr),
)
}
Testing
package handlers_test
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestGetUsersHandler(t *testing.T) {
req := httptest.NewRequest("GET", "/api/users", nil)
w := httptest.NewRecorder()
getUsersHandler(w, req)
resp := w.Result()
if resp.StatusCode != http.StatusOK {
t.Errorf("beklenen durum kodu: %d, elde edilen: %d", http.StatusOK, resp.StatusCode)
}
}
Deployment
Dockerfile
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main cmd/server/main.go
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]
Öğrenme Yolu
- Go temelleri
- HTTP server’lar
- gRPC
- Testing
- Production deployment
Yorumlar (0)
Henüz yorum yapılmamış. İlk yorumu sen yap!
Yorum Yap