引入:

时钟同步一直是一个比较热的话题。因为它涉及到许多具体场景。

场景A: 2个系统做交易,A系统下订单,B系统管理订单。结果因为B系统比A慢5分钟,A下单完了,B获得的时间居然是一个未来的时间。

场景B: 搞双十一了,某公司的网上电子商城需要在11月11北京时间凌晨0点启用应急预案,然后启用强大的促销规则。结果因为时钟比北京时间慢了10分钟。等11月11零点,大量抢购的人一下子拥入网上电子商城,造成该公司访问量的“井喷”式的增长,但是因为服务器还在11月10日晚上23:50,所以没有启用应急预案,也没有使用促销规则。结果服务器挂了,客户跑了。。。

解决方案:

以上就是目前一些非常典型的时钟不同步的例子:

对于场景A,一般做法是吧某台服务器作为中央时钟服务器,让其作为标准的时钟服务器。然后确保所有服务器都可以访问此中央时钟服务器,并且依次在每台需要时钟同步的机器上运行命令 ntpupdate <时钟服务器IP>即可。

但是场景A有一个致命缺陷就是所有的服务器都必须能连接到时钟服务器。这对于大型企业有多个区段划分,彼此多个网段,网段之间相互又不通的情况,无法解决。那么在联网的情况下,这里有一个变通的轻量级的方法,可以让所有服务器(尤其是服务器上运行着代码的地方),和北京时间同步,这种解决方案也许更加适合场景B。

这里我写了一个工具类,它可以有效的和北京时间同步,代码如下:

package com.charles.study;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Calendar;import java.util.Date;import java.util.TimeZone;/** * 这个工具类用于获得标准的北京时间 * @author charles.wang(mailto:charles_wang888@126.com) * */public class BeijingDateTimeUtil {        private BeijingDateTimeUtil(){}            public static final String BEIJING_TIME_SERVICE_URL="http://www.beijing-time.org/time.asp";    public static final String BEIJING_TIME_ZONE = "GMT+8";            /**     * retrieve the standard beijing time by beijing time service     * @return Calendar which stands for current beijing time     */    public static Calendar retrieveBeijingStandardDatetime() {                try{                                //access the webpage which can provide precise beijing-time            URL url = new URL(BEIJING_TIME_SERVICE_URL);        HttpURLConnection uc = (HttpURLConnection) url.openConnection();        uc.connect();                StringBuilder sb = new StringBuilder();        BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream()));                String line;        while ((line = br.readLine()) != null) {            sb.append(line);        }                String beijingTimeInfoString = sb.toString();        String[] infos = beijingTimeInfoString.split(";");                //split information which can stand for year/month/day/dayOfWeek/hour/minute/second        int year         = Integer.parseInt(infos[1].substring(infos[1].indexOf("=")+1));        int month        = Integer.parseInt(infos[2].substring(infos[2].indexOf("=")+1));        int day          = Integer.parseInt(infos[3].substring(infos[3].indexOf("=")+1));        int dayOfWeek    = Integer.parseInt(infos[4].substring(infos[4].indexOf("=")+1));        int hour         = Integer.parseInt(infos[5].substring(infos[5].indexOf("=")+1));        int minute       = Integer.parseInt(infos[6].substring(infos[6].indexOf("=")+1));        int second       = Integer.parseInt(infos[7].substring(infos[7].indexOf("=")+1));                //create a calendar object         //make sure that (1)using Beijing timezone        //               (2)month starts from 0 instead of 1        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(BEIJING_TIME_ZONE));        cal.set(year,month-1,day,hour,minute,second);        return cal;                }catch(MalformedURLException ex){            ex.printStackTrace();            return null;        }catch(IOException ex){            ex.printStackTrace();            return null;        }            }                public static void main(String [] args) {                        Calendar beijingCalendar = retrieveBeijingStandardDatetime();        if(beijingCalendar!=null){            Date beijingDatetime = beijingCalendar.getTime();            DateFormat df =  new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");            String dateFormatStr  = df.format(beijingDatetime);            System.out.println("Beijing Current Datetime:"+dateFormatStr);        }else{            System.out.println("BeijingTime service not available");        }                }}

执行示例代码中的测试,我们可以看到:

这里也可以看出,我的笔记本时间比北京时间快9秒。