双时钟系统服务(java)

1、描述介绍

功能:java开发的双时钟系统(也可以改成多时钟系统)
注意:
1)这不是一个定时同步集群时钟的服务,而是一个对外提供时钟同步接口、设置多时钟时间接口、查询多时钟时间接口的服务。即它需要接口触发时钟同步,而且提供时钟查询功能。
2)该服务仅能做到秒级别的时钟同步。
3)虽然是双时钟系统,但底层实际时间只有一个,另外一个时钟体系是基于实际时间加上偏移时间系数因子实现的。
4)所有要同步时间的节点都得部署该服务。

2、pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.5</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>org.example</groupId><artifactId>wyt01clockserver</artifactId><version>0.0.1-SNAPSHOT</version><name>wyt01clockserver</name><description>wyt01clockserver</description><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.62</version></dependency><!-- swagger-ui依赖 https://springdoc.org/ --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.5.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes><finalName>clockserver</finalName></configuration></plugin></plugins></build></project>

3、核心思路介绍 - 同步逻辑介绍

首先是更新当前节点的系统时间

其次是根据同步请求的参数情况,如果同步其它节点的标识设置为true,则从配置文件中获取要同步的主机(也可以自己改成从缓存或者入参中获取)。

最后轮询向每个节点发送同步请求,此时发送的请求参数中的同步标识为false,以此避免无限循环同步。

    @Overridepublic BaseResult setSyncTime(SyncTimeReq request) {//当前节点设置时间executor.submit(() -> updateSystemDate(request.getDate()));//如果syncFlag标识为true,则遍历所有非当前节点发送同步请求if (request.getSyncFlag()) {String local = getLocalHost();String[] hostArr = hostStr.split(",");for (int i = 0; i < hostArr.length; i++) {String host = hostArr[i];if (host.equalsIgnoreCase(local)) continue;executor.submit(() -> updateOtherSystemDate(host, request.getDate()));}}return BaseResult.success();}

4、完整代码介绍

4.1 application.yml

spring:application:name: wyt01clockserverserver:port: 8183servlet:context-path: /clockserverclockserver:host: 127.0.0.1

4.2 controller 

package com.clockserver.controller;import com.alibaba.fastjson.JSONObject;
import com.clockserver.bean.BaseResult;
import com.clockserver.bean.clock.SyncFactorReq;
import com.clockserver.bean.clock.SyncTimeReq;
import com.clockserver.service.IClockServerService;
import com.clockserver.util.ExceptionUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@Tag(name = "时钟控制类",description = "包含所有时钟控制相关的接口")
@RestController
@RequestMapping("/clock")
public class ClockServerController {private static Logger logger = LoggerFactory.getLogger(ClockServerController.class);@Autowiredprivate IClockServerService clockServerService;@Operation(summary = "获取天文时间接口")@GetMapping("/getAstronomicalTime")public BaseResult getAstronomicalTime(){return clockServerService.getAstronomicalTime();}@Operation(summary = "获取时间接口")@GetMapping("/getAnotherTime")public BaseResult getAnotherTime(){return clockServerService.getAstronomicalTime();}@Operation(summary = "设置时间系数接口")@PostMapping("/setAnotherTimeFactor")public BaseResult setAnotherTimeFactor(@RequestBody SyncFactorReq request){logger.info("接收到设置时间系数请求{}", JSONObject.toJSONString(request));try {BaseResult result = clockServerService.setAnotherTimeFactor(request);return  result;}catch (Exception e){logger.error("设置时间系数异常,异常信息为:",e);return BaseResult.error(ExceptionUtils.getSimpleException(e));}}@Operation(summary = "设置同步时间接口")@PostMapping("/setSyncTime")public BaseResult setSyncTime(@RequestBody SyncTimeReq request){logger.info("接收到设置时间请求{}", JSONObject.toJSONString(request));try {BaseResult result = clockServerService.setSyncTime(request);return  result;}catch (Exception e){logger.error("设置同步时间异常,异常信息为:",e);return BaseResult.error(ExceptionUtils.getSimpleException(e));}}}

4.3 service

package com.clockserver.service;import com.clockserver.bean.BaseResult;
import com.clockserver.bean.clock.SyncFactorReq;
import com.clockserver.bean.clock.SyncTimeReq;public interface IClockServerService {public BaseResult getAstronomicalTime();public BaseResult getAnotherTime();public BaseResult setAnotherTimeFactor(SyncFactorReq request);public BaseResult setSyncTime(SyncTimeReq request);
}

4.4 serviceImpl

package com.clockserver.service.impl;import com.alibaba.fastjson.JSONObject;
import com.clockserver.bean.BaseResult;
import com.clockserver.bean.clock.SyncFactorReq;
import com.clockserver.bean.clock.SyncTimeReq;
import com.clockserver.bean.clock.TimeBean;
import com.clockserver.service.IClockServerService;
import com.clockserver.util.ExceptionUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import java.net.InetAddress;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;@Component
public class ClockServerServiceImpl implements IClockServerService {private static Logger logger = LoggerFactory.getLogger(ClockServerServiceImpl.class);@Autowiredprivate RestTemplate restTemplate;@Value("${server.port}")private Integer port;@Value("${clockserver.host}")private String hostStr;//@TODO 如果需要多个作战事件系数,将该系数因子改成map即可,然后每种作战系数各个不同的唯一标识private volatile long AnotherTimeFactor = 0l;private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5, 0l, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));private static String localIp = null;@Overridepublic BaseResult getAstronomicalTime() {long millis = System.currentTimeMillis();TimeBean timeBean = TimeBean.getTimeBean(millis, millis + AnotherTimeFactor);return BaseResult.success(timeBean);}@Overridepublic BaseResult getAnotherTime() {long millis = System.currentTimeMillis();TimeBean timeBean = TimeBean.getTimeBean(millis, millis + AnotherTimeFactor);return BaseResult.success(timeBean);}@Overridepublic BaseResult setAnotherTimeFactor(SyncFactorReq request) {//当前节点设置系数this.AnotherTimeFactor = request.getFactor();//如果syncFlag标识为true,则遍历所有非当前节点发送同步请求if (request.getSyncFlag()){String local = getLocalHost();String[] hostArr = hostStr.split(",");for (int i = 0; i < hostArr.length; i++) {String host = hostArr[i];if (host.equalsIgnoreCase(local)) continue;executor.submit(() -> updateOtherSystemFactor(host, request.getFactor()));}}return BaseResult.success();}@Overridepublic BaseResult setSyncTime(SyncTimeReq request) {//当前节点设置时间executor.submit(() -> updateSystemDate(request.getDate()));//如果syncFlag标识为true,则遍历所有非当前节点发送同步请求if (request.getSyncFlag()) {String local = getLocalHost();String[] hostArr = hostStr.split(",");for (int i = 0; i < hostArr.length; i++) {String host = hostArr[i];if (host.equalsIgnoreCase(local)) continue;executor.submit(() -> updateOtherSystemDate(host, request.getDate()));}}return BaseResult.success();}private String getRequestHost() {ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest requests = attr.getRequest();String remoteHost = requests.getRemoteHost();return remoteHost;}private void updateSystemDate(String timeStr) {try {logger.info("更新系统时间为{}", timeStr);String timeCommand = "sudo date -s '" + timeStr + "'";// 以root身份执行命令String[] command = {"/bin/sh", "-c", timeCommand};Process process = Runtime.getRuntime().exec(command);process.waitFor();int exitValue = process.waitFor();if (exitValue == 0) {logger.info("系统时间已设置为:{}", timeStr);} else {logger.error("设置系统时间失败:{}", timeStr);}} catch (Exception e) {logger.error("设置系统时间异常,异常信息为:", e);}}private void updateOtherSystemDate(String host, String dateStr) {SyncTimeReq req = new SyncTimeReq();req.setDate(dateStr);String url = "http://" + host + ":" + port + "/clockserver/clock/setSyncTime";logger.info("执行url[{}]更新系统时间为{}", url, dateStr);BaseResult result = BaseResult.error();try {result = restTemplate.postForObject(url, req, BaseResult.class);} catch (Exception e) {logger.error("更新{}主机时间异常,异常信息为:", host, e);result.setMsg(ExceptionUtils.getSimpleException(e));}logger.info("更新主机{}系统时间为{}的结果是{}", host, dateStr, JSONObject.toJSONString(result));}private void updateOtherSystemFactor(String host, Long factor) {SyncFactorReq req = new SyncFactorReq();req.setFactor(factor);String url = "http://" + host + ":" + port + "/clockserver/clock/setAnotherTimeFactor";logger.info("执行url[{}]更新时间系数为{}", url, factor);BaseResult result = BaseResult.error();try {result = restTemplate.postForObject(url, req, BaseResult.class);} catch (Exception e) {logger.error("更新{}时间系数异常,异常信息为:", host, e);result.setMsg(ExceptionUtils.getSimpleException(e));}logger.info("更新主机{}时间系数为{}的结果是{}", host, factor, JSONObject.toJSONString(result));}private String getLocalHost() {if (localIp != null) return localIp;String ip = "";try {InetAddress inetAddress = InetAddress.getLocalHost();ip = inetAddress.getHostAddress();localIp = ip;} catch (Exception e) {logger.error("获取本机ip异常:", e);}return ip;}
}

4.5 其它辅助类

@Data
public class BaseResult {private Integer code;private String msg;private Object data;private Boolean success;private BaseResult(Integer code, String msg, Object data, Boolean success) {this.code = code;this.msg = msg;this.data = data;this.success = success;}public static BaseResult success(){BaseResult result = new BaseResult(200,"success",null,true);return result;}public static BaseResult success(Object data){BaseResult result = new BaseResult(200,"success",data,true);return result;}public static BaseResult error(){BaseResult result = new BaseResult(-1,"error",null,false);return result;}public static BaseResult error(String msg){BaseResult result = new BaseResult(-1,msg,null,false);return result;}
}@Data
public class SyncFactorReq {private long factor; //时钟偏移量private Boolean syncFlag = false; // true:同步给其它所有节点 false:不同步给其它节点
}@Data
public class SyncTimeReq {private String date; //要同步的时间 "yyyy-MM-dd HH:mm:ss"private Boolean syncFlag = false; // true:同步给其它所有节点 false:不同步给其它节点}@Data
public class TimeBean {private Long astronomicalTime; //天文事件(毫秒级)private Long AnotherTime; //时间(毫秒级)public static TimeBean getTimeBean(Long astronomicalTime,Long AnotherTime){TimeBean timeBean = new TimeBean();timeBean.setAstronomicalTime(astronomicalTime);timeBean.setAnotherTime(AnotherTime);return timeBean;}
}public class ExceptionUtils {public static String getSimpleException(Exception e) {return e.toString() + ":" + e.getMessage();}}

4、总结

上述代码仅仅是一个demo,主要是展示一直多时钟服务的编辑思路。如果想要更稳定的系统,还需要自行完善。比如下面两点改进建议:

1)如果某个节点掉线了,上线后怎么知道自己的时间系数?
将各个时钟系统的时间系数存放在redis缓存中。每次更新时间系数时,先刷新缓存,然后在同步通知其它节点。

2)当前服务是双时钟,如果多时钟怎么改?

当前只存储了一个时钟偏移系数,如果想多时钟系统,可以用map将不同标识的时钟偏移系数存储起来使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/19240.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python探索时钟模拟之旅:从设计到实现

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、设计时钟类 三、代码实现 四、扩展功能&#xff1a;指定步数后自动停止 五…

开一个抖音小店可以经营几个类目?经营几个类目最合适?

大家好&#xff0c;我是喷火龙。 抖音小店的商品类目和商品数量是没有限制的&#xff0c;只要是在营业执照的经营范围之内的类目都能入驻抖音小店&#xff0c;但是选择的主营类目不能超过三个。 有些商家可能会想&#xff0c;自己经营多个类目&#xff0c;做多种商品种类&…

VMware安装Ubuntu系统(超详细)

一.Ubuntu官网下载镜像 Ubuntu官网&#xff1a;Enterprise Open Source and Linux | Ubuntu 二.安装Ubuntu系统 选择文件->创建虚拟机新建虚拟机&#xff08;ControlN&#xff09;&#xff0c;这里直接选择典型即可 选择稍后安装系统 选择linux Ubuntu 64位 填写虚拟机名称…

R语言入门 | 使用 dplyr 进行数据转换

3.1简介 3.1.1准备工作 3.1.2 dplyr 基础 • 按值筛选观测&#xff08; filter() &#xff09;。 • 对行进行重新排序&#xff08; arrange() &#xff09;。 • 按名称选取变量&#xff08; select() &#xff09;。 • 使用现有变量的函数创建新变量&#xff08; …

数字孪生3D智慧园区可视化能耗管控平台

智慧园区是园区与互联网的完美融合。通过整合信息技术和各类资源&#xff0c;我们实现了园区内各个服务的数字化运营&#xff0c;不仅降低了企业的运营成本&#xff0c;更提高了管理效率 智慧园区3D互动大屏系统利用web3d开发、三维可视化、模拟仿真和数字孪生技术&#xff0c;…

【算法工程师】(三年面试五年模拟版)总结

写在前面&#xff1a; WeThinkIn &#xff08;公主号&#xff09; 学习经验分享 目录 1、机器学习基础 2、深度学习基础 2.1 1*1卷积的作用 注&#xff1a;卷积核的个数对应输出的通道数&#xff08;channels&#xff09;&#xff0c;比如输入6*6*64&#xff0c;卷积核1…

香橙派Orange AI Pro 初体验

什么是香橙派 &#xff1f; 香橙派&#xff08;Orange Pi&#xff09;是深圳市迅龙软件有限公司旗下的开源产品品牌。它专注于为全球个人和企业提供高性价比的开源硬件、开源软件以及OEM/ODM服务。香橙派已经迭代了30多款产品&#xff0c;形成了涵盖开源硬件、开源软件、开源芯…

使用Jmeter进行性能测试的基本操作方法

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

如何去除input框在复制内容时自动填充的背景颜色

今天在项目开放时遇到了一个问题在输入复制内容时会有一个自带的背景颜色无法去除&#xff1b; 效果图&#xff1a; 修改的核心代码&#xff1a; /* 修改自动填充时的背景颜色 */ input:-internal-autofill-previewed, input:-internal-autofill-selected {-webkit-text-fil…

Google Search Console:优化网站可见性的终极指南

Google Search Console 是一项由 Google 提供的免费服务&#xff0c;可帮助您监控和维护您的网站在 Google 搜索结果中的展示情况以及排查问题。即使没有注册 Search Console&#xff0c;您的网页也可能会显示在 Google 搜索结果中&#xff0c;但 Search Console 可帮助您了解并…

Java开发-特殊文本文件,日志技术

目录 01.特殊文件,日志技术概述 02.特殊文件:Properties属性文件 ​编辑Properties案例 特殊文件:XML文件 XML的作用和应用场景 读取XML文件中的数据 XML的生成 约束XML文件的编写[了解] 日志技术 日志技术的体系 ​编辑 ​编辑 Logback日志框架的概述 Logback快…

cocos creator做圆形进度条

效果图&#xff1a; 我们在开发过程中经常要用到圆形进度条&#xff0c;例如技能CD 原文链接 之前写了一篇cocos2dx-lua_ProgressTimer创建扇形进度条,这里简单记录下在cocosCreator中如何制作。 具体方法 cocosCreator做起来比2dx还是要简单很多&#xff0c;首先给节点添加p…

Activity详解,用最通俗的语言告诉你什么是Activity(一)

大家好&#xff0c;我是小布丁。 今天还是分享Android基础知识&#xff0c;有Android基础的朋友都知道&#xff0c;Activity作为Android四大组件之一&#xff0c;掌管可视化界面。也是大多数人刚接触Android学的第一课。下面我来带大家学习/复习这部分知识&#xff0c;请大家不…

DotNetty ByteBuffer

DotNetty是一个高性能的.NET网络通信框架&#xff0c;基于Netty&#xff0c;支持TCP、UDP、HTTP、WebSocket等协议。适用于高并发、低延迟场景&#xff0c;如实时通信、游戏服务器、IoT应用及大型分布式系统&#xff0c;通过异步I/O、零拷贝等技术提升性能&#xff0c;具备易用…

【3.vi编辑器使用(上)】

一、vi编辑器的三种模式及切换命令 1、vi是linux中最基本的编辑器。但vi编辑器在系统管理、服务器配置工作中永远都是无可替代的。 2、vi编辑器的三种模式&#xff1a;命令行模式、插入模式、底行模式。 &#xff08;1&#xff09;命令行模式&#xff1a;用户在用vi编辑文件…

智能变革:领域大模型重塑企业知识管理!

在如今知识密集型的行业领域里&#xff0c;企业员工每天都要与海量的文档和信息打交道&#xff0c;工作邮箱里充斥着无数邮件&#xff0c;办公桌上堆满了各种报告和文档&#xff0c;而每一个文件里都可能藏有关键信息。 然而&#xff0c;要从这些杂乱无章的信息海洋中找到需要…

AI生成四季变化解决方案,四季之美,一图尽揽

随着AI技术已经渗透到我们生活的方方面面&#xff0c;在这个充满变化的时代&#xff0c;美摄科技以其前沿的AI生成技术&#xff0c;为企业带来了全新的视觉体验——AI生成四季变化解决方案。这一方案不仅能够让车辆实拍的照片焕发不同季节的风采&#xff0c;更能在不改变原图构…

【Windows配置nginx开机自启】

Windows配置nginx开机自启 方式一&#xff1a;将nginx加入到windows服务中方式二&#xff1a;通过windows任务计划设定nginx开机自启 方式一&#xff1a;将nginx加入到windows服务中 下载window service wraper&#xff08;https://github.com/winsw/winsw/releases&#xff0…

配置PPPoE多播绑定vlanID并开启IPV6

准备工作 确保你的系统已安装ppp和rp-pppoe包&#xff1a; sudo apt-get install pppoe ppp创建VLAN接口 假设你有两个VLAN ID&#xff0c;分别为100和200。我们首先创建VLAN接口eth0.100和eth0.200。 sudo ip link add link eth0 name eth0.100 type vlan id 100 sudo ip …

自学新标日第十六课(完结)

第十五课 单词 单词假名声调词义ボート1小船ベンチ1长椅、长凳市役所しやくしょ2市政府携帯電話けいたいでんわ5手机禁煙きんえん0禁止吸烟風邪かぜ0感冒熱ねつ&#xff12;发烧睡眠すいみん&#xff10;睡眠お風呂おふろ&#xff12;澡堂、浴室薬局やっきょく&#xff10;药…