Nacos是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos的主要功能:
直接参考Nacos文档 :Nacos文档
Nacos的功能实在是太成熟了,但是我们可以通过官网和文档总结出Nacos的两大核心功能:
服务发现,与其说是实现服务与发现,不如说是实现以下三个功能:
这个项目要实现几个目的:
所以我们需要创建一个SringBoot项目,因为我们需要一个配置与发现中心的这么一个服务,类似于搭建Nacos,我们只不过是将这个中间件变成一个我们熟悉的SpringBoot项目,方便我们开发。
在 注册中心 服务端 ,我们需要一个注册接口。
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){
/**
* 获取token
*/
String token = request.getHeader("header");
/**
* 验证请求合法性
*/
Boolean isLegal = SecurityUtils.verLegal(token);
if(isLegal){
/**
* 检测 serType!=2 端口+内网地址
*/
ClientBody client = clientBodyService.checkClient(clientBody);
if(ObjectUtils.isNotEmpty(client)){
/**
* 说明是在心跳检测期间重新启动。
* 注销这台实例.
*
* Mysql client 这条数据
* deathTime 改为现在时间
* serType 改为 2
*
* Redis 直接删除这个数据id 为 Key的数据
* 直接删除这台实例
*/
clientBodyService.logoutCient(client);
/**
* redis 和 Mysql 中新增一台实例
* regSerTime 现在时间
* serType 为 0
*/
clientBodyService.insert(clientBody);
}else{
/**
* 启动了一台实例
*/
clientBodyService.insert(clientBody);
}
return true;
}else{
return false;
}
}
/**
* 60秒定时健康检测
*/
@Scheduled(cron = "0/60 * * * * ?")
public void heartbeatCheck(){
/**
* 查询 serType = 0 健康的 实例List
*/
List<ClientBody> clientBodyList = clientBodyService.selectOnlineServer();
for (ClientBody clientBody : clientBodyList) {
//回调接口
String CallbackInterface = clientBody.getCallbackInterface();
//内网地址
String inNetIp = clientBody.getInNetIp();
//拿到端口号
String port = clientBody.getPort();
//发送http请求 true为正常 false为异常
Boolean state = HttpUtils.sendHead(CallbackInterface,inNetIp,port);
if(state){
//检测次数 + 1(checkNum + 1)
clientBodyService.updateCientNormal(clientBody);
}else{
/**
* 检测ping不通原因可能时网络波动
* 检测十次 都是异常 才判定死亡
*/
if(clientBody.getCheckNum() > 10){
//修改这条数据为死亡 (serType = 2)
clientBodyService.updateCientDeath(clientBody);
}else{
//修改这条数据为异常,然后检测次数 + 1,异常次数 + 1,异常时间[现在时间字符串拼接在原有数据之后](serType = 1;checkNum + 1;exceNum + 1;)
clientBodyService.updateCientException(clientBody);
}
}
}
}
在我们的 客户端服务 我们需要在启动的时候注册:
/**
* 注册
* @PostConstruct 解释:在项目启动时加载数据
*/
@PostConstruct
public void registerService(){
HashMap<String, Object> map = new HashMap<String,Object>(){{
//项目名
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");
}
好了,我们的服务注册就完成了!
配置管理只需要我们完成两件事情
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;
}
入库的形式就是这种:
@GetMapping("/getConfigDataByTag")
public List<Map<String, String>> getConfig(@RequestParam String tag) throws JsonProcessingException {
Map<String, String> configData = configDataService.selectconfigDataList(new configData(tag));
return list;
}
src\main\resources\META-INF\spring.factories
中的spring.factories
文件中加入到最后一行。org.springframework.boot.env.EnvironmentPostProcessor=com.test.web.core.config.ServerConfigProcessor
然后我们需要创建一个拉取配置文件的类ServerConfigProcessor
实现EnvironmentPostProcessor
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<String, Object> propertySource = new HashMap<>();
try {
/**
* 拉取配置
* MyConfig.ServerConfigHttpUrl 服务端url
* MyConfig.ServerConfigInterface 服务端接口
* MyConfig.MyServerTag 本服务标识(用来判断拉取的配置条件)
*/
Map<String, Object> 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);
}
}
}
OK到此为止我们的自定义Nacos就做好了。
我们的自定义Nacos就做好了,它可以实现的功能有:
src\main\resources\META-INF\spring.factories
为什么可以自动装载配置?这就要涉及SpringBoot源码啦,关于SpringBoot启动时候加载配置的优先级和环境配置的上下文,请参考SpringBoot源码。(读源码啦~ 必须要过这一关的嘛)
可以把服务注册发现和配置管理都用前端展示到页面上方便管理。以下是我自己实现的前端界面,不美观无所谓,看的懂就行。
最近几天一直在忙着把这个东西开源,目前终于开源了。开源的过程也全部都手把手记录下来了。真的感谢各位读者,让我一个萌新这么受鼓舞。谢谢大家。 哭死~
开源项目 YanXi 地址 :
YanXi (gitee.com)
手把手搭建 YanXi :
用 SpringBoot 从0~1 手把手教你搭建 分布式 项目 ~ - 掘金 (juejin.cn)
手把手教你 SpringBoot 如何集成 gRPC 实现服务间通讯 ~ - 掘金 (juejin.cn)
手把手教你 SpringBoot 如何集成 Mybatis 连接数据库Mysql~ - 掘金 (juejin.cn)
SpringBoot 热部署 自定义配置一键刷新 ~ - 掘金 (juejin.cn)
觉得作者写的不错的,值得你们借鉴的话,就请点一个免费的赞吧!这个对我来说真的很重要。
૮(˶ᵔ ᵕ ᵔ˶)ა