Nginx - распределение трафика по хостам: различия между версиями
imported>Vix (Новая страница: «*Модуль для ngnx - который позволяет распределять трафик по доступности хоста, то есть если…») |
imported>Vix Нет описания правки |
||
| Строка 2: | Строка 2: | ||
[http://wiki.nginx.org/HttpUpstreamModule]http://wiki.nginx.org/HttpUpstreamModule | [http://wiki.nginx.org/HttpUpstreamModule]http://wiki.nginx.org/HttpUpstreamModule | ||
<hr> | |||
http://habrahabr.ru/post/116124/ | |||
Надавно была опубликована библиотека node-fibers, вносящая в nodejs и v8 поддержку замечательного fiber/coroutine — тоесть, возможность использовать yield. | |||
Параллельно, на nodejs groups прошел целый ряд обсуждений на тему всевозможных вариантов упрощения асинхронного синтаксиса. | |||
Вдохновившись возможностями, которые дают «волокна», я написал библиотеку node-sync, которая делает разработку в асинхронном окружении nodejs значительно удобнее, а код — нагляднее. | |||
== Синопсис == | |||
<nowiki>// Обычная асинхронная функция, вызывает callback с результатом через 1 сек | |||
function someAsyncFunction(a, b, callback) { | |||
setTimeout(function(){ | |||
callback(null, a + b); | |||
}, 1000) | |||
} | |||
// Вызываем эту функцию синхронно, используя Function.prototype.sync(), | |||
// работающий по тому же принципу, что и call() | |||
// на этом моменте текуший поток "зависнет" на секунду, пока функция не вернет значение | |||
var result = someAsyncFunction.sync(null, 2, 3); | |||
console.log(result); // "5" через 1 секунду | |||
</nowiki> | |||
== Философия == | |||
Главная идея в том, что метод Function.prototype.sync() встроен в любую функцию по умолчанию, а так же, его интерфейс соответствует всем давно известному call(). Подключив библиотеку sync, мы можем вызвать любую асинхронную функцию синхронно, без написания дополнительного кода. | |||
Псевдо-синхронное программирование — потому, что фактически, Function.prototype.sync() не блокирует весь процесс, а только текущий поток. Тело самой функции выполняется асинхронно, мы просто дожидаемся результата (используя «yield»). Но при этом, код читается «синхронно». | |||
node-sync решает для меня три важных вопроса: | |||
#Освобождение от бесконечной индентации с коллбэками (избавляет от «спагетти-кода») | |||
#Корректная обработка ошибок | |||
#Интеграция с существующим кодом/библиотеками без необходимости рефакторинга | |||
Уже больше месяца я использую эту node-sync в своем приложении, переход был незаметным — я просто начал писать новый код на «псевдо-синхронный» манер, старый код остался прежним. | |||
== Блокировка == | |||
Прелесть «волокон» состоит в том, что при ожидании (yield) блокируется только текущий поток, а не весь процесс. Наглядный пример блокировки всего процесса — fs.readFileSync и другие псевдо-синхронные функции. | |||
Используя «волокна» можно избежать глобальной блокировки и, при этом, прочитать файл синхронно: | |||
<nowiki>var fs = require('fs'), | |||
Sync = require('sync'); | |||
// запускаем новый поток | |||
Sync(function(){ // тело потока --> | |||
// синхронно читаем файл, используя Function.prototype.sync() | |||
var source = fs.readFile.sync(null, __filename); | |||
// выводим содержимое текущего файла | |||
console.log(String(source)); | |||
})</nowiki> | |||
Отличие этого кода в том, что пока мы ожидаем ответ от fs.readFile.sync(), приложение спокойно продолжает выполнять другие операции. | |||
== Обработка ошибок == | |||
Всем, кто хоть раз пробовал написать на nodejs что-то серьезнее, чем «hello world app», наверняка знакома рутина с обработкой ошибок. Если следовать официальному дизайну callback-функций, первым агрументом всегда должна быть возвращена ошибка. При этом, использовать throw в асинхронном окружении чревато падением всего event-loop. | |||
Весьма реальный код на nodejs с корректной обработкой ошибок: | |||
<nowiki>// Функция должна что-то сделать и вернуть результат, | |||
// при этом, корректно обработать ошибку в случае неудачи | |||
function asyncFunction(callback) | |||
{ | |||
var p_client = new Db('test', new Server("127.0.0.1", 27017, {})); | |||
p_client.open(function(err, p_client) { | |||
if (err) return callback(err); // <-- рутина | |||
p_client.createCollection('test_custom_key', function(err, collection) { | |||
if (err) return callback(err); // <-- рутина | |||
collection.insert({'a':1}, function(err, docs) { | |||
if (err) return callback(err); // <-- рутина | |||
collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) { | |||
if (err) return callback(err); // <-- рутина | |||
cursor.toArray(function(err, items) { | |||
if (err) return callback(err); // <-- рутина | |||
// результат = items | |||
callback(null, items); | |||
}); | |||
}); | |||
}); | |||
}); | |||
}); | |||
} | |||
</nowiki> | |||
Такая-же функция, только использующая sync. Результат ее работы идентичен функции выше, учитывая обработку ошибок. Если какая-то из вызываемых функций вернет ошибку в авто-callback, который передает ей sync(), то эта ошибка прозрачно попадет в результирующий callback, который мы указали потоку вторым аргументом. | |||
<nowiki>function syncFunction(callback) | |||
{ | |||
// запускаем поток | |||
Sync(function(){ | |||
var p_client = new Db('test', new Server("127.0.0.1", 27017, {})); | |||
p_client.open.sync(p_client); | |||
var collection = p_client.createCollection.sync(p_client, 'test'); | |||
collection.insert.sync(collection, {'a' : 1}); | |||
var cursor = collection.find.sync(collection, {'_id':new ObjectID("aaaaaaaaaaaa")) | |||
var items = cursor.toArray.sync(cursor); | |||
// результат = items | |||
return items; | |||
}, callback) // <-- результат возвращаем в callback | |||
} | |||
</nowiki> | |||
Используя специальный метод Function.prototype.async(), эта функция может быть еще проще (работает аналогично функции выше): | |||
<nowiki>var syncFunction = function() | |||
{ | |||
var p_client = new Db('test', new Server("127.0.0.1", 27017, {})); | |||
p_client.open.sync(p_client); | |||
var collection = p_client.createCollection.sync(p_client, 'test'); | |||
collection.insert.sync(collection, {'a' : 1}); | |||
var cursor = collection.find.sync(collection, {'_id':new ObjectID("aaaaaaaaaaaa")) | |||
var items = cursor.toArray.sync(cursor); | |||
// результат = items | |||
return items; | |||
}.async() // <-- в этом месте мы превращаем ее в асинхронную функцию</nowiki> | |||
== Параллельность == | |||
Иногда, нам нужно выполнить несколько функций параллельно, при этом, дождаться всех результатов, и только тогда продолжить. Для этого существует Sync.Parallel: | |||
<nowiki>var Sync = require('sync'); | |||
// Какая-то асинхронная функция, возвращает результат через секунду | |||
function someAsyncFunction(a, b, callback) { | |||
setTimeout(function(){ | |||
callback(null, a + b); | |||
}, 1000) | |||
} | |||
// Новый поток | |||
Sync(function(){ | |||
// Запускаем параллельно функцию с разными аргументами | |||
// поток будет заблокирован до тех пор, пока не будут возвращены оба результата | |||
var results = Sync.Parallel(function(callback){ | |||
someAsyncFunction(2, 2, callback()); | |||
someAsyncFunction(5, 5, callback()); | |||
}); | |||
console.log(results); // [ 4, 10 ] | |||
// Ассоциативный вариант | |||
var results = Sync.Parallel(function(callback){ | |||
someAsyncFunction(2, 2, callback('foo')); // assign the result to 'foo' | |||
someAsyncFunction(5, 5, callback('bar')); // assign the result to 'bar' | |||
}); | |||
console.log(results); // { foo: 4, bar: 10 } | |||
})</nowiki> | |||
На днях общался с господином laverdet (создатель node-fibers для v8), и он предложил весьма интересную парадигму «будущего». Я добавил новый метод Function.prototype.future() — его тоже можно использовать для параллельности: | |||
<nowiki>// Новый поток | |||
Sync(function(){ | |||
// Запускаем someAsyncFunction, но не блокируем поток | |||
var foo = someAsyncFunction.future(null, 2, 2); | |||
var bar = someAsyncFunction.future(null, 5, 5); | |||
// foo, bar - это билеты в будущее | |||
console.log(foo); // { [Function: Future] result: [Getter], error: [Getter] } | |||
// А вот теперь, дожидаемся значений от foo и bar | |||
console.log(foo.result, bar.result); // 4 10 - ровно через секунду (не две) | |||
})</nowiki> | |||
== Установка == | |||
$ npm install sync | |||
$ node-fibers my_file.js | |||
'''Имейте ввиду, что для поддержки fibers вам нужно использовать скрипт «node-fibers» вместо «node».''' | |||
== API == | |||
<nowiki>var Sync = require('sync'); | |||
// Новый поток, fn - функция-тело, результат не возвращается | |||
Sync(fn) | |||
// Новый поток, fn - функция-тело, результат/ошибка возвращается в callback | |||
Sync(fn, callback) | |||
// внутри используется инкрементальный callback() | |||
Sync.Parallel(function(callback){ | |||
callback() // без аргумента (вернется массив) | |||
callback('foo') // или ассоциативный ключ | |||
}) | |||
// Вызывает функцию асинхронно и ждет результата/ошибки | |||
// obj - контекст, остальные аргументы попадут по порядку в функцию | |||
Function.prototype.sync(obj, arg1, arg2) | |||
// Вызывает функцию асинхронно и не ждет результата, возвращая управление в контекст | |||
// возвращает объект/функцию Future, у которой есть getter 'result' | |||
// при попытке получения Future.result, поток будет заблокирован до тех пор, пока результат не будет получен | |||
// obj - контекст, остальные аргументы попадут по порядку в функцию | |||
Function.prototype.future(obj, arg1, arg2) | |||
// Делает из любой синхронной функции - асинхронную | |||
// возвращает функцию, которую можно вызвать асинхронно | |||
// obj - контекст | |||
Function.prototype.async(obj) | |||
</nowiki> | |||
== Резюме == | |||
Я и дальше намерен развивать это направление в nodejs, ибо мне оно кажется очень правильным. Буду рад, если кто-то из вас вдохновится этой идеей и внесет свой вклад в ее развитие. | |||
Советую посмотреть довольно подробные примеры использования библиотеки. | |||
Если вы намерены поучавствовать в разработке — форкайте, милости просим, только не забывайте про тесты. | |||
Я так же добавил скрипт в benchmarks. Если у кого-то появятся еще идеи, как можно протестировать скорость работы fibers, будет круто. | |||
Хочу поблагодарить egorF за брейнсторминг, и за то, что вообще заразил меня темой fibers :) | |||
Вам так же могут быть интересны другие https://github.com/lm1/node-fiberize библиотеки https://github.com/lm1/node-fibers-promise, основанные на node-fibers. | |||
Текущая версия от 18:46, 10 сентября 2013
- Модуль для ngnx - который позволяет распределять трафик по доступности хоста, то есть если хост загружен или занят или отключен, трафик перенаправляется на следующий свободный.
[1]http://wiki.nginx.org/HttpUpstreamModule
http://habrahabr.ru/post/116124/
Надавно была опубликована библиотека node-fibers, вносящая в nodejs и v8 поддержку замечательного fiber/coroutine — тоесть, возможность использовать yield. Параллельно, на nodejs groups прошел целый ряд обсуждений на тему всевозможных вариантов упрощения асинхронного синтаксиса.
Вдохновившись возможностями, которые дают «волокна», я написал библиотеку node-sync, которая делает разработку в асинхронном окружении nodejs значительно удобнее, а код — нагляднее.
Синопсис
// Обычная асинхронная функция, вызывает callback с результатом через 1 сек
function someAsyncFunction(a, b, callback) {
setTimeout(function(){
callback(null, a + b);
}, 1000)
}
// Вызываем эту функцию синхронно, используя Function.prototype.sync(),
// работающий по тому же принципу, что и call()
// на этом моменте текуший поток "зависнет" на секунду, пока функция не вернет значение
var result = someAsyncFunction.sync(null, 2, 3);
console.log(result); // "5" через 1 секунду
Философия
Главная идея в том, что метод Function.prototype.sync() встроен в любую функцию по умолчанию, а так же, его интерфейс соответствует всем давно известному call(). Подключив библиотеку sync, мы можем вызвать любую асинхронную функцию синхронно, без написания дополнительного кода. Псевдо-синхронное программирование — потому, что фактически, Function.prototype.sync() не блокирует весь процесс, а только текущий поток. Тело самой функции выполняется асинхронно, мы просто дожидаемся результата (используя «yield»). Но при этом, код читается «синхронно».
node-sync решает для меня три важных вопроса:
- Освобождение от бесконечной индентации с коллбэками (избавляет от «спагетти-кода»)
- Корректная обработка ошибок
- Интеграция с существующим кодом/библиотеками без необходимости рефакторинга
Уже больше месяца я использую эту node-sync в своем приложении, переход был незаметным — я просто начал писать новый код на «псевдо-синхронный» манер, старый код остался прежним.
Блокировка
Прелесть «волокон» состоит в том, что при ожидании (yield) блокируется только текущий поток, а не весь процесс. Наглядный пример блокировки всего процесса — fs.readFileSync и другие псевдо-синхронные функции.
Используя «волокна» можно избежать глобальной блокировки и, при этом, прочитать файл синхронно:
var fs = require('fs'),
Sync = require('sync');
// запускаем новый поток
Sync(function(){ // тело потока -->
// синхронно читаем файл, используя Function.prototype.sync()
var source = fs.readFile.sync(null, __filename);
// выводим содержимое текущего файла
console.log(String(source));
})
Отличие этого кода в том, что пока мы ожидаем ответ от fs.readFile.sync(), приложение спокойно продолжает выполнять другие операции.
Обработка ошибок
Всем, кто хоть раз пробовал написать на nodejs что-то серьезнее, чем «hello world app», наверняка знакома рутина с обработкой ошибок. Если следовать официальному дизайну callback-функций, первым агрументом всегда должна быть возвращена ошибка. При этом, использовать throw в асинхронном окружении чревато падением всего event-loop.
Весьма реальный код на nodejs с корректной обработкой ошибок:
// Функция должна что-то сделать и вернуть результат,
// при этом, корректно обработать ошибку в случае неудачи
function asyncFunction(callback)
{
var p_client = new Db('test', new Server("127.0.0.1", 27017, {}));
p_client.open(function(err, p_client) {
if (err) return callback(err); // <-- рутина
p_client.createCollection('test_custom_key', function(err, collection) {
if (err) return callback(err); // <-- рутина
collection.insert({'a':1}, function(err, docs) {
if (err) return callback(err); // <-- рутина
collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) {
if (err) return callback(err); // <-- рутина
cursor.toArray(function(err, items) {
if (err) return callback(err); // <-- рутина
// результат = items
callback(null, items);
});
});
});
});
});
}
Такая-же функция, только использующая sync. Результат ее работы идентичен функции выше, учитывая обработку ошибок. Если какая-то из вызываемых функций вернет ошибку в авто-callback, который передает ей sync(), то эта ошибка прозрачно попадет в результирующий callback, который мы указали потоку вторым аргументом.
function syncFunction(callback)
{
// запускаем поток
Sync(function(){
var p_client = new Db('test', new Server("127.0.0.1", 27017, {}));
p_client.open.sync(p_client);
var collection = p_client.createCollection.sync(p_client, 'test');
collection.insert.sync(collection, {'a' : 1});
var cursor = collection.find.sync(collection, {'_id':new ObjectID("aaaaaaaaaaaa"))
var items = cursor.toArray.sync(cursor);
// результат = items
return items;
}, callback) // <-- результат возвращаем в callback
}
Используя специальный метод Function.prototype.async(), эта функция может быть еще проще (работает аналогично функции выше):
var syncFunction = function()
{
var p_client = new Db('test', new Server("127.0.0.1", 27017, {}));
p_client.open.sync(p_client);
var collection = p_client.createCollection.sync(p_client, 'test');
collection.insert.sync(collection, {'a' : 1});
var cursor = collection.find.sync(collection, {'_id':new ObjectID("aaaaaaaaaaaa"))
var items = cursor.toArray.sync(cursor);
// результат = items
return items;
}.async() // <-- в этом месте мы превращаем ее в асинхронную функцию
Параллельность
Иногда, нам нужно выполнить несколько функций параллельно, при этом, дождаться всех результатов, и только тогда продолжить. Для этого существует Sync.Parallel:
var Sync = require('sync');
// Какая-то асинхронная функция, возвращает результат через секунду
function someAsyncFunction(a, b, callback) {
setTimeout(function(){
callback(null, a + b);
}, 1000)
}
// Новый поток
Sync(function(){
// Запускаем параллельно функцию с разными аргументами
// поток будет заблокирован до тех пор, пока не будут возвращены оба результата
var results = Sync.Parallel(function(callback){
someAsyncFunction(2, 2, callback());
someAsyncFunction(5, 5, callback());
});
console.log(results); // [ 4, 10 ]
// Ассоциативный вариант
var results = Sync.Parallel(function(callback){
someAsyncFunction(2, 2, callback('foo')); // assign the result to 'foo'
someAsyncFunction(5, 5, callback('bar')); // assign the result to 'bar'
});
console.log(results); // { foo: 4, bar: 10 }
})
На днях общался с господином laverdet (создатель node-fibers для v8), и он предложил весьма интересную парадигму «будущего». Я добавил новый метод Function.prototype.future() — его тоже можно использовать для параллельности:
// Новый поток
Sync(function(){
// Запускаем someAsyncFunction, но не блокируем поток
var foo = someAsyncFunction.future(null, 2, 2);
var bar = someAsyncFunction.future(null, 5, 5);
// foo, bar - это билеты в будущее
console.log(foo); // { [Function: Future] result: [Getter], error: [Getter] }
// А вот теперь, дожидаемся значений от foo и bar
console.log(foo.result, bar.result); // 4 10 - ровно через секунду (не две)
})
Установка
$ npm install sync
$ node-fibers my_file.js
Имейте ввиду, что для поддержки fibers вам нужно использовать скрипт «node-fibers» вместо «node».
API
var Sync = require('sync');
// Новый поток, fn - функция-тело, результат не возвращается
Sync(fn)
// Новый поток, fn - функция-тело, результат/ошибка возвращается в callback
Sync(fn, callback)
// внутри используется инкрементальный callback()
Sync.Parallel(function(callback){
callback() // без аргумента (вернется массив)
callback('foo') // или ассоциативный ключ
})
// Вызывает функцию асинхронно и ждет результата/ошибки
// obj - контекст, остальные аргументы попадут по порядку в функцию
Function.prototype.sync(obj, arg1, arg2)
// Вызывает функцию асинхронно и не ждет результата, возвращая управление в контекст
// возвращает объект/функцию Future, у которой есть getter 'result'
// при попытке получения Future.result, поток будет заблокирован до тех пор, пока результат не будет получен
// obj - контекст, остальные аргументы попадут по порядку в функцию
Function.prototype.future(obj, arg1, arg2)
// Делает из любой синхронной функции - асинхронную
// возвращает функцию, которую можно вызвать асинхронно
// obj - контекст
Function.prototype.async(obj)
Резюме
Я и дальше намерен развивать это направление в nodejs, ибо мне оно кажется очень правильным. Буду рад, если кто-то из вас вдохновится этой идеей и внесет свой вклад в ее развитие.
Советую посмотреть довольно подробные примеры использования библиотеки. Если вы намерены поучавствовать в разработке — форкайте, милости просим, только не забывайте про тесты. Я так же добавил скрипт в benchmarks. Если у кого-то появятся еще идеи, как можно протестировать скорость работы fibers, будет круто.
Хочу поблагодарить egorF за брейнсторминг, и за то, что вообще заразил меня темой fibers :)
Вам так же могут быть интересны другие https://github.com/lm1/node-fiberize библиотеки https://github.com/lm1/node-fibers-promise, основанные на node-fibers.
