вторник, 13 января 2015 г.

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

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





Когда я работал в одной фирме, которая занимается электронными гос-услугами, меня всегда удивляло, что системы, которые мы делали или модернизировали становятся со временем чрезвычайно сложными. Они становились рыхлыми и ненадежными с огромным количеством зависимостей. Подобно красному гиганту, система раздувалась и в любой момент могла сколапсировать.

Развитие систем — не только наращивание функционала, но еще и сокращение и оптимизация систем.

Как мне кажется, в 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.html




На скриншоте выше видно, что в момент сбора сведений, на телефоне с двухъядерным процессором 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 http://ift.tt/1xhI8xx.


От себя хотелось бы еще раз сказать, что 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.


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

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