有时候我们在开发过程中,很容易犯这样一个错误,就是在服务中写一个定时任务,然后也没做其它的处理就上线了。然后微服务架构为了保证高可用,一般都会部署多个服务,这个时候问题就来了,时间一到定时任务一启动,发现你部署多少个服务,就会跑多少个定时任务。如果服务器性能一般,定时任务占用内存又多,服务器跑死都有可能。

问题:那基于SpringCloud的架构中,这种情况我们应该如何处理呢?

这边我们先来简单概述一下,我们先来看一下任务执行的时序图。

简单的来说,我们可以分为以下步骤:

第一步先获取当前服务ip
第二步获取springcloud集群ip信息
最后将当前ip和集群的ip进行对比,如果当前ip是集群中最小的ip则执行定时任务业务,如果不是则return掉。
我们先来看一下定时任务:

/**
 *• @author qiang220316
• @date 2019/2/28
 */@Component
 public class WrokTask {
 @Autowired
 private IJobService jobService;
private static String serviceName="provider";

/**
 * 5秒更新一次
 */
@Scheduled(fixedDelay = 5000)
public void doWork(){
    if (!IPV4Util.ipCompare(this.jobService.serviceUrl(serviceName))) {
        return;
    }
    System.out.println(serviceName+"服务,地址为:"+IPV4Util.getIpAddress()+",正在执行task任务");
}


定时任务中我们可以看到this.jobService.serviceUrl方法,这个方法的作用则是获取SpringCloud集群中服务信息,IPV4Util.ipCompare这个作用就是将当前服务IP和集群所有IP进行对比,如果当前服务IP是集群服务IP最小则返回true,反之返回false。

接下来我们再看一下,如果来获取SpringCloud集群信息:

@Service
 public class JobServiceImpl implements IJobService {@Autowired
 private DiscoveryClient discoveryClient;@Override
 public List serviceUrl(String serviceName) {
 List serviceInstanceList = discoveryClient.getInstances(serviceName);
 List urlList = new ArrayList();
 if (CollectionUtils.isNotEmpty(serviceInstanceList)) {
 serviceInstanceList.forEach(si -> {
 urlList.add(si.getUri());
 });
 }
 return urlList;
 }}


其实主要还是用到DiscoveryClient类中方法,我们就可以很轻松获取到集群信息。

最后我们再来看看IPV4Util这个工具类到底是怎么进行对比的呢?

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;
}

/**
 * @param args
 */
public static void main(String[] args) {
    long result = ipToLong("192.168.11.126");
    System.out.println("long转换为IP的结果: " + longToIp(result));
    System.err.println("result IP转换为long的结果: " + result);
// long result2 = ipToLong(“192.168.11.217”);
 // System.err.println("result2IP转换为long的结果: " + result2);
 // System.out.println("long转换为IP的结果: " + longToIp(result));
 // System.out.println("long转换为IP的结果: " + longToIp2(result));
 // String ipAddress = getIpAddress();
 // System.err.println(ipAddress);
 }
 }


这个工具类作用有以下几点:

获取当前服务ip
集群服务ip都转化成long类型数据,并进行排序
当前服务ip转化成long类型数据并和集群服务ip的long类型数据进行对比
我们通过这样的方法,就可以保证SpringCloud架构中定时任务只在一个服务在执行了,这边可能童鞋们会有一些疑问,为什么不用分布式调度框架来解决这个问题呢?当然我们也可以用分布式调度框架来解决这个问题,比如用elastic-job这个框架来就可以。但是引入第三方框架有时候会增加系统的复杂程度,学习成本也会相应的变大,最重要的是有些定时任务没必要进行分片,一个单点服务就可以搞定,就没必要耗费资源进行分片跑任务服务了。好了今天的内容就介绍到这边了,谢谢大家的阅读~