Some checks failed
ZaStoGram source guards / guards (push) Failing after 22s
Build three ZaStoGram APKs / build (x86, ZaStoGram-standalone-x86, X86, x86) (push) Failing after 2m2s
Build three ZaStoGram APKs / build (armeabi-v7a, ZaStoGram-standalone-armeabi-v7a, Armv7, armv7) (push) Failing after 2m4s
Build three ZaStoGram APKs / build (arm64-v8a, ZaStoGram-standalone-arm64-v8a, Arm64, arm64) (push) Failing after 2m7s
Замеры на стенде (эмулятор Android 16, настоящий WebView и страница моста, tproxy-server в режиме websocket за nginx, netem) показали, что канал насыщают и upstream DrKLO (8ad4ac9ba), и12f2a119f, а теряется не скорость, а очередь: фиксированные 1 МиБ выгрузки и 2 МиБ кредита ограничивали скорость на длинных RTT (300 мс: 2,7 против 4,1 МБ/с), а без окон upstream держит перед чатом секунды данных. Узкое место Desktop (base64 eval по одному кадру) на Android отсутствует. - WebProxyEngine: один поток на селекторе вместо потока на сокет, исполнителя носителя и общей блокировки. Сокет tgnet читается только по гранту планировщика, прямо в пакет для страницы; приём пишется в tgnet без блокировки, зависший читатель не держит остальные потоки. - Все кадры прохода уходят странице одним сообщением (до 96 КиБ). - Граница со страницей — WebMessagePort, когда WebView умеет: кадры страницы не разбираются в UI-потоке. Иначе, или если порт не ожил за 15 с, — прежний слушатель; нонс и точный origin проверяются в обоих. - Одно управление потоком: окно релея плюс адаптивные окна выгрузки и загрузки (WebProxyFlow.AdaptiveWindow, как у Desktop, с отличиями по замерам: запас очереди от 200 мс, рост как slow start, слив только при подтверждённой очереди, пол min(2 МиБ, 0,8 с скорости)). - Выгрузка снова по четырём соединениям, как в upstream. - Медиа через MTProxy/WEB: знак DC в заголовке берётся по тому же правилу, что и медиа-ключ (hasMediaAddress), иначе медиа-ключ мог уйти в обычный кластер и получить -404 сразу после создания. Итог против upstream на 24 сценариях: скорость в пределах ±1–2 % (загрузка 128 КиБ×4 при 50 мс +7 %, смешанный трафик в шуме), задержка чата при полной выгрузке 2,3–6,3 с → 0,37–0,88 с, при загрузке 1,3–3,3 с → 0,42–0,89 с. Стенд — Tools/web_proxy_bench, JVM-тесты движка — Tools/web_proxy_flow_tests.
68 lines
2.3 KiB
Python
68 lines
2.3 KiB
Python
"""Bench backend standing in for MTProxy+Telegram: pipelined RPCs.
|
|
|
|
Request: u32 reqLen | u32 respLen | u32 tag | u32 delayMs | reqLen bytes
|
|
Response: u32 respLen | u32 tag | respLen bytes, in request order, sent no
|
|
earlier than delayMs after the request was fully read (models the relay ->
|
|
Telegram DC round trip and server time).
|
|
"""
|
|
import asyncio, struct, sys, time
|
|
ZERO = bytes(1 << 20)
|
|
RX = [0]
|
|
TX = [0]
|
|
|
|
async def meter():
|
|
# Bytes per 250 ms into rates.log: the relay-side view of throughput.
|
|
last_rx = last_tx = 0
|
|
with open("rates.log", "a", buffering=1) as log:
|
|
while True:
|
|
await asyncio.sleep(0.25)
|
|
rx, tx = RX[0], TX[0]
|
|
if rx != last_rx or tx != last_tx:
|
|
log.write("%.3f %d %d\n" % (time.time(), rx - last_rx, tx - last_tx))
|
|
last_rx, last_tx = rx, tx
|
|
|
|
async def handle(r, w):
|
|
q = asyncio.Queue()
|
|
async def writer():
|
|
while True:
|
|
item = await q.get()
|
|
if item is None:
|
|
break
|
|
due, resp, tag = item
|
|
wait = due - time.monotonic()
|
|
if wait > 0:
|
|
await asyncio.sleep(wait)
|
|
w.write(struct.pack(">II", resp, tag))
|
|
left = resp
|
|
while left:
|
|
k = min(left, len(ZERO)); w.write(ZERO[:k] if k < len(ZERO) else ZERO); left -= k; TX[0] += k
|
|
await w.drain()
|
|
task = asyncio.create_task(writer())
|
|
try:
|
|
while True:
|
|
head = await r.readexactly(16)
|
|
req, resp, tag, delay = struct.unpack(">IIII", head)
|
|
left = req
|
|
while left:
|
|
d = await r.read(min(left, 1 << 20))
|
|
if not d:
|
|
raise asyncio.IncompleteReadError(b"", left)
|
|
left -= len(d)
|
|
RX[0] += len(d)
|
|
q.put_nowait((time.monotonic() + delay / 1000.0, resp, tag))
|
|
except (ConnectionError, asyncio.IncompleteReadError):
|
|
pass
|
|
q.put_nowait(None)
|
|
try:
|
|
await task
|
|
except Exception:
|
|
pass
|
|
w.close()
|
|
|
|
async def main():
|
|
port = int(sys.argv[1]) if len(sys.argv) > 1 else 2398
|
|
asyncio.create_task(meter())
|
|
s = await asyncio.start_server(handle, "127.0.0.1", port, limit=1 << 22)
|
|
async with s:
|
|
await s.serve_forever()
|
|
asyncio.run(main())
|