找回密码
 注册
关于网站域名变更的通知
查看: 427|回复: 1
打印 上一主题 下一主题

这是一个诡异的时间问题追查

[复制链接]
  • TA的每日心情

    2019-11-20 15:22
  • 签到天数: 2 天

    [LV.1]初来乍到

    跳转到指定楼层
    1#
    发表于 2020-3-9 10:17 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

    EDA365欢迎您登录!

    您需要 登录 才可以下载或查看,没有帐号?注册

    x
    ) d, c& v' n5 W. P
    问题引入:
    2 S% j2 {2 V8 p  v( A/ z问题是我们重装了一台电脑之后,发现apache. c/ o$ v" g* o4 A8 k1 @5 f
    的日志里突然增加了很多服务时间超长的请求,如下面两条:8 `" s3 _- j2 S: ]/ G; V5 D1 a
    ** - - 4913229 11227 [29/Mar/2012:09:59:58 +0800] "GET*****
    9 A1 j) `- n4 a) c( f) T) X3 b** - - 4793564 9846 [29/Mar/2012:09:59:58 +0800] "GET *****  N1 q) h5 _6 T/ _1 ~. X- p- J
    请求服务时间都在4s以上!这绝对无法忍受啊!- G5 U# i; H, `6 ]$ m
    " {' {: l7 v3 y. K
    分析过程:) ]8 D1 o7 S$ v! X
    ( b3 _; T2 B5 K4 Y
    但是,当我们:
    - y, d5 }( P3 } * 手动访问这台服务器之后,发现基本上请求都是正常响应的;
    ' S& W( J4 X* X* j * 从前端反向代理的日志来看,服务时间也是正常的;( E4 j0 F. W0 F! ]$ e" u. `% k. h
    那这样推断,真相只有一个,那就是apache的日志时间记录有问题!' F! n! O  k% c: m
    % N+ E3 }' K- d
    什么问题呢?不清楚,但是apache  L: j1 K( M9 _$ B. J) x
    的时间记录不外乎是在收到请求时记个时间,请求处理完成之后再记一个时间,既然是时?% j: x& i2 E' R& _- v' C: E
    涞奈侍猓敲淳涂纯聪低呈奔浒桑幼罴虻サ?date 开始,多执行几次:
    # R& L. N' U2 \* P0 U5 J+ d" ? apache2 # date
    9 k/ C4 L) u, r8 `# @3 r3 N+ PWed Mar 28 17:25:08 CST 2012! ?3 t$ p! T7 M5 D1 ?9 t+ R: m
    apache2 # date) l' L/ P' U1 r! g; w$ {
    Wed Mar 28 17:25:04 CST 2012  时间减少了?
    - `. J5 Z% P' J* r) L apache2 # date- p  y7 f2 I, h; l/ O: o
    Wed Mar 28 17:25:06 CST 2012
    9 s6 @$ t; O2 q8 Qapache2 # date
    / O9 F4 s  J" Q: UWed Mar 28 17:25:12 CST 2012
    3 R1 I1 W5 @0 j7 @apache2 # date
    ; p3 P+ i) Q. |/ S- KWed Mar 28 17:25:13 CST 2012
    # }2 N( U3 I* _$ @) c; |- [apache2 # date
    - w) H) `0 Y, x& f4 o/ \( G- C2 }Wed Mar 28 17:25:09 CST 2012 时间减少了?
    ( p) L6 I. Q4 s5 R4 a- }* p! _apache2 # date: a/ n! E1 V& t+ y" N; m3 w
    Wed Mar 28 17:25:09 CST 2012) E' l; L2 Z% \: {; Z
    apache2 # date: x& ^+ ]; R) v  |9 ]
    Wed Mar 28 17:25:15 CST 2012
    2 d) A8 H  h! W4 ]& V& y/ S" d那么第一个怀疑就是系统里有进程在自己不断的调时间,比如ntpdate, ntpd8 L& R+ R5 F# M9 T/ ^
    ,那么我们首先把 ntpd给停掉;然后系统也没有任何地方会调用ntpdate;2 W6 [/ e7 `" }2 J& `9 z
    继续观察,发现停掉ntpd后,现象依旧!
    , w0 u4 q1 k# a4 t" w! {还有什么可能呢? date
    9 p* Y7 @6 p, G$ B! \' u( G程序是从内核里面读取时间还是说需要读取硬件时间?到底是怎么计算的呢?7 \* {" a3 b3 I$ W  {
    请教下同事们,据说 sys_time 是读的kernel里的时间,而 sys_gettimeofday
    " O1 f% ?( N+ p7 V0 C是需要读硬件时钟的。
    8 G- Y/ g# J! `" a# T8 ]那么就测试一把吧:
    2 \! a: p) @+ x先写个程序调用 time(), 然后打印结果,结果是这样的:% T; R. D; O4 k- e+ G" W" I7 O# _5 }
    13329866908 R7 F! _. q( m, T) d4 S
    1332986692
    0 v# d5 X2 D9 m1332986693. q9 u0 m5 g1 o4 q/ U7 s% z" U
    1332986693, X) r& @9 ?* g0 G: n
    1332986694
    % J  L  d7 `: F' D& D1332986694: X8 a) R7 x. Y' X2 Q
    13329866954 p; o" x7 I, t" S$ Z
    1332986695
    9 f9 K$ g5 D% |1332986696
    * @% M9 s3 }2 M: o7 l! c5 m1332986696
      v" Z1 j7 g/ h  a  u1332986697
      v, R! k1 [' Q: l1 r& A( @( L1332986697
      {' H) ~4 ^3 \1332986698+ f; w3 D. e$ i4 L5 M( a
    13329866987 t6 h( l( D, X! Y1 g  @. ~
    1332986699( |2 g2 `4 `" I' J7 |. }& ~$ U
    确实没有问题了!看来有可能是硬件问题了,再看看clock_gettime 的调用:3 l7 T. Z  M9 N* c2 u: F1 ~
    {" m6 u( ^4 k) L. i# g
            struct timespec ts;                              
    $ M. e2 y& ?  {; L8 K& ?( t        int ret = 0;                                      
    8 I) `& }& i7 J1 N- i( P4 k! m6 ~        ret = clock_gettime(CLOCK_REALTIME, ts);          - T, O# e2 e- d2 M9 M  P  f. u8 u
            printf("ret: %d, time: %d %d", ts.tv_sec, ts.tv_ns6 h8 c, D" w* o! P+ G; _, C$ V% ?( y
    }                                                         
    ) Z; @8 d% o3 ^编译:        gcc -g -o clock_gettime getdate.c -lrt            
    4 w+ L! T! p# T) P& i4 e                                                          " @  {3 ?* s  }! U
    ~ # ./clock_gettime                                    # j7 n: r1 w7 `/ y3 k2 \
    ret: 1332991087, time: 594076000 -1
    1 d2 n& v5 s. F3 C5 rret: 1332991088, time: 379009000 -11 U% t4 `6 {; z6 ^9 x1 c& J: ~+ _0 {
    ret: 1332991089, time: 68561000 -17 b9 d* }4 M- F
    ret: 1332991089, time: 714013000 -1
    . }% b1 c4 ~- i  Fret: 1332991086, time: 511250000 -1 时间在跳4 k4 H, H7 S$ e- n
    ret: 1332991091, time: 945626000 -1. u1 C# ~1 U, R5 l: E# K; f8 G$ a0 X
    ret: 1332991092, time: 650479000 -1. d7 Z# a) F9 U7 ]9 B) o
    ret: 1332991088, time: 734780000 -1- |4 D! P' z0 Z4 [, q6 |7 u6 P
    ret: 1332991094, time: 506114000 -1时间在跳                    0 a5 D  z. _+ {
    看来果然有可能是硬件问题!5 A- x1 k% ?$ _# z. I4 V
    那么,检查一下,直接调用一下 hwclock --show ,看看是不是可以验证这个问题:
    ) r- n5 I- H1 r4 S5 c ~ # hwclock --show                              
    # ~8 `& v9 b* ~3 f6 \# iThu 29 Mar 2012 10:22:21 AM CST  -0.948923 seconds  
    ' ^/ ]- H. R5 L ~ # hwclock --show                               + h% Y. O& I2 p5 Z3 B- a0 R$ e
    Thu 29 Mar 2012 10:22:22 AM CST  -0.188950 seconds  
    ' O) b5 O6 q) B  a ~ # hwclock --show                              
    - ~# {2 k1 v. `6 o/ g& T: WThu 29 Mar 2012 10:22:23 AM CST  -0.244766 seconds  
    5 T6 C1 ]- `; ` ~ # hwclock --show                              
    9 V/ k$ g0 p' }2 R  p- E! DThu 29 Mar 2012 10:22:24 AM CST  -0.336868 seconds  ) L) L2 d1 r7 _* z" E" B
    ~ # hwclock --show                               4 W8 R" i+ {% e: A: c5 W1 V
    Thu 29 Mar 2012 10:22:25 AM CST  4.237159 seconds   ' z' w6 y. ~$ `3 Y
    ~ # hwclock --show                              
    , X6 V0 ~2 U6 [' LThu 29 Mar 2012 10:22:26 AM CST  4.238672 seconds   3 i& r' j2 A' m" G* m7 A: V$ O9 S
    ~ # hwclock --show                               - p. u) M& P: W" k7 Y8 K! S- j
    Thu 29 Mar 2012 10:22:27 AM CST  -0.379418 seconds  # k# a( r6 a2 {2 c1 k. F0 L) t
    时间居然都是正确的!显然问题没有那么简单。。。那到底是怎么回事??还是从date
    " p" V9 E; N- Z7 p+ E, {) x开始查起吧:4 v) R3 K; I+ @% E" e" o
    date是一个glibc里面的程序,那它的时间源是从哪里来的?查看一下glibc
    - Z% m+ D, f- I3 X8 a: Z的源码,它的时间从这里来:
    2 ?, R! r( r+ S7 K1 M& \// glibc coreutils gettime.c
    ( Z, n* z- z5 g/* Get the system time into *TS.  */
    4 N9 H/ Q3 |9 ]& \7 ~void# r* y5 e6 {' j: g
    gettime (struct timespec *ts)
    5 z8 C1 ^$ F: y{
    , P' }% R2 V6 d' i$ i#if HAVE_NANOTIME# C8 h0 ~  g$ W- W; c1 o7 N
      nanotime (ts);5 o) s- {0 j7 @. Q& }; i, J' ?
    #else6 T2 s2 K! D9 E* c/ g1 Q
    0 ]) p5 S) P# f; x$ C# S  c
    # if defined CLOCK_REALTIME && HAVE_CLOCK_GETTIME
    ! s' L' g7 \. j' S& P  if (clock_gettime (CLOCK_REALTIME, ts) == 0)) s4 p& q# u1 U$ q  G8 M" D! N
        return;( ?4 G4 e% S6 R! a7 f& @
    # endif5 s! [: l% b4 _! J

    : A: H$ S7 q; @# I. `( W4 U0 [# if HAVE_GETTIMEOFDAY
    4 x2 e* r# q/ F4 X# {# k# r) d  {
    8 V! d# Q$ J  H9 M6 V8 [5 {    struct timeval tv;
    8 P% i" A0 j* y# I* ]  R3 m    gettimeofday (&tv, NULL);1 k1 R! i" y3 H+ r
        ts->tv_sec = tv.tv_sec;; ?) C2 f$ ^$ F/ I
        ts->tv_nsec = tv.tv_usec * 1000;
    # h; Y; `" ^3 _+ ^: |: R  }
    ! h/ {- C0 v: A' y# else
    8 G7 ^: L9 q) H0 g4 C  ts->tv_sec = time (NULL);1 S7 Y+ W% Z; r0 y: H+ _  a- f1 j  V5 G
      ts->tv_nsec = 0;
      e3 N! `, G! E: G7 E# endif
    0 p& H4 a& R: q1 h ; T. H9 C9 q$ T$ A. E  A% ^- H
    #endif
    ) U9 Q0 W6 x1 I}- @1 T' k+ E/ U3 b6 t
    按照 nanotime, clock_gettime, gettimeofday的顺序去读取,我们这个系统中就是 , j8 r; q3 l9 G
    clock_gettime,从上面的小程序中也可以看到clock_gettime的时间是有问题的,那么$ s0 o; ]4 q/ Q; @
    clock_gettime的时间源是怎么来的呢?
    ! T$ A) Q  l' s- x// linux kernel 2.6.18 arch/x86_64/kernel/time.c
    4 G- T0 S1 A2 i& m* {- p9 E# Jvoid do_gettimeofday(struct timeval *tv)
    0 ^& ^- A+ f6 ]. s{) d8 R+ [) t& {& S4 x& W
    unsigned long seq, t;" y2 J4 L1 O- o7 a- P
    unsigned int sec, usec;# }1 c" T+ D$ e7 R. {# D

    8 y) ]+ J1 ~9 D7 n+ d3 _5 Ido {8 z+ {# i+ m! _5 m
    seq = read_seqbegin(&xtime_lock);
    + U4 d1 F4 G9 P6 N+ g
    0 P% D6 C! m" y  ~- dsec = xtime.tv_sec;
    / |$ }% b! m0 r2 f2 Yusec = xtime.tv_nsec / NSEC_PER_USEC;
    & J7 q- B& o$ F! Mt = (jiffies - wall_jiffies) * USEC_PER_TICK +
    # C7 K* N0 u; ?  ^2 ndo_gettimeoffset();5 m' C+ h' E; D
    usec += t;3 e6 ^% _6 P0 Y

    , `5 Y' U! ^$ `6 }  k0 Z4 S* [1 ^% W} while (read_seqretry(&xtime_lock, seq));
    : W# X9 j! d1 b+ P & P- |' q! C$ q. i' [
    tv->tv_sec = sec + usec / USEC_PER_SEC;2 I" p( `. l% [3 P8 L4 G# [2 U
    tv->tv_usec = usec % USEC_PER_SEC;/ g, \( M; O( |
    }
    4 B$ v* m9 Q4 n反正就是一通计算,其中比较重要的是 do_gettimeoffset4 ?: a7 A; i) b9 B- _
    ,这个函数的目的是计算上一次更新 xtime
    ; R. x1 R' ?. L) Y$ S$ t4 F  M到当前执行这一段时间的偏移量,然后用这个偏移量去加到 xtime
    1 s3 f6 J9 G- d+ W$ F上去,就是当前的时间(因为xtime的精度不够,clock_gettime  P/ X. W- Y7 ?. c  r) b" i
    的精度要求更高,所以必须计算一个偏移量)。
    1 g/ a* }& P* F: F那么,这个 do_gettimeoffset又是从哪来的?同样的文件里面有:/ Z: C, @$ n9 |  L( g
    static inline unsigned int do_gettimeoffset_tsc(void); u9 V, p5 B! w: u+ j& j
    {
    : [" i& b, }6 b2 q  [2 Tunsigned long t;
    2 l- r5 ^" O" h& u6 munsigned long x;9 O9 I+ r7 L# v# W
    t = get_cycles_sync();
    : a9 C0 M: Y3 p1 Mif (t < vxtime.last_tsc) # A( t- q8 y7 q8 y- g6 h& x7 u
    t = vxtime.last_tsc; /* hack */
    3 I9 [+ m, I5 T' }7 N' F% X" Sx = ((t - vxtime.last_tsc) * vxtime.tsc_quot) >> US_SCALE;  J6 Z- j, V6 C7 D  u0 E# d
    return x;
      y- Q; y3 e* {2 B}3 q' B* A: x! h7 u! T3 w

    * v6 z; X6 h0 a2 [1 g: h. [static inline unsigned int do_gettimeoffset_hpet(void)4 i' ^9 j( I$ v6 f
    {
    $ K) M4 w  G: Y2 y! y( c1 M( Z4 l- P/* cap counter read to one tick to avoid inconsistencies */
    * Y7 N/ @2 M% `0 r2 q6 @unsigned long counter = hpet_readl(HPET_COUNTER) - vxtime.last;
    8 d3 h* f* u6 C: W& Rreturn (min(counter,hpet_tick) * vxtime.quot) >> US_SCALE;
    1 v+ i' _/ Q) w}
    , ?9 f( G& A) ~: W" W! ] . q7 u4 m, f, x. P# T/ }
    unsigned int (*do_gettimeoffset)(void) = do_gettimeoffset_tsc;0 g4 e5 L, t6 Q3 d7 m
    也就是说,go_gettimeoffset 是从 tsc寄存器从拿到的,这又是神马玩意呢? , w; ?0 u/ f7 F4 b1 J: ?5 F& G" W
    参考一下下面的几篇文章【1】【2】【3】,基本就明白了:! H' `$ H2 J! C; C
    tsc是一个cpu内的寄存器,用来累加从系统启动到目前,所有的时钟周期数,全称是 , M3 x9 t* E9 j* K# v
    Timestamp Counter;
    # _# x& f0 T6 ]而且,最重要的是我们发现了一个重要的线索,那就是:
    # F( o, v' Z" w7 z) M在多核cpu的架构中,tsc寄存器是很有可能不一致的,特别是在intel的比较老的多核CPU" m# W; e, g/ ~
    中!坑爹....
    ! a+ U/ e/ U5 h, }- S! ~9 b很多文章提到过这个问题,比如【1】【2】【4】,显然如果不同cpu* a( ?3 C% t7 J; [. d
    读到的值不同的话,多次执行,显然时间是不一致的。
    . ]' M5 Q/ Q1 G/ W; d9 Z' g0 N- |看起来就是这个问题了,那么就来验证一把:. I1 |1 _  e' y! m/ U
    #include<stdlib.h>% ]' b7 w' P0 K6 q% n' Y1 ?0 Q
    #include<stdio.h>
    : d* @3 m5 _% D0 \9 u4 x; W$ @#include<sys/types.h>
    6 n( ^/ X( _- ~) ?  V0 |& y#include<sys/sysinfo.h>
    # S" g# d; Z% n4 G) M7 \#include<unistd.h>7 U) ^2 F- l8 W
    #include<time.h>
    5 K# O8 J- W, j& [1 ]7 A6 Z
    ' q) y& d9 V, l* q/ q6 V; M3 d#define __USE_GNU  O" V9 t) |/ q. S8 y  k4 W
    #include<sched.h>
    7 d( R5 f! ~6 I* |#include<ctype.h>) @  D; s1 N3 f9 h
    #include<string.h>  E2 l& B% \4 x6 E
    1 I; r, h2 n2 ]4 N/ j8 K  A
    1 Z. d4 y; p1 U! Z9 W% u

    ' \7 G! U7 ^' }9 L8 z8 n& `int main(int argc, char **argv){; t2 K$ p9 n, N3 d- C# Z. k
    % Y8 ]1 R6 T' m
            cpu_set_t cs;
    3 i, Z9 N% }9 ^6 l8 \        CPU_ZERO(&cs);
    : Z' a) q  v* ^+ Z  r- m, S0 _        CPU_SET(0, &cs);
    ' S2 n/ C- W& |8 s* v3 f. r! M) B  u
    : }: E2 O3 P2 r% v0 b  T) u, o        sched_setaffinity(0, sizeof(cpu_set_t), &cs);
    8 ?4 l7 _0 H* l  `. m/ j( k  C 1 v% T3 W6 ?3 p) D$ ^8 |+ R0 h+ G
            struct timespec ts;
    0 {8 M+ ?  l8 G+ b& @  N/ M        int ret = 0;- d' T# L* s) p5 C  ?
            ret = clock_gettime(CLOCK_REALTIME, &ts);) P* N# M2 P$ I" X/ J( D
            printf("ret: %d, time: %lld %lld", ret,  ts.tv_sec, ts.tv_nsec);
    , ]* N8 E5 C! w9 M. G+ f 4 c  w+ l8 P$ Y
    }6 |  H7 h: y5 S$ Y

    - j9 a: d) q! d2 B4 O/ Vgcc -g -o cpu0_clock_gettime cpu_getdate.c -lrt
    " E. U7 `6 V2 X9 O1 z' A
    7 \) u% Z6 ~0 w& u/ L) iret: 0, time: 1332992815 537481000
    4 u9 k" N( H% D. @# Tret: 0, time: 1332992816 506704000
    " D  i- H4 i: E5 W# c! k! Nret: 0, time: 1332992817 99100001 O5 p. J5 t1 Z% [4 X
    ret: 0, time: 1332992817 480431000
    3 s4 z0 W1 s% C; k7 Fret: 0, time: 1332992817 936965000
    " W' _% Z* i  B: C) Wret: 0, time: 1332992818 416634000
    0 I: Z( j) p% ?0 i3 j; U0 Uret: 0, time: 1332992818 850319000$ N+ D7 }7 A4 u5 v; l
    ret: 0, time: 1332992819 305075000. `' Q( L- |3 _
    ret: 0, time: 1332992819 729108000
    + @3 i8 ^) M$ p9 E) Y  Jret: 0, time: 1332992820 164726000
      o# l6 E! I. rret: 0, time: 1332992820 577573000' S* l+ m: z  L7 t$ S  w
    ret: 0, time: 1332992820 985025000
    ' H- i, m9 X  H; |4 M. Wret: 0, time: 1332992821 394070000
    6 Y0 W$ V, e/ ~) U( |- @0 _ret: 0, time: 1332992821 8167080006 a* r9 U% ~7 V) v+ t% m
    ret: 0, time: 1332992822 216811000
    ; V1 k  B( {3 zret: 0, time: 1332992822 616901000
      ?, M  V- q& E; R) T+ k# E% kret: 0, time: 1332992823 18564000w) v1 i! }* p$ ?
    ret: 0, time: 1332992823 7275850006 D$ o' A4 ^" `' _
    通过把程序的调度固定在某一个具体的cpu中,那么clock_gettime的结果也是正确的!
    / U$ q2 V" b4 N- V: ^' z经过几次测试,很容易找到两个cpu时间不一致的情况,具体这个例子来说就是 cpu0和5 Q6 _9 M% t) n3 f* U7 T- q. g; `
    cpu5,把上面的程序修改一下,可以看到结果基本上每次都会跳一把。
    " o& o( I: y" z) L& t问题解决:# b& R; ]7 g' i: _# Q
    那么,tsc在多核cpu中的值不同,应该怎么解决呢? 如 【4】所示,在高版本的linux
    3 _; M" d) a3 ?, M/ C1 q5 {" ?- `内核中,有人提交了补丁,可以在内核启动时检查并且同步一把,那么升级一下内核到 2' `. P4 i! U% W9 M. Y/ [0 ?
    .6.38。$ ?2 a* @1 s$ v1 m$ i+ D
    重新启动到新内核之后,问题解决!+ v0 A) u# k  _. K  k
    总结一下:2 s$ F3 \8 ?5 Q7 u0 }) {
    1. apache的时间统计方式,没具体看实现,但是显然前后的两个时间可能是在不同的cpu( x& l: A, v0 d/ g) x; c0 D! ]4 _
    核上计算出来的。- a' N2 E$ \* e8 ~9 ]5 |
    2. tsc的值,并不能保证是同步的,特别是在某些Intel cpu
    # v7 `/ H; ^5 h# ?; g# h* `5 w4 S的情况下;但是新的内核会做一些检查和resync的工作;
    6 X, u0 x- C. S0 _5 U) v: |) p3. time()调用直接读取内核的xtime值,不会触及硬件;但是 gettimeofday(),
    - }5 z6 U5 R, k$ a' q5 Qclock_gettime() 在 x86_64下都会读取tsc值;
    / Y4 l3 y0 p0 E& T+ v9 p( c1 ?% h4. hwclock读的硬件时钟显然不是 tsc,应该是实时时钟RTC;
    2 a0 b" n5 I$ v# t. t8 K; U【1】 http://en.wikipedia.org/wiki/Time_Stamp_Counter0 `) ]7 \) r3 `' F6 }: w1 F/ n
    【2】 http://juliusdavies.ca/posix_clocks/clock_realtime_linux_faq.html( |" ~& O8 A9 l) W; g" c
    【3】http://www.linuxforum.net/forum/gshowflat.php?Cat=&Board=driver&Number=4 V% L! V9 K8 M# _) B" I# Y) c
    385219&page=0&view=collapsed&sb=5&o=all&fpart , linux时钟中断机制,Copyright 8 e# J: E0 n; H! |8 |4 u
    2003 by 詹荣开. c% X, q$ }, M7 K3 F. C7 m& B9 P
    【4】http://lwn.net/Articles/211051/ x86: unify/rewrite SMP TSC sync code  

    该用户从未签到

    2#
    发表于 2020-3-9 17:20 | 只看该作者
    时间问题追查,牛逼了
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-11-25 15:22 , Processed in 0.171875 second(s), 24 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表