forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   JavaScript / HTML (http://forum.boolean.name/forumdisplay.php?f=136)
-   -   Node.JS + MongoDB (http://forum.boolean.name/showthread.php?t=18507)

pax 01.09.2013 16:45

Node.JS + MongoDB
 
Посоветуйте пожалуйста чтиво по этому поводу.

Из основных вопросов: Пока не понял как правильно работать с открытием соединения к базе, хочу использовать модуль mongodb и думаю никакие mongoose не надо. Цель - создать REST API для начала.

moka 01.09.2013 22:00

Ответ: Node.JS + MongoDB
 
Не нужно книжек и чтивы. Если работал с mongo то там всё проактически идентично официальным докам (в случае с mongoose это не так).
Я юзал mongoose и потом перешёл на mongo-native от самих 10gen - и не жалею вовсе. Мне эти schemas - нафиг не сдались. Суть mongo - в её динамике, и нефиг пытаться прикручивать правила и структуру к collection'ам подобно это SQL бд.

Офф дока - очень полезна, т.к. как уже говорил почти идентична к mongo-native в использовании.
По факту - коммандная строка для mongo тоже на JavaScript'е ;)

Про соединение, сперва тебе нужно создать объект куда коннектиться:
PHP код:

var mongodb = require('mongodb');
var 
access = new mongodb.Server('127.0.0.1'27017, { auto_reconnecttrue });

new 
mongodb.Db('database'access, { safetrue }).open((function (errdb) {
  if (
err) throw err;
  
// db - хендл для бд (client)
}); 

Далее тебе нужно будет получить сами collection'ы, я это делаю один раз при коннекте в самом каллбеке (точнее я создаю евент и слущаю его, таким образом разные модули получают свои collection'ы:
PHP код:

var users db.collection('users'); 

Как только у тебя есть collection можешь уже работать с данными:
PHP код:

users.insert({ name'test' });

users.find({ }).toArray(function(errdata) {
  if (
err) throw err;

  
console.log(data);
}); 

Вот тут исчерпывающие примеры:
https://github.com/mongodb/node-mong...aster/examples

Есть пару тонкостей:
  • Реконнект при обрыве с бд, я не тестировал, но может даже collection хэндлы нужно будет переполучать (а может и нет). В общем тестируй если такие сценарии бывают.
  • _id - по стандарту mongo держит ObjectID - это своего рода просто string и в консоль по факту будет выводиться просто как string что пиздецки сбивает порой. Так что не забывай что каждый раз если имеешь ID например с REST запроса, нужно создать ObjectID объект из string'и.
  • Если хочешь просто номер для _id (не рекомендуется если предполагается шардинг или репликация бд). То у меня был очень простой выход: заводим collection 'ids' и имеем _id - имя collection и inc - число инкремента. Затем если хотим создать объект в бд делаем:
    PHP код:

    ids.findAndModify({ _id'users' }, { }, { $inc: { inc} }, { new: true }, function(errid) {
      if (
    err) throw err;

      
    // id == твой новый id
    }); 

  • find получает список, и в mongo-native модуле это не callback (это единственный метод с таким исключением). Так что юзаем спец модуль "toArray":
    PHP код:

    users.find({ }).toArray(function(errdata) {
      
    }); 

  • Для REST советую использовать express.js т.к. роутинг будет очень простым и удобным.
  • Валидация - важно. Типы данных бывают разные (если это POST).
  • Middleware - в express есть отличная тема с middeware, так можно написать модуль с парсингом пагинации и юзать его везде. Пример: заводим файл pagination.js и его содержимое:
    PHP код:

    exports.middleware = function(data) {
      return function(
    reqresnext) { // возвращаем middleware функцию
        
    var limit data.limit data.limit 0// limit по стандарту 0
        
    var skip 0// skip тоже нуль

        
    if (req.query.limit) { // если в запросе есть limit
          
    var tmp parseInt(req.query.limit); // парсим
          
    if (tmp != NaN && tmp 0) { // если хорошее число
            
    limit tmp;
          }
        }
        if (
    req.query.skip) { // если в запросе есть skip
          
    var tmp parseInt(req.query.skip); // парсим
          
    if (tmp != NaN && tmp 0) { // если хоршее число
            
    skip tmp;
          }
        }

        if (
    data.max) { // если указан максимальное число на страницу
          
    limit Math.min(limitdata.max); // ограничиваем limit
        
    }
        if (
    data.min) { // если указано минимальное число на страницу
          
    limit Math.max(limitdata.min); // ограничиваем limit
        
    }

        
    req.paginator = { // создаём объект с limit и skip данными в объекте запроса
          
    limitlimit,
          
    skipskip
        
    };
        
    next(); // продолжаем цепочку middleware
      
    }
    }; 

    Далее в коде:
    PHP код:

    var paginator = require('./paginator.js').middleware// получаем handle middleware пагинатора
    app.get('/users'paginator({
      
    limit8,
      
    min4,
      
    max64
    }), function(reqresnext) { // устанавливаем get запрос с пагинатором
      
    users.find({ }, { skipreq.paginator.skiplimitreq.paginator.limit }).toArray(function(errdata) { // ищем в бд с данными из пагинатора
        
    if (errnext(err); // если ошибка, бежим в express хэндлер ошибок
        
    res.json(data); // или шлём данные с бд джонсоном
      
    });
    }); 


Если есть конкретные вопросы - задавай.
Я пишу по работе RESTful API с mongo-native и express'ом ну и другими всякими плюшками.

pax 01.09.2013 22:49

Ответ: Node.JS + MongoDB
 
Правильно ли я понимаю, что для каждого REST запроса надо создавать свое подключение? Мне сначала подумалось создать одно подключение к базе на старте приложения и потом его использовать. Потом я посмотрел некоторые рест библиотеки для монго и там для каждого запроса создается свой экземпляр Db. Такой подход верный?

Еще один вопрос - есть у nodejs возможность запускаться в многопроцессном режиме, это не стоит использовать для REST API?

moka 01.09.2013 23:44

Ответ: Node.JS + MongoDB
 
Не нужно для каждого запроса свой db хэндлер - это оверкил.
Там блокировка на уровне бд будет а не на уровне node, следственно иметь более одного хэндла - смысла нету, только замедляет процесс.
Возможно mongo-native кеширует коннекты и будет выдавать уже подконекченный - но это снова лишние телодвижения.

Относительно мультипоточности - ты говоришь о cluster?
Мы используем - 4 worker'а. Всё работает как полагается, естественно если у тебя есть сессия, то её нужно хранить вне процесса, мы используем connect-mongo, тем самым не важно в какой worker идёт запрос.

По опыту пришёл к такому выводу - если у тебя есть CMS и write'ы очень редкие, и обслуживаются определённой группой людей, следственно таскать с собой функционал для CMS в основном API не оправдано.
Для этого я сделал отдельные запросы для PUSH'ей, это немного идёт против логики REST'а, т.к. /url будет отличаться (я сделал субдомейн для этого, а руты были те же), но зато когда нужно обновить основной API, не нужно было перегружать CMS и т.п.

Но это зависит от ситуации.

cluster'ы - реально помогают, их количество обычно параллельно количеству ядер. Мы на ec2 xlarge (4 CPU) 4 worker'а запускаем - и всё ок.

pax 03.09.2013 10:46

Ответ: Node.JS + MongoDB
 
Еще пара вопросов:

1. Как правильно обрабатывать ошибки, чтобы при их происхождении не падал весь процесс. Можно конкретно для express и mongodb.

2. Я так понимаю что кластер можно использовать как защиту от падения процесса, т.е. кластер при падении одного воркера может запустить нового воркера? И если надо будет перезапустить сервер, то просто необходимо будет перезапустить кластер да?

3. Какой модуль лучше использовать для логирования? Особенно если используется кластер и несколько воркеров.

moka 03.09.2013 13:53

Ответ: Node.JS + MongoDB
 
Цитата:

Сообщение от pax (Сообщение 266375)
1. Как правильно обрабатывать ошибки, чтобы при их происхождении не падал весь процесс. Можно конкретно для express и mongodb.

В примерах я делал 'throw', обычно я так не делаю.
Важно понимать что ошибки - это важно, и их не должно быть.
Если в тестировании ошибок в запросах к бд небыло, а потом есть, значит твой query слишком динамичен и нужно его немного нормализировать (это 90% случаев).
Я у себя в коде так делаю:

Ранее при настройке express'а:
PHP код:

app.configure(function() {
  
app.use(express.errorHandler());
  ...
}); 

PHP код:

app.get('/users', ..., function(reqresnext) {
  
users.find({ }, { skipreq.paginator.skiplimitreq.paginator.limit }).toArray(function(errdata) {
     if (!
err) {
       
res.json(data);
     } else {
       
next(err);
     }
  });
}); 

Далее где-то в коде самый последний middleware будет для ловли ошибок:

PHP код:

app.use(function(errreqresnext) {
  var 
obj = {
    
errortrue,
    
messageerr.message
  
};
  if (
development) {
    
obj.stackerr.stack;
  }
  
res.json(obj);
}); 



Цитата:

Сообщение от pax (Сообщение 266375)
2. Я так понимаю что кластер можно использовать как защиту от падения процесса, т.е. кластер при падении одного воркера может запустить нового воркера? И если надо будет перезапустить сервер, то просто необходимо будет перезапустить кластер да?

Да. Но нужно следить точно когда что-то падает или нет.
Вообще есть "защито" от падения процесса. Есть event который ловит не словленные exception'ы. Но естественно если что-то жесть как пошло не так, то нужно и реагировать соответственно, а не тупо игнорировать.
Но всё равно:

PHP код:

process.on('uncaughtException', function(err) {
  
console.log('Caught exception: ' errerr.stack);
}); 



Цитата:

Сообщение от pax (Сообщение 266375)
3. Какой модуль лучше использовать для логирования? Особенно если используется кластер и несколько воркеров.

Я пока настраивал node.js как сервис в linux, тупо выводил консоль в файл, естественно это не супер решение. И исследовал этот вопрос. Лидер тут конкретный: winston https://github.com/flatiron/winston , отлично поддерживается.
Суть у него в том что он имеет основу для логгирования, но логит в транспорты, которые могут быть что угодно: консоль, файл, бд, MQ, redis, socket, amazon alerts, email, и т.п.
Есть стандартный набор транспортов которых обычно хватает с головой.

Я лично юзать буду файл + бд (с индексом просрочивания неделю), и ZeroMQ в мелкий процесс который через socket.io будет слать логи от разных процессов мне на мелкую страничку мониторинга. Таким образом если что-то случиться, я сразу могу вычислить с кем, где и т.п.

pax 03.09.2013 15:10

Ответ: Node.JS + MongoDB
 
Хочу часть логики сделать на хранимом js в MongoDB, это лучше чем делать логику в NodeJS?

Upd: кстати как правильно вызывать хранимые функции?

Upd2: Получилось примерно так:
PHP код:

db.getUser = function (user_idcallback) {
        
db.eval(new Code('return user_get(user_id);', {'user_id'user_id}), {nolocktrue}, callback);
    } 

сама функция


Это нормально или я что-то не так делаю?

moka 03.09.2013 16:58

Ответ: Node.JS + MongoDB
 
Я предпочитаю ничего не хранить в DB. Можно лишь мелкие туулзы или функции утилит, но не относящиеся к логике твоего приложения - это ответственность твоего API.
Да и сразу по твоей функции - ты доверяешь назначение user_id кому? БД, или логике приложения?
Я пытался сделать подобное - вставлять если нету, но потом пришёл к выводу - нефиг! Есть куча сценариев где тебе нужно получить юзверя - но он может и не быть там, следственно и вставлять его не нужно.
Так что лучше разделить эту логику.

Да и если ты 100% вставляешь, то не используй 'save', а используй 'insert'.
save - весьма опасен, тем что если будешь обновлять что-то (ожидая update), а документа такого не будет, а обновление будет partial (только пару полей), то вставиться "не полный" документ, что твоя логика приложения не будет ожидать.

И мелкая заметка, в node, для даты проще писать так:
Date.now() - получает timestamp.

pax 03.09.2013 18:21

Ответ: Node.JS + MongoDB
 
У меня это авторегистрация такая. id это id пользователя в соц сети. На счет insert спасибо, буду иметь ввиду. И я уже решил делать логику в ноде, а не в БД, а то получается какая-то фигня (не нравится мне такой код) с вызовами.

moka 03.09.2013 19:27

Ответ: Node.JS + MongoDB
 
Я тут щас aggregation и mapReduce изучаю. Заодно открыл для себя $where.
$where - позволяет стрингой передать сроку кода, например:

Пример player'а:
PHP код:

{
  
_id42,
  
prizes: [
    { 
created123type'foo' },
    { 
created234type'bar' }
  ]


И вот такой query:
PHP код:

players.count({ $where'this.prizes.length >= 20' }, function(errcount) {
  if(!
err) {
    
// count - сколько игроков имеет призов больше чем 20
  
} else {
    ...
  }
}); 

Если в бд вбить:
PHP код:

db.players.find({ $where'this.prizes.length >= 20' }).explain() 

То выдаст факт того что тут ничего естественно не индексируется, индекс тоже игнорируется.
Следственно например для collection'а с 2800 записями, у меня затрачивается 72мс (на простом Mac'е), что ужасно долго для таких запросов.
Следственно в реальном мире лучше кешировать длину массива рядом с массивом.

Но всё же, если у тебя например запрос на 32 записи (например), то такой код - вполне приемлем. Нужно исследовать как там с блоком всего процесса (блокирует ли другие запросы и т.п.).


aggregation - это интерестный монстрик, по получению всяких данные и более сложных манипуляций над документами.
Например у меня есть записи игроков, и у каждого игрока есть любимая команда. Мне нужно получить список всех команд и сколько игроков её указали как любимую:

PHP код:

players.collection().aggregate([
  {
    
$match: {
      
favourite: { $existstrue // ищем всех игроков у которых указан 'favourite'
    
}
  }, {
    
$group: { // групируем все записи
      
_id"$favourite"// по favourite полю
      
total: { $sum// и прибавляем 1 к total
    
}
  }, {
    
$sort: { // потом сортируем по убыванию total
      
total: -1
    
}
  }
], function(
errdata) {
  if (!
err) {
    
//
  
} else {
    ...
  }
}); 

favourite - это id команды простое число.
Всего там 20 команд.
В результате получу массив с до 20 записями такого вида:
PHP код:

{
  
_id 685,
  
total 813


Для сборов статистики - самое то. Естетсвенно это не супер шустро такие статистически сборы делать. Но в aggregate есть не мало разных операторов, которые используют индексацию, что ускоряет весьма не плохо. Для не больших запросов (до 64 записей), это весьма приемлемый подход. Естественно всегда можно закешить результаты.


mapReduce - это ещё следующий шаг. Когда aggregation предоставляет операторы, и там важна их поочерёдность и т.п., то mapReduce принимает лишь 2 функции и query фильтр.
Первая функция обрабатывает каждый документ (удовлетворяющий query) и если нужно вызывает emit, что передаёт уже обработанные данные (можно и не обработанные) в reduce функцию, где уже дальше мы имеем дело с групированными данными.
Примеров много можно привезти, в моём случае, нужно было узнать сколько призов в среднем выйграл каждый игрок. Есть 2800 записей. И нужно получить общее арифметическое колличества призов:

PHP код:

players.collection().mapReduce(function() {
  
emit('stats'this.prizes.length); // map - для всех запускаем reduce с групированием (_id будет 'stats')
}, function(keyvalues) { // reduce
  
var total 0// всего призов
  
var values.length;
  while(
i--) { // для каждой длины массива призов
    
total += values[i]; // складываем
  
}
  return 
total values.length// и делим на количество значений
}, {
  
query: { confirmedtrue }, // только игрокам кто подтвердил свой аккаунт
  
out: { inline}
}, function(
errdata) {
  if (!
err) {
    
// data[0].value - общее арифметическое длин массива призов у каждого игрока
  
} else {
    ...
  }
}); 

На самом деле это можно было сделать и aggregator'ом, но я хотел попробовать mapReduce.
Снова, для тысячей записей - это не шустро, но для статистики - самое то.

pax 04.09.2013 12:21

Ответ: Node.JS + MongoDB
 
Заметил что таймштамп в ноде в миллисекундах, а не в секундах как это в php. Поделил все на 1000)

moka 04.09.2013 16:34

Ответ: Node.JS + MongoDB
 
Цитата:

Сообщение от pax (Сообщение 266439)
Заметил что таймштамп в ноде в миллисекундах, а не в секундах как это в php. Поделил все на 1000)

Дык держи в миллисекундах, их и так достаточно, и не вижу бенефита в секундах :)
Тем более что если захочешь перевезти timestamp в дату то:
PHP код:

var date = new Date(timestamp); 

Иначе прийдётся ещё и на 1000 умножать..

pax 04.09.2013 18:08

Ответ: Node.JS + MongoDB
 
Бенефит: в базе либо Int32 если секунды, либо double если миллисекунды.

PS: начинаю втягиваться, на первый взгляд сложнее чем на php, потому что надо писать асинхронный код, с другой стороны вроде как и интереснее получается.

moka 04.09.2013 18:32

Ответ: Node.JS + MongoDB
 
Цитата:

Сообщение от pax (Сообщение 266462)
Бенефит: в базе либо Int32 если секунды, либо double если миллисекунды.

Исходя из того как бд хранит данные и создаёт файлы с зазорами - это не даст практически никакой разницы, если конечно у тебя документы не просто _id и timestamp.
http://stackoverflow.com/questions/6...tes-in-mongodb

Цитата:

Сообщение от pax (Сообщение 266462)
PS: начинаю втягиваться, на первый взгляд сложнее чем на php, потому что надо писать асинхронный код, с другой стороны вроде как и интереснее получается.

На самом деле как привыкнешь, потом будет даже не сложнее.

Правда появяться некоторые "трудности", например запустить сразу 3 асинхронных функции, и потом финальную функцию, или сделать очередь асинхронных функций. Есть promise и async.js, но я как обычно юзаю свой вариант решения простой задачи:

PHP код:

function jobsQueue(jobs) {
  if (
jobs && jobs instanceof Array && jobs.length 0) {
    var 
count jobs.length;
    var 
= -1;
    var 
next = function() {
      if (++
count) {
        
jobs[i](next);
      }
    }
    
next();
  }
}

function 
jobsQueueAsync(jobscomplete) {
  if (
jobs && jobs instanceof Array && jobs.length 0) {
    var 
count jobs.length;
    var 
done = function() {
      if (--
count == && complete) {
        
complete();
      }
    }
    for(var 
0len jobs.lengthlen; ++i) {
      
jobs[i](done)
    }
  }


Первая запускает поочерёдно функции.
Вторая запускает все асинхронно и финальную когда все закончили.

Пользоваться так:
PHP код:

jobsQueue([
  function(
next) {
    ...
    
next();
  }, function() {
    ...
  }
]);

jobsQueueAsync([
  function(
next) {
    ...
    
next();
  },
  function(
next) {
    ...
    
next();
  }
], function() {
  ... 
done
}); 

Ну и тесты:
PHP код:

jobsQueue([
  function(
next) {
    
console.log('1');
    
next();
  },
  function(
next) {
    
console.log('2a')
    
setTimeout(function() {
      
console.log('2b');
      
next();
    }, 
400);
  },
  function(
next) {
    
console.log('3a');
    
setTimeout(function() {
      
console.log('3b');
      
next();
    }, 
100);
  },
  function() {
    
console.log('4');
  }
]); 

Выдаст: 1 2a 2b 3a 3b 4
Как видишь он даже не начинает выполнение следующей функции до выполнения прошлой.

PHP код:

jobsQueueAsync([
  function(
next) {
    
console.log('1');
    
next();
  },
  function(
next) {
    
console.log('2a')
    
setTimeout(function() {
      
console.log('2b');
      
next();
    }, 
400);
  },
  function(
next) {
    
console.log('3a');
    
setTimeout(function() {
      
console.log('3b');
      
next();
    }, 
100);
  } ],
  function() {
    
console.log('4');
  }
); 

Выдаст: 1 2a 3a 3b 2b 4
И тут видно что он запускает все сразу, но финальную только когда каждая функция запустит next() по завершению (асинхронно или нет).

moka 04.09.2013 18:36

Ответ: Node.JS + MongoDB
 
В тему асинхронного кода, юмор:

var result = fs.readFile(‘data.json’, function () {})

pax 05.09.2013 08:06

Ответ: Node.JS + MongoDB
 
А ты для задержки всегда setTimeout используешь? Я сейчас пользуюсь setImmediate для создания асинхронности. Она вроде откладывает выполнение до следующего цикла.

И еще вопрос: как правильно запускать/останавливать/перезапускать node js на сервере? Я пока только на windows его запускал.

moka 05.09.2013 13:57

Ответ: Node.JS + MongoDB
 
Цитата:

Сообщение от pax (Сообщение 266521)
А ты для задержки всегда setTimeout используешь? Я сейчас пользуюсь setImmediate для создания асинхронности. Она вроде откладывает выполнение до следующего цикла.

Ну если тебе нужно выполнить как можно скорее, но ты хочешь немного распределить нагрузку, то конечно можно.
Но имхо, если у тебя операция на столь тяжёлая и уже потребовала слишком много времени, тогда я советую пересмотреть либо операцию, либо выдвинуть её в отдельный node процесс и общаться с ним через ZeroMQ, тем самым - это будет точно асинхронно (главный процесс не будет выполнять задачу, и сможет паралельно заниматься своими вещами).

Цитата:

Сообщение от pax (Сообщение 266521)
И еще вопрос: как правильно запускать/останавливать/перезапускать node js на сервере? Я пока только на windows его запускал.

Я делал и на win и на unix.
На win, при разработки в Sublime Text 2, есть аддон для настроек компиляции. Тупо - билдишь, и в консоль будет выдаваться консоль. Но нужно дополнительно скрипт для остановки процесса (а то если ты сокет биндишь и не остановишь прошлый процесс, новый не сможет сокет создать на том же порту).
А для деплоймента использовал NSSM ( http://nssm.cc/ ) - Non-Sucking Service Manager - очень понравился. В win редко когда есть хорошие туулзы и минималистичные как в unix. В общем он создаёт сервис в системе, и следит за ним. Можно настроить разные сценарии если процесс сервиса падает - перезапускать например сразу.
Не для всех конечно случаев, но для многих я тупо сделал сервис на текущий разрабатываемый проект, и всё что нужно было, это останавливать процесс, и сервис перезапускал его автоматически так что заместо - остановить > запустить, я только один клик остановки - и уже мог идти тестировать.
Единственное что логи нужно тогда придумать как слать, для этого я winston сделал и отдельный монитор с дашбоардом консоля :)

На linux, есть два способа, первый проще и быстрее - screen. Делаешь отдельный screen и запускаешь там, затем детачишься из скрина если не нужно там быть.
Но лучше конечно написать скрипт для сервиса, плюсов много - логи можно в файл по стандарту выводить. Автозапуск при рестарте компа. Удобно рестартовать: service api restart

Вот файл для скрипта, кладём в /etc/init.d/ я назвал его "api". Работает на RedHat системах и Ubuntu. Естественно замени пути к твоим файлам сам.
service api start
service api stop
service api restart
Логи по стандарту тут: /var/log/api.log

PHP код:

#!/bin/sh

#
# chkconfig: 35 99 99
# name: API
# description: Node.js /var/api/app.js
#

if [ -"/etc/rc.d/init.d/functions" ] ; then
  
. /etc/rc.d/init.d/functions
  USER
="ec2-user"
  
ROOT_DIR="/var/api"
  
LOCK_FILE="/var/lock/subsys/api"
  
LOG_FILE="/var/log/api.log"
else
  . /
lib/lsb/init-functions
  USER
="root"
  
ROOT_DIR="/vagrant"
  
LOCK_FILE="/var/lock/api"
  
LOG_FILE="/var/log/api.log"

  
echo_success() {
    echo -
"[  OK  ]"
    
return 0
  
}
  
echo_failure() {
    echo -
"[FAILED]"
    
return 1
  
}
fi

DAEMON
="/usr/local/bin/node"
SERVER="$ROOT_DIR/app.js"

start() {
  
pid=`ps -aefw | grep "$DAEMON $SERVER" | grep -v "grep " | awk '{print $2}'`
  if [ -
"$pid] ; then
    
echo "$SERVER is already running."
    
RETVAL=1
  
else
    echo -
"Starting $SERVER: "
    
if [ type runuser > /dev/null 2>&] ; then
      runuser 
-"$USER-"$DAEMON $SERVER >> $LOG_FILE &" && echo_success || echo_failure
    
else
      
su "$USER-"$DAEMON $SERVER >> $LOG_FILE &" && echo_success || echo_failure
    fi
    RETVAL
=$?
    echo
    [ 
$RETVAL -eq 0 ]
  
fi
}

stop() {
  echo -
"Stopping $SERVER: "
  
pid=`ps -aefw | grep "$DAEMON $SERVER" | grep -v "grep " | awk '{print $2}'`
  
kill -9 $pid > /dev/null 2>&&& echo_success || echo_failure
  RETVAL
=$?
  echo
  [ 
$RETVAL -eq 0 ]
}

case 
"$1" in
  start
)
    
start
    
;;
  
stop)
    
stop
    
;;
  
restart)
    
stop
    start
    
;;
  *)
    echo 
"Usage: $0 {start|stop|restart}"
    
RETVAL=1
esac

exit $RETVAL 


pax 13.09.2013 12:49

Ответ: Node.JS + MongoDB
 
Я так понял что нельзя обновить несколько членов массива за один запрос?

moka 13.09.2013 14:19

Ответ: Node.JS + MongoDB
 
Почему нет?
Под обновить - что ты имеешь ввиду конкретно?

Тест - обновляет:
PHP код:

mongo
> use test
db.items.insert({ a: [ 1] })
db.items.find()
"_id" ObjectId("5232d861f37cb58b0486eeb1"), "a" : [  1,  ] }
db.items.update({ }, { $set: { 'a.0'5'a.1'} })
db.items.find()
"_id" ObjectId("5232d861f37cb58b0486eeb1"), "a" : [  5,  ] } 


pax 13.09.2013 14:40

Ответ: Node.JS + MongoDB
 
Мне надо у каждого объекта в массиве обновить поле или несколько полей. А не каждый итем обновлять по индексам.
Я пробовал такой запрос:
PHP код:

db.collection('users').update({_idparams.requester'items.robot'robotId},
       {
$unset: {'items.$.robot'0'items.$.slot'0}}, function () {}); 

Но обновляет всего одну запись в массиве.

в итоге наткнулся на это:
https://jira.mongodb.org/browse/SERVER-1243

moka 13.09.2013 15:32

Ответ: Node.JS + MongoDB
 
А оператор $ да, обновит только первый найденный элемент в твоём случае..

Выход будет - подгрузить, выбрать в JS то что нужно, и обновить уже с новым списком.

Будь осторожен если есть возможность делать эту операцию одновременно, т.к. тогда нужно будет лочить запись - чтобы предотвратить перекрёщивание двух операций с другими запросами на этот же документ.

pax 19.09.2013 12:16

Ответ: Node.JS + MongoDB
 
Получил вот такое:


Как с этим бороться?

moka 19.09.2013 14:00

Ответ: Node.JS + MongoDB
 
ETIMEDOUT это системная ошибка, а не самого node.js, видимо что-то на уровне TCP происходит, а node.js просто продвигает её.

Нужно знать как ты добиваешься такой ошибки, т.к. я на практике её не встречал.

Если хочешь избежать падения процесса, можешь попробовать её поймать, возможно есть event для сокета (т.к. возможно ты с сокетами это ловишь):
PHP код:

socket.on('error', function(err) {
  
console.log(err);
  
console.log(err.stack);


А если нету, то нужно ловить unhandledException: http://nodejs.org/api/process.html#p...aughtexception
В продакшане очень не рекомендуется - т.к. это будет ловить не пойманные ошибки, но при этом оставлять процесс жить, когда последствия ошибки могут быть "не желательными".

pax 19.09.2013 15:08

Ответ: Node.JS + MongoDB
 
Поставил обработчик пока на unhandledException.

Как оно получается я пока не знаю.
У меня сейчас два процесса, т.к. я использую кластер на одном ядре виртуального сервера. REST API на основе express (http и https) в воркере. В мастере я запустил модуль cron, по которому я подсчитываю и обновляю статистику (это только подключение к MongoDB и асинхронное обновление). Других операций с сокетами я не делаю...

pax 19.09.2013 15:40

Ответ: Node.JS + MongoDB
 
Запустил у себя на сервере наконец по дефолту вместо апача node js и думаю сейчас... а есть ли какой-то миддлваре для express, который может переадресовать для определенных хостов запрос на другой порт сервера, где запущен апач...? Если я вдруг захочу для дочернего домена сделать сайтик на php и использовать его на дефолтном порту...

moka 19.09.2013 16:19

Ответ: Node.JS + MongoDB
 
Есть, только его лучше использовать до главного API приложения.
http-proxy - проксит всё, вплоть до вебсокетов.

Шустрый и используется nodejistu (хостинг node).
https://github.com/nodejitsu/node-http-proxy

Там всё по сути просто. Пишешь мелкий proxy app на ноде, указываешь разные прокси куда и как раскидывать, там есть куча вариантов проксить, вплоть до ручного определения.

Получается запускаем proxy на 80 порту, далее API на другом, и проксим к API по дефолту.
Apache запускаем тоже на другом порту, и проксим на него тоже по субдомену (например), а можно и по субдиректории.

Если юзаешь AWS советую заюзать Route53 - домен менеджер, создание субдомена и обновление DNS записей занимает пару минут а не сутки.

pax 24.09.2013 11:21

Ответ: Node.JS + MongoDB
 


Что-то не могу составить запрос на удаление поля HandR

UPD: получилось

PHP код:

var unset = {};
    unset[
'items.$.slots.' slotName] = 1;
    
db.collection('users').update({_idparams.requester'items.id'robotId}, {$unset: unset}, function (errres) {

    }); 


pax 27.09.2013 19:55

Ответ: Node.JS + MongoDB
 
moka А не можешь ли привести пример простенького реалтаймового сервера, передающего координаты игроков. Я бы хотел node js и Unity связать через TCP.

moka 27.09.2013 20:17

Ответ: Node.JS + MongoDB
 
Примеры есть, только они на socket.io, следственно там только игровая логика и никакой сетевой, т.к. всё уже сделано используя socket.io. Но, естественно можно и сделать сериализацию и десериализацию на обоих сторонах. Проще будет начать с JSON'а, а потом что-то придумать дельнее.

Вот немного говнокода, писалось 24 часа, потом чуток приводилось в порядок но совсем чуток.
https://github.com/Maksims/beat-em-up
Играть тут: http://moka.co:88/

UPD:
Я не советую, но есть socket.io клиент для unity: https://github.com/NetEase/UnitySocketIO

pax 01.10.2013 12:38

Ответ: Node.JS + MongoDB
 
Не пойму как выполнить поиск с использованием регулярных выражений и символов юникода (русский текст).

Получилось,
PHP код:

db.collection('collection_name').find({name_upper: /.*pattern.*/i}).toArray(function(errdocuments){

}); 


moka 01.10.2013 13:53

Ответ: Node.JS + MongoDB
 
Учти что поиск по тексту как у тебя не будет использовать никакой индексации, и на больших таблицах может быть очень дорогим, тем более что ты не ограничиваешь число результатов.

Для регулярок, тебе нужно использовать `new RegExp()`. А чтобы использовать индексацию по полю, то можешь сделать так:
PHP код:

new RegExp('^' query

Что говорит "начинается с". Естественно это зависит от капитализации символов, поэтому ты может захочешь хранить копию текста с toLowerCase() заранее. Индексация как понимаю будет tree с нодами по алфавиту, что очень продуктивно, тебе не нужно париться как индекс работает и т.п., главное создай индекс по полю. Да и советую убедиться что в регулярке нету ничего "опасного", т.к. сделать её очень тяжолой - весьма просто, следственно и загрузить твою БД, сложной регуляркой не составит труда.

pax 01.10.2013 14:25

Ответ: Node.JS + MongoDB
 
Мне с индексами не судьба, хочу имена кланов сделать с именами типа -=М.О.Й.К.Л.А.Н=-

moka 01.10.2013 14:40

Ответ: Node.JS + MongoDB
 
И что мешает это всё равно заиндексировать?
Можно сделать массив `search` который будет содержать 2 вида строк: в нижнем регистре и в нижнем регистре без знаков.
Далее простой индекс по этому полю `search`, и затем поиск будет весьма продуктивен.
Главное это escape'ить регулярные знаки в прописные, например . в \. и т.п.
Далее поиск будет работать по таким запросам:
Код:

-=м.О.Й.к.л
мой
МоЙКЛаН
мо
-=м.

Что для поиска думаю вполне достаточно и гравное это будет очень быстрым поиском.

pax 03.10.2013 14:33

Ответ: Node.JS + MongoDB
 
Ближайший вопрос, который хотелось бы решить - это резервное копирование базы. Как это правильно сделать?

moka 03.10.2013 18:41

Ответ: Node.JS + MongoDB
 
Backup?

Есть трои принятых способа бэкапить бд, первый будет используя mongoexport/mongoimport второй mongodump/mongorestore, и снапшот системы (это для облаков).

mongoexport/mongoimport
Оперирует JSON данными, и предусмотрен к бэкапу только данных и не содержит никаких мета данных. Также т.к. данные в JSON формате, а он ненмого будет побольше чем BSON, нужно больше места для его хранения.
Плюс при импорте, подразумевается что у тебя уже есть сама база данных с коллекциями и всеми индексами.
Думаю это удобно для быстрого копирования блока данных между базами, но не совсем для бэкапов.

mongodump/mongorestore
Этот способ реально хорошо справляется с бэкапами, делает полную копию данных самих коллекций и всей базы (если на всю делаешь), хранит данные в BSON (что оптимальнее), а также хранит мета данные о коллекциях, индексах и других плюшках для полной копии восстановления.

Snapshot of System
Этот подход используется в облаках, когда ты можешь создать полную копию текущей системы. Такие снапшоты обычно весьма весомы, т.к. бэкапят всё что есть на системе а не только бд. Если у тебя большая бд и много ресурсов на системе - тебе такой вариант не подойдёт.


Я использую mongodump/mongorestore, с ним можно дампить как всю бд, так и отдельные базы данных, так и более детально - отдельные коллекции.
Учти что при mongorestore, если есть уже бд и коллекция - то данные добавяться паралельно к прошлым, так что если ресторишь, то удаляй коллекцию или всю бд (если всю бд ресторишь).

Я написал скрипт который запускается каждый день в 3 ночи, используя node.js, он используя child_process вызывает command line для mongodump, который в свою очередь создаёт папку. Далее я это дело tar'ю, и заливаю там же скриптом на S3 Bucket, где стоит правило - автономно удалять файлы старее 1го месяца.

Всё автономно, и никакой парки, плюс бэкапяться у меня две базы данных за один подход как два разные tar'а.

pax 04.10.2013 17:00

Ответ: Node.JS + MongoDB
 
Все еще не очень понимаю вот это: http://nodejs.org/api/buffer.html
Цитата:

'binary' - A way of encoding raw binary data into strings by using only the first 8 bits of each character. This encoding method is deprecated and should be avoided in favor of Buffer objects where possible. This encoding will be removed in future versions of Node.
Как правильно с бинарными данными работать в node js?

moka 04.10.2013 18:34

Ответ: Node.JS + MongoDB
 
Что конкретно тебе нужно с бинарными данными?

pax 04.10.2013 22:23

Ответ: Node.JS + MongoDB
 
Хочу научиться работать с бинарными данными как с MemoryStream в C#. Хочу свой протокол сериализации реализовать в будущем для клиентсервера.

moka 04.10.2013 22:29

Ответ: Node.JS + MongoDB
 
Ну ты как бы можешь да юзать то что тут есть, но для работы с бинарными данными я очень рекомендую поступить одним из способов:

1. Писать Addon на С++ для парсинга бинарных данных в JSON объект.
2. Заюзать готовые решения для этого, например protobuf.
3. Взять готовое решение на С++, и написать враппер/addon для него, например Cap'n Proto отличный выбор будет.

Для начала и JSON'а хватает с головой. Если качественно написать логику сбора данных, то заменить сборку и разборку данных сможешь потом без проблем.

pax 10.10.2013 10:05

Ответ: Node.JS + MongoDB
 
Больше всего в ноде не нравится то, что приходится делать вот такие "елки":



Особенно в те моменты когда есть асинхронные ветвления (иногда необходимо сделать лишний запрос в базу, а иногда нет). Я тогда справляюсь с использованием функций, которые вызываю несколько раз в ветвлении.

К примеру (функция addRequest):

moka 10.10.2013 13:47

Ответ: Node.JS + MongoDB
 
Угу, эта проблема популярна. Один из хороших вариантов "решения" её будут promise. Можно chain'ить асинхронные запросы как угодно, и отлавливать ошибки лучше.
И на err ты не проверяешь нигде :)..

pax 10.10.2013 13:59

Ответ: Node.JS + MongoDB
 
Цитата:

Сообщение от moka (Сообщение 268581)
И на err ты не проверяешь нигде :)..

Мне проще проверить результат на наличие)

moka 10.10.2013 14:03

Ответ: Node.JS + MongoDB
 
Цитата:

Сообщение от pax (Сообщение 268582)
Мне проще проверить результат на наличие)

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

Например если ты где-то забыл убрать "неприятные" символы в стринге, что приводит к регулярке, а это очень неприятно для бд.. Или объект для фильтров собрал нехороший :) А порой претендуешь что есть переменная в документе, а её нету, тоже бывает такие дела..

pax 10.10.2013 14:37

Ответ: Node.JS + MongoDB
 
ну наличие переменных в документе я проверяю там где надо, да и обработки ошибок я когда-нибудь впишу, когда появится на это время)

moka 10.10.2013 15:04

Ответ: Node.JS + MongoDB
 
Вот поковырялся, я писал для себя сам мелкие функции для chain'ов асинхронных функций, но можешь заюзать async https://github.com/caolan/async там етсь например series - запускает функции поочереди. Думаю там можно также вставлять по пути до функции (task injection), и т.п.

pax 10.10.2013 16:42

Ответ: Node.JS + MongoDB
 
И я подобное писал для обработки массива и секвенции, но в примерах выше оно не помогает

PHP код:

// index - startIndex
// processor - function(item, itemIndex, next)
module.exports.processArrayAsync = function (array, indexprocessoronEndCallback) {
    
index typeof index !== 'undefined' index 0;

    if (
index >= array.length) {
        if (
onEndCallbackonEndCallback();
        return;
    }
    
process.nextTick(function () {
        
processor(array[index], index, function () {
            
module.exports.processArrayAsync(array, index 1processoronEndCallback);
        });
    });
};


module.exports.runJobsQueueAsync = function(jobsindexonEndCallback) {
    
index typeof index !== 'undefined' index 0;

    if (
index >= jobs.length) {
        if (
onEndCallback) {
            
onEndCallback();
        }
        return;
    }
    
process.nextTick(function () {
        
jobs[index](function () {
            
module.exports.runJobsQueueAsync(jobsindex 1onEndCallback);
        })
    });
}; 


moka 10.10.2013 21:10

Ответ: Node.JS + MongoDB
 
Потому что тебе нужен не статичный chain, что потребует модификации в твой - возможность вставлять job'ы по пути выполнения.

pax 10.10.2013 21:16

Ответ: Node.JS + MongoDB
 
Хм, а я не подумал что можно вставлять джобы в цепочку... Спасибо, навел на мысль...

pax 10.10.2013 22:33

Ответ: Node.JS + MongoDB
 
Я это сделал!
PHP код:

function AFlow() {
    
this.index 0;
}

module.exports.AFlow AFlow;

module.exports.run = function (jobs) {
    var 
flow = new AFlow;
    
flow.jobs jobs;
    
flow.doJob();
    return 
flow;
};

AFlow.prototype.add = function (job) {
    
this.jobs.splice(this.index 10job);
};



AFlow.prototype.doJob = function () {
    var 
flow this;
    if (
flow.index >= flow.jobs.length) {
        return;
    }
    
process.nextTick(function () {
        
flow.jobs[flow.index](function () {
            
flow.index++;
            
flow.doJob();
        }, 
flow)
    });
}; 

использование:

PHP код:

var util = require('util');
var 
AFlow =  require('AFlow');

var 
data = [];

flow AFlow.run([
    function (
next) {
        
data.push('one');
        
console.log(util.inspect(data));
        
setTimeout(next500);
    },
    function (
next) {
        
data.push('two');
        
flow.add(function (next) {
            
data.push('three');
            
console.log(util.inspect(data));
            
setTimeout(next500);
        });
        
console.log(util.inspect(data));
        
setTimeout(next500)
    },
    function (
next) {
        
data.push('four');
        
console.log(util.inspect(data));
        
setTimeout(next500)
    },
    function(){
        
data.push('five');
        
console.log(util.inspect(data));
    }
]); 


pax 16.10.2013 16:38

Ответ: Node.JS + MongoDB
 
а есть ли для node js ubjson парсер?

UPD: кажется нагуглил https://github.com/Sannis/node-ubjson

ant2on 29.10.2013 18:59

Ответ: Node.JS + MongoDB
 
Цитата:

Сообщение от moka (Сообщение 267713)
UPD:
Я не советую, но есть socket.io клиент для unity: https://github.com/NetEase/UnitySocketIO

Использую UnitySocketIO, нашел 2 серьезные проблемы:

1) Клиент создает слишком большую нагрузку на CPU
https://github.com/NetEase/UnitySocketIO/issues/13
http://forum.unity3d.com/threads/204...-UnitySocketIO

2) Нельзя принимать и посылать ивенты (socket.emit). Работают только месаги (socket.send)
https://github.com/NetEase/UnitySocketIO/issues/16

pax 29.10.2013 22:46

Ответ: Node.JS + MongoDB
 
Я уже написал бинарную сериализацию пакетов и связал Unity и node js по tcp с использованием net модуля. Сейчас хочется сделать некую архитетуру для создания ММО решений.

moka 30.10.2013 02:18

Ответ: Node.JS + MongoDB
 
Не думаешь зарефакторить код, и выложить github проект? Т.к. думаю на него спрос будет весьма большой.

Если с точки зрения пользователя это работает как socket.io то вообще сказка и у тебя есть возможность менеджить популярный модуль на npm.

В общем, на такой модуль будет спрос если его maintain'ить нормально.

pax 30.10.2013 08:08

Ответ: Node.JS + MongoDB
 
Цитата:

Сообщение от moka (Сообщение 269477)
Не думаешь зарефакторить код, и выложить github проект? Т.к. думаю на него спрос будет весьма большой.

Если с точки зрения пользователя это работает как socket.io то вообще сказка и у тебя есть возможность менеджить популярный модуль на npm.

В общем, на такой модуль будет спрос если его maintain'ить нормально.

Пока еще не думал, видно будет. Я бы предложил тебе поучаствовать в его разработке если интересно, когда будет что-то получаться, то можно будет подумать о выкладывании. Пока наверное рано.

pax 30.10.2013 10:52

Ответ: Node.JS + MongoDB
 
На данный момент чат сервер "все со всеми" можно сделать вот так:

PHP код:

var nodeSharpTCP = require('NodeSharpTCP');

// полиси сервер
var SocketPolicyServer nodeSharpTCP.SocketPolicyServer;
SocketPolicyServer.listen(__dirname '/public/crossdomain.xml'843);

// чат сервер
var tcpServer = new nodeSharpTCP.TCPServer();

tcpServer.packetProcessors.push(function (serverclientpacketnext) {
    
server.broadcastObject(packet);
});

tcpServer.listen(8124, function () {
    
console.log('server bound');
}); 


moka 01.11.2013 01:18

Ответ: Node.JS + MongoDB
 
Прикольно.
Как понимаю нужен обмен Policy чтобы сделать нормальное общение? (также как в flash'е?).

packetProcesors - как понимаю ты пушаешь функцию потом которая будет вызываться для каждого пакета.

Как тебе подход как в socket.io? В плане имён пакетов и подпиской на них?
Например:

PHP код:

var UnitySocket    = require('unitySocket').Socket,
    
PolicyProvider = require('unitySocket').PolicyProvider;

// create policy server with preloaded policy provider file
var unityPolicy = new PolicyProvider(__dirname '/public/crossdomain.xml');
unityPolicy.listen(843);

// create server socket
var server = new UnitySocket();

// if error occured
server.on('error', function(err) {
  throw 
err;
})

// once bound
server.on('bound', function() {
  
// server is bound and ready to accept connections
});

// someone connects
server.on('connect', function(socket) {

  
// subscribe to generic event for each packet
  
socket.on('packet', function(namedata) {
    
// name == 'foo'
    // data == json
  
});

  
// subscribe to packet with specific name
  
socket.on('foo', function(data) {
    
// data == json
  
});

  
// subscribe to client disconnect
  
socket.on('disconnect', function(data) {
    
// data contains reason and additional details
  
});

});

// bind server socket
server.listen(8080); 

В node.js да и везде вообще, очень важен поток мысли и дизайн технического решения.
Например использовать EventEmitter'ы для использования event-driven парадигмы - очень збсь штука.
Также можно предоставить promises как альтернативная парадигма, это уже зависит от разраба что и как юзать.

Я сейчас promises юзаю, и порой очень просто и элегантно выходят решения (никаких пирамид).

Можно выложить на github и там уже по пути менеджить и т.п.

pax 01.11.2013 07:58

Ответ: Node.JS + MongoDB
 
Цитата:

Сообщение от moka (Сообщение 269522)
Прикольно.
Как понимаю нужен обмен Policy чтобы сделать нормальное общение? (также как в flash'е?).

Да, WebPlayer Unity так же проверяет возможность подключения как и флеш.

Цитата:

Сообщение от moka (Сообщение 269522)
packetProcesors - как понимаю ты пушаешь функцию потом которая будет вызываться для каждого пакета.

Да там похоже на middleware в express

Цитата:

Сообщение от moka (Сообщение 269522)
Как тебе подход как в socket.io? В плане имён пакетов и подпиской на них?

Вчера написал по просьбе ant2on'а нечто похожее на socket.io как враппер над своим сервером.
Node js
PHP код:

var util = require('util');
var 
NodeSharpTCP = require('NodeSharpTCP');

// полиси сервер
var SocketPolicyServer NodeSharpTCP.SocketPolicyServer;
SocketPolicyServer.listen(__dirname '/public/crossdomain.xml'843);

// евент сервер
var EventServer NodeSharpTCP.EventServer;

var 
server = new EventServer();

server.on('connection', function(client){

    
console.log('Clients: 'server.clients.length);

    
client.emit('welcome', {idclient.id});

    
client.on('disconnect', function(){
        
console.log('Clients: 'server.clients.length);
    });

    
client.on('message', function(msg){
        
console.log('message: 'util.inspect(msg));
        
client.sendMessage(msg);
    });

    
client.on('custom-event', function(data){
        
console.log('custom-event: 'util.inspect(data));
        
client.emit('custom-event'data);
    });
});


server.listen(8124, function(){
    
console.log('Event Server bound')
}); 

Unity/C#
PHP код:

using System.Collections.Generic;
using BinarySerialization;
using UnityEngine;
using System.Collections;

public class 
EventServerClientTest MonoBehaviour
{
    private 
EventServerClient _client;

    public 
void Start ()
    {
        
_client GetComponent<EventServerClient>();

        
_client.On("connect"OnConnect);
        
_client.On("welcome"OnWelcome);
        
_client.On("custom-event"OnCustomEvent);


        
_client.Connect();
    }

    private 
void OnCustomEvent(object obj)
    {
        
Debug.Log("Custom Event Received: " obj.ToDebugString());
    }

    private 
void OnWelcome(object obj)
    {
        var 
welcomeEvent = (Dictionary<stringobject>) obj;
        
Debug.Log("Welcome: " welcomeEvent["id"]);
    }


    private 
void OnConnect(object obj)
    {
        
Debug.Log("OnConnect");
        
_client.Emit("custom-event", new object[]{1true"three"});
    }





Часовой пояс GMT +4, время: 08:49.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot