前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【面试题】SpringCloud架构中如何保证定时任务只在一个服务在执行

【面试题】SpringCloud架构中如何保证定时任务只在一个服务在执行

作者头像
林老师带你学编程
发布2019-05-25 23:57:16
4.3K0
发布2019-05-25 23:57:16
举报
文章被收录于专栏:强仔仔强仔仔强仔仔

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1434202

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

问题:那基于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<URI> serviceUrl(String serviceName) {
      List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances(serviceName);
      List<URI> urlList = new ArrayList<URI>();
      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);
    }
}

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

  1. 获取当前服务ip
  2. 集群服务ip都转化成long类型数据,并进行排序
  3. 当前服务ip转化成long类型数据并和集群服务ip的long类型数据进行对比

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年02月28日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档