Building a Lightweight SMS Platform Service from Scratch
Motivation
In 2018, while serving as an architect, I participated in the reconstruction of an SMS platform. The SMS sending scenarios included repayment business, CRM, promotional activities, and more.
Different technical teams used varying client modes to send SMS messages, which lacked standardization:
- Using Alibaba Cloud's SMS SDK for sending
- Direct SMS sending via examples provided by Yimei
- Using Lvcheng's SMS SDK
- The architecture team's SMS SDK, similar to SMS4J's design approach, supporting Yimei and Lvcheng SMS sending
The client mode approach in multi-team collaboration scenarios has significant drawbacks:
Maintenance Burden
If operations discontinue using a particular SMS channel, many teams would be affected and would need to modify configurations and redeploy, resulting in high time costs.
Limited Advanced Functionality Support
Certain features are difficult to implement in client mode. For example, when a client experiences timeout issues with a third-party channel due to network issues, the system needs to route SMS to a backup channel to ensure delivery success rates.
Therefore, in multi-team collaboration scenarios, the SMS service should adopt a server-side mode.
Architecture Design
The project targets helping junior to mid-level engineers understand architecture design, so the implementation follows a monolithic application pattern.
The SMS platform consists of two components that can be deployed independently or combined:
1. Frontend: admin-ui
The console module is a Vue project. Administrators can manage applications, channels, SMS records, and templates after logging in.
2. Backend: admin-web
The backend module is divided into five layers based on functionality:
- Request Controller Layer
- Business Service Layer
- Command Handler
- Third-party Channel Adapter Plugins
- Database Access Layer
The design mimics Tencent Cloud's SMS service architecture:
- Following Tencent Cloud's SDK design pattern to provide simple SMS sending methods (single send, batch send, marketing single send, marketing batch send, template single send, template batch send)
- Designing an SMS service API endpoint to receive requests and push SMS data to a message queue
- Worker services consuming messages and calling different channel provider interfaces using load balancing algorithms
- Console for viewing SMS sending records and configuring channel providers and templates
After completion, the SMS platform met business requirements. SMS management became centralized, improving the efficiency of business teams integrating the SMS service.
Core Design Principles
Based on industry observations, the core challenges in SMS services typically involve:
- Boundary issues between SMS services and business services - Adding excessive business logic to the SMS platform causes feature bloat and introduces hidden risks.
- Inflexible third-party channel switching - When operations need to switch from channel A to channel B, poor code abstraction makes maintenance costly.
For this lightweight SMS service, the following features are essential:
1. Simple SMS SDK Supporting Template-based Sending
Business services don't care which third-party channel sends the SMS, only that it's delivered successfully. The SDK provides the core interface: send SMS by template ID.
Alibaba Cloud, Tencent Cloud, and Huawei Cloud all provide template-based SMS APIs. For unified management, the platform only supports template-based sending. The SMS platform provides appKey and appSecret for business services, with SDK and server communication via a fixed protocol.
2. Template Management Support
All three providers offer signature and template management APIs. The platform supports manual binding of SMS templates, meaning singatures and templates must first be applied for through Alibaba Cloud or Tencent Cloud, then bound to platform-created templates.
3. Adapter Pattern for Third-party Channel Maintenance
Inspired by canal's adapter module, third-party SMS channel APIs are isolated as separate modules. This significantly improves code maintainability.
Deployment Guide
Environment Setup
1. Database Initialization
Create database tech_platform and execute the SQL scripts in the doc/sql directory.
2. Configuration
Download platform-sms-admin.tar.gz from the Release page. After extraction, edit the application.yml file in the conf directory.
3. Startup
bin/startup.sh
Operational Workflow
1. Login
After starting the service, access: http://localhost:8089. Default credentials are stored in the application.yml file.
2. Create Application
Application information includes name, appKey, appSecret, and remarks. The appKey and secret are required when using the client SDK.
3. Configure Third-party SMS Channel
Note: Tencent Cloud's SDK requires APPID in requests, so in the Beta version, appId is stored in additional properties.
4. Create Template
In the template management module, click the "New Template" button. The signature name must match the channel-applied signature exactly.
After creating the template, channel binding is required. First, create the SMS template on the third-party channel, then submit the binding.
Client Integration
1. Add Dependency
<dependency>
<groupId>com.courage</groupId>
<artifactId>platform-sms-client</artifactId>
<version>${parent.version}</version>
</dependency>
2. Client Configuration
Configure in application.yml:
sms:
smsServerUrl: http://localhost:8089
appKey: qQjEiFzn80v8VM4h
appSecret: 9c465ece754bd26a9be77f3d0e2606bd
Create the configuration class:
@Configuration
public class SmsConfiguration {
@Value("${sms.smsServerUrl}")
private String serverUrl;
@Value("${sms.appKey}")
private String key;
@Value("${sms.appSecret}")
private String secret;
@Bean
public SmsSenderClient createClient() {
SmsConfig config = new SmsConfig();
config.setAppKey(key);
config.setSmsServerUrl(serverUrl);
config.setAppSecret(secret);
return new SmsSenderClient(config);
}
}
3. Sending SMS
@Autowired
private SmsSenderClient smsSenderClient;
@GetMapping("/test")
public String test() {
String mobile = "15011319235";
String templateId = "555829270636703745";
Map<String, String> params = new HashMap<>();
params.put("code", "1234");
params.put("time", "10");
SmsSenderResult result = smsSenderClient.sendSmsByTemplateId(mobile, templateId, params);
System.out.println("result:" + JSON.toJSONString(result));
return "SMS sent successfully";
}
Project Highlights
This project serves as an architecture learning resource. Key takeaways include:
- Designing a lightweight client SDK
- Understanding SPI mechanisms and the adapter pattern
- Implementing appropriate threading models