前幾天線上新上線一個Kafka Java Consumer程序,出現一個異常的問題,那就經過查看日誌,數據寫入到了Elasticsearch索引裏面,可是前端查詢不到數據。html
最終經過和開發一塊兒定位,是由於咱們業務上的緣由,默認數據時間戳問題,默認須要使用UTC TimeZone
;但當運維用date
命令看的時候,默認是UTC時區啊,爲啥仍是寫錯了呢?前端
由於咱們線上維護的是/etc/localtime
文件來保證時區問題,並且也是UTC
時區,可是仍是寫入數據時間對不上,以後上線操做的同事說把/etc/timezone
文件刪除,而後重啓消費者程序好了。java
好了,這是爲啥,雖然知道刪除/etc/timezone
文件後,業務數據寫入正常了,可是這是爲何呢,下面咱們就來一探究竟。bash
一般我遇到這種以前沒有遇到的問題,都會藉助Google搜索一把,搜索完成後,獲得JVM加載時區文件順序以下:運維
/etc/sysconfig/clock
文件中能夠找到"ZONE"的值,注意ZONE的值要帶雙引號,如ZONE="Asia/Shanghai"中文參考連接:https://blog.csdn.net/zj380475045/article/details/72765936 http://www.360doc.com/content/12/1011/17/110467_240881174.shtml 英文參考連接:https://bugs.java.com/view_bug.do?bug_id=6456628測試
那按照搜索到的結果,跟個人狀況不對啊,咱們線上刪除/etc/timezone
文件就行了,因此確定跟文件/etc/timezone
有關啊,因此我感受確定跟操做系統和JAVA版本有關,SO我以爲實踐一把,必定要把謎底揭開。編碼
環境 | 操做系統 | JAVA版本 |
---|---|---|
aliyun | Centos6.5 | 1.8.0_25 |
如上表格是我線上環境狀況,實踐過程以下。spa
Java測試代碼以下:操作系統
[root@Labhost2 src]# cat TimeTest.java
import java.util.Date;
import java.util.TimeZone;
public class TimeTest {
public static void main(String args[]) {
long time = System.currentTimeMillis();
String millis = Long.toString(time);
Date date = new Date(time);
System.out.println("Current time in milliseconds = " + millis + " => " + date.toString());
System.out.println("Current time zone: " + TimeZone.getDefault().getID());
}
}
[root@Labhost2 src]# javac TimeTest.java # 生成測試類
[root@Labhost2 src]# ls
TimeTest.class TimeTest.java
複製代碼
從搜索咱們知道JVM讀取時區跟系統變量TZ
和文件/etc/sysconfig/clock
、 /etc/localtime
有關,我這裏在加上咱們刪除的文件/etc/timezone
一塊兒來實踐,驗證過程以下:.net
[root@Labhost2 src]# export TZ="Pacific/Honolulu"
[root@Labhost2 src]# cat /etc/sysconfig/clock
ZONE="America/Los_Angeles"
UTC=false
ARC=false
[root@Labhost2 src]# ll /etc/localtime
lrwxrwxrwx 1 root root 23 4月 18 09:23 /etc/localtime -> /usr/share/zoneinfo/UTC
[root@Labhost2 src]# cat /etc/timezone
Asia/Shanghai
複製代碼
從上信息咱們總結一下狀態:
測試項 | 時區值 |
---|---|
TZ | Pacific/Honolulu |
/etc/sysconfig/clock | America/Los_Angeles |
/etc/localtime | UTC |
/etc/timezone | Asia/Shanghai |
上面狀態設置好了以後,測試輸出驗證以下:
[root@Labhost2 src]# java TimeTest
Current time in milliseconds = 1524275592096 => Fri Apr 20 15:53:12 HST 2018
Current time zone: Pacific/Honolulu
[root@Labhost2 src]# unset TZ
[root@Labhost2 src]# java TimeTest
Current time in milliseconds = 1524275606924 => Sat Apr 21 09:53:26 CST 2018
Current time zone: Asia/Shanghai
[root@Labhost2 src]# rm -rf /etc/timezone
[root@Labhost2 src]# java TimeTest
Current time in milliseconds = 1524275627626 => Sat Apr 21 01:53:47 UTC 2018
Current time zone: UTC
[root@Labhost2 src]# rm -rf /etc/localtime
[root@Labhost2 src]# java TimeTest
Current time in milliseconds = 1524275640872 => Sat Apr 21 01:54:00 GMT 2018
Current time zone: GMT
複製代碼
從上面測試結果可知,在我這種環境下,JVM讀取時區文件順序依次爲:$TZ
> /etc/timezone
> /etc/localtime
> 默認GMT
, 因此跟搜索到的狀況不同,跟文件/etc/sysconfig/clock
無關。
好了,到這裏獲得了正確的答案了,終於明白了,能夠解釋咱們線上的狀況了,咱們線上刪除文件/etc/timezone
後,就去讀取文件 /etc/localtime
了,咱們線上文件/etc/localtime
默認維護設置的就是UTC
時區,正好符合咱們業務需求,這就解釋了。
默認GMT說明:java.util.TimeZone類中getDefault方法的源代碼顯示,它最終是會調用sun.util.calendar.ZoneInfo類的getTimeZone 方法。這個方法爲須要的時間區域返回一個做爲ID的String參數。這個默認的時間區域ID是從 user.timezone (system)屬性那裏獲得。若是user.timezone沒有定義,它就會嘗試從user.country和java.home (System)屬性來獲得ID。 若是它沒有成功找到一個時間區域ID,它就會使用一個"fallback" 的GMT值。換句話說, 若是它沒有計算出你的時間區域ID,它將使用GMT做爲你默認的時間區域。
要避免這種問題最好的方式以下:
[推薦]Java程序在發佈後的啓動腳本中,可經過JVM參數指定應用的時區、編碼, 好比 java -Duser.timezone=Asia/Shanghai -Dfile.encoding=utf8 DateTest
無論大家公司的研發人員有沒有相應的Java開發規範,會不會在啓動腳本中指點時區都不重要,重要的是做爲一個運維須要主動去溝通,問問開發他們的程序對時區和編碼是否有要求,而後主動把這些參數在啓動腳本中內設好,加強本身的運維主觀意識,減小線上運行程序對系統環境的依賴,來規避一些問題。