Sometimes during development, it’s easy to make the mistake of writing a scheduled task in the service and then going live without doing anything else. However, in order to ensure high availability, microservice architectures usually deploy multiple services. This is when the problem arises. As soon as the scheduled tasks start, it is found that as many services as you deploy, the number of scheduled tasks will run. If the server performance is mediocre, scheduled tasks take up a lot of memory, and the server may run dead.

Question: How do we handle this in springCloud-based architectures?

Simply speaking, we can divide it into the following steps:

  • Step 1 Obtain the current service IP address
  • Step 2 Obtain the IP address of the SpringCloud cluster
  • At last, the current IP address is compared with the IP address of the cluster. If the current IP address is the smallest in the cluster, the scheduled task service is performed. If not, return.

Let’s take a look at timed tasks:

/ * * *@author zhangzhixiang
 * @date2019/12/07 * /
@Component
public class WrokTask {
    @Autowired
    private IJobService jobService;
 
    private static String serviceName="provider";
 
    /** * updates every 5 seconds */
    @Scheduled(fixedDelay = 5000)
    public void doWork(a){
        if (IPV4Util.ipCompare(this.jobService.serviceUrl(serviceName))) {
            System.out.println(serviceName+"Services, address:"+IPV4Util.getIpAddress()+", executing task"); }}}Copy the code

Timing task we can see this. The jobService. ServiceUrl method, this method is to obtain SpringCloud cluster service information, IPV4Util. IpCompare this role is compare the current service IP and cluster all IP, Return true if the current service IP is the minimum cluster service IP, false otherwise.

Let’s see if we can get SpringCloud cluster information:

/ * * *@author zhangzhixiang
 * @date2019/12/07 * /
@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());
         });
      }
      returnurlList; }}Copy the code

Using methods from the DiscoveryClient class, we can easily retrieve cluster information.

Finally, how does the IPV4Util utility class compare?

/ * * *@author zhangzhixiang
 * @date2019/12/07 * /
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 rat behavior 24,16,8,0
            // 1.192 << 24
            // 1. 168 << 16
            // 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);
    }
 
    /** * Get the IP address of the current machine **@return* /
    public static String getIpAddress(a) {
        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 instanceofInet4Address && ! address.isLoopbackAddress()) {returnaddress.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("Result of converting long to IP:" + longToIp(result));
        System.err.println("Result RESULT of IP conversion to long:" + result);
// long result2 = ipToLong("192.168.11.217");
// system.err. Println ("result2IP converted to long: "+ result2);
//        System.out.println("long转换为IP的结果: " + longToIp(result));
// system.out. println("long to IP: "+ longToIp2(result)); // system.out. println("long to IP:" + longToIp2(result));
// String ipAddress = getIpAddress();
// System.err.println(ipAddress);}}Copy the code

The utility class does the following:

  1. Obtain the current service IP address
  2. All cluster service IP addresses are converted to long data and sorted
  3. The current service IP address is converted to long data and compared with long data of the cluster service IP address

In this way, we can ensure that the scheduled tasks in the SpringCloud architecture are executed by only one service, which may cause some questions. Why not use a distributed scheduling framework to solve this problem? We can also solve this problem with distributed scheduling frameworks, such as Elastice-Job. However, the introduction of the third-party framework will sometimes increase the complexity of the system, and the learning cost will also increase accordingly. The most important thing is that there is no need for sharding for some scheduled tasks. A single point of service can be done, so there is no need to consume resources for sharding running task service.