1С: Кәсіпорын: Java, PostgreSQL, Hazelcast үшін жоғары жүктемелі масштабталатын қызметті қалай және неге жаздык

Бұл мақалада біз қалай және неге дамығанымыз туралы айтатын боламыз Өзара әрекеттесу жүйесі – клиенттік қолданбалар мен 1C: Enterprise серверлері арасында ақпаратты тасымалдайтын механизм – тапсырманы қоюдан бастап архитектурасы мен іске асыру мәліметтері арқылы ойлауға дейін.

Өзара әрекеттесу жүйесі (бұдан әрі – SV) – кепілдендірілген жеткізуі бар таратылған, ақауларға төзімді хабар алмасу жүйесі. SV желілік қызмет (1С ұсынған) ретінде де, жеке серверлік қондырғыларыңызда орналастыруға болатын жаппай өндірілген өнім ретінде де қолжетімді, ауқымдылығы жоғары жоғары жүктемелі қызмет ретінде жасалған.

SV бөлінген жадты пайдаланады Хазелкаст және іздеу жүйесі Elasticearch. Біз сондай-ақ Java және PostgreSQL-ті көлденең масштабтау туралы сөйлесетін боламыз.
1С: Кәсіпорын: Java, PostgreSQL, Hazelcast үшін жоғары жүктемелі масштабталатын қызметті қалай және неге жаздык

Мәселенің тұжырымы

Өзара әрекеттесу жүйесін не үшін жасағанымызды түсіну үшін мен 1С-те бизнес-қосымшаларды әзірлеу қалай жұмыс істейтіні туралы аздап айтып беремін.

Алдымен не істейтінімізді білмейтіндер үшін біз туралы қысқаша айта кетейін :) Біз 1С: Enterprise технологиялық платформасын жасап жатырмыз. Платформа бизнес қолданбаларын әзірлеу құралын, сондай-ақ іскери қолданбаларды платформааралық ортада іске қосуға мүмкіндік беретін орындау уақытын қамтиды.

Клиент-серверді дамыту парадигмасы

1С: Enterprise жүйесінде жасалған бизнес-қосымшалар үш деңгейде жұмыс істейді клиент-сервер архитектурасы «ДҚБЖ – қолданбалы сервер – клиент». Қолданба коды жазылған ендірілген 1С тілі, қолданба серверінде немесе клиентте орындалуы мүмкін. Қолданбалы объектілермен (каталогтар, құжаттар және т.б.) барлық жұмыс, сонымен қатар мәліметтер базасын оқу және жазу тек серверде орындалады. Пішіндердің және командалық интерфейстің функционалдығы серверде де жүзеге асырылады. Клиент пішіндерді қабылдауды, ашуды және көрсетуді, пайдаланушымен «байланысты» (ескертулер, сұрақтар...), жылдам жауап беруді қажет ететін формалардағы шағын есептеулерді (мысалы, бағаны санына көбейту), жергілікті файлдармен жұмыс істеуді, жабдықтармен жұмыс істеу.

Қолданба кодында процедуралар мен функциялардың тақырыптары кодтың қай жерде орындалатынын анық көрсетуі керек - &AtClient / &AtServer директивалары (&AtClient / &AtServer тілдің ағылшын тіліндегі нұсқасында). 1С әзірлеушілері енді директивалар шын мәнінде бар деп мені түзетеді артық, бірақ біз үшін бұл қазір маңызды емес.

Сіз клиенттік кодтан сервер кодын шақыра аласыз, бірақ сервер кодынан клиент кодын шақыра алмайсыз. Бұл біз бірқатар себептер бойынша жасаған негізгі шектеу. Атап айтқанда, сервер коды қай жерде шақырылса да, клиенттен немесе серверден бірдей орындалатындай етіп жазылуы керек. Ал сервер кодын басқа сервер кодынан шақырған жағдайда, мұндай клиент болмайды. Себебі сервер кодын орындау кезінде оны шақырған клиент жабылып, қолданбадан шыға алады және серверде енді қоңырау шалатын ешкім болмайды.

1С: Кәсіпорын: Java, PostgreSQL, Hazelcast үшін жоғары жүктемелі масштабталатын қызметті қалай және неге жаздык
Түймені басуды өңдейтін код: клиенттен сервер процедурасын шақыру жұмыс істейді, серверден клиент процедурасын шақыру жұмыс істемейді

Бұл дегеніміз, егер біз серверден клиенттік қолданбаға қандай да бір хабарлама жібергіміз келсе, мысалы, «ұзақ жұмыс істейтін» есепті құру аяқталды және есепті көруге болады, бізде мұндай әдіс жоқ. Сіз трюктерді қолдануыңыз керек, мысалы, серверді клиенттік кодтан мерзімді түрде сұраңыз. Бірақ бұл тәсіл жүйені қажетсіз қоңыраулармен жүктейді және әдетте өте талғампаз болып көрінбейді.

Сондай-ақ қажеттілік бар, мысалы, телефон қоңырауы келгенде SIP- қоңырау шалған кезде бұл туралы клиенттік қосымшаны хабардар етіңіз, сонда ол қоңырау шалушының нөмірін контрагенттің дерекқорынан тауып, пайдаланушыға қоңырау шалушы контрагент туралы ақпаратты көрсете алады. Немесе, мысалы, қоймаға тапсырыс келгенде, бұл туралы тұтынушының клиенттік өтініміне хабарлаңыз. Жалпы, мұндай механизм пайдалы болатын жағдайлар көп.

Өндірістің өзі

Хабар алмасу механизмін жасаңыз. Жылдам, сенімді, кепілдендірілген жеткізумен, хабарламаларды икемді іздеу мүмкіндігімен. Механизмге сүйене отырып, 1С қолданбаларында жұмыс істейтін мессенджерді (хабарламалар, бейне қоңыраулар) іске қосыңыз.

Жүйені көлденең масштабталатын етіп жобалаңыз. Өсіп келе жатқан жүктеме түйіндер санын көбейту арқылы жабылуы керек.

Реализация

Біз SV серверлік бөлігін тікелей 1С: Enterprise платформасына біріктірмей, оны API 1С қолданбалы шешімдерінің кодынан шақыруға болатын жеке өнім ретінде енгізуді шештік. Бұл бірнеше себептерге байланысты жасалды, олардың ең бастысы, мен әртүрлі 1С қолданбалары арасында (мысалы, Сауда менеджменті мен Бухгалтерлік есеп арасында) хабарлама алмасу мүмкіндігін жасағым келді. Әртүрлі 1С қосымшалары 1С: Кәсіпорын платформасының әртүрлі нұсқаларында жұмыс істей алады, әртүрлі серверлерде орналасады және т.б. Мұндай жағдайларда SV-ді 1С қондырғыларының «жағында» орналасқан бөлек өнім ретінде енгізу оңтайлы шешім болып табылады.

Сонымен, біз SV-ді жеке өнім ретінде жасауды шештік. Жергілікті орнатуға және серверді конфигурациялауға байланысты үстеме шығындарды болдырмау үшін шағын компанияларға бұлтта (wss://1cdialog.com) орнатқан CB серверін пайдалануды ұсынамыз. Ірі клиенттерге өз нысандарында өздерінің CB серверін орнату орынды болуы мүмкін. Біз бұлттық SaaS өнімінде ұқсас тәсілді қолдандық 1cFresh – ол клиенттердің сайттарында орнату үшін жаппай өндірілген өнім ретінде шығарылады және сонымен қатар біздің бұлтымызда орналастырылған https://1cfresh.com/.

Қолданба

Жүктеме мен ақауларға төзімділікті тарату үшін біз бір Java қолданбасын емес, олардың алдында жүктеме теңестірушісі бар бірнеше қолданбаны орналастырамыз. Егер сізге хабарламаны түйіннен түйінге тасымалдау қажет болса, Hazelcast жүйесінде жариялау/жазылу функциясын пайдаланыңыз.

Клиент пен сервер арасындағы байланыс websocket арқылы жүзеге асырылады. Ол нақты уақыттағы жүйелер үшін өте қолайлы.

Бөлінген кэш

Біз Redis, Hazelcast және Ehcache арасында таңдадық. 2015 жыл. Redis жаңа кластерді шығарды (тым жаңа, қорқынышты), көптеген шектеулері бар Sentinel бар. Ehcache кластерге қалай жиналу керектігін білмейді (бұл функция кейінірек пайда болды). Біз оны Hazelcast 3.4 нұсқасымен сынап көруді шештік.
Hazelcast қораптан кластерге жиналады. Жалғыз түйін режимінде ол өте пайдалы емес және тек кэш ретінде пайдаланылуы мүмкін - ол деректерді дискіге қалай тастау керектігін білмейді, егер сіз жалғыз түйінді жоғалтсаңыз, деректерді жоғалтасыз. Біз бірнеше Hazelcast-ты орналастырамыз, олардың арасында маңызды деректердің сақтық көшірмесін жасаймыз. Біз кэштің сақтық көшірмесін жасамаймыз - оған қарсы емеспіз.

Біз үшін Хазелкаст:

  • Пайдаланушы сеанстарын сақтау. Әр жолы сеанс үшін дерекқорға бару көп уақытты алады, сондықтан біз барлық сеанстарды Hazelcast-қа қоямыз.
  • Кэш. Пайдаланушы профилін іздесеңіз, кэшті тексеріңіз. Жаңа хабарлама жазды - оны кэшке салыңыз.
  • Қолданба даналары арасындағы байланыс тақырыптары. Түйін оқиғаны жасайды және оны Hazelcast тақырыбына орналастырады. Осы тақырыпқа жазылған басқа қолданба түйіндері оқиғаны қабылдайды және өңдейді.
  • Кластер құлыптары. Мысалы, біз бірегей кілттің көмегімен пікірталас жасаймыз (1С деректер базасындағы синглтондық талқылау):

conversationKeyChecker.check("БЕНЗОКОЛОНКА");

      doInClusterLock("БЕНЗОКОЛОНКА", () -> {

          conversationKeyChecker.check("БЕНЗОКОЛОНКА");

          createChannel("БЕНЗОКОЛОНКА");
      });

Арнаның жоқтығын тексердік. Біз құлыпты алып, оны қайтадан тексеріп, оны жасадық. Егер сіз құлыпты алғаннан кейін құлыпты тексермесеңіз, сол кезде басқа ағынның да тексеріп, енді сол талқылауды жасауға тырысуы мүмкін - бірақ ол бұрыннан бар. Синхрондалған немесе кәдімгі java Lock арқылы құлыптау мүмкін емес. Деректер базасы арқылы - бұл баяу және дерекқор үшін өкінішті; Hazelcast арқылы - бұл сізге қажет.

ДҚБЖ таңдау

Бізде PostgreSQL-пен жұмыс істеу және осы ДҚБЖ әзірлеушілерімен ынтымақтасуда мол және табысты тәжірибеміз бар.

PostgreSQL кластерімен оңай емес - бар XL, XC, Цитус, бірақ жалпы алғанда бұл қораптан тыс масштабталатын NoSQL емес. Біз NoSQL-ті негізгі сақтау орны ретінде қарастырмадық; біз бұрын жұмыс істемеген Hazelcast-ты алғанымыз жеткілікті.

Егер реляциялық дерекқорды масштабтау қажет болса, бұл дегеніміз бөлшектеу. Өздеріңіз білетіндей, sharding көмегімен біз дерекқорды әрқайсысын бөлек серверге орналастыру үшін бөлек бөліктерге бөлеміз.

Шардингіміздің бірінші нұсқасы қолданбамыздың әрбір кестесін әртүрлі серверлер арқылы әртүрлі пропорцияда тарату мүмкіндігін қабылдады. А серверінде көптеген хабарлар бар - осы кестенің бір бөлігін B серверіне жылжытайық. Бұл шешім мерзімінен бұрын оңтайландыру туралы айқайлады, сондықтан біз өзімізді көп жалға алушылық тәсілмен шектеуді шештік.

Көп жалға алушы туралы, мысалы, веб-сайттан оқуға болады Citus деректері.

SV қосымша және жазылушы ұғымдарына ие. Қолданба ERP немесе бухгалтерлік есеп сияқты іскери қолданбаның пайдаланушыларымен және бизнес деректерімен арнайы орнатылымы болып табылады. Абонент - SV серверінде оның атынан қосымша тіркелген ұйым немесе жеке тұлға. Абонентте бірнеше қолданбалар тіркелген болуы мүмкін және бұл қолданбалар бір-бірімен хабарлама алмаса алады. Абонент біздің жүйеде жалға алушы болды. Бірнеше абоненттің хабарламалары бір физикалық деректер базасында орналасуы мүмкін; егер біз абоненттің көп трафикті генерациялай бастағанын көрсек, біз оны жеке физикалық деректер базасына (немесе тіпті жеке дерекқор серверіне) көшіреміз.

Бізде негізгі дерекқор бар, онда барлық абоненттік дерекқорлардың орналасқан жері туралы ақпаратпен маршруттау кестесі сақталады.

1С: Кәсіпорын: Java, PostgreSQL, Hazelcast үшін жоғары жүктемелі масштабталатын қызметті қалай және неге жаздык

Негізгі дерекқордың кедергі болуын болдырмау үшін біз маршруттау кестесін (және басқа жиі қажет деректерді) кэште сақтаймыз.

Егер абоненттің деректер базасы баяулай бастаса, біз оны ішіндегі бөлімдерге кесеміз. Біз қолданатын басқа жобаларда pg_pathman.

Пайдаланушы хабарламаларын жоғалту жаман болғандықтан, біз дерекқорларымызды көшірмелермен қамтамасыз етеміз. Синхронды және асинхронды көшірмелердің комбинациясы негізгі деректер қоры жоғалған жағдайда өзіңізді сақтандыруға мүмкіндік береді. Негізгі дерекқор мен оның синхронды репликасы бір уақытта істен шыққан жағдайда ғана хабар жоғалады.

Синхронды реплика жоғалса, асинхронды реплика синхронды болады.
Егер негізгі дерекқор жоғалса, синхронды реплика негізгі дерекқорға, ал асинхронды реплика синхронды репликаға айналады.

Іздеу үшін Elasticsearch

Басқа нәрселермен қатар, SV де хабаршы болғандықтан, ол морфологияны ескере отырып, дәл емес сәйкестіктерді қолдана отырып, жылдам, ыңғайлы және икемді іздеуді қажет етеді. Біз дөңгелекті қайта ойлап таппауды және кітапхана негізінде жасалған Elasticsearch тегін іздеу жүйесін қолданбауды шештік Люцен. Сондай-ақ біз Elasticsearch бағдарламасын кластерде (мастер – деректер – деректер) қолданбалы түйіндер сәтсіз болған жағдайда ақауларды жою үшін орналастырамыз.

Github-та біз таптық Орыс морфологиялық плагин Elasticsearch үшін және оны пайдаланыңыз. Elasticsearch индексінде біз сөз түбірін (плагин анықтайтын) және N-граммаларды сақтаймыз. Пайдаланушы іздеу үшін мәтінді енгізген кезде біз терілген мәтінді N-граммдар арасынан іздейміз. Индекске сақталған кезде «мәтіндер» сөзі келесі N-граммаларға бөлінеді:

[сол, тек, текс, мәтін, мәтіндер, ek, ex, ext, мәтіндер, ks, kst, ksty, st, sty, you],

Ал «мәтін» сөзінің түбірі де сақталады. Бұл тәсіл сөздің басында, ортасында және соңында іздеуге мүмкіндік береді.

Жалпы сурет

1С: Кәсіпорын: Java, PostgreSQL, Hazelcast үшін жоғары жүктемелі масштабталатын қызметті қалай және неге жаздык
Мақаланың басынан бастап суретті қайталаңыз, бірақ түсініктемелермен:

  • Интернетте ашылған баланстауыш; бізде nginx бар, ол кез келген болуы мүмкін.
  • Java қолданбасының даналары бір-бірімен Hazelcast арқылы байланысады.
  • Веб-розеткамен жұмыс істеу үшін біз қолданамыз Netty.
  • Java қосымшасы Java 8 тілінде жазылған және жинақтардан тұрады OSGi. Жоспарларға Java 10-ға көшу және модульдерге көшу кіреді.

Әзірлеу және тестілеу

SV әзірлеу және сынау барысында біз қолданатын өнімдердің бірқатар қызықты мүмкіндіктеріне тап болдық.

Жүктеме сынағы және жадтың ағып кетуі

Әрбір SV шығарылымының шығарылымы жүктемені тексеруді қамтиды. Ол сәтті болған кезде:

  • Сынақ бірнеше күн бойы жұмыс істеді және қызмет көрсетуде ақаулар болған жоқ
  • Негізгі операцияларға жауап беру уақыты қолайлы шекті мәннен аспады
  • Алдыңғы нұсқамен салыстырғанда өнімділіктің нашарлауы 10% аспайды

Сынақ деректер қорын деректермен толтырамыз – бұл үшін біз өндірістік серверден ең белсенді абонент туралы ақпаратты аламыз, оның сандарын 5-ке көбейтеміз (хабарламалар, талқылаулар, пайдаланушылар) және оны осылай тексереміз.

Біз үш конфигурацияда өзара әрекеттесу жүйесін жүктемелік сынауды жүргіземіз:

  1. стресс сынағы
  2. Тек қосылымдар
  3. Абонентті тіркеу

Стресс-тест кезінде біз бірнеше жүздеген ағындарды іске қосамыз және олар жүйені тоқтаусыз жүктейді: хабарламалар жазу, талқылаулар құру, хабарламалар тізімін алу. Біз қарапайым пайдаланушылардың әрекеттерін (менің оқылмаған хабарламаларымның тізімін алу, біреуге жазу) және бағдарламалық шешімдерді (басқа конфигурация пакетін жіберу, ескертуді өңдеу) имитациялаймыз.

Мысалы, стресс-тесттің бір бөлігі келесідей көрінеді:

  • Пайдаланушы жүйеге кіреді
    • Оқылмаған талқылауларыңызды сұрайды
    • 50% хабарларды оқиды
    • 50% мәтін жіберу мүмкіндігі
    • Келесі пайдаланушы:
      • Жаңа пікірталас құру мүмкіндігі 20%
      • Кез келген талқылауды кездейсоқ таңдайды
      • Ішке кіреді
      • Хабарламаларды, пайдаланушы профильдерін сұрайды
      • Осы талқылаудан кездейсоқ пайдаланушыларға арналған бес хабарлама жасайды
      • Талқылауды қалдырады
      • 20 рет қайталайды
      • Жүйеден шығады, сценарийдің басына оралады

    • Жүйеге чатбот кіреді (қолданба кодынан хабар алмасуды эмуляциялайды)
      • Деректер алмасу үшін жаңа арна құру мүмкіндігі 50% (арнайы талқылау)
      • 50% бұрыннан бар арналардың кез келгеніне хабарлама жазуы мүмкін

«Тек қосылымдар» сценарийі белгілі бір себептермен пайда болды. Жағдай бар: пайдаланушылар жүйені қосты, бірақ әлі араласпаған. Әрбір пайдаланушы компьютерді таңғы сағат 09:00-де қосып, сервермен байланыс орнатып, үндемейді. Бұл балалар қауіпті, олардың көпшілігі бар – оларда PING/PONG ғана пакеттері бар, бірақ олар сервермен байланысты сақтайды (олар оны жалғастыра алмайды - егер жаңа хабарлама болса ше). Сынақ мұндай пайдаланушылардың көп саны жарты сағат ішінде жүйеге кіруге тырысатын жағдайды шығарады. Бұл стресс-тестке ұқсас, бірақ оның назары дәл осы бірінші енгізуге бағытталған - сондықтан ешқандай сәтсіздіктер болмайды (адам жүйені пайдаланбайды және ол қазірдің өзінде құлап кетеді - одан да жаман нәрсе туралы ойлау қиын).

Абонентті тіркеу сценарийі бірінші іске қосудан басталады. Біз стресс-тест жүргіздік және хат алмасу кезінде жүйенің бәсеңдетпегеніне сенімді болдық. Бірақ пайдаланушылар келіп, күту уақытының аяқталуына байланысты тіркелу сәтсіз аяқталды. Тіркеу кезінде біз қолдандық / dev / кездейсоқ, ол жүйенің энтропиясына байланысты. Серверде жеткілікті энтропия жинақтауға уақыт болмады және жаңа SecureRandom сұралғанда, ол ондаған секундқа қатып қалды. Бұл жағдайдан шығудың көптеген жолдары бар, мысалы: қауіпсіз емес /dev/urandom нұсқасына ауысыңыз, энтропияны тудыратын арнайы тақтаны орнатыңыз, алдын ала кездейсоқ сандарды жасаңыз және оларды пулда сақтаңыз. Біз пулға қатысты мәселені уақытша жаптық, бірақ содан бері жаңа жазылушыларды тіркеу үшін бөлек сынақ жүргізіп жатырмыз.

Біз жүктеме генераторы ретінде қолданамыз ДжМетер. Ол websocket-пен қалай жұмыс істеу керектігін білмейді; оған плагин қажет. «jmeter websocket» сұрауы бойынша іздеу нәтижелерінің біріншісі: BlazeMeter мақалалары, ұсынатын Maciej Zaleski плагині.

Міне, біз бастауды шештік.

Маңызды тестілеуді бастағаннан кейін біз JMeter жадты ағыза бастағанын білдік.

Плагин - бұл бөлек үлкен оқиға; 176 жұлдызы бар оның github-та 132 шанышқысы бар. Автордың өзі оны 2015 жылдан бері орындамады (біз оны 2015 жылы қабылдадық, содан кейін ол күдік тудырмады), жадтың ағып кетуіне қатысты бірнеше github мәселелері, 7 жабылмаған тарту сұраулары.
Егер сіз осы плагин арқылы жүктеме сынамасын орындауды шешсеңіз, келесі талқылауларға назар аударыңыз:

  1. Көп ағынды ортада тұрақты LinkedList пайдаланылды және нәтиже болды NPE орындау уақытында. Мұны ConcurrentLinkedDeque жүйесіне ауысу немесе синхрондалған блоктар арқылы шешуге болады. Біз өзіміз үшін бірінші нұсқаны таңдадық (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/43).
  2. Жадтың ағуы; ажыратқанда қосылым ақпараты жойылмайды (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/44).
  3. Ағынды режимде (веб-розетка үлгінің соңында жабылмаған, бірақ кейінірек жоспарда пайдаланылған кезде) Жауап үлгілері жұмыс істемейді (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/19).

Бұл github-тағылардың бірі. Біз не істедік:

  1. Біз алдық шанышқы Элиран Коган (@elyrank) – ол 1 және 3 мәселелерді шешеді
  2. Шешілген мәселе 2
  3. 9.2.14-тен 9.3.12-ге дейін жаңартылды
  4. ThreadLocal ішінде SimpleDateFormat оралған; SimpleDateFormat ағынмен қауіпсіз емес, бұл орындалу уақытында NPE-ге әкелді
  5. Жадтың басқа ағып кетуі түзетілді (ажыратқан кезде байланыс қате жабылды)

Сонда да ағып жатыр!

Жад бір күнде емес, екі күнде таусыла бастады. Уақыт мүлде қалмады, сондықтан біз азырақ ағындарды іске қосуды шештік, бірақ төрт агентте. Бұл кем дегенде бір апта жеткілікті болуы керек еді.

Екі күн өтті...

Қазір Хазелкасттың жады таусылды. Журналдар көрсеткендей, бірнеше күндік сынақтан кейін Хазелкаст жадтың жетіспеушілігіне шағымдана бастады, ал біраз уақыттан кейін кластер құлап, түйіндер бірінен соң бірі өле берді. Біз JVisualVM-ді hazelcast-қа қостық және «көтерілген араны» көрдік - ол үнемі GC деп атады, бірақ жадты тазарта алмады.

1С: Кәсіпорын: Java, PostgreSQL, Hazelcast үшін жоғары жүктемелі масштабталатын қызметті қалай және неге жаздык

Hazelcast 3.4 нұсқасында картаны/мультикартаны (map.destroy()) жою кезінде жад толығымен босатылмайтыны анықталды:

github.com/hazelcast/hazelcast/issues/6317
github.com/hazelcast/hazelcast/issues/4888

Қате қазір 3.5 нұсқасында түзетілді, бірақ ол кезде проблема болды. Біз динамикалық атаулары бар жаңа мультикарталарды жасап, оларды логикаға сәйкес жойдық. Код келесідей көрінді:

public void join(Authentication auth, String sub) {
    MultiMap<UUID, Authentication> sessions = instance.getMultiMap(sub);
    sessions.put(auth.getUserId(), auth);
}

public void leave(Authentication auth, String sub) {
    MultiMap<UUID, Authentication> sessions = instance.getMultiMap(sub);
    sessions.remove(auth.getUserId(), auth);

    if (sessions.size() == 0) {
        sessions.destroy();
    }
}

Қоңырау:

service.join(auth1, "НОВЫЕ_СООБЩЕНИЯ_В_ОБСУЖДЕНИИ_UUID1");
service.join(auth2, "НОВЫЕ_СООБЩЕНИЯ_В_ОБСУЖДЕНИИ_UUID1");

multiMap әрбір жазылым үшін жасалған және қажет болмаған кезде жойылған. Біз Картаны бастаймыз деп шештік , кілт жазылымның аты болады, ал мәндер сеанс идентификаторлары болады (қажет болса, одан кейін пайдаланушы идентификаторларын алуға болады).

public void join(Authentication auth, String sub) {
    addValueToMap(sub, auth.getSessionId());
}

public void leave(Authentication auth, String sub) { 
    removeValueFromMap(sub, auth.getSessionId());
}

Диаграммалар жақсарды.

1С: Кәсіпорын: Java, PostgreSQL, Hazelcast үшін жоғары жүктемелі масштабталатын қызметті қалай және неге жаздык

Жүктеме сынағы туралы тағы не білдік?

  1. JSR223 керемет түрде жазылуы керек және компиляция кэшін қамтуы керек - бұл әлдеқайда жылдам. байланыс.
  2. Jmeter-Plugins графиктерін стандарттыға қарағанда түсіну оңай. байланыс.

Hazelcast-пен тәжірибеміз туралы

Hazelcast біз үшін жаңа өнім болды, біз онымен 3.4.1 нұсқасынан жұмыс істей бастадық, қазір біздің өндірістік серверіміз 3.9.2 нұсқасымен жұмыс істейді (жазу кезінде, Hazelcast бағдарламасының соңғы нұсқасы 3.10).

ID генерациясы

Біз бүтін идентификаторлардан бастадық. Жаңа нысан үшін бізге тағы бір Лонг керек деп елестетіп көрейік. Дерекқордағы реттілік қолайлы емес, кестелер бөлшектеуге қатысады - DB1-де ID=1 хабар және DB1-де ID=2 хабарлама бар екені белгілі болды, бұл идентификаторды Elasticsearch-ке де, Hazelcast-қа да қоюға болмайды. , бірақ ең жаманы, егер сіз екі дерекқордағы деректерді бір дерекқорға біріктіргіңіз келсе (мысалы, осы жазылушылар үшін бір дерекқор жеткілікті екенін шешу). Hazelcast-қа бірнеше AtomicLong қосуға және есептегішті сол жерде сақтауға болады, содан кейін жаңа идентификаторды алу өнімділігі incrementAndGet плюс Hazelcast-қа сұрау уақыты болады. Бірақ Hazelcastта оңтайлы нәрсе бар - FlakeIdGenerator. Әрбір клиентке хабарласқанда оларға ID диапазоны беріледі, мысалы, біріншісі – 1-ден 10 000-ға дейін, екіншісі – 10 001-ден 20 000-ға дейін және т.б. Енді клиент өзіне берілген ауқым аяқталғанша жаңа идентификаторларды өзі шығара алады. Ол жылдам жұмыс істейді, бірақ қолданбаны (және Hazelcast клиентін) қайта іске қосқанда, жаңа реттілік басталады - демек, өткізіп жіберулер және т.б. Бұған қоса, әзірлеушілер идентификаторлардың неліктен бүтін сан екенін түсінбейді, бірақ соншалықты сәйкес емес. Біз бәрін өлшеп, UUID-ге көштік.

Айтпақшы, Twitter сияқты болғысы келетіндер үшін осындай Snowcast кітапханасы бар - бұл Hazelcast-тың жоғарғы жағындағы Snowflake іске асыру. Сіз оны мына жерден көре аласыз:

github.com/noctarius/snowcast
github.com/twitter/snowflake

Бірақ біз оған енді жете алмадық.

TransactionalMap.replace

Тағы бір тосынсый: TransactionalMap.replace жұмыс істемейді. Міне, сынақ:

@Test
public void replaceInMap_putsAndGetsInsideTransaction() {

    hazelcastInstance.executeTransaction(context -> {
        HazelcastTransactionContextHolder.setContext(context);
        try {
            context.getMap("map").put("key", "oldValue");
            context.getMap("map").replace("key", "oldValue", "newValue");
            
            String value = (String) context.getMap("map").get("key");
            assertEquals("newValue", value);

            return null;
        } finally {
            HazelcastTransactionContextHolder.clearContext();
        }        
    });
}

Expected : newValue
Actual : oldValue

Мен getForUpdate арқылы өзімнің ауыстыруымды жазуға тура келді:

protected <K,V> boolean replaceInMap(String mapName, K key, V oldValue, V newValue) {
    TransactionalTaskContext context = HazelcastTransactionContextHolder.getContext();
    if (context != null) {
        log.trace("[CACHE] Replacing value in a transactional map");
        TransactionalMap<K, V> map = context.getMap(mapName);
        V value = map.getForUpdate(key);
        if (oldValue.equals(value)) {
            map.put(key, newValue);
            return true;
        }

        return false;
    }
    log.trace("[CACHE] Replacing value in a not transactional map");
    IMap<K, V> map = hazelcastInstance.getMap(mapName);
    return map.replace(key, oldValue, newValue);
}

Тек тұрақты деректер құрылымдарын ғана емес, сонымен қатар олардың транзакциялық нұсқаларын да сынаңыз. IMap жұмыс істейді, бірақ TransactionalMap енді жоқ.

Тоқтаусыз жаңа JAR салыңыз

Біріншіден, біз Hazelcast-те сыныптарымыздың нысандарын жазуды шештік. Мысалы, бізде Application класы бар, біз оны сақтап, оқығымыз келеді. Сақтау:

IMap<UUID, Application> map = hazelcastInstance.getMap("application");
map.set(id, application);

Біз оқимыз:

IMap<UUID, Application> map = hazelcastInstance.getMap("application");
return map.get(id);

Барлығы жұмыс істейді. Содан кейін біз іздеу үшін Hazelcast-те индекс құруды шештік:

map.addIndex("subscriberId", false);

Жаңа нысанды жазу кезінде олар ClassNotFoundException ала бастады. Хазелкаст индекске қосуға тырысты, бірақ біздің сынып туралы ештеңе білмеді және оған осы сыныппен JAR жеткізілгенін қалады. Біз дәл осылай жасадық, бәрі жұмыс істеді, бірақ жаңа мәселе пайда болды: кластерді толығымен тоқтатпай JAR қалай жаңартуға болады? Hazelcast түйін бойынша жаңарту кезінде жаңа JAR алмайды. Осы кезде біз индекстік іздеусіз өмір сүре аламыз деп шештік. Ақыр соңында, егер сіз Hazelcast-ты кілттер дүкені ретінде пайдалансаңыз, бәрі жұмыс істей ме? Онша емес. Мұнда тағы да IMap және TransactionalMap әрекеті басқаша. IMap маңызды емес жерде TransactionalMap қате жібереді.

IMap. Біз 5000 нысан жазамыз, оларды оқимыз. Барлығы күтіледі.

@Test
void get5000() {
    IMap<UUID, Application> map = hazelcastInstance.getMap("application");
    UUID subscriberId = UUID.randomUUID();

    for (int i = 0; i < 5000; i++) {
        UUID id = UUID.randomUUID();
        String title = RandomStringUtils.random(5);
        Application application = new Application(id, title, subscriberId);
        
        map.set(id, application);
        Application retrieved = map.get(id);
        assertEquals(id, retrieved.getId());
    }
}

Бірақ ол транзакцияда жұмыс істемейді, біз ClassNotFoundException аламыз:

@Test
void get_transaction() {
    IMap<UUID, Application> map = hazelcastInstance.getMap("application_t");
    UUID subscriberId = UUID.randomUUID();
    UUID id = UUID.randomUUID();

    Application application = new Application(id, "qwer", subscriberId);
    map.set(id, application);
    
    Application retrievedOutside = map.get(id);
    assertEquals(id, retrievedOutside.getId());

    hazelcastInstance.executeTransaction(context -> {
        HazelcastTransactionContextHolder.setContext(context);
        try {
            TransactionalMap<UUID, Application> transactionalMap = context.getMap("application_t");
            Application retrievedInside = transactionalMap.get(id);

            assertEquals(id, retrievedInside.getId());
            return null;
        } finally {
            HazelcastTransactionContextHolder.clearContext();
        }
    });
}

3.8-де User Class Deployment механизмі пайда болды. Бір негізгі түйінді тағайындауға және ондағы JAR файлын жаңартуға болады.

Енді біз өз көзқарасымызды толығымен өзгерттік: біз оны өзіміз JSON-ға сериялаймыз және оны Hazelcast-та сақтаймыз. Hazelcast біздің сабақтарымыздың құрылымын білудің қажеті жоқ және біз үзіліссіз жаңарта аламыз. Домен нысандарының нұсқалары қолданба арқылы басқарылады. Қолданбаның әртүрлі нұсқалары бір уақытта жұмыс істей алады және жаңа қолданба жаңа өрістері бар нысандарды жазғанда, бірақ ескісі бұл өрістер туралы әлі білмейтін жағдай болуы мүмкін. Сонымен қатар, жаңа қолданба жаңа өрістері жоқ ескі қолданба жазған нысандарды оқиды. Біз мұндай жағдайларды қолданба ішінде өңдейміз, бірақ қарапайымдылық үшін өрістерді өзгертпейміз немесе жоймаймыз, тек жаңа өрістерді қосу арқылы сыныптарды кеңейтеміз.

Жоғары өнімділікті қалай қамтамасыз етеміз

Хазелкастқа төрт сапар - жақсы, екі дерекқорға - нашар

Деректер үшін кэшке өту әрқашан дерекқорға барудан жақсырақ, бірақ пайдаланылмаған жазбаларды да сақтағыңыз келмейді. Нені кэштеу керектігі туралы шешімді дамудың соңғы кезеңіне қалдырамыз. Жаңа функционалдылық кодталғанда, біз PostgreSQL жүйесіндегі барлық сұрауларды тіркеуді қосамыз (log_min_duration_statement 0-ге дейін) және 20 минут бойы жүктеуді тексеруді іске қосамыз.Жиналған журналдарды пайдалана отырып, pgFouine және pgBadger сияқты утилиталар аналитикалық есептерді құра алады. Есептерде біз ең алдымен баяу және жиі сұрауларды іздейміз. Баяу сұраулар үшін орындау жоспарын (ТҮСІНДІРУ) жасаймыз және мұндай сұрауды жылдамдатуға болатынын бағалаймыз. Бірдей кіріс деректеріне арналған жиі сұраулар кэшке жақсы сәйкес келеді. Біз сұрауларды «жалпақ», әр сұрауға бір кестеден сақтауға тырысамыз.

Операция

SV онлайн қызметі ретінде 2017 жылдың көктемінде пайдалануға берілді, ал жеке өнім ретінде SV 2017 жылдың қарашасында шығарылды (ол кезде бета нұсқасы күйінде).

Жұмыс істеген бір жылдан астам уақыт ішінде КБ онлайн қызметінің жұмысында күрделі проблемалар болған жоқ. арқылы онлайн сервисті бақылаймыз Zabbix, мына жерден жинап, орналастырыңыз Бамбук.

SV серверді тарату жергілікті пакеттер түрінде жеткізіледі: RPM, DEB, MSI. Сонымен қатар Windows үшін біз серверді, Hazelcast және Elasticsearch-ті бір құрылғыға орнататын жалғыз EXE түрінде бір орнатушыны ұсынамыз. Біз бастапқыда орнатудың бұл нұсқасын «демо» нұсқасы деп атадық, бірақ енді бұл орналастырудың ең танымал нұсқасы екені белгілі болды.

Ақпарат көзі: www.habr.com

пікір қалдыру