使用Java自制一个一个Nacos
作者:乐乐家的乐乐
什么是Nacos
Nacos是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos的主要功能:
- 服务发现和服务健康监测
- 动态配置服务
- 动态 DNS 服务
- 服务及其元数据管理
直接参考Nacos文档 :Nacos文档
我们要做的功能是
Nacos的功能实在是太成熟了,但是我们可以通过官网和文档总结出Nacos的两大核心功能:
- 服务发现
- 配置管理
一、实现 服务发现
服务发现,与其说是实现服务与发现,不如说是实现以下三个功能:
- 服务启动时候进行注册
- 查询已注册服务信息
- 确认服务状态是否健康
1、创建一个SpringBoot项目,用来做服务端
这个项目要实现几个目的:
- 首先我们的服务需要可以支持Http请求(GRPC更好,Nacos用的就是GRPC,Http请求我们更熟悉一点,以后我们会专门出有关于JAVA使用GRPC的文章)。
- 其次我们需要我们的服务可以集成和连接一个关系型数据库(Mysql或者Oracle...)和非关系型数据库(Redis...)
所以我们需要创建一个SringBoot项目,因为我们需要一个配置与发现中心的这么一个服务,类似于搭建Nacos,我们只不过是将这个中间件变成一个我们熟悉的SpringBoot项目,方便我们开发。
2、服务端
在 注册中心 服务端 ,我们需要一个注册接口。
实例 数据库同理创建
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); } } } }
3、客户端
在我们的 客户端服务 我们需要在启动的时候注册:
客户端注册接口
/** * 注册 * @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); }
我们还需要一个健康检测客户端接口,以便于服务注册中心心跳检测。我们选择用轻量级的头请求。
客户端健康检测接口
好了,我们的服务注册就完成了!
二、实现 配置管理
配置管理只需要我们完成两件事情
- 服务端管理配置
- 客户端启动的时候拉取配置
1、服务端管理配置
第一步我们需要在服务注册中心 服务端管理配置,我们将所有application.yml中的文件用properties的方式存入数据库。
/** * 健康检测接口 */ @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; }
2、客户端拉取配置
客户端拉取配置接口,我们客户端程序启动的时候需要从注册服务中心来拉取配置。
首先我们在路径src\main\resources\META-INF\spring.factories
中的spring.factories
文件中加入到最后一行。
@GetMapping("/getConfigDataByTag") public List<Map<String, String>> getConfig(@RequestParam String tag) throws JsonProcessingException { Map<String, String> configData = configDataService.selectconfigDataList(new configData(tag)); return list; }
然后我们需要创建一个拉取配置文件的类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就做好了,它可以实现的功能有:
- 客户端启动可以注册到服务端。
- 服务端可以心跳检测每个客户端的项目。
- 数据分析,比如我们什么时候项目异常?总共启动多少次项目?...
- 可以不用application.yml,配置文件全部放在数据库中。(和Nacos一样)
- 可以实现热部署配置文件,远程更改,实时有效。
其他问题
src\main\resources\META-INF\spring.factories为什么可以自动装载配置?
这就要涉及SpringBoot源码啦,关于SpringBoot启动时候加载配置的优先级和环境配置的上下文,请参考SpringBoot源码。(读源码啦~ 必须要过这一关的嘛)
自定义拓展功能
可以把服务注册发现和配置管理都用前端展示到页面上方便管理。以下是我自己实现的前端界面,不美观无所谓,看的懂就行。
服务注册发现
配置管理(可以和Nacos一样在页面进行修改和热部署)
以上就是使用Java自制一个一个Nacos的详细内容,更多关于Java Nacos的资料请关注脚本之家其它相关文章!