Implementing a Custom Nacos‑like Service Discovery and Configuration Management with Spring Boot
This article explains how to build a Spring Boot‑based service registry that replicates Nacos' core features—service discovery, health‑checking, and dynamic configuration management—by creating server‑side registration APIs, scheduled heartbeat checks, client registration logic, and database‑backed configuration storage.
What Is Nacos
Nacos (Dynamic Naming and Configuration Service) is a cloud‑native platform that provides service discovery, dynamic configuration, DNS services, and metadata management.
Core Functions of Nacos
Service discovery and health monitoring
Dynamic configuration service
Dynamic DNS service
Service and metadata management
Planned Features
We will focus on the two core capabilities of Nacos: service discovery and configuration management.
1. Implement Service Discovery
Service discovery consists of three sub‑tasks: registering services at startup, querying registered services, and checking service health.
1.1 Create a Spring Boot Project for the Server
The server must support HTTP (or GRPC) requests and be able to connect to relational (MySQL/Oracle) and non‑relational (Redis) databases.
1.2 Server‑Side Registration API
public class ClientBody {
private static final long serialVersionUID = 1L;
/** 自增id */
private Long id;
/** 项目名 */
private String projectName;
/** 端口 */
private String port;
/** 健康检测回调接口 */
private String CallbackInterface;
/** 内网ip */
private String inNetIp;
/** 外网ip */
private String outNetIp;
/** 注册时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date regSerTime;
/** 0-健康 1-异常 2-死亡 */
private String serType;
/** 异常时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date exceptTime;
/** 死亡时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date deathTime;
/** 检测次数 */
private Long checkNum;
/** 异常次数 */
private Long exceNum;
} @Value("${token.header}")
private String header;
@PostMapping("/serviceRegistration")
public Boolean add(HttpServletRequest request, @RequestBody ClientBody clientBody) {
String token = request.getHeader("header");
Boolean isLegal = SecurityUtils.verLegal(token);
if (isLegal) {
ClientBody client = clientBodyService.checkClient(clientBody);
if (ObjectUtils.isNotEmpty(client)) {
clientBodyService.logoutCient(client);
clientBodyService.insert(clientBody);
} else {
clientBodyService.insert(clientBody);
}
return true;
} else {
return false;
}
}1.3 Server‑Side Health‑Check Scheduler
@Scheduled(cron = "0/60 * * * * ?")
public void heartbeatCheck() {
List
clientBodyList = clientBodyService.selectOnlineServer();
for (ClientBody clientBody : clientBodyList) {
String CallbackInterface = clientBody.getCallbackInterface();
String inNetIp = clientBody.getInNetIp();
String port = clientBody.getPort();
Boolean state = HttpUtils.sendHead(CallbackInterface, inNetIp, port);
if (state) {
clientBodyService.updateCientNormal(clientBody);
} else {
if (clientBody.getCheckNum() > 10) {
clientBodyService.updateCientDeath(clientBody);
} else {
clientBodyService.updateCientException(clientBody);
}
}
}
}1.4 Client Registration Logic
@PostConstruct
public void registerService() {
HashMap
map = new HashMap
() {{
put("projectName", MyConfig.getName());
put("inNetIp", MyConfig.getUrl());
put("CallbackInterface", "/checkHealthy");
put("port", MyConfig.getPort());
}};
AjaxResult ajaxResult = HttpUtils.sendPostRequest('注册与配置中心url', "/serviceRegistration", map);
} @RequestMapping(value = "/CallbackInterface", method = RequestMethod.HEAD)
public void healthyByHead(HttpServletResponse response) {
response.setHeader("data", "200");
}2. Implement Configuration Management
Configuration management requires a server‑side API to store configuration key‑value pairs in a database and a client‑side mechanism to fetch those configurations at startup.
2.1 Server‑Side Configuration Entity
public class ConfigData {
private static final long serialVersionUID = 1L;
/** id */
private Long id;
/** key */
private String key;
/** value */
private String value;
/** tag */
private String tag;
/** remark */
private String remark;
}2.2 Server‑Side Configuration Retrieval API
@GetMapping("/getConfigDataByTag")
public List
> getConfig(@RequestParam String tag) throws JsonProcessingException {
Map
configData = configDataService.selectconfigDataList(new configData(tag));
return list;
}2.3 Client‑Side Configuration Pull
During startup, the client adds an entry to src\main\resources\META-INF\spring.factories :
org.springframework.boot.env.EnvironmentPostProcessor=com.test.web.core.config.ServerConfigProcessorThe ServerConfigProcessor implements EnvironmentPostProcessor to fetch configuration from the server and inject it into the Spring environment.
public class ServerConfigProcessor implements EnvironmentPostProcessor {
private static final String PROPERTY_SOURCE_NAME = "databaseProperties";
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
log.info("ServerConfig loading start");
Map
propertySource = new HashMap<>();
try {
Map
configSource = HttpUtils.sendGet(MyConfig.ServerConfigHttpUrl, MyConfig.ServerConfigInterface, MyConfig.MyServerTag);
propertySource.putAll(configSource);
environment.getPropertySources().addFirst(new MapPropertySource(PROPERTY_SOURCE_NAME, propertySource));
log.info("ServerConfig loading Success");
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}Summary
The custom Nacos implementation enables clients to register themselves, allows the server to perform periodic health checks, stores configuration data in a database, and lets clients pull configuration at startup, achieving service discovery, health monitoring, and dynamic configuration without relying on external YAML files.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
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.