Бұл мақалада біз қалай және не үшін дамығанымызды айтамыз – клиенттік қолданбалар мен 1C: Enterprise серверлері арасында ақпаратты тасымалдау механизмі – тапсырманы орнатудан бастап архитектурасы мен іске асыру мәліметтері арқылы ойлауға дейін.
Өзара әрекеттесу жүйесі (бұдан әрі – ISS) – кепілдендірілген жеткізуі бар таратылған, ақауларға төзімді хабар алмасу жүйесі. ISS желілік қызмет (1С ұсынған) ретінде де, жергілікті жерде қолдануға болатын коммерциялық өнім ретінде де қол жетімді, жоғары жүктемелі, жоғары масштабталатын қызмет ретінде жасалған.
SV бөлінген жадты пайдаланады және іздеу жүйесі Біз сондай-ақ Java және PostgreSQL-ті көлденең масштабтау туралы сөйлесетін боламыз.
Мәселенің тұжырымы
Өзара әрекеттесу жүйесін не үшін жасағанымызды түсінуге көмектесу үшін мен 1С жүйесінде бизнес-қосымшаларды әзірлеу қалай жұмыс істейтіні туралы аздап айтып беремін.
Біріншіден, не істейтінімізді білмейтіндер үшін біз туралы аздап. Біз 1С: Enterprise технологиялық платформасын әзірлейміз. Платформа бизнес қолданбаларын әзірлеу құралын және іскери қолданбаларды платформааралық ортада іске қосуға мүмкіндік беретін орындау уақытын қамтиды.
Клиент-серверді дамыту парадигмасы
1С: Enterprise жүйесінде жасалған бизнес-қосымшалар үш деңгейлі жүйеде жұмыс істейді архитектурасы «ДҚБЖ – қолданбалы сервер – клиент». Қолданба коды жазылған Қолданбаны қолданба серверінде немесе клиентте іске қосуға болады. Қолданбалы объектілермен (каталогтар, құжаттар және т.б.) барлық жұмыс, сонымен қатар мәліметтер базасын оқу және жазу тек серверде орындалады. Пішіндер мен пәрмен интерфейсінің функционалдығы серверде де жүзеге асырылады. Клиент пішіндерді қабылдауды, ашуды және көрсетуді, пайдаланушымен байланысуды (ескертулер, сұрақтар және т.б.), жылдам жауап беруді қажет ететін формаларда шағын есептеулерді орындауды (мысалы, бағаны санға көбейту), жергілікті файлдармен жұмыс істеуді және жабдықпен жұмыс істеуді орындайды.
Қолданба кодында процедура және функция тақырыптары &AtClient / &AtServer директивалары (&AtClient / &AtServer тілдің ағылшын тіліндегі нұсқасында) арқылы кодтың қай жерде орындалатынын нақты көрсетуі керек. 1С әзірлеушілері мені түзетіп, іс жүзінде ешқандай директивалар жоқ деп айтады , бірақ біз үшін бұл қазір маңызды емес.
Клиент коды сервер кодын шақыра алады, бірақ сервер коды клиент кодын шақыра алмайды. Бұл біз бірқатар себептерге байланысты енгізген негізгі шектеу. Атап айтқанда, сервер коды клиенттен немесе серверден шақырылғанына қарамастан бірдей орындалуы үшін жазылуы керек. Ал егер сервер коды басқа сервер кодынан шақырылса, клиенттің өзі жоқ. Тағы бір себебі, сервер коды орындалып жатқанда, оны шақыратын клиент жабылып, қолданбадан шығуы мүмкін және серверде қоңырау шалатын ештеңе қалмайды.
Түймені басуды өңдейтін код: клиенттен сервер процедурасын шақыру жұмыс істейді, серверден клиент процедурасын шақыру жұмыс істемейді
Бұл дегеніміз, егер біз серверден клиенттік бағдарламаға хабарлама жібергіміз келсе, мысалы, ұзақ жұмыс істейтін есептің жасалуы аяқталғаны және оны көруге болатыны туралы - бізде мұндай әдіс жоқ. Біз трюктерге жүгінуіміз керек, мысалы, клиенттік кодтан серверді мерзімді түрде сұрау. Бірақ бұл тәсіл жүйені қажетсіз қоңыраулармен шамадан тыс жүктейді және жалпы алғанда, өте талғампаз болып көрінеді.
Сондай-ақ, мысалы, телефон қоңырауын қабылдау кезінде қажеттілік бар - Клиент қолданбасына қоңырау туралы хабарлаңыз, ол қоңырау шалушының нөмірін оларды мердігер дерекқорынан табу және пайдаланушыға қоңырау шалушы туралы ақпаратты көрсету үшін пайдалана алады. Немесе, мысалы, қоймаға тапсырыс келген кезде тұтынушының клиенттік өтінімін хабардар етіңіз. Мұндай механизм пайдалы болатын көптеген пайдалану жағдайлары бар.
Өндірістің өзі
Жылдам, сенімді, кепілдендірілген жеткізілім және икемді хабар іздеу мүмкіндіктері бар хабар алмасу механизмін жасаңыз. Осы механизм негізінде 1С қолданбаларында жұмыс істейтін мессенджерді (хабарламалар, бейне қоңыраулар) іске асырыңыз.
Жүйені көлденең масштабталатын етіп жобалаңыз. Жүктеменің жоғарылауы түйіндердің санын көбейту арқылы орналастырылуы керек.
Реализация
Біз 1C: Enterprise платформасының серверлік жағын тікелей біріктірмей, оны API 1С қолданбасының кодынан шақыруға болатын жеке өнім ретінде енгізуді шештік. Бұл бірқатар себептерге байланысты жасалды, ең бастысы әртүрлі 1С қолданбалары арасында (мысалы, Сауда менеджменті мен Бухгалтерлік есеп арасында) хабарлама алмасуды қосу. Әртүрлі 1С қолданбалары 1C:Enterprise платформасының әртүрлі нұсқаларында жұмыс істей алады, әртүрлі серверлерде тұруы мүмкін және т.б. Осы жағдайларда 1С: Кәсіпорынның серверлік жағын 1С қондырғыларына «бүйір» орналасқан бөлек өнім ретінде енгізу оңтайлы шешім болды.
Осылайша, біз SV-ді дербес өнім ретінде дамытуды шештік. Шағын компанияларға жергілікті орнату және конфигурациямен байланысты үстеме шығындарды болдырмау үшін бұлтта (wss://1cdialog.com) орнатқан SV серверін пайдалануды ұсынамыз. Үлкенірек клиенттер үшін жергілікті SV серверін орнату тиімдірек болуы мүмкін. Біз бұлттық SaaS өнімінде ұқсас тәсілді қолдандық. – ол тұтынушылардың сайттарында орнату үшін жаппай өндірілген өнім ретінде шығарылады, сонымен қатар біздің бұлтта орналастырылған .
Қолданба
Жүктемені тарату және ақауларға төзімділікті қамтамасыз ету үшін біз бір ғана емес, бірнеше Java қолданбаларын орналастырамыз және олардың алдына жүктеме балансын орналастырамыз. Егер бізге хабарламаларды түйіннен түйінге жіберу қажет болса, біз Hazelcast компаниясының жариялау/жазылу функциясын қолданамыз.
Клиент пен сервер байланысы websocket арқылы жүзеге асырылады. Бұл нақты уақыттағы жүйелер үшін өте қолайлы.
Бөлінген кэш
Біз Redis, Hazelcast және Ehcache арасында таңдадық. Бұл 2015 жыл болды. Редис жаңа кластерді шығарды (бұл тым жаңа, қорқынышты болды) және бізде көптеген шектеулері бар Sentinel болды. Ehcache кластерді құра алмады (бұл мүмкіндік кейінірек қосылды). Біз Hazelcast 3.4 нұсқасын қолданып көруді шештік.
Hazelcast кластер ретінде алдын ала құрастырылған. Бір түйінді режимде ол өте пайдалы емес және оны тек кэш ретінде пайдалануға болады — ол деректерді дискіге өшіре алмайды, сондықтан бір түйінді жоғалтсаңыз, деректеріңізді жоғалтасыз. Біз бірнеше Hazelcast қолданамыз және олардың арасындағы маңызды деректердің сақтық көшірмесін жасаймыз. Біз кэштің сақтық көшірмесін жасамаймыз — оны жоғалтуға қарсы емеспіз.
Біз үшін Хазелкаст:
- Пайдаланушы сеансын сақтау. Дерекқордан сеансты әр уақытта шығарып алу көп уақытты қажет етеді, сондықтан біз барлық сеанстарды Hazelcast ішінде сақтаймыз.
- Кэш. Пайдаланушы профилін іздесеңіз, оны кэште тексеріңіз. Егер сіз жаңа хабарлама жазған болсаңыз, оны кэште сақтаңыз.
- Қолданба даналары арасындағы байланыс тақырыптары. Түйін оқиғаны жасайды және оны Hazelcast тақырыбына орналастырады. Осы тақырыпқа жазылған басқа қолданба түйіндері оқиғаны қабылдайды және өңдейді.
- Кластер құлыптары. Мысалы, біз бірегей кілттің көмегімен талқылауды жасаймыз (1С дерекқорындағы синглтондық талқылау):
conversationKeyChecker.check("БЕНЗОКОЛОНКА");
doInClusterLock("БЕНЗОКОЛОНКА", () -> {
conversationKeyChecker.check("БЕНЗОКОЛОНКА");
createChannel("БЕНЗОКОЛОНКА");
});Біз арнаның жоқтығын тексердік. Біз құлыпты алдық, оны қайтадан тексеріп, жасадық. Құлыпты алғаннан кейін тексермесек, басқа ағын оны тексеріп қойған болуы мүмкін және енді ұқсас сөйлесуді жасауға тырысады және ол бұрыннан бар. Синхрондалған немесе кәдімгі Java Lock арқылы құлыптау мүмкін емес. Дерекқорды пайдалану баяу және бұл уақытты ысырап етеді, бірақ Hazelcast дұрыс.
ДҚБЖ таңдау
Бізде PostgreSQL-пен жұмыс істеу және осы ДҚБЖ әзірлеушілерімен ынтымақтасуда мол және табысты тәжірибеміз бар.
PostgreSQL-де күрделі кластер бар – бар , , , бірақ бұл қораптан тыс масштабталатын NoSQL шешімдері емес. Біз NoSQL-ті негізгі сақтау шешімі ретінде қарастырмадық; Біз бұрын жұмыс істемеген Hazelcast жеткілікті болды.
Егер реляциялық дерекқорды масштабтау қажет болса, онда Өздеріңіз білетіндей, sharding көмегімен біз дерекқорды әрқайсысын бөлек серверге орналастыруға болатындай бөлек бөліктерге бөлеміз.
Біздің бірінші бөлшектеу тәсілі әр түрлі пропорцияда әр түрлі серверлер бойынша қолданба кестелерінің әрқайсысын таратуды қамтыды. А серверіндегі көптеген хабарлар—өтінеміз, осы кестенің бір бөлігін B серверіне жылжытайық. Бұл шешім мерзімінен бұрын оңтайландыруды шақырды, сондықтан біз көп пайдаланушылық тәсілді ұстануға шешім қабылдадық.
Көп жалға алушы туралы, мысалы, веб-сайттан оқуға болады .
SV қосымша және абонент ұғымдарын пайдаланады. Қолданба ERP немесе бухгалтерлік есеп сияқты іскери қолданбаның пайдаланушыларымен және бизнес деректерімен арнайы орнатылымы болып табылады. Абонент - SV серверінде оның атынан қосымша тіркелген ұйым немесе жеке тұлға. Абонентте бірнеше қолданбалар тіркелген болуы мүмкін және бұл қолданбалар бір-бірімен хабарлама алмаса алады. Абонент біздің жүйеде жалға алушыға айналады. Бірнеше жазылушылардан келген хабарламалар бір физикалық дерекқорда орналаса алады; егер белгілі бір абоненттің айтарлықтай трафикті генерациялайтынын байқасақ, біз оны жеке физикалық дерекқорға (немесе тіпті бөлек дерекқор серверіне) көшіреміз.
Бізде негізгі дерекқор бар, онда барлық абоненттік дерекқорлардың орналасқан жері туралы ақпаратпен маршруттау кестесі сақталады.
Негізгі дерекқордың тығырықтан шығуына жол бермеу үшін біз маршруттау кестесін (және басқа жиі қол жетімді деректерді) кэште сақтаймыз.
Егер жазылушының дерекқоры баяулай бастаса, біз оны іштей бөлеміз. Басқа жобаларда біз үлкен кестелер үшін [бөлу] пайдаланамыз. .
Пайдаланушы хабарламаларын жоғалту жаман идея болғандықтан, біз дерекқорларымыздың сақтық көшірмесін көшірмелермен жасаймыз. Синхронды және асинхронды көшірмелердің тіркесімі бастапқы дерекқорды жоғалтудан сақтандыруды қамтамасыз етеді. Бастапқы дерекқор мен оның синхронды репликасы бір уақытта істен шыққан жағдайда ғана хабар жоғалады.
Синхронды реплика жоғалса, асинхронды реплика синхронды болады.
Бастапқы дерекқор жоғалса, синхронды реплика негізгі дерекқорға, ал асинхронды реплика синхронды репликаға айналады.
Іздеу үшін Elasticsearch
SV сондай-ақ хабаршы болғандықтан, басқа нәрселермен қатар, анық емес сәйкестіктер үшін морфологияны ескере отырып, жылдам, ыңғайлы және икемді іздеуді қажет етеді. Біз дөңгелекті қайта ойлап таппауды шештік және кітапханада жасалған ашық бастапқы Elasticsearch іздеу жүйесін қолдандық. Қолданба түйіні сәтсіз болған жағдайда проблемаларды болдырмау үшін біз Elasticsearch-ті кластерде (негізгі – деректер – деректер) орналастырамыз.
Біз оны github сайтынан таптық Elasticsearch үшін және оны пайдаланыңыз. Elasticsearch индексінде біз сөз түбірлерін (плагин арқылы анықталған) және n-граммдарды сақтаймыз. Пайдаланушы іздеу мәтінін терген кезде терілген мәтінді n-граммдардың арасынан іздейміз. Индексте сақталған кезде «мәтіндер» сөзі келесі n-граммдарға бөлінеді:
[сол, тек, текс, мәтін, мәтіндер, ek, ex, ext, мәтіндер, ks, kst, ksty, st, sty, you],«Мәтін» сөзінің түбірі де сақталады. Бұл тәсіл сөздің басы, ортасы және аяғы бойынша іздеуге мүмкіндік береді.
Жалпы сурет
Мақаланың басынан бастап суретті қайталаңыз, бірақ түсініктемелермен:
- Интернетке әсер ететін жүктемені теңестіруші; біздің жағдайда бұл nginx, бірақ ол кез келген нәрсе болуы мүмкін.
- Java қолданбасының даналары бір-бірімен Hazelcast арқылы байланысады.
- Веб-розеткамен жұмыс істеу үшін біз қолданамыз .
- Java қосымшасы Java 8 тілінде жазылған және жинақтардан тұрады. Жоспарларға Java 10-ға көшу және модульдерге ауысу кіреді.
Әзірлеу және тестілеу
SV әзірлеу және сынақтан өткізу барысында біз қолданатын өнімдердің бірқатар қызықты мүмкіндіктеріне тап болдық.
Жүктеме сынағы және жадтың ағып кетуі
SV әрбір шығарылымы жүктеме сынағына жатады. Ол келесі жағдайларда сәтті аяқталады:
- Сынақ бірнеше күн бойы жүргізілді және қызмет көрсетуде үзіліс болған жоқ.
- Негізгі операцияларға жауап беру уақыты қолайлы шекті мәннен аспады.
- Алдыңғы нұсқамен салыстырғанда өнімділіктің төмендеуі 10% аспайды
Біз сынақ деректер қорын деректермен толтырамыз – бұл үшін біз өндірістік серверден ең белсенді абонент туралы ақпаратты аламыз, оның сандарын 5-ке көбейтеміз (хабарламалар, талқылаулар, пайдаланушылар) және оны осылай сынаймыз.
Біз үш конфигурацияда өзара әрекеттесу жүйесін жүктемелік тестілеуді жүргіземіз:
- стресс сынағы
- Тек қосылымдар
- Абонентті тіркеу
Стресс-тестілеу кезінде біз жүйені үздіксіз жүктейтін бірнеше жүздеген ағындарды іске қосамыз: хабарламалар жазу, талқылаулар құру және хабарламалар тізімін алу. Біз қарапайым пайдаланушылардың әрекеттерін (оқылмаған хабарламаларымның тізімін алу, біреуге хабарлау) және бағдарламалық шешімдерді (басқа конфигурациямен пакетті жіберу, хабарландыруды өңдеу) имитациялаймыз.
Мысалы, стресс-тесттің бір бөлігі келесідей көрінеді:
- Пайдаланушы жүйеге кіреді
- Оқылмаған ағындарыңызды сұрайды
- Хабарламаларды оқу мүмкіндігі 50%
- Оның хабарламалар жазу мүмкіндігі 50%
- Келесі пайдаланушы:
- Жаңа ағын жасау мүмкіндігі 20%
- Кез келген талқылауды кездейсоқ таңдайды
- Ішке кіреді
- Хабарламаларды, пайдаланушы профильдерін сұрайды
- Осы ағыннан кездейсоқ пайдаланушыларға бағытталған бес хабарлама жасайды.
- Талқылаудан шығады
- 20 рет қайталаңыз
- Жүйеден шығып, сценарийдің басына оралады
- Жүйеге чатбот кіреді (қолданба кодынан хабар алмасуды эмуляциялайды)
- Деректер алмасу үшін жаңа арна құру мүмкіндігі 50% (арнайы талқылау)
- Оның бар арналардың кез келгеніне хабарлама жазу мүмкіндігі 50%.
«Тек қосылымдар» сценарийі кездейсоқ пайда болған жоқ. Пайдаланушылар жүйеге қосылған, бірақ оны әлі түсінбеген жағдай бар. Әрбір пайдаланушы өз компьютерін таңғы сағат 09:00-де қосып, сервермен байланыс орнатады, содан кейін үнсіз қалады. Бұл жігіттер қауіпті және олардың көпшілігі бар — олардың жалғыз пакеттері PING/PONG, бірақ олар сервермен байланысын сақтайды (олар көмектесе алмайды — егер жаңа хабар болса ше). Сынақ осындай пайдаланушылардың көп саны жарты сағат ішінде жүйеге кіруге әрекеттенетін жағдайды имитациялайды. Бұл стресс-тестке ұқсайды, бірақ ол сәтсіздіктерді болдырмау үшін (пайдаланушы жүйені пайдаланбайды және ол қазірдің өзінде бұзылып жатыр — одан да жаман нәрсені елестету қиын) бірінші логинге ерекше назар аударады.
Абонентті тіркеу процесі бірінші іске қосудан басталады. Біз стресс-тест жүргіздік және хабар алмасу кезінде жүйенің бірқалыпты жұмыс істейтініне сенімді болдық. Бірақ пайдаланушылар келе бастағанда, тіркеу уақыты аяқтала бастады. пайдаландық , ол жүйе энтропиясына сүйенеді. Серверде жеткілікті энтропия жинақтауға уақыт болмады және жаңа SecureRandom сұраған кезде ондаған секунд қатып қалатын. Бұл мәселенің көптеген шешімдері бар, соның ішінде қауіпсіз емес /dev/urandom нұсқасына ауысу, арнайы энтропия тудыратын тақтаны орнату немесе алдын ала кездейсоқ сандарды жасау және оларды бассейнде сақтау. Біз пулға қатысты мәселені уақытша шештік, бірақ содан бері жаңа жазылушыларды тіркеу үшін бөлек сынақ жүргізіп жатырмыз.
Жүктеме генераторы ретінде біз қолданамыз Ол веб-розеткалармен жұмыс істей алмайды; плагин қажет. "jmeter websocket" үшін ең жақсы іздеу нәтижелері , ол ұсынады .
Міне, біз бастауды шештік.
Біз маңызды тестілеуді бастағаннан кейін бірден JMeter жадтың ағып жатқанын білдік.
Плагин - бұл мүлдем басқа оқиға: 176 жұлдызы бар GitHub-та 132 шанышқысы бар. Автор бұған 2015 жылдан бері міндеттеме қойған жоқ (біз оны 2015 жылы қабылдадық және ол кезде ешқандай күдік тудырмады) және жадтың ағып кетуіне және жеті жабылмаған тарту сұрауына қатысты бірнеше GitHub мәселелері бар.
Егер сіз осы плагинді пайдаланып жүктеме сынамасын орындауды шешсеңіз, келесі талқылауларды ескеріңіз:
- Көп ағынды ортада кәдімгі LinkedList пайдаланылды, нәтижесінде біз алдық орындау уақытында. Мұны ConcurrentLinkedDeque немесе синхрондалған блоктарға ауыстыру арқылы шешуге болады. Біз өзіміз үшін бірінші нұсқаны таңдадық ().
- Жадтың ағуы, ажыратқанда қосылым туралы ақпарат жойылмайды ().
- Ағынды режимде (веб-розетка үлгінің соңында жабылмаған, бірақ жоспарда әрі қарай пайдаланылған кезде) Жауап үлгілері жұмыс істемейді ().
Бұл GitHub-тағылардың бірі. Біз не істедік:
- Біз алдық (@elyrank) – ол 1 және 3 мәселелерді түзетеді
- Шешілген мәселе 2
- Jetty 9.2.14 бастап 9.3.12 дейін жаңартылды.
- ThreadLocal ішінде SimpleDateFormat оралған; SimpleDateFormat ағынға қауіпсіз емес, бұл орындалу уақытында NPE тудырды.
- Жадтың басқа ағып кетуі түзетілді (ажыратқан кезде байланыс қате жабылды)
Дегенмен ол ағып жатыр!
Жад бір күнде емес, екі күнде таусыла бастады. Уақыт қалмай, олар ағындарды азайтуды шешті, бірақ төрт агентте. Бұл кем дегенде бір аптаға созылуы керек.
Екі күн өтті…
Енді Хазелкасттың жады азайып қалды. Журналдар көрсеткендей, бірнеше күндік сынақтан кейін Хазелкаст жадтың төмендігі туралы шағымдана бастады, ал біраз уақыттан кейін кластер құлап, түйіндер бірінен соң бірі өле бастады. Біз JVisualVM-ді Hazelcast-қа қостық және жоғары қарай спиральды көрдік — ол үнемі GC-ге қоңырау шалып тұрды, бірақ жадты тазарта алмады.
Hazelcast 3.4 нұсқасында картаны/мультимапты (map.destroy()) жою кезінде жад толығымен босатылмайтыны анықталды:
Бұл қате 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());
}Графиктер түзетілді.
Жүктеме сынағы туралы тағы не білдік?
- JSR223 Groovy-де жазылуы керек және компиляция кэшін қамтуы керек - бұл әлдеқайда жылдам. .
- Jmeter-Plugins графиктерін стандарттыға қарағанда түсіну оңай. .
Hazelcast-пен тәжірибеміз туралы
Hazelcast біз үшін жаңа өнім болды; біз онымен 3.4.1 нұсқасымен жұмыс істей бастадық, ал қазір біздің өндірістік серверіміз 3.9.2 нұсқасымен жұмыс істейді (жазу кезінде Hazelcast бағдарламасының соңғы нұсқасы 3.10).
ID генерациясы
Біз бүтін идентификаторлардан бастадық. Жаңа нысан үшін бізге тағы бір Лонг керек деп елестетіп көрейік. Дерекқордағы реттілік қолайлы емес, өйткені кестелер бөлшектелген. Бұл DB1 ішінде ID=1 хабарлама және DB2 ішінде ID=1 хабар бар дегенді білдіреді. Сіз бұл идентификаторды Elasticsearch-ке де, Hazelcast-қа да қоя алмайсыз, бірақ екі дерекқордағы деректерді біреуіне біріктіргіңіз келсе (мысалы, осы жазылушылар үшін бір дерекқор жеткілікті екенін шешу) ең нашар жағдай сценарийі. Сіз Hazelcast-та бірнеше AtomicLong жасай аласыз және онда есептегішті сақтай аласыз. Содан кейін жаңа идентификаторды алу өнімділігі incrementAndGet және Hazelcast сұрауына кететін уақытты құрайды. Бірақ Hazelcastта оңтайлы нәрсе бар: FlakeIdGenerator. Әрбір клиент идентификаторлар ауқымын алады: мысалы, бірінші клиент 1-ден 10 000-ға дейінгі ауқымды алады, екінші клиент 10 001-ден 20 000-ға дейінгі диапазонды алады және т.б. Енді клиент тағайындалған ауқым таусылғанша жаңа идентификаторларды автоматты түрде бере алады. Ол жылдам жұмыс істейді, бірақ қолданба (және Hazelcast клиенті) қайта іске қосылғанда, жаңа реттілік басталады, нәтижесінде бос орындар және т.б. Сонымен қатар, әзірлеушілер идентификаторлардың неліктен бүтін сандар екенін толық түсінбейді, бірақ соншалықты сәйкес келмейтін түрде жасалады. Біз бәрін өлшеп, UUID-ге көштік.
Айтпақшы, Twitter сияқты болғысы келетіндер үшін Snowcast деп аталатын кітапхана бар - Hazelcast-тың жоғарғы жағындағы 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-қа өз сыныптарымыздың нысандарын жазуды шештік. Мысалы, бізде Қолданба сыныбы бар және біз оны сақтап, оқығымыз келеді. Біз сақтаймыз:
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 ала бастадық. Hazelcast индексті жаңартуға тырысты, бірақ ол біздің сынып туралы ештеңе білмеді және оны қамтитын 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 Пайдаланушы класын орналастыру механизмін енгізді. Жалғыз басты түйінді тағайындауға және ондағы JAR файлын жаңартуға болады.
Енді біз көзқарасымызды толығымен өзгерттік: біз JSON-ға өзіміз сериялаймыз және оны Hazelcast-та сақтаймыз. Hazelcast біздің сабақтарымыздың құрылымын білудің қажеті жоқ және біз оларды үзіліссіз жаңарта аламыз. Қолданба домен нысанының нұсқасын басқарады. Қолданбаның әртүрлі нұсқалары бір уақытта жұмыс істей алады және жаңа қолданба жаңа өрістері бар нысандарды жаза алады, ал ескісі олар туралы әлі білмейді. Сонымен қатар, жаңа қолданба жаңа өрістері жоқ ескі қолданба арқылы жазылған нысандарды оқиды. Біз мұндай жағдайларды іштей өңдейміз, бірақ қарапайымдылық үшін өрістерді өзгертпейміз немесе жоймаймыз; біз тек жаңа өрістерді қосу арқылы сыныптарды кеңейтеміз.
Жоғары өнімділікті қалай қамтамасыз етеміз
Хазелкастқа төрт сапар жақсы, BD-ге екі сапар жаман
Кэштен деректерді алу әрқашан дерекқордан гөрі жақсырақ, бірақ біз пайдаланылмаған жазбаларды сақтағымыз келмейді. Біз нені кэштеу керектігі туралы шешімді әзірлеудің соңғы кезеңіне қалдырамыз. Жаңа функционалдылық кодталғаннан кейін біз PostgreSQL жүйесіндегі барлық сұрауларды тіркеуді қосамыз (log_min_duration_statement 0-ге дейін) және 20 минуттық жүктеу сынағын іске қосамыз. pgFouine және pgBadger сияқты утилиталар жиналған журналдардан аналитикалық есептерді жасай алады. Бұл есептерде біз ең алдымен баяу және жиі сұрауларды іздейміз. Баяу сұраулар үшін орындау жоспарын (ТҮСІНДІРУ) жасаймыз және мұндай сұрауды жылдамдатуға болатынын бағалаймыз. Бірдей кіріс деректеріне арналған жиі сұраулар кэшке жақсы сәйкес келеді. Біз сұрауларды әр сұрауға бір кестеден тұратын «жалпақ» ұстауға тырысамыз.
Операция
SV онлайн қызметі ретінде 2017 жылдың көктемінде іске қосылды, ал жеке өнім ретінде SV 2017 жылдың қарашасында шығарылды (ол кезде бета нұсқасы күйінде).
Бір жылдан астам жұмыс істеген уақыт ішінде SV онлайн қызметінде күрделі мәселелер болған жоқ. арқылы онлайн сервисті бақылаймыз , біз құрастырамыз және орналастырамыз .
SV серверін тарату жергілікті пакеттерде қол жетімді: RPM, DEB және MSI. Сонымен қатар, Windows үшін біз серверді, Hazelcast және Elasticsearch файлдарын бір құрылғыға орнататын жалғыз EXE орнатушысын береміз. Біз бастапқыда бұл орнатуды «демо» нұсқасы деп атадық, бірақ содан кейін бұл ең танымал орналастыру опциясы екені белгілі болды.
Ақпарат көзі: www.habr.com
