Простой диспетчер задач с веб-интерфейсом, на GO для Unix-систем, включая Android

Простой диспетчер задач с веб-интерфейсом, написанный на языке GO для Unix-систем включая Android.528ea3989ea6444e9220e3d5f908c0aa.pngКогда я работал в одной фирме, которая занимается электронными гос-услугами, меня всегда удивляло, что системы, которые мы делали или модернизировали становятся со временем чрезвычайно сложными. Они становились рыхлыми и ненадежными с огромным количеством зависимостей. Подобно красному гиганту, система раздувалась и в любой момент могла сколапсировать.Развитие систем — не только наращивание функционала, но еще и сокращение и оптимизация систем.Как мне кажется, в it-сфере происходит то же самое. Взгляните на Windows 8.1, это же жертва идей маркетологов. Помните Torrent-клиент Azureus? Теперь это уже целый медиа-комбайн Vuze.

Мне всегда хотелось написать программу для себя. Что-нибудь простое и не сложное, работающее через веб-интерфес, что-то вроде диспетчера задач top, только работающее через браузер. Очень много систем которые могут выполнять подобного рода функционал, но они из серии:

Поставь PHP + Apache+ MySQL+… еще что-нибудь. Т.е. на мой взгляд излишне «раздутые».Когда я выбирал язык, я обратил свое внимание на GO. На «хабре» в последнее время очень много о нем заслуженно пишут.Как только я начал читать книги и статьи о нем, сразу влюбился в этот потрясающий лаконичный и естественный язык. Меня поражало то, на сколько, мнение создателей совпадало с моимВот примеры:

В нем нет шаблонов. Шаблоны это наследие макросов. Решая задачу обобщения алгоритмов и классов, использование шаблонов, на порядок усложняло понимание кода. В GO нет наследования. Как нет множественного наследования, так и нету обычного наследования. За место наследования используется более четкий и ясные механизмы встраивания и интерфейсы. По умолчанию компилируемый файл статически слинкован. Это означает, что его можно запустить на любой системе, не заботясь о зависимостях. Из коробки поддерживается кросскомпиляция. Есть Unit-тестирование. Для получения сведения о процессах и статистике системы wtop использует виртуальную файловую систему proc. Поэтому он будет работать на любой системе, которая ее использует (включая android и… я не уверен Plan9). В качестве backend используется встроенный в go http-server. А в качестве frontend html/java script. Для обмена данными между frontenf и backend используется json-сообщения. Для запуска достаточно запустить исполняемый файл и в браузере перейти по адресу x.x.x.x:9977/index.htmla8abce24059f4d37a5062bf277cae22a.png64c95440460e4508b23878469207a974.png

На скриншоте выше видно, что в момент сбора сведений, на телефоне с двухъядерным процессором Texas Instruments 4430, частотой 1.2 ГГц, wtop потребляет около 10% процентов процессорного времени и всего 4,5 мегабайта памяти, что немного. На десктопе, с операционной системой ubuntu — 0,5% процессорного времени и те же 4.5 мегабайта памяти. Если в течении 5 секунд не было запроса от клиента, то он засыпает до тех пор, пока новый json-запрос его не разбудит.Далее опишу какие конструкции основные моменты используются в коде программыПри получении объекта http.Request, метод ProduceJsonRequest «парсит» тело запроса и создает объект запроса. Который в свою очередь диспечеризуется методом Dispatch:

func (fabric *JsonFabric) ProduceJsonRequest (request *http.Request) (Request, error) { bodyData, err:= ioutil.ReadAll (request.Body) if err!= nil { stErr:= «error: Can’t read request body» log.Println (stErr) return nil, errors.New (stErr) } defer request.Body.Close ()

var basicRequest BasicRequest err = json.Unmarshal (bodyData, &basicRequest) if err!= nil { stErr:= «error: Can’t parse basic data» log.Println (stErr) return nil, errors.New (stErr) } switch basicRequest.Type { case ServiceStatus: var serviceStateRequest ServiceStateRequest err:= json.Unmarshal (bodyData, &serviceStateRequest) if err!= nil { stErr:= «error: Can’t parse service state request» log.Println (stErr) return nil, errors.New («error: Can’t parse service state request») } return serviceStateRequest, nil … … … }

return nil, errors.New («error: Unknown request type») func (requestSelector *RequestSelector) Dispatch (request Request, responseWriter http.ResponseWriter, httpRequest *http.Request) error { //don’t need protect multiple read in different thread if selector, contains:= requestSelector.selectorRequestMap[request.RequestType ()]; ! contains { stErr:= «error: Usupported message type» log.Println (stErr) return errors.New (stErr) } else { return selector.Dispatch (request, responseWriter, httpRequest) } } При диспечеризации запроса происходит поиск по словарю, соответствующего обработчика, с последующим его вызовом.В обработке запроса ничего интересного нету.Интерес предоставляет структура BatchJob. Используя ее в своем объекте, мы можем обеспечить периодичность выполнения некоторых действий (в нашем случае измерений).

type BatchJob struct { Job func () runJob bool done chan bool }

func (batchJob *BatchJob) Start () error { if batchJob.runJob {

return errors.New («error: can’t stop not stopped job») } if batchJob.Job == nil { return errors.New («error: empty job function») }

if! batchJob.runJob { batchJob.done = make (chan bool, 1) }

go batchJob.execution (batchJob.done) batchJob.runJob = true return nil } func (batchJob *BatchJob) IsRunning () bool { return batchJob.runJob

}

func (batchJob *BatchJob) Stop () error { if! batchJob.runJob { return errors.New («error: can’t stop not stopted job») } batchJob.runJob = false isDone:= <-batchJob.done if isDone { close(batchJob.done) return nil } return errors.New("error: failed stop job") }

func (batchJob *BatchJob) execution (done chan bool) { for { if batchJob.runJob { batchJob.Job () } else { done <- true return }

} }

Для этого в каждом обработчике запроса присутствует объект структуры BatchJob. Поле Job мы инстанцируем ссылкой на функцию измерения. top.collectInfoJob.Job = top.collectInfo top.lastRequestTime = time.Now () err:= top.collectInfoJob.Start () Как я уже отмечал выше, если через 5 секунд сервис никто не опросит, то он уснет.На мой взгляд, программа получилась очень простой и логичной. Каждый ее модуль выполняет конкретную функцию и может работать независимо друг от друга.

Все иcходные коды доступны на репозитарии GitHub github.com/Loafter/WebTop.

От себя хотелось бы еще раз сказать, что GO это удивительный и мощный язык. Надеюсь он займет доминирующую роль среди языков нового поколения.

Для тех, кто дочитал до конца, привожу ссылки на скомпилированные под разные архитектуры бинарники.

index.html — веб-интерфейс; wtop-armv5-linux — версия для Linux (Android) arm v5; wtop-armv6-linux — версия для Linux (Android) arm v6; wtop-x64-linux — версия для Linux (Ubuntu… etc) X86–64.

© Habrahabr.ru