Конечно, вот Вам пример такого synchronize:
Средняя длительность обработки подымается (ведь все ждут одного), количество обработанных запросов падает, и вот результат для пользователя:
В данном случае страница, которая выдавалась за 200-300 миллисекунд, стала выдаваться за 58 (!) секунд. Чувствительно?
На практике Safe… полностью хватает. Unsafe был экспериментом, попыткой улучшить. Экспериментом не до конца удачным — 0,01% ошибок из-за race condition при уничтожении лока и паралельном его-же создании…
Но знаете, что хорошо в гитхабе: можно форкнуть, попробовать и сделать pool request.
У Вас получится что-то интересное и будет нам всем от этого польза ;-)
Ну сейчас мы плавно пересекаем границу, за которой вопрос становится более субъективным и немного философским.
То есть Вы переносите проблему в кэш, а как он там её будет решать дело его. Мне нравится в моём подходе, что блокировка происходит максимально рано, но с минимальным overhead и lock contention.
Если блокировка будет проходить позже, то сложнее искать проблемы при перекрестных вызовах функций. Опять же блокировка должна поддерживать повторный вход, то есть семафоры не катят.
Но в целом это конечно дело вкуса, я использую это во многих проектах и другие люди используют. Те кто пользуются говорят: «Как отлично» и «Нет аналогов». Но это их субъективное мнение, и я уверен, что вы можете написать лучше и более подходяще по Ваш вкус и стиль!
Кстати по поводу использования, проект был частью другой библиотеки (http://svn.anotheria.net/opensource/ano-util/trunk/, search.maven.org/#search%7Cga%7C1%7Cano-util), я его вынес в отдельный git repository и отдельный мавен артефакт, чтобы убрать лишнее. Но сам код работает года 4 на разных сайтах в продакшен.
Так, что выложил просто: может кому-то пригодится.
То будет по сути тот-же ConcurrentMap и putIfAbsent но в другом месте. Количество запросов к pService будет больше чем могло бы быть. Работать будет, но имхо менее элегантно.
Насчет проще не уверен, ибо количество кода больше. Если вы про вариант с семафором то у него большая проблема с повторным вхождением. Если вместо семафора брать ReentrantLock, то есть на том-же гитхабе вариант UnsafeIdBasedLockManager но у него есть проблемы с возможными race conditions (потому и Unsafe).
Кстати замечание о проблемах с remove есть на линке Stackoverflow тоже.
Насчет производительности, на моем i7 2010 года 50.000.000 операций (из 5 потоков)
Без блокировки: 2020 мс
Safe — пример из статьи: 21044 мс
Unsafe — putIfAbsent в ConcurrentMap: 12192 мс.
Да, в два раза быстрее, но эти «2раза» — 2 наносекунды. Думаю для 99.9999999% приложений это достаточно быстро.
Кстати ConcurrentHashMap не даёт гарантию что не будет synchronized, просто вероятность в 16 раз ниже.
Идея объекта возможно не лучший термин, но другого не нашел. Речь идет о том, что мы используем не конкретный объект — экземпляр — почтового ящика джона, а тот факт, что у джона он есть. То есть мы блокируем все операции с ящиком джона в принципе, а не операции на конкретном экземпляре ящика.
Возможны вариации, но вот вариант решения. Соединяем схему 8 и 6. По прецедентам (Use Case)
Пользователь А читает мейл. Прокси-сервер 1 грузит ящик с мастера (ибо первый раз) и отвечает на запрос.
Пользователь А (снова) читает мейл. Прокси-сервер отвечает из кэша.
Пользователь Б посылает мейл пользователю А через прокси-сервер 2. Прокси сервер 2 передает мейл на мастер-сервис. Мастер сервис обрабатывает запрос и асинхронно посылает информацию «обновить кэш» на прокси сервер 1 (так как он знает алгоритм распределения по прокси-серверам). Прокси-сервер 1 получает уведомление и маркирует ящик пользователя А как дерти (или просто стирает его с кэша).
Пользователь А читает мейл. Прокси-сервер 1 грузит ящик с мастера (ибо в кэше нету) и отвечает на запрос.
Фишка в том, что Use Case 2 гораздо чаще используется чем 1,3 или 4. На тех порталах где я имел честь работать 95%++. В основном это проверка — а есть ли новые сообщения.
А убрать 95% запросов — правда неплохо?
Конечно, в схеме есть варианты, например можно сразу обновить ящик а не убирать его из кэша, или не делать нотификацию а кэшить на прокси-сервере А с expiry, например 2 минуты. В таком случае обновление в ящике мы бы увидели, самое раннее, через 2 минуты. В общем много вариантов — правильный для Вас зависит от Ваших требований.
Попробуйте сделать игру. Возможностей для усложнения достаточно, а ребята научаться всем основам информатики. Те кто пошустрее смогут сделать более сильные алгоритмы. Разделите на 3-4 группы и сделайте в конце турнир. Как игры можно сделать:
— Не вытащить последнюю палочку (для начала и освоения мин-макс).
— Крестики нолики (сначала на три клеточки, потом на больше).
— Реверси.
— Шашки.
Ещё можно порешать шахматные задачи:
— Поставить 8 ферзей на доску, чтобы они не угрожали друг-другу.
— Пройти всю доску одним конём не посетив ни одно поле дважды.
для начала позвольте заметить что у Вас очень симпатичный сайт. Единственное — при разрешении 1280х800 claim не виден. Это мне возразил один из специалистов по usability, когда я послал ему Ваш сайт как пример для хорошего IT-Сайта.
К делу:
1) Вы уверены что у Вас есть сервисы? Employer- и Recruiter- сервисы я бы такими не назвал, так как все проблемы там решаются при помощи SQL (ну или вызова к repository что в итоге тоже самое). Ваши сервисы при этом используют одни и те же repository, так что ни о каком разделении сервисов говорить не приходится: оба используют DealRepository, ApplicantRepository, UserRepository. Что ещё страшнее: RecruiterService использует EmployerRepository.
В Вашу защиту: то что spring называет сервисом и то что является сервисом с точки зрения СОА — две большие разницы.
2) Ваше разделение на пакеты! Ваше разделение на пакеты!
Разделение на пакеты должно быть по… затрудняюсь найти русское слово, на английском это Domain, а на немецком: Fachlichkeit. С таким разделением как у Вас построить СОА будет невозможно (ну или очень сложно), ведь практически невозможно проследить от каких пакетов зависит какой сервис и какой ещё зависит от тех же пакетов. Раз персистентность сервиса EmployerService его и только его, почему бы ей не находиться в под-пакете employer? Тоже касается и его данных. У вас всё в одной куче.
Если вы посмотрите на Схему 3: начинать надо с сервисов. Если бы Вы начали так, то у вас были бы совсем другие сервисы (попробуйте если не верите).
3) Ваше разделение сущностей emoployer и recruiter и последующая интеграция их при помощи общего объекта user… Это как-то ни то не сё. Либо у Вас таки есть employer и recruiter, либо их нет. Если уж собирать их в одну таблицу, то назовите это User и дайте ему какой-то enum type.
Ещё два маленьких замечания
1) javax.servlet-api надо декларировать provided. Я Ваш проект дальше compile не собирал, он пытался создать какие-то таблицы и т.д. и мне попросту было лень создавать специально базу, но теоретически томкат должен ругаться при старте.
2) Копирование разной конфигурации через мавен профайл: посмотрите на этот проект: www.configureme.org он позволяет хранить все конфиги в одном файле (stackable/cascading), и переключать нужные через параметр в рантайм. Плюс в том что Вы деплоите на все системы один и тот же, тестированный артефакт.
Я думаю, что если погуглить то можно найти использования этих терминов и другими авторами, так не буду себе приписывать авторство. На мой взгляд это сочетание настолько банально, что не стоит его возводить в ранг 'аксиомы' или 'термина'. Примерно как «сначала главное» (first things first).
Добрый день.
О распределенность напишу в следующей части, то есть в ближайшие дни.
Про CAP — думаю нет универсального ответа, но скорее всего А и Р. Хотя Р умеют немногие но как раз об этом тоже в следующей части.
Спасибо, об этом напишу в одной из следующих частей ;) Ну и конечно обменяться опытом можно всегда и вне поста.
В общем сложно ответить на этот вопрос априори, для разных систем подходят разные модели, тут нужно смотреть по конкретной ситуации.
Средняя длительность обработки подымается (ведь все ждут одного), количество обработанных запросов падает, и вот результат для пользователя:
В данном случае страница, которая выдавалась за 200-300 миллисекунд, стала выдаваться за 58 (!) секунд. Чувствительно?
Но знаете, что хорошо в гитхабе: можно форкнуть, попробовать и сделать pool request.
У Вас получится что-то интересное и будет нам всем от этого польза ;-)
То есть Вы переносите проблему в кэш, а как он там её будет решать дело его. Мне нравится в моём подходе, что блокировка происходит максимально рано, но с минимальным overhead и lock contention.
Если блокировка будет проходить позже, то сложнее искать проблемы при перекрестных вызовах функций. Опять же блокировка должна поддерживать повторный вход, то есть семафоры не катят.
Но в целом это конечно дело вкуса, я использую это во многих проектах и другие люди используют. Те кто пользуются говорят: «Как отлично» и «Нет аналогов». Но это их субъективное мнение, и я уверен, что вы можете написать лучше и более подходяще по Ваш вкус и стиль!
Кстати по поводу использования, проект был частью другой библиотеки (http://svn.anotheria.net/opensource/ano-util/trunk/, search.maven.org/#search%7Cga%7C1%7Cano-util), я его вынес в отдельный git repository и отдельный мавен артефакт, чтобы убрать лишнее. Но сам код работает года 4 на разных сайтах в продакшен.
Так, что выложил просто: может кому-то пригодится.
Кстати замечание о проблемах с remove есть на линке Stackoverflow тоже.
Насчет производительности, на моем i7 2010 года 50.000.000 операций (из 5 потоков)
Без блокировки: 2020 мс
Safe — пример из статьи: 21044 мс
Unsafe — putIfAbsent в ConcurrentMap: 12192 мс.
Да, в два раза быстрее, но эти «2раза» — 2 наносекунды. Думаю для 99.9999999% приложений это достаточно быстро.
Кстати ConcurrentHashMap не даёт гарантию что не будет synchronized, просто вероятность в 16 раз ниже.
Тут уже разница есть, не так ли?
Фишка в том, что Use Case 2 гораздо чаще используется чем 1,3 или 4. На тех порталах где я имел честь работать 95%++. В основном это проверка — а есть ли новые сообщения.
А убрать 95% запросов — правда неплохо?
Конечно, в схеме есть варианты, например можно сразу обновить ящик а не убирать его из кэша, или не делать нотификацию а кэшить на прокси-сервере А с expiry, например 2 минуты. В таком случае обновление в ящике мы бы увидели, самое раннее, через 2 минуты. В общем много вариантов — правильный для Вас зависит от Ваших требований.
— Не вытащить последнюю палочку (для начала и освоения мин-макс).
— Крестики нолики (сначала на три клеточки, потом на больше).
— Реверси.
— Шашки.
Ещё можно порешать шахматные задачи:
— Поставить 8 ферзей на доску, чтобы они не угрожали друг-другу.
— Пройти всю доску одним конём не посетив ни одно поле дважды.
А Вам большой респект за идею.
для начала позвольте заметить что у Вас очень симпатичный сайт. Единственное — при разрешении 1280х800 claim не виден. Это мне возразил один из специалистов по usability, когда я послал ему Ваш сайт как пример для хорошего IT-Сайта.
К делу:
1) Вы уверены что у Вас есть сервисы? Employer- и Recruiter- сервисы я бы такими не назвал, так как все проблемы там решаются при помощи SQL (ну или вызова к repository что в итоге тоже самое). Ваши сервисы при этом используют одни и те же repository, так что ни о каком разделении сервисов говорить не приходится: оба используют DealRepository, ApplicantRepository, UserRepository. Что ещё страшнее: RecruiterService использует EmployerRepository.
В Вашу защиту: то что spring называет сервисом и то что является сервисом с точки зрения СОА — две большие разницы.
2) Ваше разделение на пакеты! Ваше разделение на пакеты!
Разделение на пакеты должно быть по… затрудняюсь найти русское слово, на английском это Domain, а на немецком: Fachlichkeit. С таким разделением как у Вас построить СОА будет невозможно (ну или очень сложно), ведь практически невозможно проследить от каких пакетов зависит какой сервис и какой ещё зависит от тех же пакетов. Раз персистентность сервиса EmployerService его и только его, почему бы ей не находиться в под-пакете employer? Тоже касается и его данных. У вас всё в одной куче.
Если вы посмотрите на Схему 3: начинать надо с сервисов. Если бы Вы начали так, то у вас были бы совсем другие сервисы (попробуйте если не верите).
3) Ваше разделение сущностей emoployer и recruiter и последующая интеграция их при помощи общего объекта user… Это как-то ни то не сё. Либо у Вас таки есть employer и recruiter, либо их нет. Если уж собирать их в одну таблицу, то назовите это User и дайте ему какой-то enum type.
Ещё два маленьких замечания
1) javax.servlet-api надо декларировать provided. Я Ваш проект дальше compile не собирал, он пытался создать какие-то таблицы и т.д. и мне попросту было лень создавать специально базу, но теоретически томкат должен ругаться при старте.
2) Копирование разной конфигурации через мавен профайл: посмотрите на этот проект: www.configureme.org он позволяет хранить все конфиги в одном файле (stackable/cascading), и переключать нужные через параметр в рантайм. Плюс в том что Вы деплоите на все системы один и тот же, тестированный артефакт.
П.С. Я уже упоминал Ваше разделение на пакеты?
О распределенность напишу в следующей части, то есть в ближайшие дни.
Про CAP — думаю нет универсального ответа, но скорее всего А и Р. Хотя Р умеют немногие но как раз об этом тоже в следующей части.
В общем сложно ответить на этот вопрос априори, для разных систем подходят разные модели, тут нужно смотреть по конкретной ситуации.