golang构建生产级可扩展Web服务的插件库ardanlabs/service的使用

Golang构建生产级可扩展Web服务的插件库ardanlabs/service的使用

简介

ardanlabs/service是一个用于构建生产级可扩展Web服务的Go语言插件库和starter kit。它采用领域驱动、数据导向的架构设计,可以运行在Kubernetes环境中。

这个项目的目标是提供一个经过验证的起点,减少新项目投入生产所需的重复性任务。它使用最少的依赖,实现符合Go语言习惯的代码,并遵循Go的最佳实践。

主要特性

  • 为生产级Web服务提供完整的架构和实现
  • 领域驱动设计(DDD)和数据导向架构
  • Kubernetes支持
  • 最小化依赖
  • 符合Go语言习惯的实现
  • 生产环境最佳实践

安装

要克隆项目,创建一个文件夹并使用git clone命令:

$ cd $HOME
$ mkdir code
$ cd code
$ git clone https://github.com/ardanlabs/service.git
$ cd service

创建自己的版本

如果你想创建项目的自定义版本,可以使用gonew命令:

$ go install golang.org/x/tools/cmd/gonew@latest

$ cd $HOME
$ mkdir code
$ cd code
$ gonew github.com/ardanlabs/service github.com/mydomain/myproject
$ cd myproject
$ go mod vendor

运行项目

使用以下命令运行项目:

# 安装工具
$ make dev-gotooling
$ make dev-brew
$ make dev-docker

# 运行测试
$ make test

# 运行项目
$ make dev-up
$ make dev-update-apply
$ make token
$ export TOKEN=<COPY TOKEN>
$ make users

# 运行负载测试
$ make load

# 运行监控工具
$ make grafana
$ make statsviz

# 关闭项目
$ make dev-down

示例代码

以下是一个使用ardanlabs/service构建Web服务的简单示例:

package main

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/ardanlabs/service/app/services/sales-api/handlers"
	"github.com/ardanlabs/service/business/web/v1/debug"
	"github.com/ardanlabs/service/foundation/web"
)

func main() {
	// 创建日志器
	log := debug.New()

	// 创建应用
	app := web.NewApp(log)

	// 注册路由
	app.Handle(http.MethodGet, "/", handlers.Home)
	app.Handle(http.MethodGet, "/health", handlers.Health)

	// 启动服务器
	api := http.Server{
		Addr:         "localhost:3000",
		Handler:      app,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
		IdleTimeout:  120 * time.Second,
	}

	// 启动服务器
	go func() {
		log.Info(context.Background(), "startup", "status", "api router started", "host", api.Addr)

		if err := api.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Error(context.Background(), "shutdown", "msg", err)
		}
	}()

	// 等待中断信号
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit

	// 优雅关闭
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if err := api.Shutdown(ctx); err != nil {
		log.Error(ctx, "shutdown", "msg", err)
	}

	fmt.Println("Server stopped")
}

项目优势

  1. 减少复杂性:项目通过合理的架构设计帮助管理和减少系统复杂性
  2. 生产就绪:包含监控、日志、配置管理等生产环境所需功能
  3. 可扩展性:支持Kubernetes,可以轻松扩展
  4. 最佳实践:遵循Go语言和Web开发的最佳实践
  5. 领域驱动:采用领域驱动设计,代码结构清晰

学习资源

该项目背后有一个完整的培训课程,教你如何利用领域驱动和数据导向的架构在Go中构建生产级软件。课程从设计哲学和指南开始,逐步添加功能,并学习如何部署和管理Kubernetes环境。


更多关于golang构建生产级可扩展Web服务的插件库ardanlabs/service的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang构建生产级可扩展Web服务的插件库ardanlabs/service的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用ardanlabs/service构建生产级可扩展Web服务

ardanlabs/service是Go语言中一个优秀的Web服务框架,它提供了一套完整的工具和最佳实践来构建生产级、可扩展的Web服务。下面我将详细介绍如何使用这个库。

核心特性

  1. 内置优雅关闭支持
  2. 健康检查端点
  3. 标准化日志记录
  4. 配置管理
  5. 中间件支持
  6. 监控和指标集成

基本使用示例

package main

import (
	"context"
	"net/http"

	"github.com/ardanlabs/service/app/services/sales-api/handlers"
	"github.com/ardanlabs/service/business/web/v1/debug"
	"github.com/ardanlabs/service/foundation/logger"
	"github.com/ardanlabs/service/foundation/web"
)

func main() {
	// 初始化日志
	log := logger.New("SALES-API")
	
	// 创建Web应用
	app := web.NewApp(
		log,
		web.WithShutdown(func(ctx context.Context) {
			// 自定义关闭逻辑
			log.Info(ctx, "shutdown complete")
		}),
	)

	// 注册路由
	app.Handle(http.MethodGet, "/v1/health", handlers.Health)
	
	// 注册调试路由
	debug.Mux(app)
	
	// 启动服务
	if err := web.Run(context.Background(), app, web.Config{
		Host:         "0.0.0.0",
		Port:         3000,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
		IdleTimeout:  120 * time.Second,
	}); err != nil {
		log.Error(context.Background(), "startup", "msg", err)
		os.Exit(1)
	}
}

配置管理

ardanlabs/service推荐使用环境变量进行配置:

type Config struct {
	Web struct {
		APIHost         string        `default:"0.0.0.0:3000"`
		DebugHost       string        `default:"0.0.0.0:4000"`
		ReadTimeout     time.Duration `default:"5s"`
		WriteTimeout    time.Duration `default:"10s"`
		IdleTimeout     time.Duration `default:"120s"`
		ShutdownTimeout time.Duration `default:"20s"`
	}
	DB struct {
		User         string `default:"postgres"`
		Password     string `default:"postgres" mask:"true"`
		Host         string `default:"localhost"`
		Name         string `default:"postgres"`
		MaxIdleConns int    `default:"2"`
		MaxOpenConns int    `default:"0"`
		DisableTLS   bool   `default:"true"`
	}
}

func main() {
	var cfg Config
	if err := config.Load(&cfg); err != nil {
		log.Error(context.Background(), "config", "msg", err)
		os.Exit(1)
	}
	
	// 使用配置...
}

数据库集成

// 初始化数据库连接
db, err := database.Open(database.Config{
	User:         cfg.DB.User,
	Password:     cfg.DB.Password,
	Host:         cfg.DB.Host,
	Name:         cfg.DB.Name,
	MaxIdleConns: cfg.DB.MaxIdleConns,
	MaxOpenConns: cfg.DB.MaxOpenConns,
	DisableTLS:   cfg.DB.DisableTLS,
})
if err != nil {
	log.Error(ctx, "startup", "msg", err)
	os.Exit(1)
}
defer db.Close()

// 将数据库连接注入到handler中
userHandler := handlers.NewUser(log, db)
app.Handle(http.MethodGet, "/v1/users/:id", userHandler.GetByID)

中间件使用

// 添加全局中间件
app.Use(
	middleware.Logger(log),
	middleware.Errors(log),
	middleware.Metrics(),
	middleware.Panics(log),
)

// 路由特定中间件
app.Handle(http.MethodGet, "/v1/users", userHandler.Query, middleware.Authenticate())

监控和指标

// 初始化指标
metrics := expvar.New()

// 注册指标端点
app.Handle(http.MethodGet, "/debug/vars", web.WrapHandler(expvar.Handler()))

// 在业务代码中记录指标
func (h *UserHandler) GetByID(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	h.metrics.Add("users", 1)
	
	// 业务逻辑...
}

测试支持

func TestUserHandlers(t *testing.T) {
	// 创建测试应用
	app := web.NewApp(
		logger.NewTestLogger(),
		web.WithShutdown(func(ctx context.Context) {}),
	)
	
	// 注册测试路由
	app.Handle(http.MethodGet, "/v1/users/:id", testHandler)
	
	// 创建测试服务器
	srv := httptest.NewServer(app)
	defer srv.Close()
	
	// 执行测试请求
	resp, err := http.Get(fmt.Sprintf("%s/v1/users/123", srv.URL))
	if err != nil {
		t.Fatalf("should not be error: %v", err)
	}
	defer resp.Body.Close()
	
	// 验证响应
	if resp.StatusCode != http.StatusOK {
		t.Errorf("should be 200: %v", resp.StatusCode)
	}
}

最佳实践

  1. 使用context传递请求范围的值
  2. 所有错误都通过中间件统一处理
  3. 业务逻辑与传输层分离
  4. 使用结构体标签进行配置验证
  5. 为所有外部依赖设置超时
  6. 实现健康检查端点

ardanlabs/service框架提供了一套完整的工具来构建生产级服务,遵循这些模式和最佳实践可以创建出健壮、可维护和可扩展的Web服务。

回到顶部