Tại sao team Discord chuyển từ Go sang Rust?

Lập Trình
Tại sao team Discord chuyển từ Go sang Rust?

Tác giả: Jesse Howarth

Ngôn ngữ Rust đang dần trở thành sự lựa chọn hàng đầu cho rất nhiều domain. Điển hình với Discord, chúng ta có thể thấy thành công của Rust trên cả client side và server side. Ví dụ, chúng tôi dùng nó cho đường pipeline mã hoá video từ Go Live (client side) và dùng cho Elixir NIFs (server side). Mới đây nhất, chúng tôi đã tiến hành cấp tốc cải thiện hiệu suất dịch vụ bằng cách chuyển từ Go sang Rust. Bài viết này sẽ giải thích thêm tại sao nó lại thích hợp với Discord chúng tôi, quy trình nó thế nào và kết quả hậu chuyển đổi ra sao.

The Read States service

Discord là một công ty thiên sản phẩm, nên hãy bắt đầu câu chuyện bằng một số chi tiết về product. Mảng dịch vụ mà chúng tôi thay Go bằng Rust được gọi là dịch vụ “Read States” – Theo dõi trạng thái đọc tin nhắn. Mục đích tối thượng của nó là theo dõi xem bạn đã đọc những tin nhắn và kênh nào. Cứ mỗi khi bạn mở Discord thì “Read States” sẽ được kích hoạt với mỗi khi tin nhắn được gửi đi hoặc được đọc.

Ngày trước khi ứng dụng Go, Read States không thể hỗ trợ một số yêu cầu về sản phẩm. Thường thì nó khá nhanh, nhưng cứ lâu lâu sẽ lại bắt gặp những khoảng chậm ảnh hưởng xấu đến trải nghiệm người dùng. Sau khi tìm hiểu chúng tôi đã hiểu ra các đợt nhiễu này là do feature chính của Go: Model bộ nhớ và Garbage Collector (GC) – bộ phận gom rác của nó.

Tại sao Go không đạt được kỳ vọng của chúng tôi?

Để giải thích cụ thể hơn tại sao Go không thể giúp chúng tôi đạt được thứ mình muốn, hãy cùng “đào mộ” sâu hơn về cấu trúc dữ liệu, quy mô, các access pattern, và kiến trúc của dịch vụ.

Cấu trúc data mà chúng ta hay dùng để lưu trữ thông tin trạng thái đọc thường gọi cho thuận là “Read State” (kiểu trạm Read State chứa read states…). Discord có cả tỉ cái Read State này. Cứ mỗi User một kênh là sẽ có một Read State. Mỗi Read State có rất nhiều counter cần được update hết và thường sẽ reset về lại 0. Ví dụ, một trong các counter sẽ là bạn có bao nhiêu lần @được_tag trên một kênh.

Để có được update counter nhanh gọn, mỗi Read State server đều có một cache Least Recently Used – LRU (cache ít được dùng nhất gần đây) trên Read States. Có đến hàng triệu users trên mỗi cache, và cả triệu Read States trên mỗi cache. Và chưa kể là cả trăm ngàn cái update cache mỗi giây. 

Cụ thể hơn, chúng tôi đã hỗ trợ cache bằng một cái database cluster Cassandra. Một khi cache key đã bỏ, chúng tôi sẽ gửi Read States của bạn về database. Chúng tôi còn set thời gian cho mỗi lần gửi về database mỗi 30 giây trong tương lai bất cứ khi nào Read State được cập nhật. Có khoảng cả mười ngàn database viết đè mỗi giây.

Trong ảnh bên dưới, bạn sẽ thấy thời gian phản hồi và cpu hệ thống trong thời gian đỉnh điểm khi còn dùng Go. Nếu bạn để ý thì cứ 2 phút sẽ có một đợt nhiễu CPU và chậm trễ khá mạnh.

Vậy tại sao lại là 2 phút?

Về việc dùng Go, mỗi khi bỏ cache key, bộ nhớ vẫn chưa trống ngay. Thay vào đó, bộ phận gom rác (garbage collector) sẽ chạy liên tục để tìm xem có phần bộ nhớ nào không có references và giải phóng nó. Nói cách khác, thay vì giải phóng phần bộ nhớ không còn dùng nữa, bộ nhớ sẽ còn ở đó một lát cho đến khi bộ phận gom rác xác định được có cần dùng đến nó nữa không. Trong thời gian này, Go sẽ phải làm rất nhiều thứ để xác định bộ nhớ nào làm chậm chương trình và cần giải phóng. 

Những đợt nhiễu chắc chắn đã biết được tác động của hiệu suất của bộ phận gom rác, tuy nhiên chúng tôi đã viết code Go rất hiệu quả rồi và có rất ít allocation (chỉ định). Chúng tôi không tạo quá nhiều rác.

Sau khi đào sâu hơn vào trong source code Go, chúng tôi nhận ra rằng Go sẽ buộc bộ phận gom rác này phải chạy mỗi 2 phút (tối thiểu). Nói cách khác, nếu bộ phận gom rác không hoạt động trong 2 phút, mặc cho có heap growth, Go vẫn bắt nó phải chạy.

Chúng tôi tìm ra rằng có thể điều chỉnh bộ phận gom rác để hoạt động thường xuyên hơn để ngăn chặn các trường hợp nhiễu lớn, vì thế chúng tôi ứng dụng một điểm endpoint lên dịch vụ để thay đổi ngay phần gom rác GC Percent. Đáng tiếc là dù cho chúng tôi định hình GC Percent như thế nào chẳng có gì thay đổi cả. Tại sao? Hoá ra lý do là vì chúng tôi chưa định vị bộ nhớ đủ nhanh để buộc garbage collector phải chạy thường xuyên hơn. 

Chúng tôi tiếp tục đào sâu hơn và hiểu ra rằng các đợt nhiễu lớn không phải do khối lượng lớn bộ nhớ cần giải phóng, mà là do garbage collector cần phải scan cả LRU cache để xác định xem bộ nhớ có hoàn toàn không được dùng đến nữa không. Ngay khi chúng tôi đã tìm được một cache LRU nhỏ hơn để nhanh hơn vì garbage collector chỉ sẽ scan ít hơn. Từ đó chúng tôi add thêm một setting mới vào service để thay đổi size của LRU cache và thay đổi cả kiến trúc để có thể chia ra nhiều cache LRU trên một server.

Và chúng tôi đã đúng! Với cache LRU nhỏ hơn, garbage collector gây ra các đoạn nhiều nhỏ hơn. 

Không may thay, cái phải đánh đổi cho việc dùng cache LRU nhỏ hơn đó là thời gian trễ cao hơn. Bởi vì nếu cache nhỏ hơn thì Read State của user có thể sẽ không nằm trên cache đó. Nếu vậy đồng nghĩa rằng sẽ phải lội vào database load …

Sau một lượng load đáng kể thử nhiều cache capacity khác nhau, chúng tôi tìm được một setting khá oke. Không thật sự xuất sắc, nhưng vừa-đủ-xài và với quy mô ngày càng lớn, chúng tôi để cho nó chạy như thế một thời gian.

Trong thời gian đó chúng tôi thấy được ngày càng nhiều công ty thành công với Rust ở những phần khác của Discord và cuối cùng đã quyết định sẽ tạo nên một framework và thư viện cần để build đầy đủ các dịch vụ trong Rust. Đây là ứng cử viên sáng giá để chuyển sang Rust vì nó nhỏ gọn, nhưng tôi cũng mong rằng Rust sẽ sửa được các lỗi nhiễu này. Vì thế chúng tôi tiến hành nhiệm vụ mới: chuyển Read State sang Rust, hi vọng rằng Rust đúng là một ngôn ngữ dịch vụ và cải thiện được trải nghiệm người dùng.

Quản lý bộ nhớ trong Rust

Rust rất nhanh và thân thiện với bộ nhớ: không có runtime và garbage collector, nó có thể thúc đẩy các dịch vụ quan trọng về hiệu suất, chạy trên dịch vụ nhúng, và dễ tích hợp với các ngôn ngữ khác.

Rust không có bộ phận garbage collection, nên chúng ta sẽ tìm hiểu xem liệu nó có cùng nhiễu chậm như Go.

Rust sử dụng một phương pháp quản lý bộ nhớ rất độc mà kết hợp với dạng bộ nhớ “sở hữu”. Căn bản là, Rust sẽ theo dõi được ai có thể đọc và viết lên bộ nhớ. Nó hiểu rằng khi nào chương trình cần dùng bộ nhớ và giải phóng ngay lập tức khi nó không cần dùng đến nữa. Nó sẽ thúc đẩy quy luật của bộ nhớ trong thời gian compile, dường như không thể có bug runtime memory. Bạn sẽ không cần phải theo dõi bộ nhớ một cách thủ công – Compiler sẽ lo chuyện này.

Vậy trong phiên bản Rust của Read States services, khi Read State của một user đã bị xoá khỏi LRU cache nó sẽ lập tức được giải phóng khỏi bộ nhớ. Bộ nhớ read state không ở yên đợi garbage collector gom nó. Rust biết rằng nó không còn được dùng đến nữa và sẽ giải phóng nó ngay. Không có quá trình runtime nào để xác định nó có cần được giải phóng hay không.

Async Rust

Có một vấn đề với hệ sinh thái Rust. Vào thời gian này service đã được ứng dụng lại, Rust stable (**) không được thích hợp và thân thiện với Rust async cho lắm. Với dịch vụ network, lập trình bất đồng bộ (asynchronous programming) là một yêu cầu bắt buộc. Có rất ít các thư viện cộng đồng có thể kích hoạt được async Rust, nhưng nó sẽ đòi hỏi một lượng lớn thủ tục và các thông báo lỗi thì vô cùng khó hiểu.

May mắn thay, team Rust đã miệt mài ngày đêm để làm sao cho lập trình bất đồng bộ dễ dàng hơn, và nó đã có sẵn trên kênh của Rust. 

(**) Rust cung cấp 2 kênh distribution chính: nightly, beta, và stable. Các feature bất ổn định (unstable features) thì chỉ có trên nightly Rust.

Discord chưa bao giờ e dè trước công nghệ mới hứa hẹn cả. Ví dụ, chúng tôi là một trong những người đầu tiên áp dụng Elixir, React, React Native, và Scylla. Nếu có một phần công nghệ nào đó hứa hẹn, chúng tôi không ngại xử lý các khó khăn sẵn có và bất ổn định về edge. Đây là một trong những cách chúng tôi nhanh chóng đạt 250+ triệu user với chỉ dưới 50 kỹ sư.

Việc đón nhận các feature async mới trong Rust là một ví dụ điển hình cho thấy chúng tôi luôn sẵn lòng đón nhận các công nghệ mới và hứa hẹn. Là team kỹ thuật, chúng tôi quyết định chọn Rust nightly và sẽ tiếp tục chạy nightly cho đến khi async được hỗ trợ trên stable. Cứ thế chúng tôi đã cùng xử lý không biết bao nhiêu vấn đề, và đến hiện tại Rust stable đã hỗ trợ cho Rust async. Sự liều lĩnh của chúng tôi cuối cùng cũng xứng đáng.

Áp dụng, load testing và launch 

Việc viết lại code khá là nhanh gọn. Nó bắt đầu từ giai đoạn dịch, rồi gút nó lại sao cho có nghĩa. Ví dụ, Rust có hệ thống gõ rất tuyệt vời hỗ trợ rất tốt cho generic programming, nên chúng ta có thể vứt phần code Go trước đó tồn tại vì thiếu generics đi được rồi. Ngoài ra, Model bộ nhớ của Rust cũng có phần an toàn bộ nhớ trên các thread, nên chúng tôi mới có thể bỏ đi một số mục bảo vệ bộ nhớ thủ công trong Go.

Khi bắt đầu load testing, chúng tôi ngay lập tức hài lòng với kết quả. Độ trễ trong Rust thì tương đương với Go mà không có latency spikes nào!

Cái đáng nói là chúng tôi không phải tốn nhiều công sức vào việc tối ưu hoá vì bản Rust đã được viết sẵn rồi. Kể cả khi nó là tối ưu hoá căn bản, Rust cũng đã vượt trội hơn hẳn so với phiên bản cực kì thủ công của Go. Đây là một minh chứng tuyệt vời cho việc viết các chương trình hiệu quả với Rust dễ dàng như thế nào so với việc “đào bới” mà chúng tôi phải làm với Go.

Tuy nhiên, chúng tôi không hài lòng chỉ đơn giản vì nó giống với hiệu suất của Go. Sau một thời gian kiểm tra và tối ưu hoá hiệu suất, chúng tôi đã vượt xa Go trên gần như mọi thông số hiệu suất. Tất cả từ độ trễ, CPU, và bộ nhớ trên Rust đều tốt hơn.

Tối ưu hoá hiệu suất của Rust bao gồm: 

  1. Chuyển sang BTreeMap thay vì HashMap trên LRU cache để tối ưu hoá dung lượng bộ nhớ.
  2. Thay thế bớt thư viện thông số đầu tiên và thay bằng cái được dùng trong Rust concurrency mới.
  3.  Giảm số bản sao bộ nhớ đang làm. 

Từ đó mà chúng tôi chính thức vận hành như thế.

Việc launch thì khá trơn tru vì chúng tôi đã cho chạy thử. Chúng tôi đưa nó vào một canary node duy nhất, tìm thấy một vài trường hợp còn thiếu và sửa chúng. Ngay sau đó chúng tôi đã đưa nó ra toàn bộ dịch vụ.

Bên dưới là kết quả thu được.

Go là màu tím, Rust là màu xanh.

Nâng khả năng của cache 

Sau khi service đã chạy khá thành công được vài ngày, chúng tôi quyết định đã đến lúc thử nâng năng suất của LRU cache lên lần nữa. Trên bản Go như đã đề cập, việc nâng mức trần của LRU cache sẽ dẫn đến việc thu gom rác lâu hơn. Chúng tôi không phải giải quyết với phần gom rác nữa, nên chúng tôi phải tìm cách tăng size của cache và đạt hiệu suất tốt hơn. Chúng tôi tăng bộ nhớ trong các box, tối ưu hoá cấu trúc data để dùng ít bộ nhớ hơn, và tăng khả năng cache lên đến 8 triệu Read States. 

Chỉ cần nhìn các kết quả bên dưới bạn sẽ nắm rõ hơn rất nhiều. Bạn có thể thấy, thời gian trung bình đang được đo bằng micro giây và tối đa số @mentions được đo bằng mili giây.

Hệ sinh thái cải tiến 

Finally, another great thing about Rust is that it has a quickly evolving ecosystem. Recently, tokio (the async runtime we use) released version 0.2. We upgraded and it gave us CPU benefits for free. Below you can see the CPU is consistently lower starting around the 16th.

Cuối cùng, một điều tuyệt vời khác về Rust là nó có một hệ sinh thái phát triển nhanh chóng. Gần đây, tokio (thời gian chạy async mà chúng tôi sử dụng) đã phát hành phiên bản 0.2. Chúng tôi đã nâng cấp nó và nhận được CPU miễn phí. Dưới đây bạn có thể thấy CPU luôn thấp hơn bắt đầu từ ngày 16.

Lời kết

Đến thời điểm này, Discord đang sử dụng Rust cho nhiều mảng xuyên suốt stack phần mềm. Chúng tôi dùng nó cho game SDK, thu video và mã hoá cho Go Live, Elixir NIFs, các dịch vụ backend và nhiều thứ khác.

Khi bắt đầu project mới hay thành phần phần mềm, chúng tôi luôn cân nhắc về Rust trước. Dĩ nhiên là chúng tôi chỉ dùng nó khi thật sự phù hợp.

Ngoài hiệu suất ra, Rust cũng có nhiều ưu điểm cho team kĩ sư sử dụng. Ví dụ, phần type safety và borrow checker của nó rất dễ cho kỹ sư có thể code tái cấu trúc khi yêu cầu về sản phẩm thay đổi hoặc ngôn ngữ mới được phát hiện cần học. Ngoài ra, hệ sinh thái và công cụ rất tuyệt vời và có một động lượng đáng kể đằng sau chúng.

Nếu đã đọc đến đây, hy vọng rằng bạn đã có chút hứng thú với Rust hoặc đã thích Rust được một thời gian. Nếu bạn muốn được giải quyết các vấn đề liên quan đến Rust một cách chuyên nghiệp, đừng ngần ngại cân nhắc apply vào Discord. 

Đừng bỏ lỡ những bài viết hay liên quan:

Xem thêm việc làm Software Developers hấp dẫn tại Station D

Bài viết gốc được đăng tải tại Blog.Discorsapp

Bài viết liên quan

Thị trường EdTech Vietnam- Nhiều tiềm năng nhưng còn bị bỏ ngỏ tại Việt Nam

Thị trường EdTech Vietnam- Nhiều tiềm năng nhưng còn bị bỏ ngỏ tại Việt Nam

Lĩnh vực EdTech (ứng dụng công nghệ vào các sản phẩm giáo dục) trên toàn cầu hiện nay đã tương đối phong phú với nhiều tên tuổi lớn phân phối đều trên các hạng mục như Broad Online Learning Platforms (nền tảng cung cấp khóa học online đại chúng – tiêu biểu như Coursera, Udemy, KhanAcademy,…) Learning Management Systems (hệ thống quản lý lớp học – tiêu biểu như Schoology, Edmodo, ClassDojo,…) Next-Gen Study Tools (công cụ hỗ trợ học tập – tiểu biểu như Kahoot!, Lumosity, Curriculet,…) Tech Learning (đào tạo công nghệ – tiêu biểu như Udacity, Codecademy, PluralSight,…), Enterprise Learning (đào tạo trong doanh nghiệp – tiêu biểu như Edcast, ExecOnline, Grovo,..),… Hiện nay thị trường EdTech tại Việt Nam đã đón nhận khoảng đầu tư khoảng 55 triệu đô cho lĩnh vực này nhiều đơn vị nước ngoài đang quan tâm mạnh đến thị trường này ngày càng nhiều hơn. Là một trong những xu hướng phát triển tốt, và có doanh nghiệp đã hoạt động khá lâu trong ngành nêu tại infographic như Topica, nhưng EdTech vẫn chỉ đang trong giai đoạn sơ khai tại Việt Nam. Tại Việt Nam, hệ sinh thái EdTech trong nước vẫn còn rất non trẻ và thiếu vắng nhiều tên tuổi trong các hạng mục như Enterprise Learning (mới chỉ có MANA), School Administration (hệ thống quản lý trường học) hay Search (tìm kiếm, so sánh trường và khóa học),… Với chỉ dưới 5% số dân công sở có sử dụng một trong các dịch vụ giáo dục online, EdTech cho thấy vẫn còn một thị trường rộng lớn đang chờ được khai phá. *** Vừa qua Station D đã công bố Báo cáo Vietnam IT Landscape 2019 đem đến cái nhìn toàn cảnh về các ứng dụng công...

By stationd
Bộ cài đặt Laravel Installer đã hỗ trợ tích hợp Jetstream

Bộ cài đặt Laravel Installer đã hỗ trợ tích hợp Jetstream

Bài viết được sự cho phép của tác giả Chung Nguyễn Hôm nay, nhóm Laravel đã phát hành một phiên bản chính mới của “ laravel/installer ” bao gồm hỗ trợ khởi động nhanh các dự án Jetstream. Với phiên bản mới này khi bạn chạy laravel new project-name , bạn sẽ nhận được các tùy chọn Jetstream. Ví dụ: API Authentication trong Laravel-Vue SPA sử dụng Jwt-auth Cách sử dụng Laravel với Socket.IO laravel new foo --jet --dev Sau đó, nó sẽ hỏi bạn thích stack Jetstream nào hơn: Which Jetstream stack do you prefer? [0] Livewire [1] inertia > livewire Will your application use teams? (yes/no) [no]: ... Nếu bạn đã cài bộ Laravel Installer, để nâng cấp lên phiên bản mới bạn chạy lệnh: composer global update Một số trường hợp cập nhật bị thất bại, bạn hãy thử, gỡ đi và cài đặt lại nha composer global remove laravel/installer composer global require laravel/installer Bài viết gốc được đăng tải tại chungnguyen.xyz Có thể bạn quan tâm: Cài đặt Laravel Làm thế nào để chạy Sql Server Installation Center sau khi đã cài đặt xong Sql Server? Quản lý các Laravel route gọn hơn và dễ dàng hơn Xem thêm Tuyển dụng lập trình Laravel hấp dẫn trên Station D

By stationd
Principle thiết kế của các sản phẩm nổi tiếng

Principle thiết kế của các sản phẩm nổi tiếng

Tác giả: Lưu Bình An Phù hợp cho các bạn thiết kế nào ko muốn làm code dạo, design dạo nữa, bạn muốn cái gì đó cao hơn ở tầng khái niệm Nếu lập trình chúng ta có các nguyên tắc chung khi viết code như KISS , DRY , thì trong thiết kế cũng có những nguyên tắc chính khi làm việc. Những nguyên tắc này sẽ là kim chỉ nam, nếu có tranh cãi giữa các member trong team, thì cứ đè nguyên tắc này ra mà giải quyết (nghe hơi có mùi cứng nhắc, mình thì thích tùy cơ ứng biến hơn) Tìm các vị trí tuyển dụng designer lương cao cho bạn Nguyên tắc thiết kế của GOV.UK Đây là danh sách của trang GOV.UK Bắt đầu với thứ user cần Làm ít hơn Thiết kế với dữ liệu Làm mọi thứ thật dễ dàng Lặp. Rồi lặp lại lần nữa Dành cho tất cả mọi người Hiểu ngữ cảnh hiện tại Làm dịch vụ digital, không phải làm website Nhất quán, nhưng không hòa tan (phải có chất riêng với thằng khác) Cởi mở, mọi thứ tốt hơn Bao trừu tượng luôn các bạn, trang Gov.uk này cũng có câu tổng quát rất hay Thiết kế tốt là thiết kế có thể sử dụng. Phục vụ cho nhiều đối tượng sử dụng, dễ đọc nhất nhất có thể. Nếu phải từ bỏ đẹp tinh tế – thì cứ bỏ luôn . Chúng ta tạo sản phẩm cho nhu cầu sử dụng, không phải cho người hâm mộ . Chúng ta thiết kế để cả nước sử dụng, không phải những người đã từng sử dụng web. Những người cần dịch vụ của chúng ta nhất là những người đang cảm thấy khó sử dụng dịch...

By stationd
Applicant Tracking System là gì? ATS hoạt động ra sao

Applicant Tracking System là gì? ATS hoạt động ra sao

Công nghệ phát triển hướng đến giải quyết và cải tiến cho mỗi quy trình, hoạt động của doanh nghiệp cũng như đời sống. Đối với lĩnh vực tuyển dụng, sự xuất hiện của phần mềm ATS (Applicant Tracking System) mang đến nhiều thay đổi đáng kể, cả đối với nhà tuyển dụng và ứng viên. Vậy phần mềm ATS là gì? Chúng được sử dụng ra sao? Những thắc mắc về phần mềm ATS trong tuyển dụng sẽ được Station D giải đáp tại bài viết dưới đây. Hệ thống sàng lọc ứng viên ATS (Applicant Tracking System) Applicant Tracking System là gì? Applicant Tracking System (ATS) hay còn gọi là Hệ thống quản lý hồ sơ ứng viên là phần mềm quản lý quy trình tuyển dụng từ đầu đến cuối một cách tự động hóa. ATS được thiết kế để giúp nhà tuyển dụng tiết kiệm thời gian và chi phí trong việc thu thập, sắp xếp và sàng lọc hồ sơ các ứng viên. Các tính năng nổi bật của Applicant Tracking System Các tính năng nổi bật của Applicant Tracking System Applicant Tracking System (ATS) là một công cụ quan trọng giúp các công ty quản lý quy trình tuyển dụng hiệu quả hơn. Cùng chúng tôi điểm qua các tính năng nổi bật của ATS: Sàng lọc và quản lý hồ sơ ứng viên Khả năng tìm kiếm và sàng lọc ứng viên là một trong các tính năng nổi bật nhất của ATS. Với việc sử dụng từ khóa và tiêu chí cụ thể, hệ thống có thể nhanh chóng tìm kiếm và sàng lọc các hồ sơ phù hợp, tự động loại bỏ những ứng viên không đạt yêu cầu. Điều này giúp nhà tuyển dụng tập trung vào những ứng viên...

By stationd
Hiểu về trình duyệt – How browsers work

Hiểu về trình duyệt – How browsers work

Bài viết được sự cho phép của vntesters.com Khi nhìn từ bên ngoài, trình duyệt web giống như một ứng dụng hiển thị những thông tin và tài nguyên từ server lên màn hình người sử dụng, nhưng để làm được công việc hiển thị đó đòi hỏi trình duyệt phải xử lý rất nhiều thông tin và nhiều tầng phía bên dưới. Việc chúng ta (Developers, Testers) tìm hiểu càng sâu tầng bên dưới để nắm được nguyên tắc hoạt động và xử lý của trình duyệt sẽ rất hữu ích trong công việc viết code, sử dụng các tài nguyên cũng như kiểm thử ứng dụng của mình. Cách để npm packages chạy trong browser Câu hỏi phỏng vấn mẹo về React: Component hay element được render trong browser? Khi hiểu được cách thức hoạt động của trình duyệt chúng ta có thể trả lời được rất nhiều câu hỏi như: Tại sao cùng một trang web lại hiển thị khác nhau trên hai trình duyệt? Tại sao chức năng này đang chạy tốt trên trình duyệt Firefox nhưng qua trình duyệt khác lại bị lỗi? Làm sao để trang web hiển thị nội dung nhanh và tối ưu hơn một chút?… Hy vọng sau bài này sẽ giúp các bạn có một cái nhìn rõ hơn cũng như giúp ích được trong công việc hiện tại. 1. Cấu trúc của một trình duyệt Trước tiên chúng ta đi qua cấu trúc, thành phần chung và cơ bản nhất của một trình duyệt web hiện đại, nó sẽ gồm các thành phần (tầng) như sau: Thành phần nằm phía trên là những thành phần gần với tương tác của người dùng, càng phía dưới thì càng sâu và nặng về xử lý dữ liệu hơn tương tác. Nhiệm...

By stationd
Tối ưu tỉ lệ chuyển đổi với Google Optimize và Google Analytics

Tối ưu tỉ lệ chuyển đổi với Google Optimize và Google Analytics

Tối ưu tỷ lệ chuyển đổi là một trong những yếu tố quan trọng mà một Growth Marketer không thể bỏ qua. Google Analytics là công cụ hữu hiệu và phổ biến nhất giúp chúng ta theo dõi, đo lường và tối ưu tỷ lệ chuyển đổi. Số liệu từ Google Analytics giúp chúng ta nhìn ra điểm cần thay đổi để tăng tỷ lệ chuyển đổi. Theo dõi chuyển đổi là quá trình thiết yếu nhưng cũng rất “khó nhằn”. Ngày nay, mọi người đang sử dụng điện thoại di động nhiều hơn và thường xuyên hơn khi mua hàng trực tuyến. Việc tối ưu hóa website phiên bản di động rất quan trọng. Traffic đến từ mobile khá lớn và làm ảnh hưởng nhiều tới tỷ lệ chuyển đổi. Để xem website của bạn có hoạt động tốt trên mobile hay không như thế nào? Làm gì khi tỷ lệ chuyển đổi giảm? Website phiên bản di động của bạn có nội dung hấp dẫn không? Không có những nút điều hướng hành động? Trang bị lỗi …, những điều này có thể ảnh hưởng tới tỉ lệ chuyển đổi Google Analytics, hiểu được điều này, bạn sẽ có cơ sở để khắc phục và hoàn thiện website của mình. Hãy cùng đến với chủ đề “DÙNG GOOGLE ANALYTICS TỐI ƯU TỈ LỆ CHUYỂN ĐỔI TRÊN MOBILE” với sự chia sẻ của diễn giả Nguyễn Minh Đức, CEO IM GROUP tại Vietnam Mobile Day 2018 nhé

By stationd