Главная » Хабрахабр » Сбор статистики MTProto Proxy

Сбор статистики MTProto Proxy

Содержание

  • Предыстория
  • Сбор статистики
  • Отображение статистики
  • Визуализация и ведение статистики
  • Развертка
  • Заключение

Предыстория

Привет хабр, телеграм сейчас на пике популярности, все скандалы, интриги, блокировки вертятся вокруг него, в связи с чем телеграм выкатил свой вариант прокси под названием MTProto Proxy который призван помочь с обходом блокировки. Однако предоставленные телеграмом сервисы для мониторинга MTProto Proxy не дают возможности наблюдать статистику в реальном времени и собирать её для наблюдения за её изменениями, потому мы будем решать проблему своими силами.

Сбор статистики

На официальной странице MTProto Proxy на Docker Hub указано что мы можем использовать команду docker exec mtproto-proxy curl http://localhost:2398/stats для получения статистики напрямую от MTProto Proxy который находится в контейнере, так что наш код будет выглядеть следующим образом.

package main import ( "io/ioutil" "net/http" "runtime" "strings" "time"
) type User struct { Num string
} var Users User func CurrenUsers() (err error) body, err := ioutil.ReadAll(response.Body) if err != nil { return } defer response.Body.Close() stat := strings.Split(string(body), "\n") for _, item := range stat { // Проверяем что у нас есть нужное поле // которое содержит количество пользователей if strings.HasPrefix(item, `total_special_connections`) { Users.Num = strings.Split(item, "\t")[1] } } return nil
} func main() { for { defer runtime.GC() CurrenUsers() time.Sleep(10 * time.Second) }
}

total_special_connections указано на том же Docker Hub как число входящих подключений клиентов

Отображение статистики

Далее нам нужно в простой и удобной форме выводить текущее количество пользователей, мы будем выводить её в браузер.

package main import ( "html/template" "io/ioutil" "net/http" "runtime" "strings" "time"
) type User struct { Num string
} type HTML struct { IndexPage string
} var Users User var IndexTemplate = HTML{ IndexPage: `<!DOCTYPE html> <html> <head> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous"> <title>Stats</title> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> </head> <body> <div class="container-fluid"> <div class="row justify-content-center text-center" style="margin-top: 20%"> <h1>Count of current users of MTProto Proxy: {{.Num}}</h1> </div> </div> </body> </html>`,
} func CurrenUsers() (err error) { // Тянем статистику response, err := http.Get(`http://localhost:2398/stats`) if err != nil { return } body, err := ioutil.ReadAll(response.Body) if err != nil { return } defer response.Body.Close() stat := strings.Split(string(body), "\n") for _, item := range stat { // Проверяем что у нас есть нужное поле // которое содержит количество пользователей if strings.HasPrefix(item, `total_special_connections`) { Users.Num = strings.Split(item, "\t")[1] } } return nil
} func sendStat(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { t := template.Must(template.New("indexpage").Parse(IndexTemplate.IndexPage)) t.Execute(w, Users) }
} func init() { go func() { for { defer runtime.GC() CurrenUsers() time.Sleep(10 * time.Second) } }()
} func main() { http.HandleFunc("/", sendStat) http.ListenAndServe(":80", nil)
}

что такое init

init в любом случае будет вызван перед вызовом main

Теперь перейдя по IP адресу нашего MTProto Proxy мы сможем увидеть текущее количество клиентов.

image

Визуализация и ведение статистики

Есть много вариантов для визуализации и ведения статистики Datadog, Zabbix, Grafana, Graphite. Я буду использовать Datadog. С помощью команды go get -u github.com/DataDog/datadog-go/statsd импортируем библиотеку statsd и используем её в коде.

package main import ( "github.com/DataDog/datadog-go/statsd" "html/template" "io/ioutil" "net/http" "os" "runtime" "strconv" "strings" "time"
) var datadogIP = os.Getenv("DDGIP")
var tagName = os.Getenv("TGN") type User struct { Num string
} type HTML struct { IndexPage string
} var Users User var IndexTemplate = HTML{ IndexPage: `<!DOCTYPE html> <html> <head> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous"> <title>Stats</title> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> </head> <body> <div class="container-fluid"> <div class="row justify-content-center text-center" style="margin-top: 20%"> <h1>Count of current users of MTProto Proxy: {{.Num}}</h1> </div> </div> </body> </html>`,
} func (u User) convert() int64 { num, _ := strconv.Atoi(u.Num) return int64(num)
} func CurrenUsers() (err error) { // Тянем статистику response, err := http.Get(`http://localhost:2398/stats`) if err != nil { return } body, err := ioutil.ReadAll(response.Body) if err != nil { return } defer response.Body.Close() stat := strings.Split(string(body), "\n") for _, item := range stat { // Проверяем что у нас есть нужное поле // которое содержит количество пользователей if strings.HasPrefix(item, `total_special_connections`) { Users.Num = strings.Split(item, "\t")[1] } } return nil
} func sendStat(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { t := template.Must(template.New("indexpage").Parse(IndexTemplate.IndexPage)) t.Execute(w, Users) }
} func init() { go func() { for { defer runtime.GC() CurrenUsers() time.Sleep(10 * time.Second) } }() // Отправляем статистику агенту Datadog go func() { c, _ := statsd.New(datadogIP + ":8125") c.Namespace = "mtproto." c.Tags = append(c.Tags, tagName) for { c.Count("users.count", Users.convert(), nil, 1) time.Sleep(10 * time.Second) } }()
} func main() { http.HandleFunc("/", sendStat) http.ListenAndServe(":80", nil)
}

Осталось собрать всё в докер образ

FROM telegrammessenger/proxy
COPY mtproto_proxy_stat .
RUN echo "$(tail -n +2 run.sh)" > run.sh && echo '#!/bin/bash\n./mtproto_proxy_stat & disown' | cat - run.sh > temp && mv temp run.sh
CMD [ "/bin/sh", "-c", "/bin/bash /run.sh"]

Развертка

Сначала нам нужно запустить контейнер с агентом Datadog

docker run -d --name dd-agent -v /var/run/docker.sock:/var/run/docker.sock:ro -v /proc/:/host/proc/:ro -v /sys/fs/cgroup/:/host/sys/fs/cgroup:ro -e DD_DOGSTATSD_NON_LOCAL_TRAFFIC=true -e DD_API_KEY=ВАШ_КЛЮЧ datadog/agent:latest

ВАЖНО для того чтобы мы могли слать агенту наши данные нужно установить значение true для переменной окружения DD_DOGSTATSD_NON_LOCAL_TRAFFIC

Далее с помощью команды docker inspect dd-agent нам нужно посмотреть IP контейнера чтобы слать ему данные

image

и запустить наш MTProto Proxy соединив его мостом с контейнером агента

docker run -d -p 443:443 -p 80:80 -e WORKERS=16 -e DDGIP=172.17.0.2 -e TGN=mtproto:main --link=dd-agent --name=mtproto --restart=always -v proxy-config:/data trigun117/mtproto_proxy_stat

И через пару минут мы уже можем построить график выбрав нужную метрику и источник (тег который указан при запуске контейнера с MTProto Proxy)

image

и отобразить на нём нашу статистику

image

Живой пример

Заключение

Для себя я открыл новые инструменты для удобной работы с данными, познакомился с их большим разнообразием и выбрал что-то подходящее на свой вкус.

Спасибо за уделенное внимание, предлагаю всем поделится мнением, замечаниями и предложениями в комментариях.

GitHub репозиторий


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

[Перевод] Создатели ботнета Mirai теперь сражаются с преступностью на стороне ФБР

Три подзащитных студента, стоявшие за ботнетом Mirai – онлайн-инструментом, учинившим разрушения по всему интернету осенью 2016 при помощи мощнейших распределённых атак на отказ от обслуживания – в четверг предстанут перед судом на Аляске и попросят судью вынести новый приговор: они ...

[Из песочницы] RESS — Новая архитектура для мобильных приложений

Вопреки провокационному заголовку, это никакая не новая архитектура, а попытка перевода простых и проверенных временем практик на новояз, на котором говорит современное Android-комьюнити Введение В последнее время стало больно смотреть на то, что творится в мире разработки под мобильные платформы. ...