← Back to articles

Infrastructure (golang)

An overview of the infrastructure and common components involved in developing Go services.

When writing a Go service, business code is only one part of it. Where the data is stored, how configuration is read, and where to look for logs after something goes wrong — these also need to be thought through in advance. Here I look at these foundational components along the path of a single request, so they can serve as a reference in later projects.

What is infrastructure?

Put simply, it is the supporting capability behind the business that gets used again and again. Take a task list: creating a task is the business; the database that stores tasks, the login state, logs, deployment, and backups are what keep the business running.

Infrastructure is also not the same thing as a microservice framework. Kratos and go-zero mainly organize service code and wire in components; MySQL, Redis, and message queues are independent systems. Even when you use a framework, you still have to understand what each of these components is responsible for.

What does a request pass through?

Take “create a task” as an example:

浏览器 → HTTP 接口 → 参数校验 → 业务逻辑 → 数据库 → 响应
                           ↓
                    日志、指标、链路记录

A minimal version can have just one Go process and one database. Get input validation, error responses, timeouts, and data storage right first. Add caching, queues, and service discovery only once a clear need appears.

How do the common components divide the work?

ComponentWhat problem it solvesWhat to check first when integrating
ConfigurationDistinguishes local, test, and production environmentsDefaults, required fields, source of sensitive configuration
DatabaseStores data that must be kept long termConstraints, indexes, transactions, connection pool
CacheReduces repeated queries and holds temporary results that can be lostExpiration time, invalidation timing, data consistency
Message queueMoves slower work out of the requestConsumption retries, duplicate messages, backlog
Service discoveryFinds available instances by service nameHealth status, address changes, call timeouts
ObservabilityShows whether the service is healthy and where problems occurCorrelation among logs, metrics, and traces

Database: get correctness right first

A task title must not be empty, and a task ID must not repeat; put constraints like these in the database wherever possible. When a single business operation involves several writes, consider whether a transaction can keep them consistent. For MySQL transactions and locking, a good place to start is the InnoDB documentation.

Also, more database connections is not better. The connection pool limit needs to be set according to database capacity and request latency, and queries should have timeouts. In Go, you can pass the request’s context.Context to QueryContext and ExecContext; see Canceling database operations for details.

Cache: don’t treat it as a second database you can write to carelessly

For example, if task details are read cache-first and fall back to the database on a miss, then after a task is updated the stale cache has to be dealt with. Work out the cache invalidation rules first, then think about performance.

For a Go client for Redis, see the official go-redis usage. The client should be reused over the application’s lifetime; don’t create a new one for every HTTP request.

Message queue: after going async, the problems have not gone away

Work such as sending notifications or generating reports can be recorded as a task first and then handed to a background consumer. The easiest thing to miss here is duplicate execution: if the same message is consumed twice because of a retry, will the business still be correct? This generally requires a business ID, state constraints, or other idempotency measures.

If it is just a low-frequency small tool, synchronous execution or a simple background job may be enough. Every additional component also means more deployment, monitoring, and troubleshooting work.

Logs, metrics, and traces: what does each one tell you?

  • Logs: what happened during this request and where the error is.
  • Metrics: whether the error rate has risen recently, and whether latency or queue length is abnormal.
  • Traces: which calls a single request spans and where the time is mostly spent.

These kinds of information can be tied together with a request ID or trace ID. For what each is used for, see OpenTelemetry’s explanation of signals. Record the necessary context in logs, but do not print passwords, tokens, or complete user data directly.

How should this be organized in a project?

A small project can start with a layout like this:

cmd/server/           程序入口,组装依赖,启动和退出
internal/service/     接口层,转换请求和响应
internal/biz/         业务规则
internal/data/        数据库、缓存等访问实现
configs/              可以公开的配置示例

This is one way to organize things, not a structure every project must follow. When there is only one simple endpoint, there is no need to build an interface layer for every function. The point is that business rules should not be tied to a particular database client, and the creation and closing of resources should be easy to find.

A pass before going live

I prefer to check by following an actual request: is bad input stopped, are there timeouts after the database slows down, does the program stop accepting requests when it exits, can backups be restored, and can a problem be traced from the logs?

No matter how many component names you know, the service will still be unstable if these things are not done. Make one small service complete first, then add components step by step; it is easier to understand why each step is necessary.

Consul: service registration and discovery, microservice frameworks, Kratos, go-zero.

About this article’s date

The old date, 2025-01-16, does not match the article's publication record; the date shown here is 2026-01-20, the earliest publication date that can be confirmed from the old site. The actual writing date remains unconfirmed. The old page had only a placeholder title; the body was added on 2026-10-06.

Original date: 2025-01-16 21:15:06 +0800