Skip to content

Bắt đầu

Hướng dẫn này sẽ giúp bạn cài đặt Fountain, khởi tạo ứng dụng đầu tiên và chạy nó. Fountain tổ chức hệ thống theo mô hình App → Services → Servers (Pods): App là tiến trình ở cấp cao nhất, nó chứa một hoặc nhiều Service, và mỗi Service cung cấp một hoặc nhiều Server theo từng giao thức.

Yêu cầu

  • Go 1.25.4 trở lên — Fountain hướng đến directive go 1.25.4 được khai báo trong go.mod của nó.
  • Quyền truy cập module GitLab riêng tư — Fountain được phân phối dưới dạng module riêng tư gitlab.soludian.com/soludian/fountain, vì vậy bộ công cụ Go cần được cấu hình để coi nó là riêng tư và có cách phân giải module này.

Đánh dấu namespace của module là riêng tư để lệnh go không cố lấy nó qua proxy công khai hay cơ sở dữ liệu checksum:

bash
go env -w GOPRIVATE=gitlab.soludian.com/*

Nếu bạn không thể phân giải module trực tiếp, hãy clone nó và trỏ module của bạn về bản sao cục bộ bằng một replace directive:

bash
git clone gitlab.soludian.com/soludian/fountain
go mod init ${PROJECT_PATH}
go mod edit -replace=gitlab.soludian.com/soludian/fountain=./fountain

Cài đặt

Sau khi đã thiết lập module riêng tư như trên, thêm Fountain vào module của bạn:

bash
go get gitlab.soludian.com/soludian/fountain

Vì đây là module riêng tư, bước GOPRIVATE (hoặc cách dùng go mod edit -replace) là bắt buộc phải làm trước — nếu không go get sẽ thất bại khi cố truy cập proxy module công khai.

Ứng dụng đầu tiên của bạn

Một chương trình Fountain được xây dựng từ các thành phần AppInstance mà framework sẽ kết nối và chạy giúp bạn. Bạn khởi tạo ứng dụng bằng fountain.New() (hoặc dùng phím tắt ở cấp package fountain.WithAppInstances(...)), đăng ký các instance, rồi gọi Serving() để bắt đầu vòng đời. Serving() là lệnh chặn (blocking): Fountain khởi tạo từng instance, khởi động các server của nó, và giữ cho tiến trình tiếp tục chạy cho đến khi có tín hiệu tắt.

Ví dụ bên dưới định nghĩa một APIServer nhúng một fhttp.Server. Trong Initialize(), nó cài đặt HTTP server từ khóa cấu hình server.fhttp, kết nối các core controller và các instance crypto, rồi đăng ký một số route — bao gồm cả phản hồi được mã hóa thông qua fhttp.WithContext(ctx).WithEncryption(). Hàm main() chỉ đơn giản trao instance cho Fountain và gọi Serving().

go
/* !!
 * File: main.go
 * File Created: Friday, 28th October 2022 5:19:38 pm
 * Author: Kim Ericko ([email protected])
 * -----
 * Last Modified: Wednesday, 8th March 2023 4:40:18 pm
 * Modified By: Kim Ericko ([email protected])
 * -----
 * Copyright 2022 Soludian, soludian.com
 * All rights reserved.
 *
 * Licensed under the SOLUDIAN TECHNOLOGY SOLUTION CO., LTD Software License Agreement.
 * Unauthorized use, reproduction, or distribution is prohibited (the "License");
 *
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 * [email protected] / https://www.soludian.com/license
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * -----
 *
 * HISTORY:
 *
 * Date      	By	Comments
 * ----------	---	---------------------------------------------------------
 */

package main

import (
	"fmt"
	"log"
	"time"

	"github.com/gofiber/fiber/v3"
	"gitlab.soludian.com/soludian/fountain"
	"gitlab.soludian.com/soludian/fountain/biz/core"
	"gitlab.soludian.com/soludian/fountain/libs/base/lib_3rd"
	"gitlab.soludian.com/soludian/fountain/libs/crypto"
	"gitlab.soludian.com/soludian/fountain/libs/flog"
	"gitlab.soludian.com/soludian/fountain/libs/fnet/fhttp"
)

var logger = flog.NewFountainLoggerOnce()

type NoModule struct {
}

func (NoModule) Liveness() bool {
	return true
}

func (NoModule) Readiness() bool {
	return false
}

type APIServer struct {
	*fhttp.Server
}

func (s *APIServer) Initialize() error {
	s.Server = fhttp.WithConfigKey("server.fhttp").InstallFountainInstance()

	core.InstallCoreControllers()

	cr := crypto.InstallFountainInstances()
	log.Printf("Initializing: %+v", cr.GetFountainInstanceNames())

	s.Server.Get("/panic", func(ctx fiber.Ctx) error {
		err := fmt.Errorf("route panic")
		logger.WErr(err).Panicf(err.Error())
		return err
	})

	s.Server.Get("/200", func(ctx fiber.Ctx) error {
		return ctx.SendString("hello")
	})

	s.Server.Get("/hello", func(ctx fiber.Ctx) error {
		logger.Infof(ctx.Route().Path)
		return ctx.JSON("Hello client: " + ctx.Get("app"))
	})

	s.Server.Get("/encrypt", func(ctx fiber.Ctx) error {
		logger.Infof(ctx.Route().Path)
		return fhttp.WithContext(ctx).WithEncryption().WithStatus(200).WriteSuccess("Hello client: " + ctx.Get("app"))
	})

	s.Server.Get("/encrypt-cbc", func(ctx fiber.Ctx) error {
		data := make(map[string]any)
		data["server"] = "encrypt-cbc"
		data["time"] = time.Now().String()

		logger.Infof(ctx.Route().Path)
		return fhttp.WithContext(ctx).WithEncryptionFunc(crypto.GetFountainInstance("AES-CBC")).WithStatus(200).WriteSuccess(data)
	})

	s.Server.Get("/encrypt-ctr", func(ctx fiber.Ctx) error {
		data := make(map[string]any)
		data["server"] = "encrypt-ctr"
		data["time"] = time.Now().String()

		logger.Infof(ctx.Route().Path)
		return fhttp.WithContext(ctx).WithEncryptionFunc(crypto.GetFountainInstance("AES-CTR")).WithStatus(200).WriteSuccess(data)
	})

	s.Server.Get("/500", func(ctx fiber.Ctx) error {
		return ctx.Status(500).JSON("Hello client: " + ctx.Get("app"))
	})

	lib_3rd.RegisterHealthCheck("NoModule", &NoModule{})
	s.Server.InitHealthCheckPath("/")

	return nil
}

func main() {
	server := &APIServer{}
	fountain.WithAppInstances(server).Serving()
}

Cấu hình

Fountain đọc cấu hình từ một tệp config.yaml sử dụng các khóa lồng nhau. Mỗi thư viện gắn với một khóa — ví dụ HTTP server trong ví dụ trên được cài đặt với fhttp.WithConfigKey("server.fhttp"), nên các thiết lập của nó nằm trong khối server.fhttp. Bất kỳ giá trị nào cũng có thể được ghi đè lúc chạy thông qua biến môi trường dùng tiền tố FOUNTAIN_; một biến môi trường ánh xạ tới đường dẫn cấu hình lồng nhau tương ứng, rất tiện cho việc triển khai theo từng môi trường và quản lý secret.

yaml
env:
  log_print_level: 5
  log_file_level: 5
  
server.fhttp:
  addr: ":9007"
  enable_csrf: true
  enable_access_interceptor: true
  enable_access_interceptor_req: true
  enable_access_interceptor_res: true
  access_interceptor_req_res_filter: 'hahaha'
  server_read_timeout: 1m
  server_read_header_timeout: 1h
  server_write_timeout: "30m"
  server_http_timeout: "5s"
  metrics_enable: true

trace:
  service_name: "server"

crypto:
  - algorithm: AES-CTR
    key: "1234567890123456"
  - algorithm: AES-CBC
    key: "1234567890123456"

Chạy ứng dụng

Từ thư mục chứa main.goconfig.yaml, khởi động ứng dụng:

bash
go run .

Fountain khởi tạo các instance đã đăng ký và khởi động HTTP server trên địa chỉ lấy từ server.fhttp.addr (:9007 trong ví dụ). Khi đã chạy, bạn có thể truy cập các route mà ví dụ định nghĩa, chẳng hạn GET /200, GET /hello, và route mã hóa GET /encrypt. Tiến trình tiếp tục chạy cho đến khi bạn ngắt nó (Ctrl+C), lúc đó Fountain sẽ tắt các instance một cách an toàn.

Bước tiếp theo

  • Khái niệm cốt lõi — mô hình App → Services → Servers, vòng đời và controller được trình bày chi tiết.
  • Xây dựng Service — lắp ráp một service thực tế từ các thành phần của Fountain.