在电池质检系统中,需要每天对昨天质检数据进行统计分析,就在微服务架构中添加了定时统计任务。由于服务部署在多个节点上导致任务重复执行,数据出现重复。在此简单谈谈解决过程。
解决思路
-
第一步先获取当前服务IP
-
第二部获取Springcloud当前项目集群IP信息
-
最后将当前IP和集群的IP进行对比,如果当前IP是集群中最小的IP则执行定时任务,否则return掉
我们首先来看一下定时任务:
/**
* Created with IntelliJ IDEA.
*
* @Auther: yangxuejian
* @Date: 2020/06/08/上午10:01
* @Description:
*/
@Component
public class StatisticsTask {
private static Logger logger = LoggerFactory.getLogger(StatisticsTask.class);
@Autowired
private JobService jobService;
private static String serviceName = "ocean-sunfish";
// #格式:秒 分 时 天 月 星期
@Scheduled(cron ="0 30 7 * * ?")
@RequestMapping(value = "/statisticsTask")
public void statisticsTask() {
if (!IPV4Util.ipCompare(jobService.serviceUrl(serviceName))) {
return;
}
logger.info("{}服务,地址为:{},正在执行任务...", serviceName, IPV4Util.getIpAddress());
}
}
定时任务中的jobService.serviceUrl()方法,主要是用来获取SpringCloud集群中服务的信息。IPV4Util.ipCompare()这个方法的作用就是将当前服务的IP与集群中IP进行比较,如果当前服务IP最小则返回true,否则返回false。
接下来我们看一下如何获取SpringCloud集群信息
/**
* Created with IntelliJ IDEA.
*
* @Auther: yangxuejian
* @Date: 2020/06/08/下午3:17
* @Description:
*/
@Service
public class JobServiceImpl implements JobService {
@Autowired
private DiscoveryClient discoveryClient;
@Override
public List<URI> serviceUrl(String serviceName) {
List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances(serviceName);
List<URI> urlList = new ArrayList<URI>();
if (!CollectionUtils.isEmpty(serviceInstanceList)) {
serviceInstanceList.forEach(si -> {
urlList.add(si.getUri());
});
}
return urlList;
}
}
其中主要是用DiscoveryClient获取到集群中服务信息。
最后再看一看IPV4Util这个工具类中如何进行IP对比的
/**
* Created with IntelliJ IDEA.
*
* @Auther: yangxuejian
* @Date: 2020/06/08/下午3:18
* @Description:
*/
public class IPV4Util {
/**
* @param ipAddress
* @return
*/
public static long ipToLong(String ipAddress) {
long result = 0;
String[] ipAddressInArray = ipAddress.split("\\.");
for (int i = 3; i >= 0; i--) {
long ip = Long.parseLong(ipAddressInArray[3 - i]);
// left shifting 24,16,8,0 and bitwise OR
// 1. 192 << 24
// 1. 168 << 16
// 1. 1 << 8
// 1. 2 << 0
result |= ip << (i * 8);
}
return result;
}
/**
* @param ip
* @return
*/
public static String longToIp(long ip) {
StringBuilder result = new StringBuilder(15);
for (int i = 0; i < 4; i++) {
result.insert(0, Long.toString(ip & 0xff));
if (i < 3) {
result.insert(0, '.');
}
ip = ip >> 8;
}
return result.toString();
}
/**
* @param ip
* @return
*/
public static String longToIp2(long ip) {
return ((ip >> 24) & 0xFF) + "." + ((ip >> 16) & 0xFF) + "." + ((ip >> 8) & 0xFF) + "." + (ip & 0xFF);
}
/**
* 获取当前机器的IP
*
* @return
*/
public static String getIpAddress() {
try {
for (Enumeration<NetworkInterface> enumNic = NetworkInterface.getNetworkInterfaces();
enumNic.hasMoreElements(); ) {
NetworkInterface ifc = enumNic.nextElement();
if (ifc.isUp()) {
for (Enumeration<InetAddress> enumAddr = ifc.getInetAddresses();
enumAddr.hasMoreElements(); ) {
InetAddress address = enumAddr.nextElement();
if (address instanceof Inet4Address && !address.isLoopbackAddress()) {
return address.getHostAddress();
}
}
}
}
return InetAddress.getLocalHost().getHostAddress();
} catch (IOException e) {
//log.warn("Unable to find non-loopback address", e);
e.printStackTrace();
}
return null;
}
/**
* 对比方法
*
* @param serviceUrl
* @return
*/
public static boolean ipCompare(List<URI> serviceUrl) {
try {
String localIpStr = IPV4Util.getIpAddress();
long localIpLong = IPV4Util.ipToLong(localIpStr);
int size = serviceUrl.size();
if (size == 0) {
return false;
}
Long[] longHost = new Long[size];
for (int i = 0; i < serviceUrl.size(); i++) {
String host = serviceUrl.get(i).getHost();
longHost[i] = IPV4Util.ipToLong(host);
}
Arrays.sort(longHost);
if (localIpLong == longHost[0]) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
这个工具类作用主要一下几点
-
获取当前服务IP
-
将当前服务IP转成Long类型
-
集群中服务IP都转成Long类型,并进行排序
-
当前服务IP的Long类型数据和集群中IP的Long类型数据进行对比
如有问题请联系作者:yxj1068@126.com
参考博客:https://blog.youkuaiyun.com/linzhiqiang0316/article/details/88047138