From 35b9d4098d2541585358770ad195b380a9c84135 Mon Sep 17 00:00:00 2001 From: milk <53408947@qq.com> Date: Wed, 12 Nov 2025 13:21:36 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=A4=84=E7=9B=91=E5=90=ACip?= =?UTF-8?q?roxy=E9=80=BB=E8=BE=91=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Module/DeviceInfo.py | 193 +++++++++--------- Module/__pycache__/DeviceInfo.cpython-312.pyc | Bin 42543 -> 42898 bytes .../__pycache__/FlaskService.cpython-312.pyc | Bin 43138 -> 43148 bytes Utils/__pycache__/LogManager.cpython-312.pyc | Bin 14674 -> 14674 bytes .../__pycache__/ThreadManager.cpython-312.pyc | Bin 11794 -> 11838 bytes 5 files changed, 100 insertions(+), 93 deletions(-) diff --git a/Module/DeviceInfo.py b/Module/DeviceInfo.py index 9701058..d053f9f 100644 --- a/Module/DeviceInfo.py +++ b/Module/DeviceInfo.py @@ -6,6 +6,7 @@ - 并发提速:_add_device 异步化(受控并发) - iproxy 守护:本地端口 + /status 探活,不通则自愈重启;连续失败达阈值才移除 """ +import datetime import os import time import threading @@ -310,107 +311,111 @@ class DeviceInfo: FAIL_THRESHOLD = int(os.getenv("IPROXY_FAIL_THRESHOLD", "3")) # 连续失败阈值(可用环境变量调) INTERVAL_SEC = int(os.getenv("IPROXY_CHECK_INTERVAL", "10")) # 巡检间隔 - while True: - snapshot = list(self._models.items()) # [(deviceId, DeviceModel), ...] - for device_id, model in snapshot: - try: - if model.type != 1: - # 离线设备清零计数 - self._iproxy_fail_count.pop(device_id, None) - continue + try: + while True: + snapshot = list(self._models.items()) # [(deviceId, DeviceModel), ...] + for device_id, model in snapshot: + try: + if model.type != 1: + # 离线设备清零计数 + self._iproxy_fail_count.pop(device_id, None) + continue - port = int(model.screenPort) - if port <= 0 or port > 65535: - continue + port = int(model.screenPort) + if port <= 0 or port > 65535: + continue - # 健康探测 - ok = self._iproxy_health_ok(device_id, port) - if ok: - # 健康:清零计数 - if self._iproxy_fail_count.get(device_id): + # 健康探测 + ok = self._iproxy_health_ok(device_id, port) + if ok: + # 健康:清零计数 + if self._iproxy_fail_count.get(device_id): + self._iproxy_fail_count[device_id] = 0 + + # CHANGED: 若之前降级过,这里标记恢复并上报 + need_report = False + with self._lock: + m = self._models.get(device_id) + if m: + prev_ready = getattr(m, "ready", True) + prev_broken = getattr(m, "streamBroken", False) + if (not prev_ready) or prev_broken: + m.ready = True + if prev_broken: + try: + delattr(m, "streamBroken") + except Exception: + setattr(m, "streamBroken", False) + need_report = True + if need_report and m: + try: + print(f"[iproxy-check] 自愈成功,恢复就绪 deviceId={device_id} port={port}") + self._manager_send(m) + except Exception as e: + print(f"[iproxy-check] 上报恢复异常 deviceId={device_id}: {e}") + + # print(f"[iproxy-check] OK deviceId={device_id} port={port}") + continue + + # 第一次失败:尝试自愈重启 + print(f"[iproxy-check] 探活失败,准备自愈重启 deviceId={device_id} port={port}") + healed = self._restart_iproxy(device_id, port) + + # 重启后再探测一次 + ok2 = self._iproxy_health_ok(device_id, port) if healed else False + if ok2: + print(f"[iproxy-check] 自愈成功 deviceId={device_id} port={port}") self._iproxy_fail_count[device_id] = 0 - # CHANGED: 若之前降级过,这里标记恢复并上报 - need_report = False - with self._lock: - m = self._models.get(device_id) - if m: - prev_ready = getattr(m, "ready", True) - prev_broken = getattr(m, "streamBroken", False) - if (not prev_ready) or prev_broken: - m.ready = True - if prev_broken: - try: - delattr(m, "streamBroken") - except Exception: - setattr(m, "streamBroken", False) - need_report = True - if need_report and m: + # CHANGED: 若之前降级过,这里也顺便恢复并上报 + need_report = False + with self._lock: + m = self._models.get(device_id) + if m: + prev_ready = getattr(m, "ready", True) + prev_broken = getattr(m, "streamBroken", False) + if (not prev_ready) or prev_broken: + m.ready = True + if prev_broken: + try: + delattr(m, "streamBroken") + except Exception: + setattr(m, "streamBroken", False) + need_report = True + if need_report and m: + try: + self._manager_send(m) + except Exception as e: + print(f"[iproxy-check] 上报恢复异常 deviceId={device_id}: {e}") + continue + + # 自愈失败:累计失败计数 + fails = self._iproxy_fail_count.get(device_id, 0) + 1 + self._iproxy_fail_count[device_id] = fails + print(f"[iproxy-check] 自愈失败 ×{fails} deviceId={device_id} port={port}") + + # 达阈值 → 【不移除设备】,改为降级并上报(避免“删了又加”的抖动) + if fails >= FAIL_THRESHOLD: + with self._lock: + m = self._models.get(device_id) + if m: + m.ready = False + setattr(m, "streamBroken", True) try: - print(f"[iproxy-check] 自愈成功,恢复就绪 deviceId={device_id} port={port}") - self._manager_send(m) + if m: + print( + f"[iproxy-check] 连续失败 {fails} 次,降级设备(保留在线) deviceId={device_id} port={port}") + self._manager_send(m) except Exception as e: - print(f"[iproxy-check] 上报恢复异常 deviceId={device_id}: {e}") + print(f"[iproxy-check] 上报降级异常 deviceId={device_id}: {e}") - # print(f"[iproxy-check] OK deviceId={device_id} port={port}") - continue + except Exception as e: + print(f"[iproxy-check] 单设备检查异常: {e}") - # 第一次失败:尝试自愈重启 - print(f"[iproxy-check] 探活失败,准备自愈重启 deviceId={device_id} port={port}") - healed = self._restart_iproxy(device_id, port) - - # 重启后再探测一次 - ok2 = self._iproxy_health_ok(device_id, port) if healed else False - if ok2: - print(f"[iproxy-check] 自愈成功 deviceId={device_id} port={port}") - self._iproxy_fail_count[device_id] = 0 - - # CHANGED: 若之前降级过,这里也顺便恢复并上报 - need_report = False - with self._lock: - m = self._models.get(device_id) - if m: - prev_ready = getattr(m, "ready", True) - prev_broken = getattr(m, "streamBroken", False) - if (not prev_ready) or prev_broken: - m.ready = True - if prev_broken: - try: - delattr(m, "streamBroken") - except Exception: - setattr(m, "streamBroken", False) - need_report = True - if need_report and m: - try: - self._manager_send(m) - except Exception as e: - print(f"[iproxy-check] 上报恢复异常 deviceId={device_id}: {e}") - continue - - # 自愈失败:累计失败计数 - fails = self._iproxy_fail_count.get(device_id, 0) + 1 - self._iproxy_fail_count[device_id] = fails - print(f"[iproxy-check] 自愈失败 ×{fails} deviceId={device_id} port={port}") - - # 达阈值 → 【不移除设备】,改为降级并上报(避免“删了又加”的抖动) - if fails >= FAIL_THRESHOLD: - with self._lock: - m = self._models.get(device_id) - if m: - m.ready = False - setattr(m, "streamBroken", True) - try: - if m: - print( - f"[iproxy-check] 连续失败 {fails} 次,降级设备(保留在线) deviceId={device_id} port={port}") - self._manager_send(m) - except Exception as e: - print(f"[iproxy-check] 上报降级异常 deviceId={device_id}: {e}") - - except Exception as e: - print(f"[iproxy-check] 单设备检查异常: {e}") - - time.sleep(INTERVAL_SEC) + time.sleep(INTERVAL_SEC) + except Exception as e: + print("检查iproxy状态遇到错误:",e) + LogManager.error("检查iproxy状态遇到错误:",e) def listen(self): LogManager.method_info("进入主循环", "listen", udid="system") @@ -435,6 +440,7 @@ class DeviceInfo: for udid in online - known: if (now - self._first_seen.get(udid, now)) >= self.ADD_STABLE_SEC: + print(datetime.datetime.now().strftime("%H:%M:%S")) print(f"[Add] 检测到新设备: {udid}") try: self._add_device(udid) # ← 并发包装器 @@ -561,6 +567,7 @@ class DeviceInfo: print(f"[Manager] 准备发送设备数据到前端 {udid}") self._manager_send(model) + print(datetime.datetime.now().strftime("%H:%M:%S")) print(f"[Add] 设备添加成功 {udid}, port={port}, {w}x{h}@{s}") def _remove_device(self, udid: str): diff --git a/Module/__pycache__/DeviceInfo.cpython-312.pyc b/Module/__pycache__/DeviceInfo.cpython-312.pyc index 64fcdabdb62ea7162b16a155c1af2e6c47a22a3f..cdcfe14aefbb61afc2ff028be4dcb9bd711b2f8c 100644 GIT binary patch delta 2091 zcmZXVdr(yO702(nkA1KYb{CeF%VQzC%R^p42sShXnHmuk=Nrv3dcwf&=a zc0a%G@0{~HzsI?cOCPewKVn@UxLk1zx&Cy^+wJ9Z^iO_S2pbSYg8NSAU0FfV1x0XaUJBYUE`#Jal6B727gDNkM{`-nAx{9Vx;VofC0 ze3q$ZWdATDJCZK2^iUW^Dlpn1g@a7vAm6}3*qE_2^H5}@Ira(AXnfyOC4gki`#)kq zGfuB+7+T?vI9N8EYrX>D3|na`4=y9} zjiGqRGtO80EW*zPhB<+rf=pwn?lQm#PHyS8>xC<;VUBnD+1Z--B00sX#y5CIEBMJqd$%78X5!c$D10o$Ap-DfYhBtmgCZvWvXkIM3Ar>!)ae-0;6Wmu-S#3hVp{8VvXYlt zj~^;yuCPy37xPylZpsq2x{Uc%MYtx8`%TDO6JW1dy|p5Htym!TYi0HtD|fB3qK4&W zSb^x7IH4xdIuo$h285Z6{92zd>l28c744fuVK$V%$uE517l_s_lI1UY^(+8} zYXzz+?e&@5b(_0BmAjrQP#w0{7jxI21jL1>F9{(Cz8z9PJ*4V+uULiCBw&6`6L?rmK59 z_V#MJX3+6Id>G4SGXQtAr{J@L&w^qM4j$z*$*Ygi0yN^61i8GnmFn+M+@bgabA~p; z4~>08J6H!tgO`c9KZFIN>G*Ki51*iCWK9A?JwWFbx|H6Xy2b)-9chKL#(N_{7IJXz z@JU-S@#nHy5$-**7v9I4M>L3F%V>rBDLN2zt%PPN#i`LGi<8KKFkUzkhKt71=pNSc zh%P4;TgE2g3tS#cCeOCx)d*a{;#Y53$lIRj0_X>1wDmMot%ikSB=C68`K= z*wX;QW;oS+pE1Q<+4$?3e8?zVw03mJy`3E$E6-9X&1KekmzfpUoU8UX(X!Pd9)g9nL+{FuHs3GQ+Lu*_S4zwc(i^LxgQQ*XZ#zr_I+ L_ZUh|BvJncCiP8F delta 1792 zcmZvdYitx%6vywG+1cIcKDv)?TiWeo+ugPc-Ga1&5eifdeb}WfrM8_YMVKuXc3PMg z5V8e%$fH&SPO4RkRN^y4tGO6InE1jVg;wQ@C~82FfQUem!~{gWcS7P9C%eDWUSyXt>?1y0=NnWHnv&u&9^)9kD_dO<~a~0;wdbZyQll4-0%P zh)9%TszRm+x%4Shy#V?2l)2FaB{XDPEkQkf!BrtaEB(&*l>n>hTj|XvSWh9V&jc^h z{=iWa9HI3ERtZLF>y%s(F3}AoFN$!TrcA#LAW&DC-vo9Vxo=A{Os4Nvdd$KC2`3H& z>80q0V67vh*P4(LI4#GApVZZ+t8&7+pBxYlgE2e;L?#KcVk)}t_2S9~yVPJJJ*9Z0 zz9G$VcM7CeW)AWOwz({`4E+tEH0S@i%)IL|8=l8E8xD%le+g?A=EJ?%$v6$~nd*fk zEN%=~Y`k8zA^>v*FznC)BG5?u&ia@azZ{8^5)Mn zeHwCdmI{@Gd{!Q;N)bPgxT^xfF}J%qC>)z5vG^B6u5ycC$mLab@wi=L`gn>|m2Ej5 zkgEgI@yW&2ereP%F+CcTYl6~fq_`$WI*}vQ1ZkvoM1+g@C>FURN0aEC-#kZvl zHJAW4^-w}!EJ#=w3ytX(;i^~!TJYuAT()pO;JH{e0Ar490@6zMK>um{IzK(Bf8+T_(9IY4^6a7Cub2?#-gqcS<>aYswY=+ufpbdEUYvDF5|v0UH`%2Bg87I8*F90!T% zaaoQ|5yw5wXn{ChAaPwHM`wxS_XFn~X@SLpdo8Kd-gQj0oo2^OPjKAEAN8Yf7GLSk zh8gse?h8VOz=pQL`%r9c$Ff*;XM0ykTaUh`yC!lnQId0(AtyM5U$F?pJZ0uPe`c)QQlaD+5vaxBq4^HFC?bE$?c<$c}XQcLc*W*3< zKLp?1-VDRkx+6z`5Ekv)Z!KZvVL>m(le<>IdpNsKhddnVD-TU)9cQ3NpT_;~Vd%Cp zt0hZv*)?yTp2^K+bUzb@!*td&or3umk4(amXNTYh*6yCfPJR3C5PXFFyU#;9UG-dt zXk#xrqE~P{fWPbuK_(_)7CUN1=n_n6ERH|zO~LvBD_g|Yfyy*Kudt+N@`_~czm~&~ z9}GMU=c)L7p#U>+%3vJIaBy%jT%&&u?h@fI+&okx+!5&b&`kkm<7Wp4;6ZE|E@kW= zo&iogI{dTsSMCk^EqwcxMhGJuQp#^~_njPPIrv8F#%APmRm{OY18e{s7M}7DSJfPu zIC!WMYU#N{5s)TJ851q&c{S*&1F05<8~-4NxW1Imf3+AgBWEmaZEDxDwl=+t+Zda@ z+%Uv_uT}c$xZ?!BAL0=CZ2?|=?T9bTrJn~&Ic9Kl;0Lc4``54@_9NXdFrv7rAvWbL(GV(J!)N*!-+-lLWWHXJ!T_-49I6j0TezFTW1} D^PE8V delta 183 zcmeCV$kcR^iT5-wFBbz4*xcmJwA;wLu$OVdEGL>+G zRDnPZV>-iRnZ8g_=At)1ZqXSKaTQ1uGXsgqJ$>>HHb6!L!xLVS>%6iTd1Wv2$~U;( z5sF7(Tc6-hR;!YD-HgNTGsWq)07p85%_abp2zy95>(jSv+WrsQ<|(lR>TqPFn0kU^6c znMH2lr`A>+Wvv9QI86*A2{;bv+E!4iQ7?a;?m!(cnM;?oj0Q4idt zLy)34P@TkZVZ&H58c9n<8-Sbd!m7YoK^?4E$2+P5z%6j`8`3zFTDgI635J)>e2(W+ z6TmHW3mnoVLEsXmbgIBn3f?Jli5lcwE}6yU8c3`g<}UeS-W&6tv(XRxV^_Xdm}FK6m{01V1DBMPEF&=+VV5~qKuCIjG%*qFycA>o(awR{ zr3S2+Ct)<(+k?jUTjtTHTA$pDZ{aEn-&)PAflHx(OmjAgoYzuROOhfCV-MHRIH@qKe%XR;v8R{2SQa4EhJ5ut-`{?a7+f7Un$* zW8cFR!sOr1n{-D>BDmebFhWRSb|XM1jo^)z<_O>JJl14$Fa$TbdQE$J@@Q7HEnzGU zrmco)aW>QTh%7PJ*qR*{=0t5H(~KyDwbs@cA&$4SG$!0d1XicR$yn?W%$jf-!D=FW z#?n-4YhLab-fK^X@Xk`JC8mms3hwnarS1K&34iMVkJTVOBdk=1GK+_fg_NZsN$F3T zELIX;p=1SNm4+3CHJNa!&1PkBf+y1UE8_r8gBgSNk*Yx3y*$%Xv`!@BVxY@mi5Y3` zNM(>5-gRI4Y8;Rv!+2S^e9J?1MR2d*(S7RNslaPPt7;!;N<-=jb_ax+jJ&|=>5QU9 zfJ+U2ES#ww5cUZB#GH^?MYyIFtGIPk#@-L^OA5pKe0KYc!f+iAR*j18E6Sn1lp3eQ zo3}m8svK8bEF0L^voX*X%zL0K3uRUM1rTPm1%di$Z3&cDQ2wpq?3{s}Jv;k$aeY*A zdp1Z{z2sl132BW(j*!wEl9+#8Gy%<4>?sa?B+UH2m<@DQC?Ta1)t}jS!iRiIPFCmR zw^Z8d9R4k10gS&>Dk0uBlGSGN_Ihn~iSSNZ0gUgMln|$K5P!;$PgbuLOqC$gl$osF zB-%sa%)3yKu_0)Hz#T3s$BuJdMFUuH^p&+AVSD1d^<>#aN1(E&Ee>Ef^k&dJj*lPs z)^Ous$h`fYamOM`KfwWC@fdLv&CKLMF-palkgx)Q48aL5*LcXxqA(LdqIGjog!oJ+ j0`x5~8VJvXo0?i^XQO2^bA}r+zAfL1V`Yl~VS@A*MsOMb delta 1929 zcmcIkZ%k8H6uU6(%)z|V)nf=v)=IGwP#OadgpqOqjW8d{i% z{45GO!;(&?T9il=w46?*g$)>QX^>h}Cx|P2FeLkR?P^Lp>=qX3R)-Z$Hfu{QYi*`& zt!yn_Z$1=GVQ7}I()Pqi-d1mAYwaw3G(I*H+>k+4D#QWk1f5tX-YGZ>oX`R77zB;L z1)b0i+aY3v1!EHl5n6B#TS}QR5AG2(2rPmVDJ~%^N}5^80+S_~6JCJ{ZXvE<2{tH9 z&aDM5krP&dF0mPF1ZM1@3ILbbDQY6Q5}mq4oLte3`7{FHlDNcd>Oy2mGD;OVNTGF7 zr<98=RdMCnV(#fuf)u-CP8kDznNFffiQ*~U;^Ybld@BWf-9`MD%mM+cPDZyZ_@d|+ zQS=li!PlmxkJTo9nlh5?rM4{WDqctO)#{j%ZtTnQ5Xmk5V|j@Gy*%W9Esv9Q5x#%` zpi-dbXqTE%LCly-+y-($39!R9G~Ndw`h2$>_&(4E|HUIe;X%n-)u=7{#$0DtptX>kP=ECz1B5*^Xl}?v6<5Y`rU^Zy56$WW;e?-YTPDE8lE0UyaQSoQhr#BsLrAycs<+ z#F&wV5;BL8Vk|rq@X*M^As(zf`CE72Ld+`TveCvw&d9l5=IQnAZZ z5mKuB@A%hxwlVzcF^B#wK*6xAx!zIRW@8T1jD7nYdH~S>9+y}-g>l~eMOkDRx3df* zMzhQ|9(boP-fV5*M)TLv^2BZ$c*`qW;(Y`S_0}UyGf&0zi_3_gA}nfXWm_5A9>x)g zpD7G$!$O8`uD7<>Gn72%$MMNuQ-EAQSUG|Z8Hc68iphfVhvcRhAS5}r zYeHp}k2O_6h2L@3-R%yv4;db5*Gy=tJkU zYMJ{OmoX1yk}S{WCra&;H1$+UT9?Ud^4onoLTasN%akH#1Rtpk3WnbZGLyv>4;7Ut zDM{USgetZ?&Z-W+d2W4wd2ji^hN1dLy7d!T)qW9zDP8`6Xi`^0l8Wd3 z!(M}`dq+VS^YA;GOrt?~rzjWkZ`4YJcQ+8mb;RAWOk=6|o;DZpd#@`IjvILY@jSv< zBpNT}ea6=jTYzLcg*Sp1ujHmV0!41@>*eKsITu*55gy~dU(pZm4