პირველი აპრილის საპატივცემულოდ Reddit-მა გახსნა გვერდი მომხმარებლებისთვის, რომ გამოეხატათ საკუთარი თავი, მაგრამ პროექტი, რომელიც ხუმრობით შეიქმნა, გახდა ერთგვარი კედელი კოლექტიური წარწერებისთვის მთელი მსოფლიოდან, სადაც ნაჩვენებია, თუ რა კვალი უნდა დატოვონ ისტორიაში. .
1 აპრილს Reddit-მა გამოუშვა პროექტი Place, გვერდი ცარიელი ტილოებით, რომელზეც ფორუმის თითოეულ მომხმარებელს შეეძლო ნებისმიერი სურათის დახატვა. მხატვრებს ჰქონდათ შეზღუდვები: მათ შეეძლოთ 16 ფერიდან მხოლოდ ერთი პიქსელის დახატვა ყოველ ხუთ წუთში, ასევე შეზღუდული იყო ტილოს ზომები. სხვა პიქსელების დახატვა შესაძლებელია დახატულის თავზე (და შემდეგ პირველ ავტორს შეუძლია კვლავ დახატოს საკუთარი პიქსელი მოწინააღმდეგის პიქსელის თავზე), რის გამოც გამოსახულებების ავტორები აპრიორი კონფლიქტში არიან. როგორც ტილოს აღწერილობაშია აღნიშნული, პროექტი შექმნილია ერთობლივი შემოქმედებისთვის - ”თითოეულ თქვენგანს შეუძლია შექმნას რაღაც ინდივიდუალური. ერთად თქვენ შეგიძლიათ შექმნათ რაღაც უფრო დიდი."
პროექტის დაწყების მომენტში ყველამ სიტყვასიტყვით შეუტია ტილოს - მასზე ყველა პიქსელი ივსებოდა. თავდაპირველად, მომხმარებლებმა უბრალოდ ფერებს ანაწილებდნენ თავისუფალ პიქსელებში, მაგრამ შემდეგ გუნდებმა დაიწყეს ჩამოყალიბება და დაიწყეს ერთ-ერთი უმარტივესი შესაძლო იდეის დახატვა, რომელმაც იპოვა თანამოაზრეები - სახელმწიფო დროშები. რაც უფრო მეტი მომხმარებელი მუშაობდა ტილოზე, მით უფრო საინტერესო და რთული იდეებიმოვიდა მათ გონებაში. და "ადგილი" პირველი აპრილის პროექტიდან გადაიზარდა ადგილად, სადაც მომხმარებლები მთელი მსოფლიოს მასშტაბით გაერთიანდნენ რეალურ თემებად, რათა აჩვენონ რაღაც მსოფლიოს და მხარი დაუჭირონ მათ შექმნას რეიდერების ჯგუფებისგან, რომლებსაც ნამდვილად სურთ რაღაცის დახატვა. ნახატი.
განათავსეთ პირველი გახსნის საათებში
„ადგილი“ გახდა ონლაინ სტიკერ დაფა, მაგრამ თითოეული მათგანი შრომისმოყვარეობით არის შექმნილი. მაგალითად, 48 x 68 პიქსელის ზომის ლინუქსის ლოგოს დასახატად და დასაცავად, 3264 ადამიანმა უნდა გააკეთოს ეს ერთდროულად.
![](https://i2.wp.com/medialeaks.ru/wp-content/uploads/2017/04/Bezyimyannyiy-4.jpg)
მომხმარებლები იკრიბებიან იმისათვის, რომ განახორციელონ იდეები, რომლებიც უფრო მცირე და მარტივია, მაგრამ მაინც ძალიან გუნდზე ორიენტირებული, როგორიცაა გულების ეს რიგი დროშებით სხვა და სხვა ქვეყნები(და არა მარტო): ყველა პასუხისმგებელია „საკუთარ“ გულზე, მაგრამ ყველა ერთად ადამიანი უნებურად ქმნის ერთ გუნდს.
![](https://i0.wp.com/medialeaks.ru/wp-content/uploads/2017/04/Bezyimyannyiy-5.jpg)
და სხვები წერენ ტექსტის მთელ ტილოებს, მაგალითად, თაყვანისმცემელთა ამ ჯგუფის მსგავსად. Ვარსკვლავური ომები”, რომელმაც დაწერა და მხარი დაუჭირა უზენაესი კანცლერის პალპატინის ცნობილ მონოლოგს სით დართ პლაგეისის შესახებ კოსმოსური საგის მესამე ეპიზოდიდან.
![](https://i0.wp.com/medialeaks.ru/wp-content/uploads/2017/04/Bezyimyannyiy-6.jpg)
თუმცა, ზოგიერთი მომხმარებელი ჩიოდა, რომ პროექტის მონაწილეებმა თავი შეცვალეს ბოტებით, რომლებიც ავტომატურად განაახლებს ავტორის მიერ დაკავებულ პიქსელს ყოველ ხუთ წუთში ერთხელ. იმისდა მიუხედავად, რომ თითოეული პიქსელის დახატვისას მომხმარებელმა უნდა გაიმეოროს მოქმედებების ნაკრები (მაგალითად, კონკრეტული ფერის არჩევა), ზოგიერთმა შეძლო უსაფრთხოების მექანიზმის გვერდის ავლით და შექმნა ბოტები, რომლებიც მათთვის სურათებს ხატავენ.
თუმცა, არიან ისეთებიც, რომლებიც ჯერ კიდევ ქმნიან სპეციალურ ძაფებს და ცდილობენ წაახალისონ თანამოაზრეები, რომლებიც დაეხმარებიან ხატვაში და მომავალ თაობებს დაუტოვებენ რაღაცის მსგავსი... რიკ სანჩესის ღებინება მულტფილმიდან "Rick and Morty". სერიოზულად? დარწმუნებული ხარ რომ ეს ბოტები არ არის?
![](https://i2.wp.com/medialeaks.ru/wp-content/uploads/2017/04/Bezyimyannyiy-7.jpg)
72 საათიანი მუშაობის შემდეგ პროექტი დაიხურა. რესურსების ადმინისტრაციამ მადლობა გადაუხადა ყველას მონაწილეობისთვის და იმისთვის, რომ ხალხი გაერთიანდა „რაღაც მეტის შესაქმნელად“.
Reddit ხშირად ხდება სხვადასხვა ადგილის ადგილი სოციალური აქტივობები. მაგალითად, ახლახანს რესურსის მოწყენილმა მომხმარებლებმა გადაწყვიტეს ეკითხათ, როგორია ყოველდღე ღიმილით გაღვიძება. მანამდე კი Reddit-ის მკითხველებმა ერთმანეთს გაუზიარეს ისტორიები გოგონების შესახებ. ეს არ არის მხოლოდ ანონიმური ადამიანები, რომლებიც იყენებენ პოპულარულ რესურსს: მსახიობმა ცოტა ხნის წინ რამდენიმე საათი უპასუხა მომხმარებლების კითხვებს ფილმის "Trainspotting" შესახებ.
დასაწყისისთვის, უაღრესად მნიშვნელოვანი იყო პირველი აპრილის პროექტის მოთხოვნების დადგენა, რადგან ის უნდა გაშვებულიყო „გადატვირთვის“ გარეშე, რათა Reddit-ის ყველა მომხმარებელს დაუყოვნებლივ შეეძლო მასზე წვდომა. თავიდანვე იდეალურად რომ არ ემუშავა, ძნელად ბევრი ადამიანის ყურადღებას მიიპყრობდა.
"დაფა" უნდა იყოს 1000x1000 ფილების ზომის, რომ ძალიან დიდი გამოიყურებოდეს.
ყველა კლიენტი უნდა იყოს სინქრონიზებული და აჩვენოს დაფის იგივე მდგომარეობა. ყოველივე ამის შემდეგ, თუ სხვადასხვა მომხმარებლებს აქვთ სხვადასხვა ვერსიები, მათ გაუჭირდებათ ურთიერთობა.
თქვენ უნდა მხარი დაუჭიროთ მინიმუმ 100000 ერთდროულ მომხმარებელს.
მომხმარებლებს შეუძლიათ ყოველ ხუთ წუთში ერთი კრამიტის განთავსება. აქედან გამომდინარე, აუცილებელია შენარჩუნდეს განახლების საშუალო სიჩქარე 100,000 ფილა ხუთ წუთში (333 განახლება წამში).
პროექტი უარყოფითად არ უნდა იმოქმედოს საიტის სხვა ნაწილებისა და ფუნქციების მუშაობაზე (თუნდაც r/Place-ზე მაღალი ტრეფიკია).
- მოქნილი კონფიგურაცია უნდა იყოს უზრუნველყოფილი მოულოდნელი შეფერხებების ან წარუმატებლობის შემთხვევაში. ანუ, თქვენ უნდა შეგეძლოთ დაფის ზომის და დაშვებული ნახაზის სიხშირის რეგულირება, თუ მონაცემთა რაოდენობა ძალიან დიდია ან განახლების სიხშირე ძალიან მაღალია.
Backend
განხორციელების გადაწყვეტილებები
ბექენდის შექმნის მთავარი სირთულე იყო დაფის სტატუსის ჩვენების სინქრონიზაცია ყველა კლიენტისთვის. გადაწყდა, რომ კლიენტებმა მოესმინათ კრამიტის განთავსების მოვლენები რეალურ დროში და დაუყოვნებლივ მოეკითხათ მთელი დაფის მდგომარეობა. ოდნავ მოძველებული სრული მდგომარეობის არსებობა მისაღებია, თუ თქვენ გამოიწერეთ განახლებები სრული მდგომარეობის გენერირებამდე. როდესაც კლიენტი იღებს სრულ მდგომარეობას, ის აჩვენებს ყველა ფილას, რომელიც მიიღო ლოდინის დროს; ყველა შემდგომი ფილა უნდა გამოჩნდეს დაფაზე მიღებისთანავე.
იმისათვის, რომ ამ სქემმა იმუშაოს, მოთხოვნა დაფის სრული მდგომარეობის შესახებ უნდა დასრულდეს რაც შეიძლება სწრაფად. თავიდან გვინდოდა მთელი დაფა ერთ რიგში შეგვენახა კასანდრაში და ყოველი მოთხოვნა უბრალოდ წაეკითხა ეს მწკრივი. ამ სტრიქონში თითოეული სვეტის ფორმატი იყო:
(x, y): („დროის შტამპი“: ეპოქები, „ავტორი“: მომხმარებლის_სახელი, „ფერი“: ფერი)
მაგრამ რადგან დაფა შეიცავს მილიონ ფილას, დაგვჭირდა მილიონი სვეტის წაკითხვა. ჩვენს წარმოების კლასტერზე ამას 30 წამამდე დასჭირდა, რაც მიუღებელი იყო და შეიძლება გამოიწვიოს კასანდრაზე გადაჭარბებული დატვირთვა.
შემდეგ გადავწყვიტეთ მთელი დაფა შეგვენახა რედისში. ჩვენ ავიღეთ მილიონი ოთხბიტიანი რიცხვის ბიტის ველი, რომელთაგან თითოეულს შეეძლო ოთხბიტიანი ფერის დაშიფვრა, ხოლო x და y კოორდინატები განისაზღვრა ოფსეტით (ოფსეტი = x + 1000y) ბიტის ველში. დაფის სრული მდგომარეობის მისაღებად, მთელი ბიტის ველი უნდა წაიკითხოთ.
შესაძლებელი იყო ფილების განახლება მნიშვნელობების განახლებით კონკრეტული ოფსეტებით (არ არის საჭირო მთელი წაკითხვის/განახლების/ჩაწერის პროცედურის დაბლოკვა ან განხორციელება). მაგრამ ყველა დეტალი მაინც უნდა იყოს შენახული კასანდრაში, რათა მომხმარებლებმა გაარკვიონ, ვინ და როდის მოათავსა თითოეული ფილა. ჩვენ ასევე ვგეგმავდით კასანდრას გამოყენებას დაფის აღსადგენად, როდესაც რედისი ჩამოვარდა. მისგან მთელი დაფის წაკითხვას 100 ms-ზე ნაკლები დასჭირდა, რაც საკმაოდ სწრაფი იყო.
აი, როგორ ვინახავდით ფერებს Redis-ში 2x2 დაფის მაგალითის გამოყენებით:
ჩვენ ვნერვიულობდით, რომ შეიძლება შეგვექმნა წაკითხვის გამტარუნარიანობა Redis-ში. თუ ბევრი კლიენტი დაუკავშირდება ან განახლდება ერთდროულად, ისინი ყველა ერთდროულად აგზავნიან მოთხოვნებს დაფის სრული მდგომარეობის შესახებ. ვინაიდან დაფა წარმოადგენდა საერთო გლობალურ სახელმწიფოს, აშკარა გამოსავალი იყო ქეშირების გამოყენება. ჩვენ გადავწყვიტეთ ქეში ჩაგვეყენებინა CDN (სწრაფად) დონეზე, რადგან მისი დანერგვა უფრო ადვილი იყო, ხოლო ქეში მიღებულ იქნა კლიენტებთან ყველაზე ახლოს, რამაც შეამცირა პასუხის მიღების დრო.
მოთხოვნები სრული დაფის მდგომარეობაზე ქეშირებული იყო Fastly-ის მიერ წამის დროით. Თავიდან ასაცილებლად დიდი რიცხვიითხოვს, როდესაც ვადა ამოიწურა, ჩვენ გამოვიყენეთ stale-while-revalidate header. Fastly მხარს უჭერს დაახლოებით 33 POP-ს, რომლებიც დამოუკიდებლად ქეშირებენ ერთმანეთს, ამიტომ ველოდით, რომ მივიღებდით 33-მდე სრული დაფის სტატუსის მოთხოვნას წამში.
ყველა კლიენტისთვის განახლებების გამოსაქვეყნებლად, ჩვენ გამოვიყენეთ ჩვენი ვებსოკეტის სერვისი. ჩვენ ადრე წარმატებით ვიყენებდით Reddit.Live-ს 100000-ზე მეტ ერთდროულად მომხმარებელთან ერთად Live პირადი შეტყობინების შეტყობინებებისა და სხვა ფუნქციებისთვის. მომსახურებაც იყო ქვაკუთხედიჩვენი წარსული პირველი აპრილის პროექტები - The Button and Robin. r/Place-ის შემთხვევაში, კლიენტებმა მხარი დაუჭირეს ვებსოკეტის კავშირებს, რათა მიეღოთ რეალურ დროში განახლებები ფილების განთავსებაზე.
API
სრული დაფის მდგომარეობის მიღება
![](https://i1.wp.com/habrastorage.org/files/c6b/c1b/5e7/c6bc1b5e76f443d5867d93cd0fb120c0.png)
თავდაპირველად, მოთხოვნები წავიდა Fastly-ში. თუ მას ჰქონდა დაფის მოქმედი ასლი, ის დაუყოვნებლივ დააბრუნებდა მას Reddit-ის აპლიკაციის სერვერებთან დაკავშირების გარეშე. თუ არა, ან ასლი ძალიან ძველი იყო, მაშინ Reddit აპი წაიკითხავდა სრულ დაფას Redis-დან და დააბრუნებდა მას Fastly-ში, რათა ქეშებულიყო და დაუბრუნდეს კლიენტს.
![](https://i0.wp.com/habrastorage.org/files/16e/ee3/8bc/16eee38bcb1c43d99700a9d1425e2dcd.png)
გაითვალისწინეთ, რომ მოთხოვნის სიჩქარე არასოდეს აღწევდა 33 წამში, რაც იმას ნიშნავს, რომ ქეშირება Fastly-ით ძალიან იყო ეფექტური საშუალებები Reddit აპის დაცვა უმეტესი მოთხოვნებისგან.
![](https://i1.wp.com/habrastorage.org/files/245/f4c/700/245f4c7007914e29a61c9c8cef1f0fdb.png)
და როდესაც მოთხოვნამ მიაღწია განაცხადს, რედისმა ძალიან სწრაფად უპასუხა.
კრამიტის დახატვა
![](https://i1.wp.com/habrastorage.org/files/7ce/d3f/c1e/7ced3fc1e0e7426592a3d1afe77543d5.png)
კრამიტის დახატვის ეტაპები:
- მომხმარებლის მიერ კრამიტის ბოლო განთავსების დროის ანაბეჭდი იკითხება კასანდრადან. თუ ხუთ წუთზე ნაკლები იყო, მაშინ არაფერს ვაკეთებთ და შეცდომა უბრუნდება მომხმარებელს.
- კრამიტის დეტალები ეწერება რედისს და კასანდრას.
- მიმდინარე დრო ჩაწერილია კასანდრაში, როგორც ბოლო დროს, როდესაც ფილა მოათავსეს მომხმარებლის მიერ.
- ვებსოკეტის სერვისი უგზავნის შეტყობინებას ყველა დაკავშირებულ კლიენტს ახალი კრამიტის შესახებ.
მკაცრი თანმიმდევრულობის შესანარჩუნებლად, ყველა ჩაწერა და წაკითხვა კასანდრაში შესრულდა QUORUM თანმიმდევრულობის ფენის გამოყენებით.
სინამდვილეში, ჩვენ გვქონდა რბოლა აქ, სადაც მომხმარებლებს შეეძლოთ ერთდროულად რამდენიმე ფილების განთავსება. 1-3 ეტაპებზე არ იყო დაბლოკვა, ამიტომ ფილების დახატვის ერთდროული მცდელობა შეიძლება გაიაროს შემოწმება პირველ ეტაპზე და დახაზულიყო მეორეში. როგორც ჩანს, ზოგიერთმა მომხმარებელმა აღმოაჩინა ეს შეცდომა (ან გამოიყენეს ბოტები, რომლებიც იგნორირებას უკეთებდნენ მოთხოვნის სიხშირის ლიმიტს) - და შედეგად, დაახლოებით 15,000 ფილა განთავსდა მისი გამოყენებით (სულის 0,09%).
მოთხოვნის განაკვეთები და პასუხების დრო, რომელიც იზომება Reddit აპლიკაციით:
![](https://i1.wp.com/habrastorage.org/files/360/16d/850/36016d850d0349d88b12e971c372a723.png)
ფილების განლაგების პიკი იყო თითქმის 200 წამში. ეს ჩვენს სავარაუდო ლიმიტს 333 ფილა/წმ-ზე დაბალია (საშუალოდ იმ ვარაუდით, რომ 100 000 მომხმარებელი ათავსებს ფილებს ყოველ ხუთ წუთში).
![](https://i1.wp.com/habrastorage.org/files/ecd/ca9/7a7/ecdca97a7ee64bf5ad6c982938365598.png)
დეტალების მიღება კონკრეტული ფილისთვის
![](https://i0.wp.com/habrastorage.org/files/12f/b92/563/12fb925631094e738354c4ad3b186320.png)
კონკრეტული ფილების მოთხოვნისას, მონაცემები პირდაპირ კასანდრადან იკითხებოდა.
მოთხოვნის განაკვეთები და პასუხების დრო, რომელიც იზომება Reddit აპლიკაციით:
![](https://i0.wp.com/habrastorage.org/files/49b/157/286/49b157286eed4041962eae41c0337de8.png)
ეს მოთხოვნა ძალიან პოპულარული აღმოჩნდა. კლიენტის რეგულარული მოთხოვნის გარდა, ადამიანებს აქვთ დაწერილი სკრიპტები, რომ მოიძიონ მთელი დაფა ერთი ფილა ერთდროულად. ვინაიდან ეს მოთხოვნა არ იყო ქეშირებული CDN-ში, ყველა მოთხოვნას ემსახურებოდა Reddit აპლიკაცია.
![](https://i1.wp.com/habrastorage.org/files/730/443/62b/73044362b2b44ab0805a5fad19639cdd.png)
ამ თხოვნებზე რეაგირების დრო საკმაოდ მოკლე იყო და პროექტის მთელი ცხოვრების მანძილზე რჩებოდა იმავე დონეზე.
ვებსოკეტები
ჩვენ არ გვაქვს ინდივიდუალური მეტრიკა, რომელიც აჩვენებს, თუ როგორ იმოქმედა r/Place-მა websocket სერვისზე. მაგრამ ჩვენ შეგვიძლია შევაფასოთ მნიშვნელობები პროექტის დაწყებამდე და მისი დასრულების შემდეგ მონაცემების შედარებით.
ვებსოკეტის სერვისთან კავშირების საერთო რაოდენობა:
![](https://i1.wp.com/habrastorage.org/files/7f0/a07/fdb/7f0a07fdbf164baba18d5f3749fad963.png)
საბაზისო დატვირთვა r/Place-ის გაშვებამდე იყო დაახლოებით 20000 კავშირი, პიკი იყო 100000 კავშირი. ასე რომ, პიკზე ჩვენ ალბათ გვყავდა დაახლოებით 80,000 კონკურენტი მომხმარებელი დაკავშირებული r/Place-თან.
Websocket სერვისის გამტარუნარიანობა:
![](https://i2.wp.com/habrastorage.org/files/b72/cfb/6d4/b72cfb6d4b054a538711d760008d1448.png)
r/Place-ზე დატვირთვის პიკზე, websocket სერვისმა გადაიტანა 4 გბიტ/წმ-ზე მეტი (150 Mbps თითო ინსტანციაზე, სულ 24 შემთხვევა).
Frontend: ვებ და მობილური კლიენტები
Place-ის ფრონტენტის შექმნის პროცესში ჩვენ მოგვიწია მრავალი რთული პრობლემის გადაჭრა, რომლებიც დაკავშირებულია კროს-პლატფორმის განვითარებასთან. ჩვენ გვინდოდა, რომ პროექტი ერთნაირად მუშაობდეს ყველა ძირითად პლატფორმაზე, მათ შორის დესკტოპ კომპიუტერებზე და მობილურ მოწყობილობებზე iOS და Android-ზე.
მომხმარებლის ინტერფეისს სამი მნიშვნელოვანი ფუნქცია უნდა შეესრულებინა:
- დაფის სტატუსის ჩვენება რეალურ დროში.
- ნება მიეცით მომხმარებლებს ურთიერთქმედონ დაფასთან.
- იმუშავეთ ყველა პლატფორმაზე, მათ შორის მობილურ აპლიკაციებზე.
ინტერფეისის მთავარი ობიექტი იყო ტილო და Canvas API იდეალური იყო მისთვის. ჩვენ გამოვიყენეთ ელემენტი
ტილოს დახატვა
ტილო რეალურ დროში უნდა ასახავდეს დაფის მდგომარეობას. საჭირო იყო მთელი დაფის დახატვა, როდესაც გვერდი ჩაიტვირთა და დასრულებულიყო ვებსოკეტების მეშვეობით შემოსული განახლებების ნახაზი. ტილოს ელემენტი, რომელიც იყენებს CanvasRenderingContext2D ინტერფეისს, შეიძლება განახლდეს სამი გზით:
- დახაზეთ არსებული სურათი ტილოში drawImage()-ის გამოყენებით.
- ფორმების დახატვა გამოყენებით სხვადასხვა მეთოდებიხატვის ფორმები. მაგალითად, fillRect() ავსებს მართკუთხედს გარკვეული ფერით.
- შექმენით ImageData ობიექტი და დახატეთ იგი ტილოზე putImageData()-ის გამოყენებით.
პირველი ვარიანტი არ შეგვეფერა, რადგან არ გვქონდა დაფა მზა გამოსახულების სახით. ეს დარჩა 2 და 3 ვარიანტები. უმარტივესი გზა იყო ცალკეული ფილების განახლება fillRect()-ის გამოყენებით: როდესაც განახლება მოდის websocket-ის მეშვეობით, ჩვენ უბრალოდ ვხატავთ 1x1 ოთხკუთხედს (x, y) პოზიციაზე. ზოგადად, მეთოდი მუშაობდა, მაგრამ არ იყო ძალიან მოსახერხებელი რენდერისთვის საწყისი მდგომარეობადაფები. putImageData() მეთოდი ბევრად უკეთესი იყო: ჩვენ შეგვიძლია განვსაზღვროთ თითოეული პიქსელის ფერი ერთ ImageData ობიექტში და ერთდროულად დავხატოთ მთელი ტილო.
დაფის საწყისი მდგომარეობის დახატვა
putImageData()-ის გამოყენება მოითხოვს დაფის მდგომარეობის განსაზღვრას Uint8ClampedArray-ის სახით, სადაც თითოეული მნიშვნელობა არის რვა ბიტიანი ხელმოუწერელი რიცხვი 0-დან 255-მდე დიაპაზონში. თითოეული მნიშვნელობა წარმოადგენს ფერის არხს (წითელი, მწვანე, ლურჯი, ალფა) და თითოეული პიქსელს სჭირდება ოთხი ელემენტი მასივში. 2x2 ტილო მოითხოვს 16-ბაიტიან მასივს, რომელშიც პირველი ოთხი ბაიტი წარმოადგენს ტილოს ზედა მარცხენა პიქსელს, ხოლო ბოლო ოთხი წარმოადგენს ქვედა მარჯვენა პიქსელს.
აი, როგორ უკავშირდება ტილოს პიქსელები მათ Uint8ClampedArray წარმოდგენებს:
![](https://i1.wp.com/habrastorage.org/files/5d9/738/4f0/5d97384f09ef4b648f3b5a97338b5199.png)
ჩვენი პროექტის ტილოსთვის დაგვჭირდა ოთხი მილიონი ბაიტის მასივი - 4 მბ.
უკანა ნაწილში, დაფის მდგომარეობა ინახება ოთხბიტიან ველად. თითოეული ფერი წარმოდგენილია რიცხვით 0-დან 15-მდე, რაც საშუალებას გვაძლევს შეგვეკრა ორი პიქსელი თითოეულ ბაიტში. კლიენტის მოწყობილობაზე გამოსაყენებლად, თქვენ უნდა გააკეთოთ სამი რამ:
- გადაიტანეთ ორობითი მონაცემები ჩვენი API-დან კლიენტზე.
- ამოალაგეთ მონაცემები.
- გადაიყვანეთ ოთხბიტიანი ფერები 32-ბიტიანში.
ორობითი მონაცემების გადასატანად, ჩვენ გამოვიყენეთ Fetch API იმ ბრაუზერებში, რომლებიც მხარს უჭერენ მას. და მათ, ვინც მხარს არ უჭერს, ჩვენ გამოვიყენეთ XMLHttpRequest answerType-ზე დაყენებულია “arraybuffer”-ზე.
API-დან მიღებული ბინარული მონაცემები შეიცავს ორ პიქსელს თითოეულ ბაიტში. ყველაზე პატარა TypedArray კონსტრუქტორი, რომელიც ჩვენ გვქონდა, საშუალებას გაძლევთ იმუშაოთ ბინარულ მონაცემებთან ერთბაიტიანი ერთეულების სახით. მაგრამ მათი გამოყენება რთულია კლიენტის მოწყობილობებზე, ამიტომ ჩვენ გავხსენით მონაცემები, რათა გაადვილებულიყო მუშაობა. პროცესი მარტივია: ჩვენ გავიმეორეთ შეფუთული მონაცემების მეშვეობით, ამოვიღეთ მაღალი და დაბალი რიგის ბიტები და შემდეგ დავაკოპირეთ ისინი ცალკეულ ბაიტებად სხვა მასივში.
საბოლოოდ, ოთხი ბიტიანი ფერები უნდა გადაექცია 32 ბიტიანზე.
![](https://i1.wp.com/habrastorage.org/files/890/500/cb8/890500cb8f6b4dfd82ea1f708753935b.png)
ImageData სტრუქტურა, რომელიც გვჭირდებოდა putImageData()-ის გამოსაყენებლად ამას მოითხოვს საბოლოო შედეგიიყო Uint8ClampedArray-ის სახით ბაიტებით, რომლებიც კოდირებენ ფერთა არხებს RGBA თანმიმდევრობით. ეს ნიშნავს, რომ ჩვენ გვჭირდებოდა კიდევ ერთი დეკომპრესია, თითოეული ფერის დაყოფა არხის კომპონენტურ ბაიტებად და განთავსება მათ სწორ ინდექსში. არ არის ძალიან მოსახერხებელი პიქსელზე ოთხი ჩაწერის შესრულება. მაგრამ, საბედნიეროდ, სხვა ვარიანტიც იყო.
TypedArray ობიექტები არსებითად ArrayBuffer-ის მასივის წარმოდგენებია. აქ არის ერთი სიფრთხილე: რამდენიმე TypedArray ინსტანციას შეუძლია წაიკითხოს და ჩაწეროს იმავე ArrayBuffer მაგალითზე. ჩაწერის ნაცვლად ოთხი ღირებულებარვა-ბიტიან მასივში შეგვიძლია ჩავწეროთ ერთი მნიშვნელობა 32-ბიტიანში! წერისთვის Uint32Array-ის გამოყენებით, ჩვენ შევძელით ადვილად განვაახლოთ კრამიტის ფერები მხოლოდ ერთი მასივის ინდექსის განახლებით. თუმცა, ჩვენ უნდა შევინახოთ ჩვენი ფერის პალიტრა დიდი ბაიტის თანმიმდევრობით (ABGR), რათა ბაიტები ავტომატურად დასრულდეს სწორ ადგილებში Uint8ClampedArray-ის გამოყენებით წაკითხვისას.
![](https://i1.wp.com/habrastorage.org/files/f10/6bd/3d0/f106bd3d01b84a699a11d40a5904c6bd.png)
მიმდინარეობს ვებსოკეტით მიღებული განახლებების დამუშავება
drawRect() მეთოდი კარგი იყო ცალკეული პიქსელების განახლებების გამოსაყენებლად მათი მიღებისას, მაგრამ იყო ერთი სისუსტე: განახლებების დიდმა პარტიამ, რომელიც ერთბაშად მოვიდა, შეიძლება გამოიწვიოს ბრაუზერების შენელება. ჩვენ გვესმოდა, რომ საბჭოს სტატუსის განახლებები შეიძლება ძალიან ხშირად მოხდეს, ამიტომ პრობლემა როგორმე უნდა მოგვარებულიყო.
იმის ნაცვლად, რომ დაუყოვნებლივ გადაგვეხატა ტილო ყოველ ჯერზე, როდესაც განახლება მიიღება ვებსოკეტის საშუალებით, ჩვენ გადავწყვიტეთ, რომ ის ისე გაგვეკეთებინა, რომ ვებსოკეტის განახლებები, რომლებიც ერთსა და იმავე დროს მოდის, შესაძლებელი იყოს ჯგუფური და მასობრივად გადაცემა. ამის მისაღწევად ორი ცვლილება განხორციელდა:
- შეწყვიტე drawRect()-ის გამოყენება - აღმოვაჩინეთ მოსახერხებელი გზაგანაახლეთ მრავალი პიქსელი ერთდროულად putImageData() გამოყენებით.
- ტილოს რენდერის გადატანა requestAnimationFrame ციკლში.
რენდერის ანიმაციის მარყუჟში გადატანით, ჩვენ შევძელით დაუყოვნებლივ ჩაგვეწერა ვებსოკეტის განახლებები ArrayBuffer-ზე, ხოლო რეალური რენდერის გადადება. ყველა websocket განახლება, რომელიც მოდის ჩარჩოებს შორის (დაახლოებით 16 ms) დაჯგუფებული და ერთდროულად გადაიცემა. requestAnimationFrame-ის გამოყენების წყალობით, თუ რენდერირებას ძალიან დიდი დრო დასჭირდა (16 ms-ზე მეტი), ეს გავლენას მოახდენდა მხოლოდ ტილოს განახლების სიხშირეზე (ვიდრე მთელი ბრაუზერის მუშაობის შემცირებას).
ტილოსთან ურთიერთობა
მნიშვნელოვანია აღინიშნოს, რომ ტილო იყო საჭირო იმისათვის, რომ მომხმარებლებისთვის უფრო მოსახერხებელი ყოფილიყო სისტემასთან ურთიერთობა. ურთიერთქმედების მთავარი სცენარი არის ფილების განთავსება ტილოზე.
მაგრამ თითოეული პიქსელის ზუსტი რენდერის გაკეთება 1:1 მასშტაბით ძალიან რთული იქნება და შეცდომებს არ ავიცილებთ თავიდან. ასე რომ, ჩვენ გვჭირდებოდა (დიდი!) მასშტაბირება. გარდა ამისა, მომხმარებლებს სჭირდებოდათ ტილოზე ადვილად ნავიგაცია, რადგან ის ძალიან დიდი იყო ეკრანების უმეტესობისთვის (განსაკუთრებით მასშტაბირების გამოყენებისას).
მასშტაბირება
ვინაიდან მომხმარებლებს შეეძლოთ ფილების განთავსება ხუთ წუთში ერთხელ, განლაგების შეცდომები მათთვის განსაკუთრებით იმედგაცრუებული იქნებოდა. საჭირო იყო ისეთი ფაქტორის გადიდების განხორციელება, რომ ფილა საკმარისად დიდი ყოფილიყო და ადვილად მოთავსებულიყო Სწორი ადგილი. ეს განსაკუთრებით მნიშვნელოვანი იყო სენსორულ მოწყობილობებზე.
ჩვენ განვახორციელეთ 40x მასშტაბირება, ანუ თითოეულ ფილას ჰქონდა ზომა 40x40. ჩვენ შევფუთეთ ელემენტი