Sut a pham y gwnaethom ysgrifennu gwasanaeth graddadwy llwyth uchel ar gyfer 1C: Enterprise: Java, PostgreSQL, Hazelcast

Yn yr erthygl hon byddwn yn siarad am sut a pham y gwnaethom ddatblygu System Rhyngweithio - mecanwaith sy'n trosglwyddo gwybodaeth rhwng cymwysiadau cleient a gweinyddwyr 1C:Menter - o osod tasg i feddwl trwy'r pensaernïaeth a manylion gweithredu.

Mae'r System Ryngweithio (y cyfeirir ati o hyn ymlaen fel SV) yn system negeseuon ddosbarthedig sy'n goddef diffygion ac yn cael ei dosbarthu'n sicr. Mae SV wedi'i gynllunio fel gwasanaeth llwyth uchel gyda graddadwyedd uchel, sydd ar gael fel gwasanaeth ar-lein (a ddarperir gan 1C) ac fel cynnyrch màs-gynhyrchu y gellir ei ddefnyddio ar eich cyfleusterau gweinydd eich hun.

Mae SV yn defnyddio storfa ddosbarthedig gollen a pheiriant chwilio Elastig. Byddwn hefyd yn siarad am Java a sut rydym yn graddio PostgreSQL yn llorweddol.
Sut a pham y gwnaethom ysgrifennu gwasanaeth graddadwy llwyth uchel ar gyfer 1C: Enterprise: Java, PostgreSQL, Hazelcast

Datganiad o'r broblem

Er mwyn ei gwneud yn glir pam y gwnaethom greu'r System Ryngweithio, dywedaf ychydig wrthych am sut mae datblygu cymwysiadau busnes yn 1C yn gweithio.

I ddechrau, ychydig amdanom ni ar gyfer y rhai nad ydynt yn gwybod eto beth rydym yn ei wneud :) Rydym yn gwneud y llwyfan technoleg 1C:Menter. Mae'r platfform yn cynnwys offeryn datblygu cymwysiadau busnes, yn ogystal ag amser rhedeg sy'n caniatáu i gymwysiadau busnes redeg mewn amgylchedd traws-lwyfan.

Paradeim datblygu cleient-gweinydd

Mae cymwysiadau busnes a grëwyd ar 1C:Menter yn gweithredu ar dair lefel cleient-gweinydd pensaernïaeth “DBMS - gweinydd cais - cleient”. Cod cais wedi'i ysgrifennu yn iaith 1C adeiledig, gellir ei weithredu ar weinydd y cais neu ar y cleient. Mae'r holl waith gyda gwrthrychau cais (cyfeiriaduron, dogfennau, ac ati), yn ogystal â darllen ac ysgrifennu'r gronfa ddata, yn cael ei berfformio ar y gweinydd yn unig. Mae ymarferoldeb ffurflenni a rhyngwyneb gorchymyn hefyd yn cael ei weithredu ar y gweinydd. Mae'r cleient yn perfformio derbyn, agor ac arddangos ffurflenni, "cyfathrebu" gyda'r defnyddiwr (rhybuddion, cwestiynau ...), cyfrifiadau bach ar ffurf sy'n gofyn am ymateb cyflym (er enghraifft, lluosi'r pris â maint), gweithio gyda ffeiliau lleol, gweithio gydag offer.

Yn y cod cymhwysiad, rhaid i benawdau gweithdrefnau a swyddogaethau nodi'n benodol lle bydd y cod yn cael ei weithredu - gan ddefnyddio'r cyfarwyddebau &AtClient / &AtServer (&AtClient / &AtServer yn fersiwn Saesneg yr iaith). Bydd datblygwyr 1C nawr yn fy nghywiro trwy ddweud bod cyfarwyddebau mewn gwirionedd gwell, ond i ni nid yw hyn yn bwysig yn awr.

Gallwch ffonio cod gweinydd o god cleient, ond ni allwch ffonio cod cleient o god gweinydd. Mae hwn yn gyfyngiad sylfaenol a wnaethom am nifer o resymau. Yn benodol, oherwydd bod yn rhaid i god gweinydd gael ei ysgrifennu yn y fath fodd fel ei fod yn gweithredu yn yr un modd ni waeth ble mae'n cael ei alw - gan y cleient neu gan y gweinydd. Ac yn achos galw cod gweinydd o god gweinydd arall, nid oes cleient fel y cyfryw. Ac oherwydd yn ystod gweithredu cod y gweinydd, gallai'r cleient a'i galwodd gau, gadael y cais, ac ni fyddai gan y gweinydd unrhyw un i'w ffonio mwyach.

Sut a pham y gwnaethom ysgrifennu gwasanaeth graddadwy llwyth uchel ar gyfer 1C: Enterprise: Java, PostgreSQL, Hazelcast
Cod sy'n trin clic botwm: bydd galw gweithdrefn gweinydd gan y cleient yn gweithio, ni fydd galw gweithdrefn cleient o'r gweinydd

Mae hyn yn golygu, os ydym am anfon rhywfaint o neges o'r gweinydd i'r rhaglen cleient, er enghraifft, bod cynhyrchu adroddiad “hirhoedlog” wedi dod i ben a bod modd gweld yr adroddiad, nid oes gennym ddull o'r fath. Mae'n rhaid i chi ddefnyddio triciau, er enghraifft, pleidleisio'r gweinydd o bryd i'w gilydd o'r cod cleient. Ond mae'r dull hwn yn llwytho'r system â galwadau diangen, ac yn gyffredinol nid yw'n edrych yn gain iawn.

Ac mae angen hefyd, er enghraifft, pan fydd galwad ffôn yn cyrraedd SIP- wrth wneud galwad, hysbysu'r rhaglen cleient am hyn fel y gall ddefnyddio rhif y galwr i ddod o hyd iddo yn y gronfa ddata gwrthbarti a dangos gwybodaeth i'r defnyddiwr am y gwrthbarti sy'n galw. Neu, er enghraifft, pan fydd archeb yn cyrraedd y warws, rhowch wybod i gais cleient y cwsmer am hyn. Yn gyffredinol, mae yna lawer o achosion lle byddai mecanwaith o'r fath yn ddefnyddiol.

Y cynhyrchiad ei hun

Creu mecanwaith negeseuon. Cyflym, dibynadwy, gyda dosbarthiad gwarantedig, gyda'r gallu i chwilio'n hyblyg am negeseuon. Yn seiliedig ar y mecanwaith, gweithredu negesydd (negeseuon, galwadau fideo) yn rhedeg y tu mewn i geisiadau 1C.

Dyluniwch y system i fod yn llorweddol scalable. Rhaid gorchuddio'r llwyth cynyddol trwy gynyddu nifer y nodau.

Gweithredu

Fe wnaethom benderfynu peidio ag integreiddio rhan gweinydd SV yn uniongyrchol i'r platfform 1C:Menter, ond ei weithredu fel cynnyrch ar wahân, y gellir galw'r API ohono o'r cod datrysiadau cymhwysiad 1C. Gwnaed hyn am nifer o resymau, a'r prif un oedd fy mod am ei gwneud hi'n bosibl cyfnewid negeseuon rhwng gwahanol geisiadau 1C (er enghraifft, rhwng Rheoli Masnach a Chyfrifyddu). Gall gwahanol gymwysiadau 1C redeg ar wahanol fersiynau o'r platfform 1C:Menter, eu lleoli ar wahanol weinyddion, ac ati. Mewn amodau o'r fath, gweithredu SV fel cynnyrch ar wahân "ar ochr" gosodiadau 1C yw'r ateb gorau posibl.

Felly, fe benderfynon ni wneud SV fel cynnyrch ar wahân. Rydym yn argymell bod cwmnïau bach yn defnyddio'r gweinydd CB a osodwyd gennym yn ein cwmwl (wss://1cdialog.com) i osgoi'r costau cyffredinol sy'n gysylltiedig â gosod a ffurfweddu'r gweinydd yn lleol. Efallai y byddai'n syniad da i gleientiaid mawr osod eu gweinydd CB eu hunain yn eu cyfleusterau. Fe wnaethon ni ddefnyddio dull tebyg yn ein cynnyrch cwmwl SaaS 1c ffres - mae'n cael ei gynhyrchu fel cynnyrch màs-gynhyrchu i'w osod ar safleoedd cleientiaid, ac mae hefyd yn cael ei ddefnyddio yn ein cwmwl https://1cfresh.com/.

Cais

Er mwyn dosbarthu'r goddefgarwch llwyth a namau, byddwn yn defnyddio nid un cymhwysiad Java, ond sawl un, gyda chydbwysedd llwyth o'u blaenau. Os oes angen i chi drosglwyddo neges o nod i nod, defnyddiwch cyhoeddi/tanysgrifio yn Hazelcast.

Mae cyfathrebu rhwng y cleient a'r gweinydd trwy websocket. Mae'n addas iawn ar gyfer systemau amser real.

Celc wedi'i ddosbarthu

Dewison ni rhwng Redis, Hazelcast ac Ehcache. Mae'n 2015. Mae Redis newydd ryddhau clwstwr newydd (rhy newydd, brawychus), mae Sentinel gyda llawer o gyfyngiadau. Nid yw Ehcache yn gwybod sut i ymgynnull i glwstwr (ymddangosodd y swyddogaeth hon yn ddiweddarach). Fe benderfynon ni roi cynnig arni gyda Hazelcast 3.4.
Mae Hazelcast wedi'i ymgynnull i mewn i glwstwr allan o'r bocs. Yn y modd nod sengl, nid yw'n ddefnyddiol iawn a dim ond fel storfa y gellir ei ddefnyddio - nid yw'n gwybod sut i ollwng data i ddisg, os byddwch chi'n colli'r unig nod, byddwch chi'n colli'r data. Rydym yn defnyddio sawl Hazelcast, a byddwn yn gwneud copi wrth gefn o ddata hanfodol rhyngddynt. Nid ydym yn gwneud copi wrth gefn o'r storfa - does dim ots gennym ni.

I ni, Hazelcast yw:

  • Storio sesiynau defnyddwyr. Mae'n cymryd amser hir i fynd i'r bas data am sesiwn bob tro, felly rydyn ni'n rhoi'r sesiynau i gyd yn Hazelcast.
  • Cache. Os ydych chi'n chwilio am broffil defnyddiwr, gwiriwch y storfa. Ysgrifennodd neges newydd - rhowch hi yn y storfa.
  • Pynciau ar gyfer cyfathrebu rhwng achosion cais. Mae'r nod yn cynhyrchu digwyddiad ac yn ei osod yn y pwnc Hazelcast. Mae nodau cais eraill sydd wedi tanysgrifio i'r pwnc hwn yn derbyn ac yn prosesu'r digwyddiad.
  • Cloeon clwstwr. Er enghraifft, rydym yn creu trafodaeth gan ddefnyddio allwedd unigryw (trafodaeth sengl o fewn cronfa ddata 1C):

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

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

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

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

Gwnaethom wirio nad oes sianel. Fe wnaethon ni gymryd y clo, ei wirio eto, a'i greu. Os na fyddwch chi'n gwirio'r clo ar ôl cymryd y clo, yna mae siawns bod edefyn arall hefyd wedi gwirio bryd hynny ac y bydd nawr yn ceisio creu'r un drafodaeth - ond mae'n bodoli eisoes. Ni allwch gloi gan ddefnyddio clo java cydamserol neu reolaidd. Trwy'r gronfa ddata - mae'n araf, ac mae'n drueni i'r gronfa ddata; trwy Hazelcast - dyna sydd ei angen arnoch chi.

Dewis DBMS

Mae gennym brofiad helaeth a llwyddiannus o weithio gyda PostgreSQL a chydweithio â datblygwyr y DBMS hwn.

Nid yw'n hawdd gyda chlwstwr PostgreSQL - mae yna XL, XC, Citus, ond yn gyffredinol nid yw'r rhain yn NoSQL sy'n ymestyn allan o'r blwch. Ni wnaethom ystyried NoSQL fel y prif storfa; roedd yn ddigon inni gymryd Hazelcast, nad oeddem wedi gweithio ag ef o'r blaen.

Os oes angen i chi raddio cronfa ddata berthynol, mae hynny'n golygu darnio. Fel y gwyddoch, gyda darnio rydym yn rhannu'r gronfa ddata yn rhannau ar wahân fel y gellir gosod pob un ohonynt ar weinydd ar wahân.

Tybiodd fersiwn gyntaf ein darnio y gallu i ddosbarthu pob un o dablau ein cymhwysiad ar draws gwahanol weinyddion mewn gwahanol gyfrannau. Mae yna lawer o negeseuon ar weinydd A - os gwelwch yn dda, gadewch i ni symud rhan o'r tabl hwn i weinydd B. Roedd y penderfyniad hwn yn syml yn sgrechian am optimeiddio cynamserol, felly fe benderfynon ni gyfyngu ein hunain i ddull aml-denant.

Gallwch ddarllen am aml-denant, er enghraifft, ar y wefan Data Citws.

Mae gan SV y cysyniadau o gais a thanysgrifiwr. Mae cymhwysiad yn osodiad penodol o raglen fusnes, fel ERP neu Accounting, gyda'i ddefnyddwyr a data busnes. Mae tanysgrifiwr yn sefydliad neu'n unigolyn y mae'r cais wedi'i gofrestru ar ei ran yn y gweinydd SV. Gall tanysgrifiwr gofrestru sawl cais, a gall y cymwysiadau hyn gyfnewid negeseuon â'i gilydd. Daeth y tanysgrifiwr yn denant yn ein system. Gellir lleoli negeseuon gan nifer o danysgrifwyr mewn un gronfa ddata ffisegol; os gwelwn fod tanysgrifiwr wedi dechrau cynhyrchu llawer o draffig, byddwn yn ei symud i gronfa ddata ffisegol ar wahân (neu hyd yn oed weinydd cronfa ddata ar wahân).

Mae gennym brif gronfa ddata lle mae tabl llwybro yn cael ei storio gyda gwybodaeth am leoliad yr holl gronfeydd data tanysgrifwyr.

Sut a pham y gwnaethom ysgrifennu gwasanaeth graddadwy llwyth uchel ar gyfer 1C: Enterprise: Java, PostgreSQL, Hazelcast

Er mwyn atal y brif gronfa ddata rhag bod yn dagfa, rydym yn cadw'r tabl llwybro (a data arall sydd ei angen yn aml) mewn storfa.

Os bydd cronfa ddata'r tanysgrifiwr yn dechrau arafu, byddwn yn ei dorri'n rhaniadau y tu mewn. Ar brosiectau eraill rydym yn eu defnyddio pg_pathman.

Gan fod colli negeseuon defnyddwyr yn ddrwg, rydym yn cynnal ein cronfeydd data gyda chopïau. Mae'r cyfuniad o atgynyrchiadau cydamserol ac asyncronig yn caniatáu ichi yswirio'ch hun rhag ofn y byddwch yn colli'r brif gronfa ddata. Bydd colli neges yn digwydd dim ond os bydd y gronfa ddata gynradd a'i replica cydamserol yn methu ar yr un pryd.

Os collir replica cydamserol, daw'r atgynhyrchiad asyncronig yn gydamserol.
Os collir y brif gronfa ddata, daw'r atgynhyrchiad cydamserol yn brif gronfa ddata, a daw'r replica asyncronig yn atgynhyrchiad cydamserol.

Elasticsearch ar gyfer chwilio

Gan fod SV, ymhlith pethau eraill, hefyd yn negesydd, mae angen chwiliad cyflym, cyfleus a hyblyg, gan ystyried morffoleg, gan ddefnyddio matsis anfanwl. Penderfynasom beidio ag ailddyfeisio'r olwyn a defnyddio'r peiriant chwilio am ddim Elasticsearch, a grëwyd yn seiliedig ar y llyfrgell Lucene. Rydym hefyd yn defnyddio Elasticsearch mewn clwstwr (meistr – data – data) i ddileu problemau os bydd nodau cais yn methu.

Ar github daethom o hyd Ategyn morffoleg Rwseg ar gyfer Elasticsearch a'i ddefnyddio. Yn y mynegai Elasticsearch rydym yn storio gwreiddiau geiriau (y mae'r ategyn yn eu pennu) ac N-grams. Wrth i'r defnyddiwr fewnbynnu testun i chwilio, edrychwn am y testun wedi'i deipio ymhlith N-grams. Pan gaiff ei gadw i'r mynegai, bydd y gair “testunau” yn cael ei rannu i'r N-gramau canlynol:

[y rhai, tek, tex, testun, testunau, ek, ex, est, testunau, ks, kst, ksty, st, sty, chi],

A bydd gwraidd y gair “testun” hefyd yn cael ei gadw. Mae'r dull hwn yn caniatáu ichi chwilio ar ddechrau, yn y canol ac ar ddiwedd y gair.

Llun cyffredinol

Sut a pham y gwnaethom ysgrifennu gwasanaeth graddadwy llwyth uchel ar gyfer 1C: Enterprise: Java, PostgreSQL, Hazelcast
Ailadroddwch y llun o ddechrau'r erthygl, ond gydag esboniadau:

  • Balancer yn agored ar y Rhyngrwyd; mae gennym nginx, gall fod yn unrhyw.
  • Mae achosion cymhwysiad Java yn cyfathrebu â'i gilydd trwy Hazelcast.
  • I weithio gyda soced gwe rydym yn defnyddio Netty.
  • Mae'r cais Java wedi'i ysgrifennu yn Java 8 ac mae'n cynnwys bwndeli OSGi. Mae'r cynlluniau'n cynnwys mudo i Java 10 a throsglwyddo i fodiwlau.

Datblygu a phrofi

Yn y broses o ddatblygu a phrofi’r SV, daethom ar draws nifer o nodweddion diddorol y cynnyrch a ddefnyddiwn.

Profi llwyth a chof yn gollwng

Mae rhyddhau pob datganiad SV yn cynnwys profi llwyth. Mae'n llwyddiannus pan:

  • Bu'r prawf yn gweithio am sawl diwrnod ac nid oedd unrhyw fethiannau gwasanaeth
  • Nid oedd yr amser ymateb ar gyfer gweithrediadau allweddol yn fwy na throthwy cyfforddus
  • Nid yw dirywiad perfformiad o'i gymharu â'r fersiwn flaenorol yn fwy na 10%

Rydym yn llenwi'r gronfa ddata prawf gyda data - i wneud hyn, rydym yn derbyn gwybodaeth am y tanysgrifiwr mwyaf gweithgar o'r gweinydd cynhyrchu, yn lluosi ei rifau â 5 (nifer y negeseuon, trafodaethau, defnyddwyr) a'i brofi felly.

Rydym yn cynnal profion llwyth o'r system ryngweithio mewn tri chyfluniad:

  1. prawf straen
  2. Cysylltiadau yn unig
  3. Cofrestru tanysgrifiwr

Yn ystod y prawf straen, rydym yn lansio cannoedd o edafedd, ac maent yn llwytho'r system heb stopio: ysgrifennu negeseuon, creu trafodaethau, derbyn rhestr o negeseuon. Rydym yn efelychu gweithredoedd defnyddwyr cyffredin (cael rhestr o fy negeseuon heb eu darllen, ysgrifennwch at rywun) a datrysiadau meddalwedd (trosglwyddwch becyn o ffurfweddiad gwahanol, proseswch rybudd).

Er enghraifft, dyma sut olwg sydd ar ran o’r prawf straen:

  • Defnyddiwr yn mewngofnodi
    • Yn gofyn am eich trafodaethau heb eu darllen
    • 50% yn debygol o ddarllen negeseuon
    • 50% yn debygol o anfon neges destun
    • Defnyddiwr nesaf:
      • Siawns o 20% o greu trafodaeth newydd
      • Yn dewis unrhyw un o'i drafodaethau ar hap
      • Yn mynd i mewn
      • Negeseuon ceisiadau, proffiliau defnyddwyr
      • Yn creu pum neges wedi'u cyfeirio at ddefnyddwyr ar hap o'r drafodaeth hon
      • Gadael trafodaeth
      • Yn ailadrodd 20 gwaith
      • Yn allgofnodi, yn mynd yn ôl i ddechrau'r sgript

    • Mae chatbot yn mynd i mewn i'r system (yn efelychu negeseuon o god cymhwysiad)
      • Siawns o 50% o greu sianel newydd ar gyfer cyfnewid data (trafodaeth arbennig)
      • 50% yn debygol o ysgrifennu neges i unrhyw un o'r sianeli presennol

Ymddangosodd y senario “Cysylltiadau yn Unig” am reswm. Mae sefyllfa: mae defnyddwyr wedi cysylltu'r system, ond nid ydynt wedi cymryd rhan eto. Mae pob defnyddiwr yn troi'r cyfrifiadur ymlaen am 09:00 yn y bore, yn sefydlu cysylltiad â'r gweinydd ac yn aros yn dawel. Mae'r dynion hyn yn beryglus, mae yna lawer ohonyn nhw - yr unig becynnau sydd ganddyn nhw yw PING / PONG, ond maen nhw'n cadw'r cysylltiad â'r gweinydd (ni allant ei gadw - beth os oes neges newydd). Mae'r prawf yn atgynhyrchu sefyllfa lle mae nifer fawr o ddefnyddwyr o'r fath yn ceisio mewngofnodi i'r system mewn hanner awr. Mae'n debyg i brawf straen, ond mae ei ffocws yn union ar y mewnbwn cyntaf hwn - fel nad oes unrhyw fethiannau (nid yw person yn defnyddio'r system, ac mae eisoes yn cwympo i ffwrdd - mae'n anodd meddwl am rywbeth gwaeth).

Mae'r sgript cofrestru tanysgrifiwr yn dechrau o'r lansiad cyntaf. Gwnaethom gynnal prawf straen ac roeddem yn sicr nad oedd y system yn arafu yn ystod gohebiaeth. Ond daeth defnyddwyr a dechreuodd y cofrestriad fethu oherwydd terfyn amser. Wrth gofrestru fe wnaethon ni ddefnyddio / dev / ar hap, sy'n gysylltiedig ag entropi'r system. Nid oedd gan y gweinydd amser i gronni digon o entropi a phan ofynnwyd am SecureRandom newydd, fe rewodd am ddegau o eiliadau. Mae yna lawer o ffyrdd allan o'r sefyllfa hon, er enghraifft: newid i'r /dev/urandom llai diogel, gosod bwrdd arbennig sy'n cynhyrchu entropi, cynhyrchu rhifau ar hap ymlaen llaw a'u storio mewn pwll. Fe wnaethom gau'r broblem gyda'r pwll dros dro, ond ers hynny rydym wedi bod yn cynnal prawf ar wahân ar gyfer cofrestru tanysgrifwyr newydd.

Rydym yn defnyddio fel generadur llwyth JMesur. Nid yw'n gwybod sut i weithio gyda websocket; mae angen ategyn arno. Y cyntaf yn y canlyniadau chwilio ar gyfer yr ymholiad “jmeter websocket” yw: erthyglau o BlazeMeter, sy'n argymell ategyn gan Maciej Zaleski.

Dyna lle penderfynon ni ddechrau.

Bron yn syth ar ôl dechrau profion difrifol, fe wnaethom ddarganfod bod JMeter wedi dechrau gollwng cof.

Mae'r ategyn yn stori fawr ar wahân; gyda 176 o sêr, mae ganddo 132 ffyrc ar github. Nid yw'r awdur ei hun wedi ymrwymo iddo ers 2015 (fe wnaethom ei gymryd yn 2015, yna ni chodwyd amheuon), sawl mater github ynghylch gollyngiadau cof, 7 cais tynnu heb ei gau.
Os penderfynwch gynnal profion llwyth gan ddefnyddio'r ategyn hwn, rhowch sylw i'r trafodaethau canlynol:

  1. Mewn amgylchedd aml-edau, defnyddiwyd LinkedList rheolaidd, a'r canlyniad oedd NPE mewn amser rhedeg. Gellir datrys hyn naill ai trwy newid i ConcurrentLinkedDeque neu drwy flociau cydamserol. Fe wnaethon ni ddewis yr opsiwn cyntaf i ni ein hunain (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/43).
  2. Gollyngiad cof; wrth ddatgysylltu, nid yw gwybodaeth cysylltiad yn cael ei ddileu (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/44).
  3. Yn y modd ffrydio (pan nad yw'r websocket ar gau ar ddiwedd y sampl, ond yn cael ei ddefnyddio yn ddiweddarach yn y cynllun), nid yw patrymau ymateb yn gweithio (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/19).

Dyma un o'r rhai ar github. Beth wnaethom ni:

  1. Wedi cymryd fforch Elyran Kogan (@elyrank) – mae’n trwsio problemau 1 a 3
  2. Problem wedi'i datrys 2
  3. Lanfa wedi'i diweddaru o 9.2.14 i 9.3.12
  4. Wedi'i Lapio SimpleDateFormat yn ThreadLocal; Nid yw SimpleDateFormat yn ddiogel rhag edau, a arweiniodd at NPE ar amser rhedeg
  5. Wedi trwsio gollyngiad cof arall (caewyd y cysylltiad yn anghywir pan gafodd ei ddatgysylltu)

Ac eto mae'n llifo!

Dechreuodd y cof redeg allan nid mewn diwrnod, ond mewn dau. Nid oedd unrhyw amser ar ôl o gwbl, felly fe benderfynon ni lansio llai o edafedd, ond ar bedwar asiant. Dylai hyn fod wedi bod yn ddigon am o leiaf wythnos.

Mae dau ddiwrnod wedi mynd heibio...

Nawr mae Hazelcast yn rhedeg allan o gof. Dangosodd y logiau, ar ôl cwpl o ddyddiau o brofi, fod Hazelcast wedi dechrau cwyno am ddiffyg cof, ac ar ôl peth amser syrthiodd y clwstwr ar wahân, a pharhaodd y nodau i farw fesul un. Fe wnaethon ni gysylltu JVisualVM â chollen a gwelsom “lif yn codi” - roedd yn galw'r GC yn rheolaidd, ond ni allai glirio'r cof.

Sut a pham y gwnaethom ysgrifennu gwasanaeth graddadwy llwyth uchel ar gyfer 1C: Enterprise: Java, PostgreSQL, Hazelcast

Daeth i'r amlwg yn hazelcast 3.4, wrth ddileu map / multiMap (map.destroy()), nid yw'r cof yn cael ei ryddhau'n llwyr:

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

Mae'r byg bellach wedi'i osod yn 3.5, ond roedd yn broblem bryd hynny. Fe wnaethon ni greu amlfapiau newydd gydag enwau deinamig a'u dileu yn unol â'n rhesymeg. Roedd y cod yn edrych rhywbeth fel hyn:

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();
    }
}

Ateb:

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

Crëwyd multiMap ar gyfer pob tanysgrifiad a'i ddileu pan nad oedd ei angen. Fe benderfynon ni y bydden ni'n dechrau Map , yr allwedd fydd enw'r tanysgrifiad, a'r gwerthoedd fydd dynodwyr sesiwn (y gallwch chi wedyn gael dynodwyr defnyddiwr, os oes angen).

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

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

Mae'r siartiau wedi gwella.

Sut a pham y gwnaethom ysgrifennu gwasanaeth graddadwy llwyth uchel ar gyfer 1C: Enterprise: Java, PostgreSQL, Hazelcast

Beth arall rydyn ni wedi'i ddysgu am brofi llwyth?

  1. Mae angen ysgrifennu JSR223 mewn grwfi a chynnwys storfa grynhoi - mae'n llawer cyflymach. Cyswllt.
  2. Mae graffiau Jmeter-Plugins yn haws eu deall na rhai safonol. Cyswllt.

Am ein profiad gyda Hazelcast

Roedd Hazelcast yn gynnyrch newydd i ni, dechreuon ni weithio gydag ef o fersiwn 3.4.1, nawr mae ein gweinydd cynhyrchu yn rhedeg fersiwn 3.9.2 (ar adeg ysgrifennu, y fersiwn ddiweddaraf o Hazelcast yw 3.10).

cenhedlaeth ID

Dechreuon ni gyda dynodwyr cyfanrif. Gadewch i ni ddychmygu bod angen Hir arall arnom am endid newydd. Nid yw'r dilyniant yn y gronfa ddata yn addas, mae'r tablau'n rhan o'r darnio - mae'n troi allan bod neges ID=1 yn DB1 a neges ID=1 yn DB2, ni allwch roi'r ID hwn yn Elasticsearch, nac yn Hazelcast , ond y peth gwaethaf yw os ydych chi am gyfuno'r data o ddwy gronfa ddata yn un (er enghraifft, penderfynu bod un gronfa ddata yn ddigon i'r tanysgrifwyr hyn). Gallwch ychwanegu sawl AtomicLongs at Hazelcast a chadw'r cownter yno, yna mae'r perfformiad o gael ID newydd yn gynyddranAndGet ynghyd â'r amser ar gyfer cais i Hazelcast. Ond mae gan Hazelcast rywbeth mwy optimaidd - FlakeIdGenerator. Wrth gysylltu â phob cleient, rhoddir ystod ID iddynt, er enghraifft, yr un cyntaf - o 1 i 10, yr ail - o 000 i 10, ac yn y blaen. Nawr gall y cleient gyhoeddi dynodwyr newydd ar ei ben ei hun nes bod yr ystod a roddwyd iddo yn dod i ben. Mae'n gweithio'n gyflym, ond pan fyddwch chi'n ailgychwyn y cais (a'r cleient Hazelcast), mae dilyniant newydd yn dechrau - felly'r sgipiau, ac ati. Yn ogystal, nid yw datblygwyr wir yn deall pam mae'r IDs yn gyfanrif, ond maent mor anghyson. Fe wnaethon ni bwyso popeth a newid i UUIDs.

Gyda llaw, i'r rhai sydd eisiau bod fel Twitter, mae yna lyfrgell Snowcast o'r fath - dyma weithrediad Snowflake ar ben Hazelcast. Gallwch ei weld yma:

github.com/noctarius/snowcast
github.com/twitter/pluen eira

Ond nid ydym wedi cyrraedd ato bellach.

TransactionalMap.replace

Syndod arall: nid yw TransationalMap.replace yn gweithio. Dyma brawf:

@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

Roedd yn rhaid i mi ysgrifennu fy lle fy hun gan ddefnyddio 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);
}

Profwch nid yn unig strwythurau data rheolaidd, ond hefyd eu fersiynau trafodion. Mae'n digwydd bod IMap yn gweithio, ond nid yw TransactionalMap yn bodoli mwyach.

Mewnosod JAR newydd heb amser segur

Yn gyntaf, penderfynon ni recordio gwrthrychau ein dosbarthiadau yn Hazelcast. Er enghraifft, mae gennym ddosbarth Cais, rydym am ei gadw a'i ddarllen. Arbed:

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

Enw:

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

Mae popeth yn gweithio. Yna fe benderfynon ni adeiladu mynegai yn Hazelcast i chwilio yn ôl:

map.addIndex("subscriberId", false);

Ac wrth ysgrifennu endid newydd, fe ddechreuon nhw dderbyn ClassNotFoundException. Ceisiodd Hazelcast ychwanegu at y mynegai, ond nid oedd yn gwybod dim am ein dosbarth ac roedd am i JAR gyda'r dosbarth hwn gael ei gyflenwi iddo. Fe wnaethom yn union hynny, gweithiodd popeth, ond ymddangosodd problem newydd: sut i ddiweddaru'r JAR heb atal y clwstwr yn llwyr? Nid yw Hazelcast yn codi'r JAR newydd yn ystod diweddariad nod-wrth-nôd. Ar y pwynt hwn penderfynasom y gallem fyw heb chwiliad mynegai. Wedi'r cyfan, os ydych chi'n defnyddio Hazelcast fel storfa gwerth allweddol, yna bydd popeth yn gweithio? Ddim mewn gwirionedd. Yma eto mae ymddygiad IMap a TransactionalMap yn wahanol. Lle nad oes ots gan IMap, mae TransactionalMap yn taflu gwall.

IMap. Rydyn ni'n ysgrifennu 5000 o wrthrychau, yn eu darllen. Mae disgwyl popeth.

@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());
    }
}

Ond nid yw'n gweithio mewn trafodiad, rydym yn cael 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();
        }
    });
}

Yn 3.8, ymddangosodd y mecanwaith Defnyddio Dosbarthiadau Defnyddwyr. Gallwch ddynodi un prif nod a diweddaru'r ffeil JAR arno.

Nawr rydym wedi newid ein hymagwedd yn llwyr: rydym yn ei chyfresoli ein hunain i JSON a'i gadw yn Hazelcast. Nid oes angen i Hazelcast wybod strwythur ein dosbarthiadau, a gallwn ddiweddaru heb amser segur. Mae fersiwnio gwrthrychau parth yn cael ei reoli gan y cymhwysiad. Gall gwahanol fersiynau o'r cais fod yn rhedeg ar yr un pryd, ac mae sefyllfa'n bosibl pan fydd y rhaglen newydd yn ysgrifennu gwrthrychau gyda meysydd newydd, ond nid yw'r hen un yn gwybod am y meysydd hyn eto. Ac ar yr un pryd, mae'r cais newydd yn darllen gwrthrychau a ysgrifennwyd gan yr hen gymhwysiad nad oes ganddynt feysydd newydd. Rydym yn ymdrin â sefyllfaoedd o'r fath o fewn y cais, ond er mwyn symlrwydd nid ydym yn newid nac yn dileu meysydd, dim ond trwy ychwanegu meysydd newydd y byddwn yn ehangu'r dosbarthiadau.

Sut rydym yn sicrhau perfformiad uchel

Pedair taith i Hazelcast - da, dwy i'r bas data - drwg

Mae mynd i'r storfa ar gyfer data bob amser yn well na mynd i'r gronfa ddata, ond nid ydych chi am storio cofnodion nas defnyddiwyd chwaith. Rydym yn gadael y penderfyniad ynghylch beth i'w storio tan y cam datblygu olaf. Pan fydd y swyddogaeth newydd wedi'i chodio, rydyn ni'n troi logio'r holl ymholiadau yn PostgreSQL ymlaen (log_min_duration_statement i 0) ac yn cynnal profion llwyth am funudau 20. Gan ddefnyddio'r logiau a gasglwyd, gall cyfleustodau fel pgFouine a pgBadger adeiladu adroddiadau dadansoddol. Mewn adroddiadau, rydym yn edrych yn bennaf am ymholiadau araf ac aml. Ar gyfer ymholiadau araf, rydym yn adeiladu cynllun gweithredu (Esbonio) ac yn gwerthuso a ellir cyflymu ymholiad o'r fath. Mae ceisiadau aml am yr un data mewnbwn yn cyd-fynd yn dda â'r storfa. Rydym yn ceisio cadw ymholiadau yn “wastad”, un bwrdd fesul ymholiad.

ecsbloetio

Rhoddwyd SV fel gwasanaeth ar-lein ar waith yng ngwanwyn 2017, ac fel cynnyrch ar wahân, rhyddhawyd SV ym mis Tachwedd 2017 (ar y pryd mewn statws fersiwn beta).

Mewn mwy na blwyddyn o weithredu, ni fu unrhyw broblemau difrifol yng ngweithrediad y gwasanaeth CB ar-lein. Rydym yn monitro'r gwasanaeth ar-lein drwy Zabbix, casglu a defnyddio o Bambŵ.

Mae'r dosbarthiad gweinydd SV yn cael ei gyflenwi ar ffurf pecynnau brodorol: RPM, DEB, MSI. Hefyd ar gyfer Windows rydym yn darparu gosodwr sengl ar ffurf EXE sengl sy'n gosod y gweinydd, Hazelcast ac Elasticsearch ar un peiriant. Fe wnaethom gyfeirio i ddechrau at y fersiwn hon o'r gosodiad fel y fersiwn “demo”, ond mae bellach wedi dod yn amlwg mai dyma'r opsiwn lleoli mwyaf poblogaidd.

Ffynhonnell: hab.com

Ychwanegu sylw