How Tencent Docs Seamlessly Switches Between Monolith and Microservices
This article explains how Tencent Docs tackled the high runtime, deployment, and image‑distribution costs of a large microservice fleet by building a flexible architecture that can merge selected services into a few monolith binaries for private deployments, while retaining microservices for public scenarios, and shares the tooling, configuration, challenges, and performance gains achieved.
Background
Software architecture has no universal silver bullet; a good design must be continuously iterated. Tencent Docs faced business pressures that required a flexible solution capable of switching between a monolithic deployment and a microservice‑based deployment.
Why Switch Between Monolith and Microservices?
Monolithic services are simple to develop and maintain, making them suitable for small‑to‑medium projects, but they suffer from scalability and deployment issues as the codebase grows. Microservices provide isolation, resilience, and independent scaling, which is ideal for large projects, yet they introduce higher operational complexity, deployment overhead, and resource consumption.
Challenges Driving the Switch
Running over a hundred microservices in a private‑cloud scenario caused excessive CPU and memory usage because each service instantiated its own tRPC‑Go runtime.
Maintaining separate tad description files for each service created a massive maintenance burden during version upgrades.
Packaging each microservice as a Docker image resulted in a total image size exceeding 10 GB, making distribution to private customers impractical.
To keep the advantages of microservices while eliminating these drawbacks, Tencent Docs designed a strategy that merges selected microservices into a small number of monolith services for private deployments, yet continues to run microservices on the public TKEx platform.
Tooling: the monolith Utility
The monolith tool reads a YAML‑style configuration that lists modules and the microservices to be merged. An excerpt of the configuration format is shown below:
modules:
- name: monolith-module1
merged_servers:
- name: microserver1
...
- name: microserver2
...
- name: monolith-module2
merged_servers:
- name: microserver3
...
- name: microserver4
...During each merge request (MR), the tool automatically detects which services need to be merged and generates the corresponding monolith code.
Key Challenges and Solutions
3.1 Diverse Frameworks
Over five years, Tencent Docs experimented with several frameworks (tRPC‑Go, tRPC‑Cpp, SPP, LSF). To keep the merging process manageable, the team standardized on tRPC‑Go and only merged services that adhered to the standard tRPC‑Go conventions.
3.2 Divergent Configuration Structures
Microservices and monolith services originally used different configuration layouts. A unified configuration component was introduced, allowing both deployment models to share a common plugin.config schema. Example snippets illustrate the before‑and‑after structures:
# Microservice configuration
plugin:
config:
app:
providers:
serverA:
app.yaml:
foo: bar
foz: baz # Monolith configuration
plugin:
config:
app:
providers:
serverA:
app.yaml:
foo: bar
foz: baz
serverB:
app.yaml:
zoo: bar
zoz: baz
...Plugin configuration conflicts (e.g., different Redis targets) were also resolved by normalising the schema.
3.3 Global Variable Modifications
tRPC‑Go exposes global variables such as http.DefaultServerCodec and restful.Marshaller. Modifying these globals in a monolith can affect unrelated services. Two mitigation strategies were adopted:
Common plugin : Provide a unified CGI component that encapsulates the required behaviour without touching globals.
Module isolation : Isolate services that need to modify restful.Marshaller into a dedicated module, keeping them separate from services that do not.
3.4 Hidden Bugs When Merging
Some bugs only surface after merging. For example, the following registration code overwrites previously registered services in a monolith, leading to 404 responses:
func Register(s *server.Server) {
someserverpb.RegisterSomeServerSomeService(s, newSomeServiceImpl())
}The author proposes a “binary localisation” debugging method: repeatedly halve the set of merged services, test the behaviour, and narrow down the offending service.
3.5 Change Management
Automated checks run on every MR to verify that merged services still satisfy configuration and structural constraints. Future plans include integrating interface tests and end‑to‑end (e2e) tests to further guard against regressions.
Generated Monolith Template
// Code generated by backend/tools/monolith, DO NOT EDIT.
// Package service rpc entry layer.
package service
import (
// merged services are imported here
// e.g., "docx/backend/application/serviceA/service"
"git.code.oa.com/trpc-go/trpc-go/server"
)
// Register registers pb service implementations.
func Register(s *server.Server) {
// each merged service registers itself
serviceA.Register(s)
serviceB.Register(s)
// ...
}This template registers all standardized services into a single binary, dramatically simplifying deployment.
Performance Gains
Comparing a representative module before and after monolithisation yields the following improvements:
Binary size reduced from 264 MB to 68 MB (≈ 74 % reduction).
Memory usage dropped from 1 722 MB to 670 MB (≈ 61 % reduction).
CPU consumption fell from 6.43 cores to 4.99 cores (≈ 22 % reduction).
Average request latency changed marginally from 9.8 ms to 9.3 ms.
Extrapolating to the entire system, total image size is expected to shrink by 75 % and total memory consumption by 96 %.
Conclusion
By building a flexible architecture that can merge microservices into monolith binaries on demand, Tencent Docs achieved substantial resource savings while preserving the benefits of microservices for public scenarios. The approach demonstrates how careful standardisation, automated tooling, and systematic debugging can enable large‑scale service fleets to adapt to diverse deployment requirements.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
