Cả $_GET và $_POST đều được dùng để gửi dữ liệu lên server.
$_GET: Gửi dữ liệu lên server thông qua URL, nên thông tin dữ liệu hiển thị lên url vì thế bảo mật kém, dữ liệu gửi lên bị giới hạn 1024 ký tự.
$_POST: Gửi dữ liệu lên server dưới dạng ẩn thông qua HTTP Header vì thế nó có tính bảo mật cao hơn so với GET, dữ liệu gửi lên không bị giới hạn.
Tuy nhiên tốc độ thực thi xử lý của Post chậm hơn Get.
Câu 4: Mảng là gì? Có mấy loại mảng trong PHP?
Mảng là một biến có thể chứa được nhiều phần tử, từ đó ta có thể dễ dàng lưu trữ, xắp xếp, hay xóa bỏ các phần tử trong mảng một cách dễ dàng.
Mảng bao gồm 2 thành phần là KEY và VALUE, key dùng để truy cập vào phần tử của mảng qua đó ta có thể gán giá trị hoặc lấy giá trị của các phần tử trong mảng.
Mảng(array) có 3 loại chính là:
Mảng tuần tự: là mảng có key tự động tạo là chữ số tăng dần bắt đầu từ 0.
Mảng không tuần tự: là mảng có key mà bạn phải tự định nghĩa bằng các ký tự chữ hoặc số, và key không được sắp xếp bất kỳ thứ tự nào.
Mảng đa chiều: là mảng có chứa ít nhất một mảng khác trong nó.
Câu 5: Mảng tuần tự là gì? Khác gì với bất tuần tự? Để duyệt mảng ta dùng vòng lặp nào?
Mảng tuần tự là mảng có key là chữ số bắt đầu từ 0 và tăng dần. Nó khác với mảng bất tuần tự là các phần tử trong nó KEY được sắp xếp tăng dần từ 0 còn mảng bất từ tự thì không.
Để duyệt mảng tả có thể dùng bất cứ vùng lập nào? Tuy nhiên chuẩn nhất là dùng vòng lập foreach, vì vòng lập này được nhà phát triển PHP xây dựng riêng cho việc duyệt mảng, nêu nó dễ sử dụng hơn, tốc độ xử lý của nó nhanh hơn.
Câu 6: Để chuyển mảng thành chuỗi ta dùng hàm gì? Để tách chuỗi thành mảng ta dùng hàm gì?
Trong PHP cung cấp hàm implode(separator,array) dùng để chuyển mảng thành một chuỗi và hàm explode(separator,string,[limit]) dùng để chuyển một chuỗi thành mảng. Ngoài ra ta có hàm join($ky_tu,$array) giống hàm implode
Câu 7: Trong PHP để gộp mảng ta dùng hàm gì? Để tách mảng ta dùng hàm gì?
Trong PHP cung cấp hàm array_merge($array1, $array2, $array3,…) dùng để nối các mảng thành một mảng duy nhất và hàm array_slice(array,start,[length],[preserve]) dùng để tách mảng thành một mảng nhỏ hơn.
Câu 8: Cho biết sự khác nhau giữa serialize và json_encode? Lý giải theo cách bạn hiểu?
json_encode: là phương thức dùng để chuyển đổi một mảng(Array) hoặc Object thành string(chuỗi) dữ liệu JSON để sử dụng khi lưu trữ trong database, và để chuyển đổi ngược lại ta dùng json_decode để chuyển đổi một chuỗi dữ liệu JSON sang dạng mảng(Array) hoặc object để sử dụng trong code PHP.
serialize(array): là phương thức dùng để chuyển một mảng(Array) hoặc Object thành string(chuỗi) dữ liệu theo chuẩn của PHP để ta có thể lưu trữ hoặc truyền tải, khi muốn chuyển nó lại thành lại một mảng hoặc phương thức ta dùng phương thức unserialize().
Câu 9: Cookie và session có gì khác nhau? Người ta nói bản thân của session là cookie là đúng hay sai?
COOKIE là một tập tin nhỏ được server nhúng vào máy tính của người dùng. Nếu lần đầu tiên trình duyệt truy cập vào website nó sẽ gửi một COOKIE đến trình duyệt của người dùng, và mỗi khi người dùng tiếp tục yêu cầu một trang web từ website này thì COOKIE với các thông tin thu nhập từ phía người dùng trên website này sẽ được sẽ gửi trả về server của website.
SESSION được hiểu là khoảng thời gian mà người sử dụng giao tiếp với 1 ứng dụng. SESSION được lưu trữ hoàn toàn trên server, do vậy tính bảo mật cao hơn cookie, các website hiện này thường dùng session để lưu thông tin của người dùng khi họ đăng nhập. Chu kỳ sống của SESSION do webserver qui định, ta có thể điều chỉnh chu kỳ này khi cấu hình webserver, tại server sẽ có 1 PHPSESSID tương ứng được tạo ra, các PHPSESSID sẽ được lưu trong một tập tin văn bản ở tại vị trí được qui định trong file php.ini ở dòng session.save_path.
Người ta nói bản thân của session là cookie, về cơ bản SESSION và COOKIE đều là các tập tin lưu trữ lại thông tin của người dùng website, tuy nhiên dựa trên khái miệm, cách sử dụng, ứng dụng của chúng là khác biệt vì thế tôi cho rằng chúng là khác biệt.
Câu 10: Theo bạn, sự khác nhau của toán tử & và && trong PHP là gì?
Toán từ & và && trong PHP đều là phép toán AND, tuy nhiên toán tử một dấu & áp dụng theo kiểu bit, nói dễ hiểu hơn một dấu & là phép AND thao tác trên các bit ví dụ 1 & 0 thì ra 0. Phép toán hai dấu & thì chỉ áp dụng cho kiểu boolean True và False.
Câu 11: Hãy cho biết $a++ và ++$a khác nhau ở đâu?
Cả hai đều được dùng để tăng thêm một đơn vị cho biết số, tuy nhiên điểm khác biệt ở $a++ được thực sau khi nó được gọi, còn ++$a được thực thi ngay khi nó được gọi, lấy ví a bằng 5, khi dung hàm echo để in nó ra với giá trị a++ thì nó sẽ ra là 5, còn khi dùng echo cho ++a thì giá trị của được in ra sẽ là 6.
Câu 12: Tính nhanh kết quả của đoạn code sau
→ Lỗi không in ra được gì.
Câu 13: Mysql_close() cần thiết như thế nào trong thực tế ?. Vì sao ít thấy người dùng áp dụng nó?
Mysql_close() được dùng để đóng kết nối đến cơ sở dữ liệu đã mở trước đó. Điều này rất cần thiết vì nó giúp giảm tải xử lý của database, hiểu đơn giản thế này một người truy cập tới website của bạn và website của bạn có kết nối database và điều đó đồng nghĩa với việc sẽ có ít nhất một kết nối database được tạo, vậy 100 người sẽ có 100 kết nối, và giả sử dụng trong đó có 99 người không sử dụng nữa nhưng kết nối vẫn còn hiệu lực thì điều này có nghĩa là thay vì database phải xử lý chỉ có 1 kết nối mà nó phải xử lý 100 kết nối.
Vì sao ít người dùng đầu tiên vì hiệu quả mang lại của hàm này rất khó thấy trong các trường hợp thông thường, trên thực tế khi người dùng không còn sử dụng nữa thì sau một khoảng thời gian ngắn kết nối này sẽ tự động đóng, thứ hai vì ít người có thể nhìn thấy được vấn đề nếu không có dịp tiếp xúc hoặc làm việc với lượng dữ liệu lớn thì sẽ thấy việc mở hoặc đóng một conneciton kịp thời và chính xác sẽ tiết kiệm được rất nhiều tài nguyên cho hệ thống cũng như tốc độ xử lý của hệ thống, vì thế hàm này rất cần thiết khi bạn quản lý tài nguyên và tốc độ thực thi của hệ thống hay của website.
Câu 14: Muốn chuẩn hóa dữ liệu về utf-8 trong PHP ta phải làm gì?
Sử dụng encoding utf-8 tại meta charset trong tập tin PHP.
Sử dụng encoding utf-8-unicode-ci trong MySQL.
Chứng chỉ và kỹ năng không chỉ có thể giúp tăng lương của bạn mà còn giúp thăng chức và trở nên hấp dẫn hơn trong mắt các nhà tuyển dụng. Một khảo sát từ Global Knowledge cho thấy 83% chuyên gia CNTT ở Mỹ và Canada đều có bằng cấp IT – với mức lương trung bình cho một chuyên gia IT có bằng cấp là trung bình luôn cao hơn khoảng $ 8.400 (hoặc 11,7 phần trăm) cao so với không có các chứng chỉ cần thiết.
Các nhà tuyển dụng cũng tin rằng các chứng chỉ cũng rất có lợi cho người thuê lao động. Trong số những người được khảo sát, 44% nói rằng các nhân viên có bằng cấp thường thực hiện công việc nhanh hơn, 33% cho biết hiệu quả hơn khi triển khai hệ thống và 23% nói rằng nó giúp rất nhiều trong quá trình deploy sản phẩm cũng như hạn chế bớt lỗi do trình độ của các nhân viên đã vững vàng.
Dưới đây là 15 chứng nhận và bằng cấp có giá trị nhất cho dân công nghệ trong năm nay.
Chứng nhận trong quản trị doanh nghiệp CNTT ( Certified in the Governance of Enterprise IT – CGEIT)
Được cung cấp thông qua ISACA, chứng nhận CGEIT thể hiện kiến thức của bạn về quản trị CNTT doanh nghiệp. Nó cho thấy bạn có khả năng áp dụng những nguyên tắc và khái niệm quản trị CNTT trong môi trường chuyên nghiệp. Khóa học vốn được thiết kế cho những người có “vai trò quản lý, tư vấn hoặc các vị trí đảm bảo liên quan đến quản trị CNTT”, bao gồm IS và IT director, chuyên gia tư vấn, giám đốc điều hành, manager, theo ISACA. Chứng nhận CGEIT hiện đang được xếp hạng là một trong những chứng chỉ có giá trị nhất, theo dữ liệu từ Global Knowledge.
Yêu cầu: Năm năm kinh nghiệm quản lý hoặc nhiều hơn, đã từng đảm nhiệm vai trò tư vấn hoặc giám sát hay hỗ trợ các sáng kiến quản trị CNTT trong môi trường doanh nghiệp. Bạn sẽ cần trải nghiệm ở hai hoặc nhiều lĩnh vực CGEIT, bao gồm quản lý chiến lược, phân tích lợi ích, tối ưu hóa rủi ro và tài nguyên.
Mức lương trung bình sau khi có được chứng chỉ CGEIT: $121,363
Chứng nhận AWS cho kiến trúc sư giải pháp (AWS Certified Solutions Architect – Associate)
AWS vẫn là nền tảng đám mây hàng đầu được lựa chọn và AWS Certified Solutions Architect – Associate certification được thiết kế cho những người làm về AWS. Nói cách khác, nó tập trung vào việc chứng minh khả năng của bạn để thiết kế và triển khai các hệ thống (có thể mở rộng) trên AWS, bao gồm cách giữ cho chi phí phát triển hiệu quả mà không phải hy sinh về bảo mật, độ tin cậy và chất lượng.
Yêu cầu: Một năm kinh nghiệm thực hành thiết kế hệ thống trên AWS hoặc nhiều hơn, kiến thức về ít nhất một ngôn ngữ lập trình cấp cao cũng như hiểu biết về các phương pháp hay nhất xung quanh việc phát triển các ứng dụng dựa trên AWS.
Mức lương trung bình sau khi có được chứng chỉ AWS: $121,292
Chứng chỉ Project Management Professional (PMP)
Chứng nhận PMP được cung cấp thông qua PMI và vốn dành cho các chuyên gia quản lý dự án. PMI cho biết một chứng nhận trong PMP sẽ cho phép bạn “làm việc trong bất kỳ ngành công nghiệp nào, với bất kỳ phương pháp luận nào và ở bất kỳ địa điểm nào”. Bạn sẽ cần nắm vững năm giai đoạn chính của vòng đời dự án, bao gồm bắt đầu, lập kế hoạch, thực hiện, giám sát, kiểm soát và ngừng hoạt động.
Yêu cầu: Để tham gia kỳ thi, bạn cần ba năm kinh nghiệm trong quản lý dự án, 4.500 giờ cho các dự án ở vị trí lead và chỉ đạo và 35 giờ học các khóa quản lý dự án. Với bằng cấp thứ hai, bạn sẽ cần 5 năm kinh nghiệm, 7.500 giờ quản lí các dự án và 35 giờ học các khóa quản lý dự án.
Mức lương trung bình sau khi có được chứng chỉ PMP: $114,475
Bằng AWS Certified Developer – Associate
Chứng nhận, từ Amazon, được thiết kế cho các nhà phát triển làm việc với các dịch vụ AWS. Qua đó chứng minh khả năng của bạn để phát triển, triển khai và gỡ lỗi các ứng dụng dựa trên Cloud với AWS. Khi các tổ chức ngày càng khẩn trương áp dụng công nghệ Cloud, internet of things, machine learning và các công nghệ mới nổi khác, chứng nhận AWS Certified Developer certification đã trở nên có giá trị hơn đối với ứng viên.
Yêu cầu: Một hoặc nhiều năm kinh nghiệm thực hành thiết kế và duy trì các ứng dụng dựa trên AWS, kiến thức vững chắc về một hoặc nhiều ngôn ngữ lập trình bậc cao cũng như là sự hiểu biết về các dịch vụ AWS.
Mức lương trung bình sau khi có được chứng chỉ AWS Certified Developer – Associate certification: $114,473
Chứng nhận chuyên gia bảo mật hệ thống thông tin ( Certified Information Systems Security Professional – CISSP)
Chứng nhận CISSP được cung cấp bởi ISC và được công nhận theo ANSI, cũng như với sự chấp thuận của bộ quốc phòng Mỹ và là tiêu chuẩn cho chương trình ISEEP của cơ quan an ninh quốc gia Mỹ. Ngoài ra, nó còn được công nhận bởi toàn thế giới như một khóa huấn luyện để giúp các chuyên gia bảo mật IT thiết lập các phương pháp bảo mật hay nhất.
Yêu cầu: Bạn sẽ cần ít nhất năm năm kinh nghiệm về bảo mật thông tin và ít nhất ba năm kinh nghiệm làm người quản lý bảo mật. Kinh nghiệm phải có được trong vòng 10 năm trước khi bắt đầu kỳ thi, hoặc năm năm sau khi vượt qua nó. Bạn cần phải luôn cập nhật giấy chứng nhận với các bài kiểm tra định kì (CPE) và bạn cần có ít nhất năm năm kinh nghiệm làm việc toàn thời gian trong hai hoặc nhiều chủ đề sẽ được đưa vào bài kiểm tra.
Mức lương trung bình sau khi có được chứng chỉ CISSP certification: $111,475
Chứng nhận trong kiểm soát hệ thống thông tin và rủi ro ( Certified in Risk and Information Systems Control – CRISC)
CRISC là “chứng chỉ duy nhất chuẩn bị và cho phép các chuyên gia CNTT đối đầu với những thách thức về IT và quản lý rủi ro cũng như là biến chúng trở thành những lợi ích chiến lược cho doanh nghiệp”. Chứng nhận bảo mật thông tin này là bằng chứng cho khả năng quản lý rủi ro của bạn với các kĩ năng như đánh giá, ứng phó, giám sát và báo cáo.
Yêu cầu: Để đạt được chứng nhận CRISC, bạn sẽ cần ba năm hoặc nhiều hơn trong ít nhất hai trong bốn chủ đề được đề cập trong kì thi. Bạn cũng sẽ cần thực hiện bài thi mỗi năm để duy trì chứng nhận.
Mức lương trung bình sau khi có được chứng chỉ CRISC certification: $111,049
Chứng nhận quản lí bảo mật thông tin ( Certified Information Security Manager – CISM)
ISACA cũng cung cấp chứng chỉ CISM, vốn tập trung vào bảo mật CNTT ở cấp quản lý. Nó được thiết kế để giúp kiểm chứng sự thành thạo trong xây dựng, thiết kế và quản lý các sáng kiến bảo mật doanh nghiệp. Bài kiểm tra chỉ được cung cấp trong khoảng thời gian 16 tuần – với năm nay, khóa học có sẵn bắt đầu từ ngày 1 tháng 2 đến ngày 1 tháng 6.
Yêu cầu: Để có được chứng nhận CISM, bạn cần ít nhất năm năm kinh nghiệm bảo mật thông tin và phải trong vòng 10 năm kể từ ngày thi hoặc năm năm sau khi vượt qua. Bạn cũng sẽ cần làm bài thi mỗi năm để duy trì chứng nhận.
Mức lương trung bình sau khi có được chứng chỉ CISM certification: $108,043
Chứng nhận ScrumMaster
Scrum Alliance cung cấp chứng nhận ScrumMaster vốn rất nổi tiếng và được tôn trọng trong ngành IT. Trở thành một ScrumMaster tức bạn đã thành thạo về Scrum và cách áp dụng nó tại nơi làm việc. Đó là một chứng nhận đặc biệt hữu ích cho các vị trí quản lý dự án, chương trình, cũng như là các nhà phát triển sản phẩm. Song song đó, bạn cũng sẽ nhận được tư cách là thành viên hai năm với Scrum Alliance, điều này sẽ cho phép bạn truy cập vào các sự kiện dành riêng cho các thành viên.
Yêu cầu: Bạn sẽ cần tham dự khóa học CSM trực tiếp, do một giảng viên Scrum giảng dạy.
Mức lương trung bình sau khi có được chứng chỉ Certified ScrumMaster certification: $106,938
Có thể bạn muốn xem:
Chứng nhận Hacker (Certified Ethical Hacker – CEH)
Được cung cấp bởi EC-Council, chứng chỉ Hacker (CEH) được chứng nhận cho thấy bạn có các kỹ năng và kiến thức để tìm lỗ hổng trong hệ thống máy tính và có khả năng ngăn chặn tấn công. Một “hacker có đạo đức” là người sử dụng cùng một kỹ năng và kiến thức như một hacker mũ đen để phát triển các biện pháp an ninh nhằm ngăn chặn các cuộc tấn công tiềm năng. Thông thường, các doanh nghiệp sẽ yêu cầu bạn phải nghĩ như một hacker để tìm điểm yếu trong hệ thống và mạng máy tính, nhờ đó sẽ dễ dàng hơn trong việc sửa các lỗ hổng bảo mật và ngăn chặn các mối đe dọa tiềm ẩn.
Yêu cầu: Bạn sẽ cần hai năm kinh nghiệm làm việc trong lĩnh vực bảo mật thông tin.
Mức lương trung bình sau khi có được chứng chỉ Certified Ethical Hacker (CEH): $106,375
Bằng đai xanh Six Sigma
Six Sigma là một phương pháp quản lý được sử dụng để đơn giản hóa quản lý chất lượng liên quan đến nhiều cấp độ. Vì vậy mà bạn không thể nhảy vượt các cấp trong chứng chỉ Six Sigma, vì vậy bất kể bạn đang ở đâu trong sự nghiệp của mình thì đều phải bắt đầu với chứng chỉ Green Belt – cấp thấp nhất. Là một đai xanh Six Sigma, bạn sẽ được hướng dẫn bởi một đai đen trong tổ chức của mình, người sẽ cố vấn bạn trong khi bạn học phương pháp luận.
Yêu cầu: Ba năm kinh nghiệm làm full-time tại một hoặc nhiều lĩnh vực của Six Sigma Green Belt.
Mức lương trung bình sau khi có được chứng chỉ Six Sigma Green Belt: $104,099
Chứng chỉ Citrix Certified Professional – Virtualization (CCP-V)
Chứng nhận của CCP-V thể hiện năng lực trong các ứng dụng desktop ảo sử dụng công nghệ Citrix. Trong đó bao gồm cài đặt các công nghệ, định thành phần kỹ thuật và cấu hình thử nghiệm. Trong năm 2014, chứng nhận này cũng đã thay thế chứng chỉ Citrix Certified Enterprise Engineer (CCEE) và tập trung vào các kỹ năng để triển khai, duy trì và xác định giải pháp dựa trên phần mềm XenDesktop của Citrix.
Yêu cầu: Bạn cần phải đạt được chứng nhận cấp associate của mình trước khi có thể chuyển sang CCP-V.
Mức lương trung bình sau khi có được chứng chỉ CCP-V certification: $103,424
Chứng chỉ Microsoft Certified Solutions Expert (MCSE) – Cơ sở hạ tầng máy chủ
Chứng nhận MCSE là đặc biệt nhất trong danh sách này, vì nó không còn được cung cấp thông qua Microsoft – mà được thay thế bằng chứng nhận MCSE: Cloud Platform và Infrastructure. Tuy nhiên, chứng nhận vẫn được góp mặt vào trong danh sách vì nó vẫn hợp lệ nếu bạn nhận được chứng chỉ trước tháng 3 năm 2017.
Chứng nhận này chứng minh khả năng của bạn để chạy một trung tâm dữ liệu hiệu quả cũng như là kiến thức vững vàng trong các lĩnh vực như công nghệ đám mây, ảo hóa, lưu trữ, mạng, hệ thống và quản lý danh tính.
Yêu cầu: Để kiếm được chứng nhận MCSE, trước tiên bạn cần có một trong bốn chứng chỉ MCSA được cung cấp bởi Microsoft.
Mức lương trung bình sau khi có được chứng chỉ Microsoft Certified Solutions Expert (MCSE) – Server Infrastructure: $100,656
Chứng chỉ kiểm toán viên hệ thống thông tin (CISA)
ISACA cho biết chứng chỉ CISA là “chứng nhận được công nhận toàn cầu về kiểm soát IS.” Đã được bắt đầu kể từ khoảng năm 1978 và tập trung vào việc thể hiện khả năng của bạn trong mọi giai đoạn của quá trình kiểm toán, báo cáo về thủ tục cũng như kĩ năng có thể đánh giá như thế nào về lỗ hổng bảo mật.
Yêu cầu: Bạn sẽ cần ít nhất năm năm kinh nghiệm trong việc kiểm tra, kiểm soát hoặc bảo mật IS. Có bài kiểm tra mà bạn sẽ cần vượt qua trước khi chuyển sang kỳ thi CISA. Giống như các chứng chỉ ISACA khác, bạn sẽ cần phải liên tục làm bài thi mỗi năm để duy trì chứng nhận của mình.
Mức lương trung bình sau khi có được chứng chỉ CISA certification: $99,648
Chứng nhận Cisco Certified Network Professional (CCNP) Routing và Switching
Chứng nhận CCNP dành cho các chuyên gia IT đã hoàn thành chứng chỉ CCNA và chuyển sang lớp tiếp theo. Theo giấy chứng nhận CCNP, bạn có thể chọn chuyên về Cloud, trung tâm dữ liệu, định tuyến và chuyển mạch cũng như là bảo mật.
Yêu cầu: Bạn sẽ cần hoàn thành bài kiểm tra CCNA Routing và Switching hoặc bất kỳ bài kiểm tra CCIE nào trước khi chuyển sang kỳ thi chứng nhận CCNP.
Mức lương trung bình sau khi có được chứng chỉ CCNP Routing và Switching certification: $99,402
Chứng chỉ Citrix Certified Associate – Networking (CCA-N)
Chứng chỉ CCA-N tập trung vào việc chứng minh trình độ sử dụng NetScaler Gateway trong môi trường doanh nghiệp. Bài kiểm tra bao gồm các lĩnh vực xung quanh việc sử dụng NetScaler Gateway để truy cập an toàn các máy tính desktop, ứng dụng và dữ liệu từ xa.
Yêu cầu: Không có điều kiện tiên quyết để lấy chứng chỉ CCA-N.
Mức lương trung bình sau khi có được chứng chỉ CCA-N certification: $99,217
Đây là mười điều răn mà tất cả các lập trình viên nên tuân theo, theo Jerry Weinberg – Tâm lý học lập trình:
Hiểu và chấp nhận rằng chúng ta sẽ có lúc phạm sai lầm. Vấn đề là tìm ra chúng sớm, trước khi đưa vào sản xuất. May mắn thay, ngoại trừ vài người trong chúng tôi phát triển phần mềm hướng dẫn tên lửa tại JPL, những sai lầm hiếm khi gây tử vong trong ngành của chúng tôi, vì vậy chúng tôi có thể, và nên học, vui vẻ và tiếp tục công việc.
Bạn không phải là code của bạn. Hãy nhớ rằng bản chất của việc review là tìm ra vấn đề và các vấn đề sẽ được tìm thấy. Đừng khư khư giữ cho riêng mình khi mà vấn đề chưa được tìm ra
Đừng tự cho mình là cao thủ sẽ luôn có người nào đó giỏi hơn bạn: Mỗi người đều có thể là thầy của bạn; có thể dạy bạn một số kiến thức mới nếu bạn hỏi. Tìm kiếm và chấp nhận học hỏi từ những người khác, đặc biệt là ngay cả khi bạn nghĩ rằng nó không cần thiết.
Không viết lại code khi chưa thảo luận với đồng nghiệp: Có một đường thẳng song song giữa “fixing code” và “rewriting code”. Chấp nhận sự khác biệt, và tuân thủ sự thay đổi trong khuôn khổ, đừng tự ý hành động.
Đối xử với những người ít biết về bạn bằng sự tôn trọng, kính nể và kiên nhẫn. Những người không hiểu gì về kỹ thuật, những người thường xuyên làm việc với các nhà phát triển, hầu như giữ quan điểm rằng chúng tôi là những người tự mãn, cô độc, hay than vãn. Đừng củng cố thêm quan niệm này bằng thái độ giận dữ, thiếu kiên nhân
Cách duy nhất để phát triển chính là luôn thay đổi bản thân. Hãy cởi mở và chấp nhận những điều mới. Hãy sẵn lòng xem xét thay đổi đối với yêu cầu, framework hoặc công cụ như một thử thách mới, không phải là một sự bất tiện.
Quyền lực thực sự bắt nguồn từ tri thức, không phải từ vị trí của bạn. Kiến thức tạo ra quyền hạn, và quyền lực tạo ra sự tôn trọng – vì vậy nếu bạn muốn được tôn trọng trong một môi trường, hãy trau dồi kiến thức từ bây giờ.
Chiến đấu cho những gì bạn tin tưởng, nhưng cũng biết chấp nhận thất bại. Hãy hiểu rằng đôi khi ý tưởng của bạn sẽ bị loại bỏ. Ngay cả khi bạn đúng, đừng trả thù hay nói, “Tôi đã nói với bạn như vậy” nhiều hơn một vài lần, và đừng làm cho ý tưởng của bạn trở thành một người tử vì đạo hay khóc.
Đừng là “một coder đơn độc”. Đừng là anh chàng coder suốt ngày ngồi trong phòng: không tiếp xúc với mọi người, không có những thú vui khác, cô lập tại văn phòng làm việc.
Review code thay vì người code – hãy tử tế với người lập trình chứ không phải code của họ. Làm cho tất cả các ý kiến của bạn tích cực và định hướng để cải thiện code càng nhiều càng tốt. Đưa ra những nhận xét gắn với các tiêu chuẩn, giúp tăng hiệu suất,…
Python là một ngôn ngữ lập trình tuyệt vời, và nó chứa nhiều module tích hợp sẵn nhằm giúp chúng ta viết code tốt hơn, code đẹp hơn.
Mục tiêu
Thông qua bài viết này, chúng ta sẽ sử dụng một vài module và phương pháp ít ai biết mà có thể cải thiện được cách code của chúng ta cả về phần nhìn và tính hiệu quả.
NamedTuple
Tôi tin rằng một vài người hẳn đã biết tới câu lệnh phổ biến hơn là namedtuple từ module collections (Nếu chưa biết bạn có thể xem tại đây), nhưng kể từ phiên bản Python 3.6, một loại mới đã có trong module typing là NamedTuple. Cả hai đều được thiết kế để giúp bạn nhanh chóng tạo ra những đối tượng bất biến có thể đọc được.
NamedTuple thực ra là một phiên bản viết lại của namedtuple, và theo quan điểm của tôi thì nó dễ đọc hơn nhiều:
In [2]: import typing
In [3]: class BetterLookingArticle(typing.NamedTuple):
...: title: str
...: id: int
...: description: str = "No description given."
...:
In [4]: BetterLookingArticle(title="Python is cool.", id=1)
BetterLookingArticle(title='Python is cool.', id=1, description='No description given.')
Còn thay vào đó nếu ta sử dụng namedtuple:
In [6]: import collections
In [7]: Article = collections.namedtuple("Article", ["title", "description", "id"])
In [8]: Article(title="Python is cool.", id=1, description="")
Article(title='Python is cool.', description='', id=1)
array.array
Các mảng giá trị số hiệu quả. Mảng là những loại trình tự hoạt động rất giống như list, ngoại trừ các loại đối tượng được lưu trữ bị hạn chế.
Khi sử dụng module array, chúng ta cần khởi tạo bằng 1 typecode kiểu mã mà trong đó tất cả những element phần tử đều được sử dụng. Hãy so sánh tính hiệu quả về thời gian với một list thông thường, viết nhiều số nguyên trong một file (sử dụng module pickle để viết một list thông thường):
Có thể thấy là nhanh hơn gấp 14 lần bình thường. Tất nhiên là cũng phụ thuộc vào module pickle , nhưng sử dụng mảng vẫn gọn hơn so với sử dụng list. Vậy nên nếu bạn đang dùng những giá trị số đơn giản, bạn nên cân nhắc việc sử dụng module array.
itertools là một module ấn tượng. Nó có rất nhiều phương thức khác nhau giúp tiết kiệm thời gian, tất cả đều được liệt kê ở đây. Thậm chí còn có cả một kho GitHub viết nhiều hơn về itertools.
Tôi đã sử dụng phương thức combinations trong tuần này và tôi nghĩ là tôi sẽ chia sẻ về nó. Phương thức này lấy một số lặp và một số nguyên làm đối số, và tạo ra một trình bao gồm tất cả các tổ hợp có thể có của phép lặp với một dãy số nguyên dài nhất mà không bị trùng lặp:
Cuối cùng nhưng không kém phần quan trọng – Module dis
Module dis hỗ trợ phân tích bytecode CPython bằng cách phân tách nó.
Có thể bạn đã biết hoặc chưa biết, Python dịch mã nguồn thành một tập hợp các chỉ thị gọi là “bytecode”. Module dis giúp chúng ta xử lý những chỉ thị này, và đồng thời nó cũng là một công cụ debug tuyệt vời.
Thanks for reading! For more Python related articles and other cool stuff, you can follow me on Medium or GitHub (I star some awesome repos!).
Xin cảm ơn các bạn đã đọc bài viết này! Để đọc nhiều bài viết hơn về Python và những chủ đề hay khác, bạn có thể follow tôi trên Medium hoặc GitHub (….
Nếu bạn thích bài viết này, hãy nhấn giữ nút để những người khác có thể tìm thấy bài viết. Bạn càng giữ lâu, bạn sẽ càng chia sẻ cho nhiều người hơn!
Và đừng ngần ngại chia sẻ thêm nhiều kiến thức bổ ích về Python ở phần bình luận bên dưới nhé!
Bạn thường xuyên viết những đoạn mã dài và lặp đi lặp lại và cần một pattern để tối ưu. Regular Expression (Regex) chính là công cụ sẽ giúp bạn đơn giản hóa công việc này. Vậy chính xác thì Regular Expression, hay còn gọi là Regex, là gì? Hãy cùng TopDev tìm hiểu chi tiết trong bài viết dưới đây!
Regex là gì?
Regex, viết tắt của Regular Expression (biểu thức chính quy), là các mẫu (pattern) dùng để tìm/thay thế (Find/Replace) và thao tác với chuỗi văn bản. Là một công cụ cực mạnh cho xử lí chuỗi trong Php, JavaScript…
Regex còn được viết là RegExp hay RegExr
Ví dụ: Khi kiểm tra tính hợp lệ của email hoặc số điện thoại thì điều bạn nghĩ tới đầu tiên chính là regex.
Để xác định xem một chuỗi có phải là địa chỉ email hợp lệ không, bạn có thể sử dụng Regex để kiểm tra xem chuỗi đó có đúng định dạng email hay không, như là có chứa ký tự @, theo sau là tên miền hợp lệ. Một mẫu Regex đơn giản cho email có thể là: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
Mẫu này kiểm tra rằng chuỗi bắt đầu với một hoặc nhiều ký tự không phải khoảng trắng hoặc @, sau đó là ký tự @, tiếp theo là một hoặc nhiều ký tự không phải khoảng trắng hoặc @, rồi đến dấu . và cuối cùng là một hoặc nhiều ký tự không phải khoảng trắng hoặc @.
Tìm hiểu Regex cơ bản
Các cú pháp cơ bản Regular Expression
Nói chung là Regex còn nhiều ứng dụng hữu ích khác, tóm gọn lại một chút là hiểu đơn giản thì regex là một chuỗi các kí tự miêu tả một bộ các chuỗi ki tự khác, theo những quy tắc và cú pháp nhất định.
Dưới đây là bảng tóm tắt các cú pháp cơ bản của Regular Expression (Regex):
Ký hiệu
Ý Nghĩa
Ví Dụ
Giải Thích
.
Bất kỳ ký tự nào
a.c
Tìm các chuỗi như abc, a1c, nhưng không phải ac
\d
Chữ số (0-9)
\d{3}
Tìm ba chữ số liên tiếp, ví dụ: 123
\D
Không phải chữ số
\D{2}
Tìm hai ký tự không phải chữ số
\w
Ký tự chữ và số (a-z, A-Z, 0-9, _)
\w+
Tìm một hoặc nhiều ký tự chữ và số
\W
Ký tự không phải chữ và số
\W+
Tìm một hoặc nhiều ký tự không phải chữ và số
\s
Ký tự khoảng trắng (space, tab, line break)
\s+
Tìm một hoặc nhiều ký tự khoảng trắng
\S
Ký tự không phải khoảng trắng
\S+
Tìm một hoặc nhiều ký tự không phải khoảng trắng
^
Bắt đầu chuỗi
^abc
Tìm chuỗi bắt đầu bằng abc
$
Kết thúc chuỗi
abc$
Tìm chuỗi kết thúc bằng abc
*
Lặp lại 0 hoặc nhiều lần
a*
Tìm 0 hoặc nhiều ký tự ‘a’
+
Lặp lại 1 hoặc nhiều lần
a+
Tìm 1 hoặc nhiều ký tự ‘a’
?
Tùy chọn (0 hoặc 1 lần)
a?
Tìm 0 hoặc 1 ký tự ‘a’
{n}
Lặp lại chính xác n lần
a{3}
Tìm ba ký tự ‘a’ liên tiếp
{n,}
Lặp lại ít nhất n lần
a{2,}
Tìm ít nhất hai ký tự ‘a’
{n,m}
Lặp lại từ n đến m lần
a{2,4}
Tìm từ hai đến bốn ký tự ‘a’
[]
Nhóm ký tự
[abc]
Tìm một ký tự trong nhóm ‘a’, ‘b’, hoặc ‘c’
[^ ]
Ký tự không nằm trong nhóm
[^a-c]
Tìm ký tự không phải ‘a’, ‘b’, hoặc ‘c’
`
`
Hoặc
`a
()
Nhóm ký tự
(abc)+
Tìm nhóm ‘abc’ lặp lại một hoặc nhiều lần
\
Escape ký tự đặc biệt
\.
Tìm ký tự ‘.’ (giá trị chính xác của dấu chấm)
Các cú pháp này giúp bạn xây dựng các mẫu để tìm kiếm, thay thế và phân tích dữ liệu một cách linh hoạt và hiệu quả.
Nhắc lại: Nguyên tắc hoạt động của biểu thức RegEx là so khớp dựa vào mẫu (pattern), mẫu được xây dựng từ các quy tắc căn bản của biểu thức RegEx. Và để các bạn nắm rõ regular expression là gì thì có một hàm xử lý Regular Expression trong php đó là hàm preg_match, sau đó đưa ra một vài ví dụ nhỏ để các bạn thực hành trước khi tìm hiểu các phần nâng cao hơn.
Một số ứng dụng của Regex:
Tìm kiếm: Xác định và tìm kiếm các mẫu cụ thể trong văn bản.
Thay thế: Thay thế các phần của văn bản dựa trên mẫu.
Xác thực: Kiểm tra xem dữ liệu có phù hợp với định dạng yêu cầu không, chẳng hạn như email, số điện thoại.
Tách dữ liệu: Phân tích và tách dữ liệu từ văn bản dựa trên mẫu.
Hàm Preg_match trong php
Cú pháp là: preg_match($pattern, $subject, $matches)
$pattern là chuỗi Regex Pattern
$subject là chuỗi nguồn để chúng ta so khớp với $pattern
$matches là danh sách kết quả trả về đúng khi so khớp 2 chuỗi trên
VD 1: kiểm tra mội chuỗi là số
if (preg_match('/^[0-9]+$/', '123', $matches)){
var_dump($matches);
}
Kết quả:
array
0 => string '123' (length=3)
VD 2: Kiểm tra một chuỗi là các ký tự thường
if (preg_match('/^[a-z]+$/', 'topdev', $matches)){
var_dump($matches);
}
Kết quả
array
0 => string 'topdev' (length=6)
VD 3: Kiểm tra một chuỗi là các ký tự hoa
if (preg_match('/^[A-Z]+$/', 'TOPDEV', $matches)){
var_dump($matches);
}
Kết quả
array
0 => string 'TOPDEV' (length=6)
Tạo 1 Regular Expressions trong JS
Trong Javascript thì Regular Expression là một chuỗi nhưng nó không được bao quanh bởi cặp dấu nháy đơn ' hoặc nháy kép " mà nó được bao quanh bởi cặp dấu /. Có 2 cách tạo:
Cú pháp: new RegExp(pattern[, flags])
VD:
var regexConst = new RegExp('abc');
Hoặc sử dụng dấu /
Cú pháp:/pattern/flags
Trong đó:
pattern là chuỗi Regular Expression
flags là thông số cấu hình cho chuỗi pattern và nó có các giá trị:
i : so khớp không quan tâm đến chữ hoa chữ thường
g : so khớp toàn bộ chuỗi cần tìm
m : so khớp luôn cả các dữ liệu xuống dòng (multiline)
Ví dụ: Pattern kiểm tra chuỗi có tồn tại chữ “topdev” không, không phân biệt chữ hoa chữ thường và tìm toàn bộ tài liệu.
var pattern = /topdev/igm;
pattern là topdev
flags là igm
Cách viết Regex chi tiết
Trong Regex có vài cái khá là điển hình và được sử dụng rất nhiều bao gồm so khớp chuỗi, tách chuỗi, tìm kiếm trong chuỗi, thay thế chuỗi… Các cách sử dụng chi tiết Regex trong Javascript bạn có thể tham khảo chi tiết tại đây nhé hoặc ở đây.
Ký tự thường
STT
Biểu thức
Mô tả
Ghi chú
01
a|b
Khớp với a hoặc b
02
[0-9]
Khớp với một trong số từ 0 tới 9
03
[a-z]
Khớp với một trong chữ từ a tới z
04
[abc]
Có thể khớp với a, b hoặc c
05
[^abc]
Khớp với bất kì kí tự nào ngoài a, b và c
Nếu ^ xuất hiện đầu tiên sau ngoặc vuông, nó có nghĩa là phủ định
06
\d
Số bất kì
Thay thế cho [0-9]
07
\D
Ký tự không phải là số
Thay thế cho [^0-9]
08
\s
Kí tự khoảng trắng
09
\S
Không phải kí tự khoẳng trắng
Thay thế cho [^\s]
10
\S+
Một số kí tự không phải khoẳng trắng
Một hoặc một số
11
\w
Kí tự chữ
Thay thế cho [a-zA-Z0-9]
12
\W
Kí tự không phải chữ
Thay thế cho [^\w]
13
\b
Ký tự thuộc a-z hoặc A-Z hoặc 0-9 hoặc _
Thay thế cho [a-zA-Z0-9_]
Ký tự đặc biệt
TT
Biểu thức
Mô tả
Ghi chú
14
.
Khớp với bất kỳ ký tự đơn nào ngoại trừ \
15
^
Bắt đầu của từ
16
$
Kết thúc của từ
17
/
Bắt đầu hoặc kết thúc chuỗi regex
18
|
Sủ dụng tương đương phép or
Hay dùng trong cặp ngoặc tròn
19
\
Biểu diễn một kí tự ngay sau nó từ kí tự đặc biệt thành kí tự thường và ngược lại
VD: \b sẽ trở thành như mình nói ở trên, * sẽ trở thành kí tự * chứ không phải số lần lặp nữa
Toán tử và ký hiệu
STT
Biểu thức
Mô tả
Ghi chú
20
*
Xuất hiện 0 hoặc nhiều lần
viết ngắn gọn cho {0,}
21
+
Xuất hiện 1 hoặc nhiều lần
viết ngắn gọn cho {1,}
22
?
Xuất hiện 0 hoặc 1 lần
viết ngắn gọn cho {0,1}
23
{X}
Xuất hiện X lần
X không phải số âm
24
{X,Y}
Xuất hiện trong khoảng X tới Y lần
X,Y không phải số âm
25
?
có nghĩa là xuất hiện 0 hoặc nhiều lần, thêm ? phía sau nghĩa là tìm kiếm khớp nhỏ nhất
Khớp nhóm
STT
Biểu thức
Mô tả
Ghi chú
26
()
Khớp với một nhóm các kí tự đồng thời nhớ kết quả khớp
Ví dụ (e|g)mail sẽ khớp với email hoặc gmail. /(ab) (cd) \1 \2/ sẽ khớp với “ab cd ab cd”
27
(?:x)
Khớp với x nhưng không nhớ kết quả khớp
“foo foo” sẽ khớp với /(foo) \1/ chứ không khớp với (?:foo) \1
28
x(?=y)
Chỉ khớp x nếu ngay sau x là y
“hello” sẽ khớp với /h(?=e)/ nhưng kết quả trả về chỉ có h
*App Store Optimization (ASO) – tối ưu hóa cửa hàng ứng dụng là tất cả các cách thức khiến app của bạn tăng thứ hạng và lượt tải tự nhiên (organic download) trên các cửa hàng ứng dụng. Tải xuống nhiều hơn có nghĩa là nhiều người dùng hơn và kéo theo đó là cải thiện và tăng trưởng doanh thu. Nghe thì có vẻ đơn giản, nhưng sự thật thì không như thế.
Có lẽ bạn đang tự hỏi ASO hoạt động như thế nào? Nó có đáng để chúng ta tìm hiểu và đầu tư? Ta nên bắt đầu từ đâu đây? Đừng lo lắng nhé! Thông qua bài viết này, bạn sẽ có cái nhìn tổng quan về ASO. Chúng tôi sẽ đề cập đến vấn đề này và nhiều câu hỏi khác liên quan đến ASO trong bài hướng dẫn dưới đây.
Vì vậy, hãy tiếp tục đọc và bắt tay vào việc tăng trưởng kinh doanh trên mobile của bạn ngay bây giờ!
App Store Optimization (ASO) là gì?
Tối ưu hóa cửa hàng ứng dụng là quá trình cải thiện khả năng hiển thị của ứng dụng của bạn trong một cửa hàng ứng dụng di động (Apple App Store & Google Play). Nguyên tắc cơ bản của ASO, bên cạnh thứ hạng cao trong cửa hàng ứng dụng, là tập trung vào tỷ lệ nhấp (Click-through rate – CTR). Điều này có nghĩa là bạn phải thuyết phục mọi người thực sự click vào ứng dụng của bạn một khi họ tìm thấy nó. Bạn có thể làm như vậy bằng cách tối ưu hóa Tên ứng dụng, Tiêu đề, Logo, Ảnh chụp màn hình và Xếp hạng ứng dụng. Nhưng nó không chỉ có vậy!
Khi mọi người nhấp vào ứng dụng của bạn và trên App Landing Page của bạn, bạn phải đảm bảo họ cũng tải ứng dụng hoặc mua hàng. Phần này của Tối ưu hóa trong cửa hàng ứng dụng còn được gọi là Tối ưu hóa tỷ lệ chuyển đổi (Conversion Rate Optimization – CRO).
ASO thường được gọi là App Store SEO (Search Engine Optimization – Tối ưu hóa công cụ tìm kiếm). Cả hai quá trình đều có chung điểm tương tự như tối ưu hóa từ khóa, liên kết ngược và tối ưu hóa chuyển đổi. Sự khác biệt chính giữa Tối ưu hóa cửa hàng ứng dụng và Tối ưu hóa công cụ tìm kiếm là các yếu tố xếp hạng. Bên cạnh đó, ASO được sử dụng cho các ứng dụng di động trong khi SEO cho các trang web.
Google xem xét hơn 200 khía cạnh và danh sách tiếp tục mở rộng. Danh sách các yếu tố xếp hạng cho ASO ngắn hơn nhiều, tuy nhiên nhiều người vẫn không chắc chắn yếu tố nào đóng vai trò quan trọng hơn và cần thêm thời gian để xác định rõ vấn đề này.
Checklist minh họa dưới đây sẽ cho thấy sự khác biệt và tương đồng chính giữa ASO & SEO.
Tại sao tối ưu hóa cửa hàng ứng dụng lại quan trọng
Hơn 2 triệu ứng dụng có sẵn trong iTunes Connect và hơn 3,3 triệu ứng dụng được cung cấp bởi Google Play Store (Nguồn: Statista). Điều đó có nghĩa là, ứng dụng của bạn đang đối mặt với một cuộc cạnh tranh rất khó khăn.
Mục tiêu chính của Tối ưu hóa cửa hàng ứng dụng là tăng lượt tải xuống và số lượng người dùng. Vì vậy, bước đầu tiên để tối đa hóa số lượt tải app của bạn là phải khiến người dùng tìm được nó. Nhưng làm thế nào bạn có thể làm điều đó?
Để hiểu làm thế nào để thúc đẩy mức tăng trưởng tự nhiên, trước tiên bạn phải hiểu cách mọi người bắt đầu search cho đến khi họ tìm được ứng dụng của bạn.
Làm thế nào người dùng tìm kiếm và tải app?
Mọi người chủ yếu tìm kiếm và “săn lùng” các ứng dụng cụ thể trong các cửa hàng ứng dụng hàng đầu như Google Play Store và Apple App Store (iTunes). Theo một nghiên cứu chi tiết hơn của Tune, 47% ứng dụng iOS được phát hiện thông qua tìm kiếm cửa hàng ứng dụng. Trên Google Play Store, thậm chí 53% lượt tải xuống có được bởi tìm kiếm. Rõ ràng, tìm kiếm cửa hàng ứng dụng là phương pháp phổ biến nhất để khám phá các ứng dụng mới.
Hơn nữa, vị trí xếp hạng của một ứng dụng tương quan trực tiếp với số lượt tải xuống. Ứng dụng của bạn xếp hạng càng cao trong một cửa hàng, nó càng xuất hiện nhiều hơn với người dùng. Các ứng dụng xếp hạng cao hơn cũng nhận được nhiều lượt tải xuống hơn vì người dùng thường không kéo xuống và xem qua tất cả các kết quả tìm kiếm. Nếu như không có ASO, bạn đang bỏ lỡ một phương thức để tiếp thị mạnh nhất, nhanh nhất, lớn nhất cho các ứng dụng và trò chơi. Do đó, học về ASO và đầu tư thời gian vào có thể là vũ khí bí mật mang lại kết quả lâu dài cho bạn. Tò mò về những lợi thế khác của ASO? Chỉ cần tiếp tục đọc và bạn sẽ tìm ra!
Như bây giờ bạn biết rằng hầu hết người dùng tìm thấy ứng dụng bằng cách tìm kiếm các cửa hàng ứng dụng, bạn có thể nhận ra rằng Tối ưu hóa App Store là điều bắt buộc nếu bạn muốn có được ứng dụng của mình trước người dùng phù hợp. Trên thực tế, nhiều người cho rằng tối ưu hóa ứng dụng của bạn là chiến lược tiếp thị hiệu quả nhất cho các ứng dụng và trò chơi di động. Đây là cách bạn có thể hưởng lợi từ ASO:
Cải thiện Độ hiển thị và Sự nổi bật trên App Store
Cho dù ứng dụng hoặc trò chơi của bạn tốt đến đâu, nếu không thể tìm thấy nó trong các cửa hàng thì nó không tốt. Vì vậy, đừng để mọi thứ khó khăn hơn, bắt đầu tối ưu hóa!
Giúp người dùng có nhu cầu dễ dàng tìm thấy
ASO sẽ giúp bạn “đúng app đúng người” và trở thành “điểm đến lí tưởng” cho những người dùng thực sự tìm kiếm và cần một ứng dụng như cái bạn đang có.
Tăng lượt tải xuống tự nhiên (organic download)
Chiến lược ASO tốt là cách tuyệt vời để tăng lượng download tự nhiên của bạn và đảm bảo kết quả lâu dài. Với việc theo dõi và điều chỉnh thường xuyên, những nỗ lực của bạn chắc chắn sẽ thành công.
Cắt giảm chi phí chuyển đổi users
Thay vì chi tiền cho quảng cáo, bạn có thể giảm chi phí chuyển đổi người dùng bằng cách tập trung vào tăng trưởng tự nhiên với ASO. Điều này không chỉ giúp bạn tiết kiệm tiền mà còn đảm bảo sự tăng trưởng ổn định.
Tăng doanh thu và tỷ lệ chuyển đổi (CR) của bạn
Quảng cáo, mua trong ứng dụng và các cách tương tự để kiếm tiền từ ứng dụng chỉ là một phần của doanh thu. Nếu người dùng đến trang của bạn thông qua quảng cáo nhưng không tải xuống, bạn sẽ không nhận được gì cả. Đó là lí do tại sao ASO là cần thiết.
Mở rộng ứng dụng của bạn trên toàn thế giới
Bằng cách làm cho ứng dụng của bạn tích hợp nhiều ngôn ngữ khác, bạn có thể tìm thấy người dùng từ khắp nơi trên thế giới. ASO có thể giúp bạn đưa ứng dụng của bạn toàn cầu.
Phần 2 bạn sẽ tìm hiểu xem cách thức App Store Optimization hoạt động như thế nào….cùng theo dõi nhé!
Source: appradar.com
Vietnam Mobile Day 2019 – nơi hội tụ những chuyên gia trong lĩnh vực ứng dụng sẽ cùng bạn chinh phục những bảng xếp hạng trên App Store, đừng bỏ lỡ nhiều topic hay tại VMD2019 do TopDev tổ chức năm nay nhé!
Được nhà tuyển dụng của một công ty công nghệ lớn chú ý không phải là điều dễ dàng gì. Cùng xem các mẹo sau để lọt vào mắt xanh các ông trùm nhé!
Những công ty công nghệ hàng đầu từ Silicon Valley như Google, Microsoft và Apple được xem là môi trường làm việc trong mơ của hàng ngàn lập trình viên. Hàng ngàn nhà phát triển, thiết kế, kỹ sư, nhà khoa học dữ liệu và quản lý giỏi và có tiềm năng nhất sẽ làm bất cứ điều gì để có một suất làm việc ở đây.
Ba công ty này nhiều năm nay đã nhận được vô số hồ sơ ứng tuyển. Trên thực tế, Michael Morell, một chuyên gia tuyển dụng tại Palo Alto, California cho biết: trong một cuộc phỏng vấn với tờ New York Times, năm 2008, Google tiết lộ họ nhận được 20.000 hồ sơ mỗi tuần, nhưng càng về sau, con số này đã lên đến 70.000.
Bạn có thể tưởng tượng được núi hồ sơ Google, Microsoft và Apple nhận được chưa? Đương nhiên, số lượng đơn xin việc tại các công ty này vẫn tiếp tục tăng lên đáng kể.
Đối với ứng viên nộp đơn, tỉ lệ cạnh tranh vô cùng lớn, nhưng đừng vì vậy mà từ bỏ ý định gửi đơn nhé!
Hãy để Techtalk chia sẻ vài bí kíp giúp bạn tăng cơ hội được chọn tại các công ty đáng mơ ước này. Bài viết này đã nghiên cứu kỹ lưỡng về lĩnh vực tuyển dụng thực tế, kỹ năng làm việc và những sai lầm điển hình của ứng cử viên mà bạn chắc chắn có thể học hỏi chút ít.
Với số lượng hồ sơ mà Google, Microsoft và Apple nhận được thì việc xem xét kỹ lưỡng từng đơn sẽ tốn nhiều tháng, thậm chí hàng năm! Rõ ràng, các nhà tuyển dụng sẽ không làm điều đó. Họ xem xét càng nhiều ứng viên càng tốt bằng cách giảm thời gian họ dành để xem một hồ sơ.
Theo nghiên cứu của TheLadders, các nhà tuyển dụng chỉ dành 6s để xem xét một hồ sơ cá nhân. Đây là cách duy nhất họ có thể phân tích càng nhiều hồ sơ càng tốt.
Vì vậy, bạn phải tạo ấn tượng tốt, một cách nhanh chóng!
Dưới đây là những gì bạn có thể làm để cho nhà tuyển dụng biết bạn là nhân tố họ đang tìm kiếm:
Đính kèm tên của bạn cùng với thư giới thiệu và chứng chỉ, bằng cấp liên quan (MBA, v.v.) với font chữ dễ đọc, rõ ràng nhất
Tránh liệt kê quá nhiều nhiều việc làm ngắn hạn
Làm nổi bật vai trò của bạn trong từng công việc
Không sử dụng font chữ và hình ảnh rườm rà rối mắt
Ảnh cá nhân phải chuyên nghiệp
Vượt qua quy luật 6s, chào mừng bạn đến với một bài kiểm tra nghiêm túc hơn. Bạn chỉ có thể vượt qua nếu khéo léo tránh được một số lỗi nhất định. Scott Bacon, một cựu quản lý nhân sự của Google đã đồng ý chia sẻ một số bí mật tuyển dụng của gã công nghệ khổng lồ này trong một cuộc phỏng vấn với công ty Fast Company.
Sau đây là những sai lầm đảm bảo sẽ khiến bạn auto rớt trong bài kiểm tra đánh giá này:
Quá nhiều chữ. Bacon khuyên chúng ta nên đặt những thông tin phù hợp nhất gần góc trên cùng bên trái vì đó là nơi các nhà tuyển dụng Google sẽ quét qua đầu tiên.
Mục tiêu nghề nghiệp ư, xưa rồi! Tốt hơn là nên soạn một list với tối đa 10 kỹ năng mà bạn có thể thảo luận với nhà tuyển dụng trong cuộc phỏng vấn.
Loại trừ những kinh nghiệm không liên quan. Một công ty nghiêm túc như Google, Microsoft hoặc Apple không quan tâm đến kinh nghiệm làm người pha chế của bạn.
Điều chỉnh resume cho phù hợp với từng công ty. Một nhà tuyển dụng giàu kinh nghiệm sẽ phát hiện gần như ngay tức khắc nếu bạn có đầu tư cho CV của mình hay không. Đối với họ, những hồ sơ như vậy đồng nghĩa với việc xúc phạm nhà tuyển dụng.
Sơ lược những thành tích bạn đạt được. Lời khuyên của Bacon là “1 dòng tóm lược, 2 dòng thành tích”.
Google, Microsoft và Apple đều sẽ muốn nhân viên của mình có nhiều kỹ năng và đa tài. Nhưng những kỹ năng nào sẽ được các ông lớn đánh giá cao? Nghiên cứu của Paysa, một hội đồng quản trị phổ biến, sẽ cho chúng ta câu trả lời.
Các nhà nghiên cứu đã phân tích hồ sơ của những người làm việc tại Microsoft, Apple và Google, và lọc ra một số điểm chung trong các kỹ năng mà họ có.
Đối với người quản lý sản phẩm:
Dịch vụ khách hàng
Khả năng lãnh đạo
Quản lý dự án, chiến lược
Điện toán đám mây
Tiếp thị sản phẩm
Đối với nhà thiết kế web:
Thiết kế web
Adobe Photoshop, Adobe Illustrator
Thiết kế giao diện người dùng (UI/UX)
Thiết kế đồ họa
Cấu trúc thông tin
Đối với các nhà khoa học dữ liệu:
Quản lý dự án
SQL
Thích ứng nhanh với máy móc
Phân tích và khai thác dữ liệu
Python
Đối với kỹ sư:
SQL
Python
Phát triển phần mềm
Java
JavaScript
Phần này dành cho những ai muốn tìm việc tại Apple. Apple được cho là công ty có văn hóa công ty độc đáo. Để được nhận vào Apple, bạn phải có chiến lược nhất định. Một số việc sau đây có thể giúp bạn thu hút sự chú ý của Apple:
Hãy là một thực tập sinh tiềm năng. Apple thường xuyên mở chương trình thực tập sinh, vì vậy nếu bạn làm tốt, họ có thể xem xét tuyển bạn vào làm nhân viên chính thức. Kiểm tra các vị trí thực tập có sẵn tại đây
Tích cực trên các diễn đàn của Apple. Từng có trường hợp thành viên chuyên bình luận hoặc chia sẻ trên các diễn đàn của Apple đã trở thành nhân viên chính thức của công ty, ví dụ như Jordan Hubbard. Từ đó trở đi, nhiều nhà phát triển đã tìm cách ghi điểm trên diễn đàn của Apple bằng cách hiển thị kinh nghiệm cá nhân.
Phát triển ứng dụng iOS hoặc Mac cho AppStore. Điều này minh chứng cho khả năng mã hóa tuyệt vời của bạn, cũng là yếu tố mà các nhà tuyển dụng tại Apple yêu thích.
Quá trình ứng tuyển tại Apple, Google, và Microsoft đồng nghĩa với việc tham gia vào một cuộc cạnh tranh khốc liệt, nhưng điều đó không có nghĩa là bạn không có cơ hội nào. Note lại những mẹo mà Techtalk chia sẻ để tăng cơ hội việc làm của mình. Và đừng lo lắng nếu bạn không nhận được phản hồi ngay vì nhà tuyển dụng sẽ mất hàng tuần, thậm chí vài tháng để lọc lại tất cả hồ sơ.
Laravel là một framework theo mô hình MVC (Model – View – Controller). Trong mô hình này, một request sẽ chạy đến Router. Sau đó nó tiếp tục đến Controller(C), Controller sẽ có thể tiếp tục xử lý tại Model (M) và trả dữ liệu ra View (V).
Trong Laravel framework, một request về cơ bản thì sẽ chạy theo cách nói ở trên. Tuy nhiên, bên dưới của nó thực sự có những gì đang diển ra.
2. Nội dung
Khi một request được bắt đầu từ Router thì để có được một response, nó phải trải qua những gì. Mọi thứ sẽ được bắt đầu trong app từ public/index.php.
2.1. Auto Loader
require __DIR__.'/../vendor/autoload.php';
Đầu tiên nó sẽ gọi đến file autoload.php. Nó sẽ tự động load tất cả các package/library được đăng ký bởi Composer trong quá trình sử dụng.
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
Bên trong có gì:
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
$this->registerBaseBindings();
$this->registerBaseServiceProviders();
$this->registerCoreContainerAliases();
}
Trong __constructor nó nhận vào một cái $basePath = null. Sau đó nó sẽ kiểm tra sự tồn tại của $basePath này và sẽ tạo ra các global path cho các thư mục như base, lang, config, public, storage, database, resources, bootstrap mà chúng ta vẫn sử dụng.
Ví dụ:
$this->instance('path.config', $this->configPath());
public function configPath($path = '')
{
return $this->basePath.DIRECTORY_SEPARATOR.'config'.($path ? DIRECTORY_SEPARATOR.$path : $path); // define ('DIRECTORY_SEPARATOR', "/");
}
Vậy tại sao nói nó tạo ra các global đường dẫn ??? Đã bao giờ các bạn sử dụng các hàm như config_path, public_path, ... chưa.
Tất cả các hàm này đều tạo ra một instance với cái tên 'path.name', trùng với các thư mục trong application của bạn. Và chúng được tạo ra bởi các hàm trong file helpers.php.
if (! function_exists('config_path')) {
/**
* Get the configuration path.
*
* @param string $path
* @return string
*/
function config_path($path = '')
{
return app()->make('path.config').($path ? DIRECTORY_SEPARATOR.$path : $path);
}
}
Khi bạn gọi config_path(), mặc định Laravel sẽ tạo ra một instance của cái tên path.config. Mà hàm này chính là đường dẫn đến file folder config đã nói ở trên.
Hàm $this->registerBaseBindings(); sẽ tạo một instance của 'app' và một instance của class Container::class bằng chính cái class Illuminate\Foundation\Application.php này.
Và cuối cùng là khởi tạo một instance của PackageManifest.
Tiếp theo:
$this->registerBaseServiceProviders();
Laravel sẽ đăng kí các Service Provider cơ bản cho application. Tưởng tượng như project của chúng ta sẽ luôn luôn cần có những cái cơ bản, cái gốc để nó có thể hoạt động được. Chúng ta có thể thấy Laravel đăng ký các provider như Event, Log, Routing:
public function registerCoreContainerAliases()
{
foreach ([
'app' => [\Illuminate\Foundation\Application::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class, \Psr\Container\ContainerInterface::class],
'auth' => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class],
// ...
] as $key => $aliases) {
foreach ($aliases as $alias) {
$this->alias($key, $alias);
}
}
}
Hàm này có tác dụng đăng ký các tên cho các class trong Laravel Application. Ví dụ như 'app' sẽ được gán cho \Illuminate\Foundation\Application::class, \Illuminate\Contracts\Container\Container::class, \Psr\Container\ContainerInterface::class].
Ví dụ khi bạn gọi app()->make('hash') thì 'hash' chính là \Illuminate\Hashing\HashManager::class.
Bước tiếp theo, Laravel sẽ đăng kí binding cho class Http\Kernel và Console\Kernelcùng với đó là một class Exception, mục tiêu là để phục vụ cho việc handle Request và handle các lỗi khi xảy ra từ Http và giao diện Console. (Terminal).
Sau khi binding xong , $app này sẽ được trả về.
Ở trên chúng ta thấy Laravel binding class Illuminate\Contracts\Http\Kernel::classbằng một App\Http\Kernel::class. Vậy hàm này sẽ trả về cho chúng ta một instance của Illuminate\Contracts\Http\Kernel::class, chính là App\Http\Kernel.php
2.4. Handle Request
Http Kernel này sẽ xử lý $request và $response là kết quả của quá trình xử lý:
Giờ chúng ta cùng tìm hiểu xem Laravel của chúng ta thực sự đã làm những gì để xử lý một request nhé. Chúng ta hãy mở class App\Http\Kernel ra. Trong này có rất nhiều groupMiddleware khác nhau. Nếu để ý bạn có thể thấy có một nhóm globalMiddleware, một middlewareGroup và một routeMiddleware, nhiệm vụ của chúng là dùng để lọc các request chạy đến application của bạn, sau đó xử lý như thế nào tiếp theo sẽ là do phần xử lý bên trong của middleware đó.
Nhóm globalMiddleware là phần mà mỗi request đều phải đi qua nó. Ví dụ như middleware \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class
Hàm này chắc các bạn cũng hiểu rồi phải không.
Bạn mở class mà Kernel kế thừa ra tại Illuminate\Foundation\Http\Kernel.
Kéo xuống phần hàm handle($request) ta thấy có đoạn
Mở hàm sendRequestThroughRouter($request) và 2 hàm dưới luôn nhé.
protected function sendRequestThroughRouter($request)
{
$this->app->instance('request', $request); // Đăng ký request hiện tại với Laravel Service Container với tên `'request'`
Facade::clearResolvedInstance('request'); // Loại bỏ request facade khác
$this->bootstrap(); // Load các bootstrapper
return (new Pipeline($this->app)) //Đẩy request qua các middleware và cuối cùng là đẩy request lên router
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}
public function bootstrap()
{
if (! $this->app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers());
}
}
/**
* Get the route dispatcher callback.
*
* @return \Closure
*/
protected function dispatchToRouter()
{
return function ($request) {
$this->app->instance('request', $request);
return $this->router->dispatch($request);
};
}
Đến đây, gần như quá trình handle một request đã xong. Quay lại hàm handle($request) tại Illuminate\Foundation\Http\Kernel.php, sau khi dispatch request lên router và đã được xử lý, nó sẽ dispatch một event bao để báo rằng request đã được xử lý, hàm này gồm 2 tham số quen thuộc là $request và $response.
$this->app['events']->dispatch(
new Events\RequestHandled($request, $response)
);
return $response // cuối cùng là phải return cái $response này
2.5. Kết thúc
Sau khi có $response nó sẽ được trả về cho người dùng
$response->send();
Quá trình handle một request được kết thúc, cuối cùng là đóng Kernel và kết thúc.
$kernel->terminate($request, $response);
3. Tổng kết
Vòng đời của một request trong Laravel.
Auto Loader.
Khởi tạo ứng dụng, đăng ký các global_path, provider cơ bản, các bí danh cho các class.
Binding class Kernel, Exception.
Tạo Kernel.
Đăng ký Service Provider
Send request qua middleware => send request tới router
Gửi response về cho người dùng
Kết thúc
Và thành phần quan trọng nhất trong quá trình handle một request trong Laravel chính là Service Provider.
Tham khảo thêm việc làm laravel không yêu cầu kinh nghiệm cao tại đây
Trong bài viết này, chúng ta sẽ đưa ra những tiêu chí, những nhu cầu của lập trình viên khi chọn một công nghệ nào giữa Flutter, React Native hay Xamarin để phát triển ứng dụng di động cross-platform cho dự án của mình.
So sánh Flutter, React Native và Xamarin
So sánh về độ dễ học
Flutter dễ học nếu bạn biết Dart – ngôn ngữ lập trình hướng đối tượng của Google. Nó không tốn nhiều thời gian để học một ngôn ngữ mới và nếu bạn biết các ngôn ngữ khác như JavaScript, nó sẽ trở nên dễ dàng hơn nữa.
Với React Native, các khái niệm sẽ rất dễ dàng nếu bạn là một developer có kinh nghiệm về JavaScript và bạn có một số kỹ năng React.
Nếu bạn biết Android, việc nắm bắt Xamarin sẽ dễ dàng hơn vì nhiều thứ giống hệt nhau và C # rất giống với Java. Xamarin có thể cho phép bạn tiết kiệm tới 70% công việc phát triển trên nền tảng di động thứ hai bằng cách sử dụng lại mã.
Trong khi xây dựng một ứng dụng di động với Flutter, bạn không cần phải quan tâm đến tốc độ vì framework sẽ mang lại cho bạn hiệu suất khiến người dùng hài lòng. Flutter cho phép bạn sử dụng mã gốc và các widget để đạt được hiệu suất tốt hơn và tương tự nhau trên Android và iOS.
React Native mang lại hiệu suất tương tự như các ứng dụng native. Vì vậy, nếu bạn đang xây dựng một ứng dụng di động với framework này, ứng dụng của bạn sẽ cực nhanh. Nó cho phép bạn viết mã bằng các ngôn ngữ native, chẳng hạn như Objective-C, Java hoặc Swift. Bạn thậm chí có thể xây dựng một phần ứng dụng của mình trong React Native trong khi phần còn lại có thể được xây dựng bằng mã gốc.
Các ứng dụng được xây dựng trong Xamarin sẽ cung cấp cho bạn hiệu suất tốt hơn và sẽ liên tục cải tiến để phù hợp với các tiêu chuẩn phát triển ứng dụng native. Với sự trợ giúp của Xamarin, ứng dụng iOS và Android có thể là ứng dụng gốc hoàn toàn bằng cách tận dụng mọi nền tảng. Xamarin Forms 2.0, được giới thiệu là một phần của Xamarin 4, bao gồm một số tính năng như các form, thư viện giao diện người dùng UI đặc biệt và nâng cấp mới để giúp bạn xây dựng các ứng dụng nhanh.
Hiệu năng phát triển
Tính năng “Hot Reload” có sẵn trong Flutter giúp tiết kiệm thời gian của các Developer. Có rất nhiều widget và API trong Flutter cho phép bạn hoàn thành công việc phát triển ứng dụng một cách nhanh chóng. Vì vậy, với sự trợ giúp của framework này, các nhà phát triển có thể làm việc một cách hiệu quả và tiết kiệm rất nhiều thời gian trong khi xây dựng một ứng dụng di động. Nền tảng này khá thuận lợi cho các doanh nghiệp.
Với React Native, bạn sẽ linh hoạt làm việc trên bất kỳ trình soạn thảo văn bản / IDE nào mà bạn thích. Vì vậy, bạn có thể chọn một trình soạn thảo văn bản có thể tiết kiệm cả thời gian và tiền bạc. Nó cung cấp cho bạn một tính năng được xây dựng trước có tên “Hot Reload”, giúp hoàn thành dự án của bạn nhanh hơn. Với sự trợ giúp của tính năng này, người ta có thể tải lại ứng dụng của mình mà không cần biên dịch lại ứng dụng, cho phép kết quả hơn nữa trong việc nâng cao năng suất của nhà phát triển.
Trong Xamarin, Tải lại “Live Reload” trao quyền cho các developer triển khai các cải tiến cho XAML và xem chúng được phản ánh trực tiếp mà không cần bất kỳ quá trình biên dịch và triển khai nào. Vì ứng dụng của bạn được biên dịch trong khi sử dụng “Live Reload”, nên nó hoạt động với tất cả các thư viện và điều khiển của bên thứ ba. “Live Reload” hoạt động trên tất cả các nền tảng Xamarin. Các hình thức hỗ trợ, bao gồm iOS, Android, UWP và WPF và hoạt động ở tất cả các mục tiêu triển khai hợp pháp, bao gồm giả lập, giả lập, cũng như các thiết bị vật lý.
Kiến trúc của Flutter rất dễ hiểu và ngay cả các developer mới, những người bắt đầu với mã App hiện có, có thể theo framework một cách dễ dàng. Kiến trúc của Flutter giúp làm việc nhóm dễ dàng hơn. Nó hỗ trợ cách tiếp cận Reactive UI của Flutter và nó không bao giờ làm tổn hại đến hiệu năng của Flutter. Một thư viện kiến trúc ứng dụng Dart với luồng dữ liệu đơn hướng được điều chỉnh bởi RefluxJS và Facebook ED Flux.
Kiến trúc ứng dụng React bản địa được gọi là Flux. Facebook sử dụng Flux để xây dựng các ứng dụng web phía khách hàng. Mọi khung công tác thường tuân theo khung MVC. Luồng dữ liệu đơn hướng là ý tưởng cơ bản của Flux.
Kiến trúc Xamarin bao gồm một nền tảng thiết kế trực quan để xây dựng native như ứng dụng, bộ thử nghiệm, hỗ trợ thư viện riêng và cửa hàng thành phần kiểu nugget. Thiết kế hình ảnh iOS đã được cung cấp thông qua IDE của họ để giúp các nhà phát triển mở X-Code.
Cộng đồng hỗ trợ
Flutter được biết đến với sự hỗ trợ cộng đồng tốt. Trên GitHub, Flutter có 33.248 ngôi sao và 223 người đóng góp giúp nó ngày một tốt hơn. Họ làm việc trong các lĩnh vực khác nhau của Flutter để cung cấp cho bạn trải nghiệm phát triển tốt nhất. Các nhà phát triển Flutter thậm chí còn hoạt động trên các trang web QA cũng như các diễn đàn.
React Native cũng có một cộng đồng hỡ trợ mạnh mẽ. Trên GitHub, React Native có 66.693 sao và 1.692 người đóng góp React Native để nâng cao nó. Framework có một cộng đồng lớn các nhà phát triển trên toàn cầu và họ có thể hỗ trợ bạn trả lời các truy vấn của bạn trên các trang web và diễn đàn QA.
Đối với Xamarin, cộng đồng hỗ trợ rất hạn chế. Diễn đàn và hỗ trợ trợ giúp không có sẵn ngay lập tức cho người dùng Xamarin trên Internet.
Các ứng dụng nổi bật
Flutter: AppTree, Alibaba, Topline, Google AdWords, Google Greentea, Reflectly, Hamilton Musical, OfflinePal, Hookle, Birch Finance, BetaBub,….
Chọn Flutter: Nếu bạn cần một framework có hiệu suất cao và cung cấp giao diện người dùng nhất quán trên cả iOS và Android với cùng một mã nguồn. Flutter là lựa chọn lý tưởng cho các ứng dụng cần giao diện phức tạp, hoạt ảnh mượt mà và hiệu suất cao.
Chọn React Native: Nếu bạn đã có kinh nghiệm với JavaScript và muốn phát triển ứng dụng di động với hiệu suất tốt và tận dụng được sự phong phú của thư viện bên thứ ba. React Native là lựa chọn tốt cho các ứng dụng có yêu cầu đa nền tảng, cần sự linh hoạt và tốc độ phát triển nhanh.
Chọn Xamarin: Nếu bạn làm việc trong môi trường Microsoft và muốn tích hợp sâu với các công nghệ .NET. Xamarin là lựa chọn phù hợp cho các ứng dụng doanh nghiệp hoặc các hệ thống yêu cầu tính năng phức tạp và truy cập sâu vào các API của hệ điều hành.
Việt Nam được đánh giá là một thị trường rất tiềm năng về IT. Không những thế nghề lập trình viên luôn được đánh giá nằm trong top các nghề được nhiều bạn sinh viên lựa chọn. Tuy nhiên thực trạng nghề lập trình cho thấy, do tâm lí theo đám đông xen lẫn chưa hiểu rõ về IT khiến một số lượng không nhỏ người theo nghề lập trình chỉ đơn giản vì thu nhập khủng. Kết quả là đầu ra luôn có chất lượng không đồng đều. Điều này cũng phản ánh rõ ràng với thực trạng thiếu nguồn nhân lực với tay nghề cao. Vậy thì những lập trình viên với khả năng còn thấp này làm cách nào để có thể kiếm tiền nhanh?
Xin thưa, đó là họ chấp nhận làm các dự án với giá cực kì thấp có thể gọi là “phá giá”. Vô hình chung chính hành động này khiến cho việc phát triển IT bị kiềm hãm, khi các công ty về công nghệ cũng như những lập trình viên chân chính cũng phải điều chỉnh hoặc là hạ theo để không bị mất khách. Đọc đến đây bạn hẳn ngờ ngợ rằng chuyện này khá quen thuộc. Quả là đúng như vậy, cách đây vài năm, nghề thiết kế – design trở thành một những lựa chọn đình đám và thế là, dẫn đến việc có quá nhiều người theo học chỉ vì mộng làm giàu, rồi cũng nhanh chóng lụi tàn và quyết định “phá giá” để kiếm miếng ăn. Khiến cho giá một bức hình, một bản thiết kế mà chỉ có giá vài chục đến vài trăm ngàn.
Vì thế mà không có gì ngạc nhiên khi có rất nhiều lập trình viên tỏ ra bức xúc khi thấy nhiều “con sâu làm rầu nồi canh”, bán nghề với một cái giá rẻ mạt. Một member của fanpage Lập Trình Viên Confession chia sẽ nỗi lòng của mình về vấn đề này.
“Từ giờ tôi nghe đứa nào đi đồn thổi “học IT giàu lắm” “lương cao chót vót” “chục triệu”,… là tôi bẻ cụp mỏ nhé.
Không phải những điều nói trên là sai, nhưng nói ra vậy để làm chi? Kéo thêm nhiều người vào ngành thì càng có nhiều đối thủ, càng xâu xé khắc nghiệt hơn, tính đi lại vết xe đổ của ngành kinh tế à? Khoảng chục năm trước bỗng rộ lên học kinh tế là giàu lắm thế là người người, nhà nhà đổ xô học kinh tế. Đại học cũng có cao đẳng cũng đầy, cứ 100 người là có 1 người học kinh tế như 99 đứa còn lại. Rồi giờ sao, đầy đứa thất nghiệp ất ơ luôn hoặc toàn làm mấy việc không liên quan. Cái gì tràn lan thường không quý, ít mà chất đi. Cái chuyện bức xúc dẫn tui phải viết cfs này là vì thấy nhiều dev bán web giá rẽ mạc.
Nó phá giá đấy ạ. Kiểu là giờ ai cũng xô đi học và có tool làm web tận răng nhiều khi d** cần biết code cũng làm được, xong bán với giá dưới hầm mộ. Giống hệt kiểu lao động nước mình qua làm lao động giá rẻ cho Mỹ để đá nồi cơm của người lao động Mỹ gốc í. Hỏi lao động Mỹ nó có ưa nổi lao động nhập cư không, tôi thì là tôi không ưa nổi rồi.
Trong ngành này tồn tại 1 dòng chảy rất khó ưa mà thường bắt đầu từ mấy dev chập chững vào ngành, đó là: nghe người ta đồn ngành này giàu -> bu nhau đi học -> mơ có tiền nhanh -> học sơ xài lấy tool làm đại 1 cái web để kiếm tiền sớm như kì vọng họ hàng -> phá giá để hút khách -> hại cả lũ đồng nghiệp bị hạ giá theo.
Cái trò này designer cũng dính nhiều rồi, nhưng thực chất thì ngành nào cũng vậy, cứ đại trà lên là sẽ có mấy con phá giá xuất hiện. Tóm lại mấy dev nào lương chục củ thì im lặng mà tận hưởng đi, đừng đem khoe khoang khổ cho mấy lứa sau. Đấy tôi hơi cọc tính với cũng chả giỏi văn nên chỉ viết được thế thôi.”
Tuy lời nói có chút nóng giận nhưng ta cũng thấy rõ sự chạnh lòng của một lập trình viên khi nói về viễn cảnh “phá giá” trong nghề IT. Thiết nghĩ nếu họ có tâm và chịu khó bám trụ, nâng cao bản thân thì có lẽ nghề cũng sẽ nuôi và trả cho công sức của mình bỏ ra. Tiếc rằng suy nghĩ như vậy không phải ai cũng có và đôi khi vì lòng tham, họ sẵn sàng bỏ dài lấy ngắn, vì miếng ăn mà phá cả nồi cơm.
Java vẫn là một trong những ngôn ngữ lập trình phổ biến nhất hiện nay. Phần lớn là nhờ vào lịch sử lâu đời và danh tiếng của nó: dễ học nhưng khó thành thạo. Sau khi bạn đã hiểu rõ hơn về lập trình Java, việc tìm hiểu để tiến xa hơn có thể sẽ trở nên khó khăn. Bài viết này nhằm mục đích giúp các Java developer vượt qua các rào cản đấy thông qua việc cung cấp mười tài liệu hữu ích về Java.
Tài liệu học lập trình Java từ cơ bản đến nâng cao
Java Core (Tiếng Việt) Java Core (Tiếng Việt) được websites USD (updatesofts.com) tổng hợp lại với 10 chương chi tiết, văn phong dễ hiểu, chắc chắn sẽ phù hợp với những bạn tìm hiểu lập trình Java.
Tổng hợp tài liệu JAVA (Tiếng Việt)
Bộ Tài liệu lập trình Java Tiếng Việt Căn Bản Tổng Hợp này, sẽ mang đến cho bạn những tài liệu hay ho bằng Tiếng Việt về lập trình Java biên soạn bởi 2 trường ĐH lớn của Việt Nam là Bách Khoa Đà Nẵng và Hà Nội. Bên cạnh đó còn 2 cuốn ebook Java tiếng Việt khác đang chờ đón bạn. Bộ tổng hợp nhỏ này bao gồm: Java Core – ĐH Bách Khoa Đà Nẵng Lập trình Java Tiếng Việt Slide Lập trình Jave – Phạm Quang Dũng Tài liệu lập trình Java – ĐH Bách Khoa Hà Nội.
Head First Java, 2nd Edition Head First Java mang đến trải nghiệm học tập đa phương tiện, tương tác cao, cho phép các lập trình viên mới tiếp nhận các nguyên tắc cơ bản của ngôn ngữ Java một cách nhanh chóng. Thông qua các bài tập kéo dàI, các phép loại suy đáng nhớ, hình ảnh hài hước và ngôn ngữ thông thường, Head First Java khuyến khích người đọc suy nghĩ như một lập trình viên Java. Với cách trình bày độc đáo, phong cách sinh động, câu đố, hình ảnh trực quan… Head First Java không chỉ hay cho bất cứ ai mới học về Java mà còn tốt cho những bạn đã có trình độ cao.
Java Performance
James Gosling từng chia sẻ suy nghĩ của ông rằng: “Nếu bạn yêu thích từng chi tiết nhỏ nhất về performance của các ứng dụng Java thì đây là quyển sách dành cho bạn.”
Cuốn này phù hợp nếu bạn đã khá tốt về Java. Kiến thức trong sách giúp những chương trình của bạn đạt được tối đa hiệu quả và giá trị, dù nó có độ phức tạp ra sao, đang chạy trên nền tảng nào, hoặc đã được viết bao lâu. Tác giả: Scoat Oaks
Java Concurrency in Practice Java Concurrency in Practice được viết bởi Brian Goetz là cuốn sách bao quát toàn diện và chuyên sâu Concurrency trong lập trình Java. Mỗi trang là những vấn đề từ cơ bản đến phức tạp (kèm theo giải pháp) bạn gặp hàng ngày. Nội dung trình bày rất chi tiết và khéo léo theo cách rõ ràng, súc tích, được viết cẩn thận, dễ bám sát, mạnh về áp dụng thực tế.
Java Puzzlers : Traps, Pitfalls, And Corner Cases
Được biết bởi các tác giả Joshua Bloch, Neal Gafter, quyển sách sẽ đưa bạn trải qua những cảm xúc “kinh ngạc”, “sáng mắt”, “nhũn não”… Nó cho bạn thấy những điểm “lừa tình” trong ngôn ngữ Java theo cách mà bạn không thể ngờ tới. Thậm chí nếu bạn là một lập trình viên lão luyện, những thử thách trong sách vẫn thật sự thú vị vì mỗi câu đố đều có độ khó được viết theo phong cách lôi cuốn và hài hước, luôn tỏ vẻ cho bạn cơ hội dễ dàng suy luận, giải quyết vấn đề và rồi, bạn lại thấy mình sai.
THE JavaTM Programming Language Trực tiếp từ những người sáng tạo ngôn ngữ lập trình Java ™, tái bản thứ tư được sửa đổi hoàn toàn của THE JavaTM Programming Language là một tài nguyên không thể thiếu đối với người mới làm quen và lập trình viên cao cấp. Các nhà phát triển trên toàn thế giới đã sử dụng các ấn bản trước để nhanh chóng có được sự hiểu biết sâu sắc về ngôn ngữ lập trình Java, mục tiêu thiết kế và cách sử dụng nó hiệu quả nhất trong phát triển thực tế.
Optimizing Java
Điều chỉnh hiệu suất là một công việc thử nghiệm, nhưng điều đó không có nghĩa là các kỹ sư nên ngồi một chỗ để phỏng đoán và mong đợi công việc hoàn thành. Với cuốn sách thực tế này, các lập trình viên Java từ trung cấp đến cao cấp sẽ học cách làm việc với các công nghệ sắp xếp phức tạp và học cách điều chỉnh hiệu suất các ứng dụng Java bằng cách sử dụng phương pháp định lượng, có thể kiểm chứng được.
Modern Java Recipes Việc giới thiệu các khái niệm lập trình chức năng trong Java SE 8 là một sự thay đổi mạnh mẽ cho ngôn ngữ hướng đối tượng này. Các biểu thức Lambda, các tham chiếu phương thức và các luồng cơ bản đã thay đổi thành ngữ của ngôn ngữ và nhiều nhà phát triển đã cố bắt kịp từ đó. Tài liệu này sẽ giúp ích. Với hơn 70 công thức chi tiết, tác giả Ken Kousen cho thấy cách sử dụng các tính năng mới nhất của Java để giải quyết một loạt các vấn đề.
Modern Java EE Design Patterns
Được viết bởi Markus Eisele, quyển sách này sẽ giúp bạn hiểu ngắn gọn về cách phát triển phần mềm ngày nay cho Enterprise và cũng với lịch sử được tiếp tục từ bản phát hành Java Enterprise Edition đầu tiên cho đến ngày nay. Điều này khiến người đọc hiểu tại sao một số phương pháp kiến trúc nhất định được áp dụng trong những năm qua và tại sao nó lại không thịnh hành. Không gian mở này để hiểu khoảnh khắc mà chúng ta đang sống bây giờ và chủ đề của cuốn sách: DevOps, microservice, Cloud, v.v.
Năm năm trước, một trong những vấn đề đau đầu lớn nhất mà các nhà quản lý IT phải đối mặt là sự xuất hiện của BYOD: Bring Your Own Device, chúng tôi xin được tạm hiểu là: sử dụng smartphone của mình trong công việc. Nói cách khác chiếc điện thoại của họ vô tình chứa toàn bộ dữ liệu của công ty.
“BYOD đã trở thành BYOIT”, Mike Meikle, Giám đốc điều hành của secureHIM, một công ty bảo hiểm và an ninh mạng, từng nói “Nhân viên có thể nhanh chóng truy cập các nền tảng vốn được bảo mật từ thiết bị di động của họ”.
Theo lời của Bobby Cameron, phó chủ tịch của Forrester Research, thuật ngữ “shadow IT” được sử dụng để ám chỉ việc các đội kỹ thuật sử dụng một server chỉ để chạy những việc riêng của mình vốn không liên quan tới công việc. Tuy vậy, bây giờ nó còn ám chỉ cả sales và marketing team đăng nhập và sử dụng một dịch vụ phần mềm hoặc up dữ liệu lên máy chủ trên AWS mà không thông qua bất kì sự cho phép nào.
“Nhưng bạn sẽ không thể làm gì!”, Cameron nói. “CNTT giờ đã thay đổi và những việc như vậy dần được chấp nhận bởi sự linh hoạt cũng như cần thiết trong chia sẻ và tìm hiểu về khách hàng”
Công việc của người quản lý CNTT cũng đã chuyển từ kiểm soát những sản phẩm nhân viên công nghệ sử dụng thành cung cấp hướng dẫn về các dịch vụ mà các thành viên nên dùng qua, Steven A. Lowe, chuyên gia tư vấn chính cho ThoughtWorks, nói.
“Vấn đề không phải là kiểm soát”, ông lập luận. “CNTT đã mất khả năng kiểm soát nhiều năm trước và sẽ không thể lấy lại nó. Giờ đây bạn phải sử dụng kiến thức CNTT của mình để giúp doanh nghiệp đưa ra quyết định tốt hơn về các ứng dụng và dịch vụ của bên thứ ba”.
Cloud không phải là giải pháp tối ưu cho mọi thứ
Sáu năm trước, hơn 40% các CIO, được khảo sát bởi Gartner, tin rằng họ sẽ chạy hầu hết các hoạt động CNTT của họ trên đám mây điện tử. Trong khi thực tế thì việc thay đổi hoàn toàn này vẫn còn chưa phổ biến.
Thay vào đó, Gartner dự đoán rằng 90% các tổ chức sẽ áp dụng cơ sở hạ tầng hybrid vào năm 2020, giữ một số tài nguyên CNTT trong phần cứng tại công ty và sử dụng dịch vụ outsourcing từ các nhà cung cấp đám mây công cộng hoặc tư nhân.
Không thể chối cãi rằng Cloud đã có một tác động đáng kể đến các hoạt động CNTT, nhưng nó không to lớn tới mức như tin tức đã thổi phồng. Một cuộc khảo sát vào tháng 6 năm 2017 với hơn 300 chuyên gia CNTT đã phát hiện ra rằng 80% tin rằng công nghệ đám mây sẽ không đáp ứng được kỳ vọng của họ do các vấn đề về an ninh, luật, sự phức tạp và chi phí.
Nói cách khác việc di chuyển một dịch vụ quan trọng lên Cloud không tự động làm cho dịch vụ trở nên đáng tin cậy hoặc có khả năng mở rộng hơn. Mặt khác, các công ty sẽ luôn tìm thấy một số ứng dụng mà họ không thể nhờ tới dịch vụ của cloud. Giống như một chương trình đã 25 tuổi và công ty tạo ra nó đã biến mất từ 15 năm trước. Bạn có thể sẽ không bao giờ loại bỏ hoàn toàn các ứng dụng độc quyền cũ mà công ty sử dụng hàng ngày.
Hệ thống của bạn có thể đã bị hack
Trong thực tế, vi phạm dữ liệu đã tăng 40% trong năm 2016, theo Identity Theft Resource Center.
Câu hỏi được đặt ra là liệu bạn có thể làm gì với nó? Nhiều doanh nghiệp trả lời bằng cách đầu tư vào các thiết bị an ninh mạng. Nhưng đó là cách tiếp cận sai lầm, theo Meikle.
“Mọi người đều muốn các hệ thống dễ quản lý và khó vi phạm” – ông nói – “Nhưng họ thường chọn các thiết bị an ninh cồng kềnh khó quản lý chứa dữ liệu nhạy cảm mà vẫn không được bảo vệ. Một cách tiếp cận thông minh hơn là thiết kế kế hoạch bảo mật của bạn xung quanh viễn cảnh cho rằng mình đã bị hack”.
Thay vì cố gắng bảo vệ các mạng và thiết bị, các tổ chức CNTT nên tập trung vào việc bảo vệ dữ liệu của công ty trên những thiết bị đầu cuối đó, theo Mainelli.
An ninh đã trở nên tồi tệ hơn một phần vì có nhiều thiết bị và dữ liệu hơn để bảo vệ, Cameron nói. Nhưng các công nghệ như các container dựa trên Docker cho dữ liệu đám mây và AI-driven automated breach detection đang giúp giảm thiểu vấn đề.
Các software của bạn luôn unpatched và thiếu bảo mật
Phần mềm chưa được patch là một nguy cơ bảo mật rất lớn. Tuy nhiên, theo khảo sát của Febera vào tháng 2 năm 2017, 10% người dùng ở Mỹ đang chạy các phiên bản Windows chưa được patch.
James Lee, phó chủ tịch điều hành và CMO của Waratek – một công ty bảo mật ứng dụng, cho biết: “Chúng tôi đã nhìn thấy nhiều khách hàng không update thường xuyên các patch. Điều này được kết hợp với các ứng dụng cũ không thể được cập nhật càng làm trầm trọng vấn đề hơn”
Tệ hơn nữa là việc bảo mật thường ít được ưu tiên bởi các nhà phát triển phần mềm, do đó mà kết quả là phần mềm ngày càng dễ bị tấn công.
Vấn đề bắt nguồn từ sự thất bại trong việc đảm bảo chất lượng phần mềm, theo lời Mark S. Kadrich, CISO tạm thời cho bệnh viện Martin Luther King Jr. ở Los Angeles – “Tám mươi phần trăm của phần mềm là rác, trong khi 20 phần trăm của nó thì dở tệ. Chỉ có rất ít là có thể được coi là thiết kế tốt.”
Bạn sẽ không bao giờ có đủ băng thông
Đó là điều không thể tránh khỏi, nhờ vào các thiết bị di động và IoT, lượng dữ liệu được truyền qua các mạng kinh doanh, được dự kiến sẽ tăng gấp đôi vào năm 2021, theo Cisco.
Tin tốt là các công ty đang ngày càng giỏi quản lý vấn đề tắc nghẽn mạng thông minh, Jones nói thêm.
IT vẫn sẽ đóng vai trò quan trọng nhưng nó cần biết cách tự thích ứng
Bất chấp sự bùng nổ về các dịch vụ CNTT, chuyên môn kỹ thuật vẫn được đánh giá cao trong các tổ chức. Nhưng các chuyên gia công nghệ sẽ cần phải update chính bản thân họ thông qua việc trau dồi kỹ năng mới và sẵn sàng chấp nhận một số trợ giúp từ rô bốt.
Một vài năm trước, các chuyên gia CNTT đã nói về quản lý và phát triển dữ liệu; bây giờ họ lại bàn về IoT và devops. Các chủ đề có thể thay đổi, Agarwal nói, nhưng các kỹ năng cần thiết thì không.
Isabelle Dumont, VP tại Lacework, một công ty bảo mật điện toán đám mây cho biết, tự động hóa theo định hướng AI sẽ thay đổi phân phối công nghệ theo những cách đáng kể – loại bỏ các công việc ở mức độ thấp trong khi nâng cao khả năng phân tích kết quả từ các dữ liệu.
“Bảo mật điện toán đám mây là một ví dụ điển hình cho việc machine learning có thể làm tăng khả năng của CNTT”, cô nói “Từ phát hiện vi phạm đến phân tích điều tra, ML có thể compile và phân tích hàng tỷ sự kiện nhanh hơn bất kỳ con người nào, để các nhóm CNTT có thể tập trung vào những thứ quan trọng nhất. ”
Trước khi có bản cập nhật ES7, chúng ta thường sử dung callback và promise xử lý các dòng code bất đồng bộ, tuy nhiên sau đó, JavaScript cho ra mắt Async/Await, việc viết code bất đồng bộ trở nên dễ dàng hơn. Vậy Async/Await là gì?
Async/Await là gì?
Async/Await được giới thiệu trong ES2017 (ES8) và được xây dựng trên nền tảng của Promise và tương thích với tất cả các Promise dựa trên API để làm việc với các hàm bất đồng bộ một cách nhanh chóng và dễ hiểu hơn. Trong đó:
Async (Asynchronous) dùng để khai báo hàm bất đồng bộ và biến nó thành một Promise, và các hàm này sẽ luôn phải trả về một giá trị, dễ hiểu là Promise sẽ trả lại kết quả như một “lời hứa”, nếu không trả kết quả thì JS sẽ tự động kết thúc Promise đó.
Await chỉ có thể được sử dụng bên trong một hàm async. Nó tạm dừng việc thực thi của hàm cho đến khi Promise được giải quyết (resolved) hoặc bị từ chối (rejected). Thay vì sử dụng .then() để xử lý kết quả của một Promise, bạn có thể sử dụng await để “đợi” kết quả và xử lý nó như một giá trị đồng bộ.
Cú pháp Async/Await trong JavaScript
Để sử dụng Async/Await, bạn phải khai báo hàm với từ khóa async. Khi đó bên trong hàm bạn có thể dùng await.
Khai báo một hàm bất đồng bộ (async)
Để sử dụng await, trước tiên cần khai báo hàm là bất đồng bộ bằng cách sử dụng từ khóa async. Khi một hàm được khai báo với async, nó sẽ luôn trả về một Promise.
asyncfunctionmyFunction() {
// logic bất đồng bộ bên trong
}
Sử dụng await để đợi Promise
Từ khóa await chỉ có thể được sử dụng bên trong một hàm khai báo bằng async. Nó tạm dừng việc thực thi của hàm cho đến khi Promise được giải quyết (resolved) hoặc bị từ chối (rejected). Kết quả sẽ được trả về dưới dạng giá trị.
let result = awaitsomePromise();
Cú pháp này giúp tránh việc lồng nhiều .then(), giúp mã dễ đọc và tuần tự hơn.
Ví dụ cơ bản về Async/Await
Ví dụ dưới đây minh họa cách sử dụng async và await để lấy dữ liệu từ một API và xử lý nó một cách tuần tự.
asyncfunctionfetchData() {
try {
let response = awaitfetch('https://api.example.com/data'); // Đợi Promise từ fetch() được resolvedlet data = await response.json(); // Đợi Promise của response.json() được resolvedconsole.log(data); // Xử lý dữ liệu sau khi nhận được
} catch (error) {
console.error('Lỗi:', error); // Xử lý lỗi nếu xảy ra
}
}
fetchData();
Promise trả về trong hàm async
Một hàm async luôn trả về một Promise, bất kể bên trong có return một giá trị bình thường hay không.
Nếu hàm trả về một giá trị, giá trị đó sẽ được bọc trong một Promise đã được giải quyết (resolved).
Nếu hàm không trả về giá trị nào, nó sẽ trả về một Promise đã resolved với giá trị undefined.
Bạn có thể sử dụng khối try...catch để bắt lỗi trong async/await. Bất kỳ Promise nào bị từ chối (rejected) sẽ được chuyển đến phần catch để xử lý lỗi.
asyncfunctionfetchData() {
try {
let response = awaitfetch('https://api.example.com/data');
let data = await response.json();
console.log(data);
} catch (error) {
console.error('Đã xảy ra lỗi:', error);
}
}
fetchData();
Trong ví dụ trên, nếu việc gọi API không thành công hoặc gặp lỗi, chương trình sẽ chuyển vào khối catch và in ra lỗi.
Chạy nhiều tác vụ bất đồng bộ song song
Đôi khi bạn cần chạy nhiều Promise cùng lúc thay vì đợi từng cái hoàn thành tuần tự. Trong trường hợp này, bạn có thể sử dụng Promise.all() để thực hiện các tác vụ song song.
Ở đây, Promise.all() chạy cả hai yêu cầu API song song và đợi cho đến khi cả hai đều hoàn thành, giúp tiết kiệm thời gian.
Căn bản về cú pháp async/await là vậy. Hiện nay, bạn đã có thể sử dụng async/await ở tất cả các trình duyệt hiện đại (trừ IE11 ra nhé, bạn vẫn cần polyfill cho nó).
Ưu điểm của async/await là gì?
Code ngắn và sạch hơn
Đơn giản nhất chính là số lượng code ta cần viết đã giảm đi đáng kể. Trong ví dụ trên, rõ ràng rằng ta đã tiết kiệm được rất nhiều dòng code. Ta không cần viết .then, tạo 1 hàm anonimous để xử lý response, hay là đặt tên data cho 1 biến ta không sử dụng. Ta tránh được các khối code lồng nhau. Những lợi ích nho nhỏ này sẽ tích tụ dần dần trong những đoạn code lớn, những project thật và sẽ trở nên rất đáng giá.
Error handling
Async/await giúp ta xử lý cả error đồng bộ lẫn error bất đồng bộ theo cùng 1 cấu trúc. Tạm biệt try/catch. Với đoạn code dưới dùng promise, try/catch sẽ không bắt được lỗi nếu JSON.parselỗi do nó xảy ra bên trong promise. Ta cần gọi .catch bên trong promise và lặp lại code xử lý error, điều mà chắc chắn sẽ trở nên rắc rối hơn cả console.logtrong đoạn code production.
const makeRequest = () => {
try {
getJSON()
.then(result => {
// this parse may fail
const data = JSON.parse(result)
console.log(data)
})
// uncomment this block to handle asynchronous errors
// .catch((err) => {
// console.log(err)
// })
} catch (err) {
console.log(err)
}
}
Bây giờ hãy nhìn vào đoạn code sử dụng async/await. Khối catchgiờ sẽ xử lý các lỗi parsing.
const makeRequest = async () => {
try {
// this parse may fail
const data = JSON.parse(await getJSON())
console.log(data)
} catch (err) {
console.log(err)
}
}
Câu lệnh điều kiện
Hãy xem thử 1 đoạn code như dưới đây. Đoạn code này sẽ fetch dữ liệu và quyết định trả về giá trị hay là lấy thêm dữ liệu.
Đoạn code đã dần dần giống với mô hình “xyz hell” mà ta thường thấy. Tổng cộng code có 6 level nested. Khi sử dụng async/await, ta sẽ có đoạn code mới dễ đọc hơn.
Hẳn bạn đã từng lâm vào tính huống sau: bạn cần gọi promise1, sau đó sử dụng giá trị nó trả về để gọi promise2 cuối cùng sử dụng kết quả trả về của cả 2 promise trên để gọi promise3. Code của bạn sẽ thành ra thế này.
Nếu promise3không yêu cầu tham số value1, promise sẽ bớt lớp nest đi 1 chút. Nếu bạn theo chủ nghĩa cầu toàn, bạn có thể giải quyết bằng cách wrap cả 2 giá trị value1 và value2 bằng Promise.all tránh được các lớp nest giống như đoạn code dưới.
Phương pháp này đã hi sinh tính ngữ nghĩa để đổi lấy tính dễ đọc của code. Đơn giản vì chả có lý do gì mà value1 & value2 được đặt chung vào 1 mảng, ngoại trừ việc làm như thế sẽ tránh được promise bị nest.
Tuy nhiên cái logic này trở nên cực kì ngớ ngẩn khi ta sử dụng async/await.
Khi bạn phát triển ứng dụng trên môi trường local, điều này thoạt nhìn không có quá nhiều tác dụng. Tuy nhiên với production server, nó lại rất hữu ích với Error Logs. Với những tình huống đó, biết được error xảy ra trong makeRequestsẽ tốt hơn rất nhiều khi được báo rằng error nằm trong thenphía sau then phía sau then….
Debug
Điều tuyệt vời cuối cùng khi bạn làm việc với async/await đó là việc debug trở nên rất đơn giản. Debug với Promise chưa bao giờ là công việc dễ chịu vì 2 lý do sau:
1/ Bạn không thể đặt breakpoint trong arrow function trả về expression.
2/ Nếu bạn đặt breakpoint bên trong khối .thenvà sử dụng short-cut debug như step-over, trình debug sẽ không chuyển đến khối .then kế tiếp bởi vì nó chỉ “step” ở các đoạn code đồng bộ. Với async/await, bạn không cần arrow function quá nhiều nữa, bạn hoàn toàn có thể step qua lời gọi await y như với code đồng bộ.
Async/await – mảnh ghép còn thiếu của Promise?
Để tạo ra một Promise, chúng ta sử dung cú pháp new Promise, nhưng với async/await, đơn giản chỉ cần khai báo một hàm async.
asyncfunctionfn(x) {
if (x % 2 === 0) {
returntrue;
} else {
thrownewError('x is not even');
}
}
Trong khi Promise sử dụng then để xử lý kết quả trả về tại một thời điểm nào đó trong tương lai, thì với async/await chỉ đơn giản là sử dụng await.
const result = awaitfetch('https://example.com');
const resultJSON = await result.json();
Kể từ phiên bản Node.js 14.8, chúng ta có tính năng top-level await, tức là việc gọi await không bị giới hạn trong một hàm async nữa, mà có thể gọi được ở bên ngoài. Trước đó, để sử dụng await bắt buộc phải ở trong một hàm async.
Có thể thấy async/await giúp chúng ta viết mã bất đồng bộ như đồng bộ, không có callback, cũng không cần then, chỉ cần chờ kết quả bằng await.
Tuy vậy, điều này cũng gây ra một số nhầm lẫn tai hại như quên từ quá await để chờ kết quả của hành vi bất đồng bộ hoặc “ngỡ” hàm bất đồng bộ là hàm đồng bộ.
Về bản chất, getData trả về một Promise, cho nên chương trình trên sẽ hoạt động không chính xác nếu mục đích là lấy giá trị từ một cuộc gọi API. Để khắc phục, chúng ta cần sửa lại.
asyncfunctionmain() {
const data = awaitgetData();
console.log(data);
}
Bạn có thể thấy Promise và async/await không hoàn toàn thay thế mà hỗ trợ lẫn nhau. Mặc dù chúng ta có thể dùng async/await ở đa số các trường hợp, Promise vẫn là nền tảng cần thiết khi thực thi các tác vụ bất đồng bộ trong JavaScript. Do đó bạn nên xem xét và lựa chọn giải pháp phù hợp, tùy vào tình hình thực tế nhá.
Async/Await giúp viết mã bất đồng bộ trở nên dễ hiểu hơn và giảm thiểu sự phụ thuộc vào callback hoặc .then(). Tuy nhiên, để sử dụng hiệu quả, lập trình viên cần hiểu rõ cách hoạt động của chúng, hi vọng qua bài viết này của TopDev, bạn đã nắm được khái niệm cũng như cú pháp sử dụng Async/Await.
Ở góc nhìn của nhiều nhà tuyển nhân viên IT đa phần luôn cố gắng đào sâu đúng nhu cầu của doanh nghiệp nhưng thường quên là “Muốn gặt trái ngọt, phải có công gieo trồng”. Chúng ta nên hiểu rằng để tìm kiếm được các ứng viên tiềm năng không đơn giản chỉ là phúc lợi và mô tả công việc đầy đủ, mà còn cần một số lưu ý. Bí kíp để mang một chuyên viên IT tài giỏi về đội là gì?
1. Tạo thiện cảm tốt từ lần tiếp xúc đầu tiên
Sự kết nối đầu tiên luôn mang lại ấn tượng mạnh mẽ nhất. Một Email tuyển dụng IT chuyên nghiệp phải thật sự thông minh và đúng “trình” của doanh nghiệp. Hãy cố gắng cá nhân hóa từng ứng viên của bạn để các Dev cảm nhận bạn khác biệt và họ cũng thế. Thay cho một lời chào quá đỗi bình thường như Thân gửi bạn/ Thân gửi ứng viên hãy gia vị thêm bằng cách “Thân gửi Dev nhà Top”.
Luôn thân thiện bằng cách ghi nhận năng lực và quá trình làm việc của các ứng viên. Cố gắng nói một cách ngắn gọn về những gì bạn biết về công việc trước đó của họ. Đừng quên giới thiệu về bản thân & công ty của bạn, sẽ không ai thích làm việc với một nhà tuyển dụng vô danh. Các Dev cần cân nhắc năng lực bản thân với công việc mới vì thế bạn cần sơ lược về các yêu cầu của vị trí ứng tuyển nhưng phải thật ngắn gọn.
VD: Thân gửi [Dev nhà Top],
Mình được biết [tên ứng viên] đang có nhiều kinh nghiệm trong lĩnh vực Data Science với nhiều dự án lớn. Hiện nay, [tên công ty của bạn] đang tìm kiếm ứng viên tiềm năng cho vị trí Data Scientist, rất mong được giới thiệu đến bạn [tên ứng viên] và mong có thể hợp tác với bạn trong thời gian tới.
[Đoạn giới thiệu ngắn về công ty và sản phẩm/dự án mà bạn đang tuyển]
[Đoạn giới thiệu về mô tả công việc và các phúc lợi liên quan]
*Lưu ý: Nhưng đừng quá kỳ vọng ứng viên sẽ đọc hết nội dung trong email của bạn, hãy chọn những ý nổi bật và khái quát nhất.
Nhà tuyển dụng IT mong chờ điều gì từ ứng viên? Có phải là năng lực chuyên môn, tinh thần và sự gắn kết bền vững? Ứng viên họ cũng vậy, lập trình viên luôn muốn tạo ra sự thay đổi. Hãy cho họ thấy những đổi mới khi đi cùng công ty bạn. Nếu đang có những dự án mới đừng ngần ngại chia sẻ ở mức độ vừa đủ vì đó sẽ là đòn tâm lý khơi gợi hứng thú và sự tò mò của ứng viên.
Kết thúc email bằng sự thân thiện và nhiệt tình của bạn điều đó thể hiện sự sẵn sàng chào đón một Dev mới về chung đội. Những từ liên quan đến Best – Best Wishes hoặc một câu kết đầy thiện cảm như “Sự phản hồi của bạn luôn là mong đợi của công ty chúng tôi.” nên được cân nhắc ở cuối email.
2. Làm sao để không lỡ mất ứng viên tiềm năng
“Khi nhìn thấy cá lớn, làm sao để câu cá lên bờ”
Nếu không muốn lỡ mất ứng viên tiềm năng thì lời khuyên cho bạn là hãy thực sự dành thời gian ra chăm sóc họ. Một HR “xịn” sẽ phản hồi thông tin ứng viên trong 3 ngày kể từ ngày nhận được CV và luôn giữ thông tin cập nhật không quá 5 ngày hoặc chủ động thông báo về sự kéo dài thời gian hơn dự định thay vì chọn cách im lặng. Thời gian là vàng vì vậy đừng để ứng viên phí thời gian chờ đợi quy trình của bạn. Việc giữ đúng lời hứa như đã trao đổi thể hiện sự chuyên nghiệp của nhà tuyển dụng. Ngoài ra, để thể hiện sự tôn trọng, bạn hãy cố gắng giúp đỡ với bất kỳ phản hồi nào từ các Developer.
3. Hãy là bạn & đồng nghiệp của ứng viên
Là một nhà tuyển dụng IT hơn ai hết ngoài chuyên môn nhân sự, bạn nên trang bị cho mình một số kiến thức cơ bản về ngành IT để hiểu những gì các ứng viên trao đổi và phản hồi họ một cách nhanh và chính xác nhất. Sự tương tác sẽ trở nên gắn kết hơn khi các lập trình viên nể trọng kiến thức và sự am hiểu của bạn dù bạn là HR hay IT Recruiter. Trong cuộc trò chuyện, bạn có thể đề cập đến kinh nghiệm làm việc của họ trong những công việc trước đó, điều này vừa giúp bạn có cơ hội khai thác được nhiều hơn thông tin về năng lực ứng viên cũng như rút ngắn được khoảng cách giữa nhà tuyển dụng và ứng viên tiềm năng.
4. Cách viết mô tả công việc hấp dẫn
Kỹ thuật thiết kế Email cũng là một điểm nhấn cho nhà tuyển dụng IT. Những lưu ý bạn nên cân nhắc đầu tiên là không nên sử dụng quá nhiều gạch đầu dòng cho phần mô tả công việc điều này sẽ mất đi tính khoa học trong văn bản. Tiếp theo là tiêu đề (Title job) công việc rõ ràng, ngắn gọn và đầy đủ điều này mang lại hiệu quả vì ở giai đoạn “ mời gọi” này ứng viên đang ở thế thượng phong. Bạn cần phân loại và tách riêng những yêu cầu bắt buộc trong công việc với những yêu cầu có thể bỏ qua giúp ứng viên dễ dàng cân nhắc.
5. Bạn đã từng tự hỏi Developer họ muốn gì?
Theo nghiên cứu có đến 64% Developer nói rằng xây dựng một thứ gì đó mới mẻ rất quan trọng với họ. Điều này cũng nghĩa họ mong muốn được tư duy sáng tạo và sự độc lập trong từng dự án. Sức thuyết phục mạnh mẽ đối với ứng viên là nhìn được con đường sự nghiệp vẻ vang trong tương lai. Không ai khác nhiệm vụ này là ở cách công ty bạn đề ra lộ trình thăng tiến rõ ràng mang tính thỏa thuận thích đáng.
6. Nếu không muốn ăn “Bơ” phải có tuyệt chiêu liên lạc hiệu quả
Những con số không biết nói dối, nghiên cứu chỉ ra rằng hơn 52% Developer ghét khi bị liên lạc qua Facebook vì đây là kênh mạng xã hội và họ cảm thấy không thật sự nghiêm túc trong công việc, 22% Developer không có LinkedIn đặc biệt là các Developer tại Việt Nam cho biết LinkedIn còn khá mới mẻ với họ. Mặt khác,đến 65% Developer cho rằng gửi e-mail là một cách thức giao tiếp tốt để bắt đầu các mối quan hệ hợp tác. Hãy nhớ rằng có hơn 65% lập trình viên nói rằng mức lương là rất quan trọng với họ khi tìm một công việc mới. Gợi mở những cánh cửa tham vọng của từng ứng viên sẽ giúp họ phản hồi với công ty bạn một cách nhanh nhất có thể.
Để mang đến kết quả tuyển dụng nhanh nhất bạn cần tối ưu quy trình tuyển dụng bằng cách cố gắng linh hoạt thời gian và địa điểm phỏng vấn. Cung cấp các thông tin liên hệ với công ty trong ngày phỏng vấn. Giới thiệu ứng viên tiềm năng với đội ngũ hiện tại để làm quen và gây ấn tượng. Nếu được, hãy cho họ xem một vài dòng code của dự án điều này âm thầm nói với ứng viên “Cơ hội của bạn là công ty tôi”. Hạn chế đánh đố ứng viên những câu hỏi về trí tuệ vì nó không đồng nghĩa với việc ứng viên có thể viết Code tốt.
Xét về mặt hiệu quả, 7 bí kíp trên sẽ giúp ích cho hiệu suất làm việc của các cá nhân nhân sự và IT Recruiter trong việc tiếp cận và thu hút được ứng viên IT tiềm năng cho công ty. Nhưng để đẩy mạnh hiệu quả ở quy mô lớn hơn hoặc giúp cho cả quá trình tuyển dụng IT tại công ty bạn được thúc đẩy nhanh một cách rõ ràng, việc làm thương hiệu tuyển dụng – Employer Branding là điều bạn không nên bỏ qua. Một thương hiệu tuyển dụng tốt được phủ rộng tới cộng đồng luôn là một điểm mạnh với nhà tuyển dụng IT. Luôn truyền tải thông điệp, sứ mệnh và tầm nhìn của mình, đặc biệt trong ngành IT, việc thể hiện những khía cạnh lợi thế kỹ thuật, dự án độc đáo cũng như văn hóa độ Tech năng động sẽ là điểm cộng rất lớn khi ứng viên tìm kiếm thông tin trên Internet. Tham khảo 03 quy tắc vàng làm Employer Branding tại đây.
Giữ cho kĩ năng của bạn luôn được cập nhật xu hướng lập trình có thể là một điều không dễ, nhưng nó là vô cùng quan trọng đối với tất cả mọi người và nhất là những lập trình những người trong ngành công nghiệp công nghệ cao, nơi mọi thứ luôn thay đổi nhanh chóng.
Sarah Franklin, phó chủ tịch phụ trách quan hệ phát triển và tổng giám đốc của Salesforce, cho biết: “Tốc độ đổi mới đang gia tăng nhanh chóng trong hệ sinh thái kinh doanh ngày nay, do đó mà nhu cầu nhân lực có tay nghề luôn ở mức cao. Hơn nữa, giáo dục đại học truyền thống chỉ đơn giản là không đủ khi rất nhiều sinh viên dù đã tốt nghiệp nhưng vẫn không có các kỹ năng cần thiết trong thị trường công nghệ hiện nay”.
Todd Thibodeaux, chủ tịch và giám đốc điều hành của CompTIA, cho biết tốc độ đổi mới nhanh chóng cũng tạo ra những cơ hội nghề nghiệp mới: “Các công nghệ hiện tại và tương lai sẽ luôn cần những kỹ thuật viên và kỹ sư lành nghề để thực hiện chúng. Tuy nhiên, tốc độ thay đổi nhanh chóng làm cho hầu hết các ứng viên không thể nào theo kịp mọi thứ. Lựa chọn tốt nhất là chọn tập trung vào một vài lĩnh vực mà bạn quan tâm.”
Dưới đây là 10 lời khuyên để đảm bảo kỹ năng của bạn vẫn có luôn hữu ích đối với thị trường việc làm.
Lên kế hoạch rõ ràng
Sandy Carielli, giám đốc công nghệ an ninh của Entrust Datacard, nói: “Thật dễ dàng để bị mắc bẫy trong công việc hàng ngày và không dành thời gian cho những gì sắp xảy ra”.
Raphael Arar, nhà thiết kế và nghiên cứu của IBM Research, cho biết ông đã cố gắng dành thời gian, thậm chí chỉ cần 15 phút mỗi ngày để học một kỹ năng mới. Arar cho biết: “Đây có thể là hình thức xem các hướng dẫn trực tuyến, tham gia lớp MOOC hoặc một cuốn sách kỹ thuật.
Quan sát những con người dẫn đầu
Theo dõi blog, đăng ký bản tin, và facebook các nhà lãnh đạo ngành công nghiệp, Carielli nói. Nếu bạn không chắc chắn nơi để bắt đầu, hãy hỏi những người cố vấn hoặc những người khác trong lĩnh vực của bạn. Và nếu công ty của bạn đăng ký bất kỳ báo cáo phân tích nào, hãy xem liệu bạn có thể truy cập vào chúng hay không.
Carielli cho biết: “Không chỉ bạn sẽ tìm hiểu về các công nghệ và xu hướng mới, bạn sẽ thấy được cách suy nghĩ của họ”.
Siddhartha Agarwal, phó giám đốc quản lý sản phẩm và chiến lược của Oracle, cho biết các sự kiện ngành công nghiệp thường là nguồn thông tin chi tiết nhất và là cơ hội học hỏi từ những chuyên gia công nghệ cao. Agarwal nói: “Các cuộc sự kiện công nghê là cách tuyệt vời để kết nối với cộng đồng các đồng nghiệp tương lai của bạn và thu thập các kỹ năng công nghệ mới”.
Mở rộng network của bạn
Nói chuyện với bạn bè và đồng nghiệp về những gì họ đang làm, Melnicki nói. Ông cho biết: “Những người có nhiều đặc điểm khác nhau mà tôi có thể liên lạc khi cần tìm kiếm hướng dẫn trong lĩnh vực chuyên môn của họ. ”
Phương tiện truyền thông xã hội là một cách khác để kết nối và theo xu hướng công nghệ, hãy tham gia các nhóm LinkedIn với các chuyên gia khác trong lĩnh vực của bạn, Agarwal nói.
Tìm kiếm các khóa học và thông tin trực tuyến
Dwayne Melancon, phó chủ tịch phụ trách sản phẩm tại iovation nói: “Có rất nhiều lớp học trực tuyến có thể giúp bạn không chỉ thuần thục kỹ thuật hiện nay, mà thậm chí còn đạt được các chứng chỉ cao cấp về kỹ thuật trong ngành công nghiệp”.
Andrew Selepak, giáo sư về viễn thông tại đại học Florida, cho biết việc tham gia một khóa học trực tuyến thông qua một trường đại học cũng có thể mang lại lợi ích cho việc hợp tác và học tập như một tập thể. Selepak nói: “Điều này đặc biệt quan trọng bởi vì công việc của chúng ta hiện nay đòi hỏi phải làm việc theo nhóm nhiều hơn với những người làm việc từ xa hoặc tổ chức các cuộc họp thông qua video hoặc Slack”.
Tham gia một tổ chức chuyên nghiệp
Các tổ chức chuyên nghiệp, chẳng hạn như ISACA và ISSA, thường xuyên tổ chức các tuần huấn luyện, Melancon nói. Bạn sẽ không chỉ được cung cấp các tài liệu đào tạo, mà còn cho phép tiếp cận với các cố vấn hoặc các chuyên gia giàu kinh nghiệm, giúp cho việc học tập và làm việc trở nên hiệu quả hơn.
Camp cho biết tất cả các thành phố lớn và nhiều trung tâm nhỏ hơn đều có một số hiệp hội về lập trình máy tính chuyên nghiệp gần đó. “Hãy tham gia những cuộc trò chuyện thú vị nhất với bạn để nâng cao kĩ năng của mình”
Đọc, đọc nữa, đọc mãi
Nghe có vẻ đơn giản, nhưng đọc tin tức và blog công nghệ mỗi ngày là một trong những cách tốt nhất để cập nhật những xu hướng và kỹ năng mới nhất và cần thiết trong công nghệ, Melnicki nói.
Robert Pryor, giám đốc dịch vụ của Key Information Systems cho biết: “Tìm một vài blogger có liên quan đến lĩnh vực chuyên môn của bạn và theo dõi họ”.
Thử nghiệm với các dự án cá nhân
Bạn có thể thường xuyên học hỏi nhiều nhất bằng cách cố gắng tự mình làm chúng, Melnicki nói. “Bạn sẽ nhận ra rằng một khi bạn có cơ hội để làm các dự án phụ cho chính mình, những gì được sử dụng sẽ trở nên vô cùng tuyệt vời”, ông nói thêm.
Peter Yang, đồng sáng lập của ResumeGo, cho biết nếu bạn là một lập trình viên, hãy viết code vào thời gian rảnh rỗi cho những dự án bạn yêu thích. Ông nói thêm: “Điều này sẽ giúp giữ trình độ của bạn trở nên sắc bén hơn bao giờ hết”.
Đánh giá nội bộ
Aniket Sharma, chuyên gia phân tích chất lượng tại Work & Co cho biết: “Đánh giá ngang hàng nội bộ – có thể là thông qua đánh giá code hoặc đánh giá hiệu suất – có thể là một cách tuyệt vời để hiểu được vị trí hiện tại của bạn”, Sharma nói. “Nó có thể mang lại lợi ích cho cả nhân viên và công ty vì không chỉ phát triển các kỹ năng công nghệ của nhân viên mà còn nâng cao chất lượng sản phẩm / công ty.”
Đa dạng hóa kiến thức của bạn
Các mạng lưới trong tương lai sẽ bao gồm các thiết bị đầu cuối rất đa dạng, Thibodeaux nói. Eric M. Rintell, CEO & Co-founder của Rintell Technologies, nói – “Tôi đã học được sau nhiều thập kỷ trong ngành công nghiệp là những người thông minh luôn chấp nhận và thích nghi để thay đổi, trong khi những người ngu ngốc chống lại nó, thường dẫn đến mất việc làm”.
Tháng thứ 4 – Hoàn thành khóa học backend tại FreeCodeCamp, Yeggle
Tôi đã tự học lập trình bao lâu qua và làm việc qua tất cả các dự án API trong freeCodeCamp, nhưng tôi bắt đầu tách ra khỏi freeCodeCamp
Với thôi thúc được thực hiện các full stack web application, vì vậy ngay khi tôi nhìn thấy tiêu đề của dự án Image Search Abstraction Layer, một một ý tưởng mới đã xuất hiện ra trong tôi. Tôi sẽ làm một node app chuyên lưu trữ các URL imgur ngẫu nhiên trong một cơ sở dữ liệu, và nó sẽ đánh số những hình ảnh ngẫu nhiên để khi user nhập vào thì sẽ truy xuất được chúng.
Bạn sẽ làm việc chăm chỉ và có nhiều thành công hơn khi bạn đang làm việc cho một dự án của chính mình.
Cuối cùng tôi cảm thấy đã sẵn sàng để bắt đầu làm cho các ứng dụng web full-stack cho riêng mình.
Khi tìm kiếm nhà hàng mới, tôi luôn thấy mình hay dùng Yelp để kiểm tra đánh giá và sau đó mở Maps để kiểm tra đánh giá của họ. Điều gì sẽ xảy ra nếu tôi tạo một ứng dụng kết hợp so sánh cả hai bên với nhau?
Và như vậy, tôi đã tạo ra Yeggle. Tôi sử dụng Node / Express / React cùng với Google Maps và Yelp API. Có một vài trở ngại mà tôi không nghĩ mình có thể vượt qua, nhưng cuối cùng tôi đã hoàn thành và rất tự hào về ứng dụng của mình. Sau đó, tôi up nó lên Reddit, nhưng không ai quan tâm. Đó là một sự thất vọng nhưng nó không làm tôi nản lòng.
Tháng thứ 5 – StockIT
Tôi bắt đầu với một kỳ nghỉ hai tuần tới Nhật Bản và Thái Lan!
Sau đó, tôi mới bắt tay vào dự án tiếp theo. Ý tưởng của tôi là làm một trò chơi về cơ hội để mua và bán cổ phiếu, nhưng bạn sẽ chơi với một thuật toán learning machine. Vì vậy, tôi tạo ra StockIT.
Tôi đã làm một video hướng dẫn về Pandas và Scikit Learn bao gồm nhiều kỹ thuật machine learning.
Sau khi hoàn thành và chia sẻ nó với Reddit, phản ứng từ mọi người khá là tích cực.
Hóa ra, giống như các nhà đầu tư, các redditors đều quan tâm về machine learning. Kết quả là mọi người đang chơi trò chơi của tôi và tận hưởng nó!
Tháng thứ 6 – jobSort (), Job Hunt Prep
Sau StockIT, tôi lăn ngay vào dự án cá nhân tiếp theo. Tôi muốn tổng hợp các trang web tuyển dụng công nghệ như Stack Overflow, Github và Hacker News. Để thêm tính độc đáo, tôi quyết định sắp xếp nó dựa trên bảng xếp hạng công nghệ đang được săn đón.
Tôi gặp nhiều trở ngại khác nhau trong dự án này và phải thay đổi kế hoạch một vài lần, nhưng tôi cũng đã hoàn thành nó tốt đẹp. Tech stack của tôi có sử dụng React/Node/Express/MySQL. Bạn có thể thấy là với stack như vậy thì jobSort () đã chiếm một phần kha khá thời gian của tôi trong tháng. Một người bạn đã khuyên tôi nên bắt đầu tìm kiếm việc làm ngay bây giờ.
Vốn ban đầu, tôi dự định sẽ tích lũy thêm nhiều kinh nghiệm nhưng người bạn này thuyết phục tôi bỏ kế hoạch đó và bắt đầu đi xin việc làm luôn. Vì vậy, tháng này tôi đã làm một portfolio và một sơ yếu lý lịch. Trong tháng tiếp theo tôi sẽ bắt đầu đi xin việc.
Tôi đã thêm flexbox vào CodeClub.Social để làm cho nó đáp ứng hơn. Tôi đã cải tiến UX di động trên jobSort (). Tôi thêm testing vào jobSort () với mocha / chai / enzyme vốn rất khó thiết lập.
Đến cuối tháng, tôi đã nộp đơn cho 63 chỗ việc làm. Trên Hacker News, tôi đã sử dụng jobSort () để xác định danh sách để đăng ký. Trên thực tế, tôi đã thử các công ty không phải là phần mềm để xem liệu tôi có thể nhận được cuộc gọi hoặc cuộc phỏng vấn ở bất cứ đâu.
Lúc đầu, tôi đã lựa chọn cách nộp hồ sơ thông thường. Sau đó, tôi quyết định cá nhân hóa thư xin việc và lý lịch của mình, và sau đó cố gắng gửi một email cho ai đó trong công ty. Phương pháp này rõ ràng là tốt hơn phương pháp cứ nhắm mắt gởi đại.
Tôi nhận được 5 cuộc gọi trong tháng đó – hai từ các công ty tuyển dụng và ba công ty phần mềm với những offer bao gồm:
DevOps /testing tại một công ty dotcom
Một công ty phân tích thực phẩm B,
Một startup khá lớn và thành công mà gần đây đã được mua bởi một công ty lớn
Tôi đã khá hài lòng với ba cuộc gọi phỏng vấn, và tôi đã học được rất nhiều từ họ. Tuy nhiên, những gì tôi học được từ những cuộc gọi này là không ai tìm kiếm một junior developer. Họ mong bạn biết bạn đang làm gì từ ngày đầu tiên.
Những cuộc gọi này đã dạy tôi rằng cần phải:
Giỏi để đủ để tăng giá trị từ ngày đầu tiên
Hãy tự tin để thuyết phục họ rằng bạn có giá trị cho công ty
Tháng thứ 8 – Ca đêm, Redux, Open source và các cuộc phỏng vấn
Tôi bắt đầu tháng này làm việc ca đêm trong khoảng 40 ngày với 6 ngày một tuần, 12 giờ mỗi ngày, 5 giờ chiều đến 5 giờ sáng.
Tôi biết bản thân sẽ không thể làm được nhiều điều trong tháng này, nhưng tôi đã có một mục đích và tôi muốn đặt được nó.
Tôi đã tái cấu trúc lại jobSort () để sử dụng Redux, điều đáng ngạc nhiên là nó không hề khó khăn như tôi nghĩ. Tôi thực sự thích dòng dữ liệu với Redux. Thật thú vị khi nhìn thấy mọi người phàn nàn về Redux.
Đây cũng được coi là tháng của open source cho tôi. Và tất nhiên, React là lựa chọn để tôi đóng góp bởi nó đóng vai trò quan trọng của project mình.
Tại một trong những buổi họp mặt tôi đã tham dự, Anthony Ng đã khuyên tôi nên thử Downshift, một thư viện tự động hoàn chỉnh của Kent C. Dodds. Đây chính là thời điểm thay đổi mọi thứ. Downshift là một giải pháp hoàn hảo cho một số vấn đề tôi đã có với ứng dụng jobSort ().
Khoảng nửa tháng sau, tôi nhận được một email từ một trong những công ty tôi đã nộp đơn vào tháng trước. Các công nghệ họ đang tìm kiếm là chính xác những gì tôi đã học được – React, Redux, và D3. Tôi hầu như chỉ nói về các dự án của mình và tại sao tôi lại đưa ra những quyết định nhất định. Sau đó, họ yêu cầu tôi đến nơi để phỏng vấn. Cuộc phỏng vấn tại chỗ đầu tiên của tôi!
Thành thật mà nói, tôi không hề tự tin rằng mình sẽ nhận được công việc, nhưng ít ra thì bản thân sẽ có được kinh nghiệm phỏng vấn quí giá.
Lúc đầu, tôi đã lo lắng về việc đảm bảo rằng mình phải biết mọi thứ. Khi tôi nhận ra rằng mình sẽ không hoàn thành được thử thách, tôi nhận ra rằng bản thân cần phải ngừng lo lắng những gì người phỏng vấn nghĩ về tôi và chỉ cần google / stack overflow để tìm câu trả lời. Thành thật mà nói, tôi nghĩ rằng mình đã thất bại thảm hại.
Và cũng vì đó, tôi cảm thấy thư giãn trong suốt phần còn lại của cuộc phỏng vấn..
Tháng thứ 9 – Công việc
Tôi nhận được công việc đầu tiên của mình sau 9 tháng 7 ngày tự học lập trình. Tôi cảm thấy tự tin vì tôi nhận được đề nghị sau cuộc phỏng vấn đầu tiên và tôi hài lòng với quyết định của mình. Tôi muốn được trả tiền cho code mình viết ra.
Lời kết
Tôi hy vọng rằng những lời khuyên dưới đây sẽ giúp bạn phát triển một kế hoạch và đạt được mục tiêu của bạn.
Tìm ra những gì thúc đẩy bạn và sử dụng nó làm lợi thế cho bạn.
Hãy đề ra mục tiêu và đáp ứng chúng.
Đi đến các cuộc gặp gỡ – meetup trước khi bạn nghĩ rằng mình đã sẵn sàng.
Đóng góp cho open source trước khi bạn nghĩ rằng bản thân đã sẵn sàng.
Tôi là sinh viên chuyên ngành môi trường tại đại học Yale-NUS (một trường cao đẳng nghệ thuật), có thể bạn thấy khá là bất ngờ khi tôi lại trở thành thực tập tại Google – kẻ khổng lồ về công nghệ. Bởi suy cho cùng thì việc nghiên cứu về môi trường cũng như là nghệ thuật thì có liên quan gì tới công nghệ hay kinh doanh?
Câu trả lời sẽ khiến bạn ngạc nhiên đấy! Đó là bởi bất cứ ai cũng có thể làm việc cho Google hay những kẻ khổng lồ công nghệ khác nếu họ thật sự cố gắng. Vì vậy, hãy để tôi giải thích và chia sẻ một số lời khuyên về những gì bạn có thể làm để có được một chuyến thực tập tại Google.
1. Tôi đã làm 9 công việc thực tập khác nhau
Trước khi học đại học: The Thought Collective (một doanh nghiệp xã hội.
Mùa hè đầu tiên: Philanthropy in Motion (một tổ chức phi lợi nhuận)
Học kỳ đầu tiên của năm thứ hai: Part-time intern tại Flare Communications (một công ty chuyên về huấn luyện nhân sự và điều hành)
Vào tháng 12, năm thứ hai: JFDI.Asia / QLC.io và Clickstream Ventures (nay là Cocoon Capital)
Học kỳ thứ hai của năm thứ hai: Tôi là một part-time intern tại Green Pea Cookie (một startup về đồ ăn)
Năm thứ ba: Thực tập sáu tháng tại Founder Institute (một công ty chuyên về vườn ươm) và thực tập thực tập năm tháng tại Tilt
Tôi biết bạn đang nghĩ gì: “Làm cách nào mà có thể làm ngần ấy việc?”. Đúng là nó nghe có vẻ điên cuồng, nhưng như bạn đã thấy, tôi đã làm nhiều việc thực tập khác nhau ngay từ khi còn đang đi học.
TIP:Hãy thực tập càng nhiều càng tốt để có thật nhiều kinh nghiệm làm việc. Bạn thậm chí có thể vừa làm part-time trong khi vẫn học bình thường. Startups có xu hướng giờ giấc linh hoạt hơn và sẵn sàng chấp nhận cho bạn vào làm thực tập viên ngay cả khi không có nhiều kinh nghiệm.
Hầu hết các chương trình thực tập tôi làm đều khá dễ tìm vì vậy hãy chăm đi đến các sự kiện, nói chuyện với startup founder và cho họ biết bạn đang quan tâm đến việc thực tập trong công ty của họ. Ngoài ra, bạn có thể đăng ký vào các cổng thực tập, chẳng hạn như Meetup, TopDev,…
2. Tôi đồng sáng lập một doanh nghiệp và giúp tăng vốn lên 70.000 đô la
Trong năm đầu tiên ở trường đại học, tôi đã đồng sáng lập một doanh nghiệp mạng xã hội – tên gọi là SDI Academy cùng với hai sinh viên khác. Mục tiêu của nó là để bảo vệ quyền lợi cho người lao động nhập cư thông qua việc dạy tiếng Anh cũng như giúp họ hòa nhập tốt hơn.
Với tư cách là đại diện cho SDI Academy, tôi tham gia vào hai chương trình gọi là Business Challenge và SIF Young Social Entrepreneurs Program. Hai sự kiện này đã dạy tôi rất nhiều về cách phát triển kinh doanh và cho phép tôi được gặp gỡ nhiều người tài năng trong startup cũng như là entrepreneur.
TIP:Mặt dù đây là một cơ hội học tập tuyệt vời, bạn không bắt buộc phải như tôi, làm một startup. Bởi vì nó cực kì khó nên tôi cũng khuyên bạn đừng nhảy vào nếu chỉ đơn thuần làm để thu lợi nhuận. Tuy nhiên, bạn có thể bắt đầu với các dự án nhỏ mà bạn thực sự đam mê, hoặc tham gia các cuộc thi về kinh doanh và cố gắng xây dựng một mạng lưới kết nối và quen biết.
3. Tôi tham gia 10 cuộc thi hackathons và giành được chiến thắng 4 lần
Tôi sẽ thành thực: Tôi thực sự không giỏi code đến vậy. Do đó, làm thế nào để giành chiến thắng trong hackathons? Bí quyết nằm ở việc tìm được một đội ngũ có chung lý tưởng. Thường thì nó sẽ bao gồm hai developer, một designer và một người thật giỏi trong việc thuyết trình. Mặt khác, một ý tưởng hay là khi nó giải quyết đúng vấn đề, tương đối độc đáo, không quá phức tạp, và có thể cho ra lợi nhuận.
Nếu bạn không biết làm thế nào để code, thì nên cố gắng trở thành một người thuyết trình thực sự giỏi và hùng hồn. Với khả năng có thể viết một kịch bản mang tính thuyết phục cũng như thiết kế các slide thuyết trình bắt mắt. Bằng cách đó, bạn vẫn có thể đem lại giá trị cho đội. Đôi khi, việc thuyết trình cũng quan trọng như bản thân sản phẩm, do đó bạn phải đảm bảo rằng bạn có thể làm tốt. Tôi là người thuyết trình trong hầu hết các hackathon tôi tham gia, và tôi luôn luôn đảm bảo rằng mình luôn làm tốt nhất có thể.
TIP: Hãy cứ tham gia hackathons bất kể trình độ của bạn.Bởi chắc chắn là bạn sẽ học được điều gì đó. Ngoài ra, biết đâu may mắn lại mỉm cười và bạn có khả năng để giành chiến thắng.
4. Tôi tới làm ở Thung lũng Silicon trong vòng một năm
Tôi đã trải qua một năm làm việc tại Thung lũng Silicon thông qua chương trình NUS Overseas College. Tôi tham gia các lớp học kinh doanh tại Đại học Stanford, nơi mà tôi đã học được từ các nhà đầu tư mạo hiểm và các doanh nhân nổi tiếng, bao gồm Elon Musk!
TIP: Dành thời gian học tập hoặc làm việc thực tập ở nước ngoài. Nó sẽ cho phép bạn trải nghiệm sống trong một môi trường hoàn toàn mới. Rất nhiều công ty đa quốc gia tìm kiếm những người có kinh nghiệm này vì họ có thể dễ dàng thích ứng với các nền văn hoá khác nhau.
Tôi là một kẻ cuồng các sự kiện lớn, nhưng đi đến nhiều công nghệ và sự kiện startup đã giúp tôi rất nhiều thứ:
Cải thiện những kỹ năng mềm của tôi: như tiếp cận mọi người và trò chuyện với họ một cách dễ dàng
Tìm hiểu thêm về xu hướng công nghệ mới nhất
Tiếp xúc với Startup
Kết bạn với rất nhiều người tài năng trong lĩnh vực công nghệ
Khám phá các cơ hội để trở thành một thực tập viên ở các công ty Startup.
TIP: Hãy dành thời gian đi các sự kiện! Nó có thể giúp bạn tìm hiểu những điều mới mẻ, mở rộng mạng lưới của bạn và nắm bắt cơ hội. Bạn có thể tìm kiếm sự kiện dễ dàng trên Facebook, Eventbrite và Meetup.
Tôi không thể chỉ liệt kê những kết quả mà không đề cập đến những thay đổi. Để có thể làm được nhiều việc thực tập, bắt đầu kinh doanh, tham gia nhiều cuộc thi, và tham dự hàng trăm sự kiện, tôi phải hy sinh những điều khác:
Tôi đã có ít thời gian hơn cho việc học của mình cũng như bạn bè.
Trong khi tôi là thành viên của một số câu lạc bộ trong năm đầu tiên, tôi đã phải giảm bớt thời gian tham gia các hoạt động ngoại khóa.
Tôi không thể dành nhiều thời gian đi chơi với bạn bè vì tôi thường phải làm rất nhiều việc.
TIP: Có thể bạn sẽ phải đối mặt với những điều, vì vậy hãy xem xét thật kĩ việc nếu nhận được một công việc thực tập hay làm việc trong một công ty công nghệ hàng đầu là điều bạn thật sự muốn. Nếu không, hoặc bạn chỉ thích một công việc ổn định và an toàn thì điểm số cao luôn là một trong những tiêu chí quan trọng.
***
Tôi khá chắc chắn rằng nếu bạn làm theo những gì tôi đã làm, bạn có thể có được một công việc thực tập hoặc làm việc tại một công ty công nghệ lớn. Bạn sẽ gặt hái nhiều lợi ích và thành công, nhưng hãy nhớ rằng bạn sẽ cần phải hy sinh một số thứ khác.
Bất cứ khi nào đọc được một câu chuyện về thành công, tôi sẽ ngay lập tức tìm kiếm thông tin của tác giả, hy vọng rằng nó cũng tương tự như mình. Và chưa bao giờ có một ai có quá khứ giống cuộc đời của tôi.
Tuy nhiên, tôi hy vọng rằng câu chuyện của mình sẽ truyền cảm hứng cho người khác và đóng vai trò là một bài học có giá trị cho thành công của bạn.
Tôi tốt nghiệp từ một trường đại học tốt với bằng kỹ thuật hóa học và điểm trung bình tốt. Tuy nhiên, tôi không hề thực sự nghiêm túc theo đuổi lập trình cho đến năm ngoái.
Sau khi tốt nghiệp đại học, tôi đã được nhận vào làm dưới vai trò kỹ sư tại một nhà máy lọc dầu. Mãi đến 2017 thì tôi mới chuyển qua làm lập trình viên phần mềm.
Tại sao tôi muốn thay đổi sự nghiệp?
Tôi thích giải quyết vấn đề liên quan tới kỹ thuật, nhưng đồng thời bản thân cũng muốn làm về kinh doanh.
Vào ngày 27 tháng 5 năm 2017, tôi nhận ra phát triển phần mềm có vẻ như là một sự phù hợp hoàn hảo.
Nhu cầu thì trường luôn cao, mức lương hấp dẫn và đó là ngành công nghiệp hoàn hảo để bắt đầu startup mà không cần nhiều vốn ban đầu. Tất cả bạn cần là một chiếc laptop, và cơ hội của bạn là vô hạn.
Mục tiêu
Bạn cần có một mục tiêu rõ ràng. Đặc biệt là nếu bạn đang cố gắng vừa học vừa làm. Mục tiêu của bạn nên đơn giản như sau:
Có được một công việc liên quan đến lập trình trong vòng một năm với mức lương tương đương hoặc tốt vị trí bây giờ.
Kế hoạch
Một khi bạn có một mục đích, ta sẽ cần một kế hoạch để giúp đạt được điều đó.
Vào ngày 27 tháng 5 năm 2017, tôi quyết định dành khoản 40 giờ mỗi tuần cho công việc của mình, để có thời gian để code sau khi làm việc và vào cuối tuần.
Kế hoạch của tôi khi đấy sẽ như thế này:
Tham gia khóa học về CS để có được sự hiểu biết cơ bản vững chắc về các khái niệm CS cốt lõi
Theo học tại freeCodeCamp cho đến khi tôi có thể xây dựng các full stack web app
Refactor để làm sạch mã, thêm testing, tập trung vào các khái niệm tiên tiến
Đóng góp vào nguồn mở
Chuẩn bị cho các cuộc phỏng vấn việc làm
Để bắt đầu, kế hoạch của tôi rất đơn giản. Vào thời điểm đó, tôi nghĩ là mình sẽ theo Google’s Technical Guide, vì vậy tôi bắt đầu với khóa học Udacity CS101.
Tháng 0 – Udacity CS101, Harvard CS50
Bước tiến đầu tiên này làm tôi vô cùng phấn khích. Việc học code được bắt đầu ngay khi tôi trở nhà sau giờ làm việc và sẽ không dừng lại cho đến khi tôi đi ngủ. Tôi đã hoàn thành 75% đầu tiên của khóa học Udacity CS101 trong 10 ngày. 25% cuối cùng là về đệ quy, và nó có một chút khó khăn hơn cho tôi. Do đó mà tôi mất tổng cộng 20 ngày để hoàn thành Udacity CS101.
Trong cùng lúc đó, tôi đã bắt đầu đọc các bài viết trên subreddit và điều quan trọng đối với các lập trình viên tự học cần phải có các tài khoản online. Tôi quyết định tạo các tài khoản mới trên Twitter, Reddit, Stack Overflow, Medium và Quora với tên họ đầy đủ.
Ngoài ra, tôi đã quyết định ngừng sử dụng các mạng xã hội dễ gây mất tập trung như Instagram, Facebook, …. Tôi chỉ kiểm tra điện thoại của mình về các tin tức và bài đăng liên quan đến lập trình. Điều này rất quan trọng trong việc đảm bảo rằng bản thân có thể tiếp cận với con đường học và tài liệu tốt nhất.
Và đó là nguyên cơ đã đưa tôi đến với khóa học Harvard CS50. Sinh viên CS ở các trường khác đã tham gia khóa học này và nói rằng họ đã học được nhiều thứ trong CS50 hơn cả một hai năm ngồi tại đại học.
Tháng thứ 1 – Harvard CS50, Linux, freeCodeCamp
Tôi đã hoàn thành CS50 trong vòng nửa tháng. Đó là một khóa học tuyệt vời mà bạn không thể bỏ qua. David Malan là một giảng viên xuất sắc, và có rất nhiều tài liệu để giúp bạn vượt qua nó. Bắt đầu với C, rồi chuyển sang Python, và sau đó kết thúc với việc phát triển web.
Sau khi CS50, tôi quyết định thiết lập XPS 15 cho dual boot Windows và Ubuntu. Đó là một ngày cuối tuần bực bội. Tôi làm rối tung các phân vùng và gần như biến chiếc máy của mình thành một cục gạch.
Tôi dần tách rời khỏi Windows và cuối cùng chỉ sử dụng mỗi Ubuntu.
Với kế hoạch coding 100 ngày do bản thân đề ra, bạn sẽ nhận ra rằng bản thân đã thực sự tiến bộ sau khi coi lại dữ liệu từ khoảng thời gian trên.
Mặt khác, tôi cũng chủ động mở rộng mối quan hệ trong thế giới của lập trình viên. Điều mà một người luôn ngại giao tiếp và chưa hề có tham gia buổi meetup sẽ gặp rất nhiều khó. Tôi có lúc đã định bỏ về khi chỉ còn vài bước đến buổi hẹn.
Tuy vậy, tôi nhanh chóng nhận ra rằng không có lý do để lo lắng. Không ai biết nhau, cũng như chả ai có quyền phán xét, và mọi người đều háo hức học hỏi. Và kể từ đó, tôi đã tham dự hơn 50 cuộc gặp gỡ trong 9 tháng.
Hầu hết mọi người chỉ bắt đầu tham dự buổi họp mặt khi họ đang tìm kiếm việc làm, nhưng vào thời điểm đó gần như là quá muộn. Có rất nhiều lý do để bắt đầu sớm, bao gồm:
Phát triển mối quan hệ mất rất nhiều thời gian. Bắt đầu sớm có nghĩa là bạn sẽ có kết nối để bảo đảm việc tìm kiếm một công việc trở nên dễ dàng.
Nói về lập trình với người lạ là một cách tuyệt vời để chuẩn bị cho các cuộc phỏng vấn
Bạn có thể tìm hiểu các framework, công cụ và tài nguyên học tập mới từ những người đi trước bạn. Điều này có thể ảnh hưởng đến kế hoạch học tập trong tương lai.
Cuối cùng, tôi đã chọn phát triển web vì nó có vẻ như có nhu cầu cao và cũng có rất dễ học trực tuyến. Một số người khuyên rằng ở giai đoạn này tôi nên suy nghĩ về các ứng dụng web tôi muốn xây dựng. Họ đã đề xuất Dự án Odin hoặc freeCodeCamp.
Ban đầu tôi quyết định tham gia Odin.
Và sau đó hai ngày, ý tưởng đó ngay lập tức bị bỏ qua.
Đây là một trong những nhược điểm của việc đi theo con đường tự học. Một phút bạn nghĩ rằng bạn biết con đường bạn nên đi, nhưng rồi vào ngày hôm sau bạn tự hỏi: liệu đó có phải là lựa chọn đúng !
Tháng thứ 2 – YDKJS, freeCodeCamp Front End, React
Tôi bắt đầu đọc You Don’t Know JavaScript, bởi vì tất cả mọi người khuyên bạn nên xem nó trên CodeCamp. Tôi đã phải mất một khoảng thời gian vì nó khá khó, nhưng đó là một nguồn tài liệu hoàn hảo để học về lexical scope, closures, promises, và tất cả các phần khác của JavaScript mà bạn muốn học nhưng không bao giờ làm vì chúng có vẻ khó khăn.
Tôi đã hoàn thành phần front-end của freeCodeCamp. Tuy vậy, kiến thức hiện tại chỉ tạm đủ cho những hiểu biết cơ bản. Do đó mà tôi quyết định học sâu hơn về CSS.
Tôi đã nghe rất nhiều về nó và giờ đây thì đã sẵn sàng. Tuy nhiên, tôi đã hơi do dự khi các vấn đề giấy phép bản quyền vào thời điểm đó. Thật may là Facebook đã nhanh chóng loại bỏ vấn đề trên.
Tôi đã cố gắng đọc các tài liệu và làm theo hướng dẫn của Tic-Tac-Toe của Facebook, nhưng vẫn không hiểu hết về nó. Tôi đã được bảo rằng đó là vì bản thân không hiểu JavaScript đủ. Vì vậy, sau đó tôi đã quyết định đọc lại cuốn You Don’t Know JavaScript.
Bài viết sẽ bàn về một framework C++ RP, ko yêu cầu bước code generation để glue code. Trước khi đi vào chi tiết, hãy tìm hiểu một số tính năng cơ bản của công cụ:
Đoạn mã nguồn trên vẫn chưa hoàn chỉnh. Mục tiêu của nó chỉ là để thể hiện nền tảng xây dựng framework. Hơn nữa, để rút ngắn, đoạn code là hỗn hợp của code từ repo lúc viết và custom sample code, nên có thể sẽ xảy ra lỗi.
Một số đoạn trong mã nguồn (không liên quan trực tiếp đến nội dung ta đang bàn đến) sẽ chỉ được xem xét nhanh mà không quá quan tâm đến hiệu năng. Bất cứ cải tiến nào sẽ được thêm vào repo mã nguồn sau.
Modern C++ (C++11/14)
Yêu cầu ít nhất Visual Studio 2015. Clang/GCC cũng tạm ổn.
Type-safe
Framework xác định những RPC call không hợp lệ trong lúc compile, như tên RPC không rõ, số thông số sai, hoặc kiểu thông số sai.
API tương đối nhỏ, không dài dòng
Nhiều cách xử lý RPC reply
Bộ xử lý không đồng bộ (Asynchronous handler)
Futures
Client có thể xác định nếu một RPC gây exception bên phía server
Cho phép sử dụng (gần như) bất kỳ kiểu RPC parameter nào
Giả sử nguời dùng chạy đúng hàm để xử lý kiểu parameter đó
RPC hai chiều (server có thể call RPC trên client)
Thông thường, client code không thể tin tưởng được, nhưng vì framework nhằm sử dụng giữa các bên uy tín, nên đây không phải là vấn đề quá lớn
Không xâm nhập
Một object dùng cho RPC call không cần biết bất cứ thứ gì về RPC hoặc network cả.
Từ đó có thể bọc các class bên thứ ba cho RPC call.
Chi phí băng thông tối thiểu mỗi RPC call
Không phụ thuộc bên ngoài
Dù supplied transport (trong repo mã nguồn) dùng Asio/Boost Asio, nhưng bản thân framework không phụ thuộc vòa đó. Bạn có thể tự cắm transport của mình.
Không có tính năng bảo mật
Vì frame work được thiết kế sử dụng cho các bên tin tưởng lẫn nhau (như giữa server chẳng hạn).
Ứng dụng có thể tự chọn transport riêng, nên sẽ có cơ hội encrypt bất cứ thứ gì nếu cần thiết.
Mặc dù mã nguồn vẫn chưa hoàn thiện, bài viết vẫn rất nặng về code. Code được tách thành nhiều phần nhỏ và một phần sau sẽ dựa theo phần trước. Để bạn hình dung được, sau đây là một mẫu code hoạt động hoàn chỉnh sử dụng repo mã nguồn tại thời điểm viết bài:
//////////////////////////////////////////////////////////////////////////
// Useless RPC-agnostic class that performs calculations.
//////////////////////////////////////////////////////////////////////////
class Calculator {
public:
double add(double a, double b) { return a + b; }
};
//////////////////////////////////////////////////////////////////////////
// Define the RPC table for the Calculator class
// This needs to be seen by both the server and client code
//////////////////////////////////////////////////////////////////////////
#define RPCTABLE_CLASS Calculator
#define RPCTABLE_CONTENTS \
REGISTERRPC(add)
#include "crazygaze/rpc/RPCGenerate.h"
//////////////////////////////////////////////////////////////////////////
// A Server that only accepts 1 client, then shuts down
// when the client disconnects
//////////////////////////////////////////////////////////////////////////
void RunServer() {
asio::io_service io;
// Start thread to run Asio's the io_service
// we will be using for the server
std::thread th = std::thread([&io] {
asio::io_service::work w(io);
io.run();
});
// Instance we will be using to serve RPC calls.
// Note that it's an object that knows nothing about RPCs
Calculator calc;
// start listening for a client connection.
// We specify what Calculator instance clients will use,
auto acceptor = AsioTransportAcceptor<Calculator, void>::create(io, calc);
// Start listening on port 9000.
// For simplicity, we are only expecting 1 client
using ConType = Connection<Calculator, void>;
std::shared_ptr<ConType> con;
acceptor->start(9000, [&io, &con](std::shared_ptr<ConType> con_) {
con = con_;
// Since this is just a sample, close the server once the first client
// disconnects
reinterpret_cast<BaseAsioTransport*>(con->transport.get())
->setOnClosed([&io] { io.stop(); });
});
th.join();
}
//////////////////////////////////////////////////////////////////////////
// A client that connects to the server, calls 1 RPC
// then disconnects, causing everything to shut down
//////////////////////////////////////////////////////////////////////////
void RunClient() {
// Start a thread to run our Asio io_service
asio::io_service io;
std::thread th = std::thread([&io] {
asio::io_service::work w(io);
io.run();
});
// Connect to the server (localhost, port 9000)
auto con =
AsioTransport<void, Calculator>::create(io, "127.0.0.1", 9000).get();
// Call one RPC (the add method), specifying an asynchronous handler for
// when the result arrives
CZRPC_CALL(*con, add, 1, 2)
.async([&io](Result<double> res) {
printf("Result=%f\n", res.get()); // Prints 3.0
// Since this is a just a sample, stop the io_service after we get
// the result,
// so everything shuts down
io.stop();
});
th.join();
}
// For testing simplicity, run both the server and client on the same machine,
void RunServerAndClient() {
auto a = std::thread([] { RunServer(); });
auto b = std::thread([] { RunClient(); });
a.join();
b.join();
}
Đây đa phần là setup code, vì transport được nhắc đến sử dụng Asio. Bản thân RPC có thể đơn giản như:
// RPC call using asynchronous handler to handle the result
CZRPC_CALL(*con, add, 1, 2).async([](Result<double> res) {
printf("Result=%f\n", res.get()); // Prints 3.0
});
// RPC call using std::future to handle the result
Result<double> res = CZRPC_CALL(*con, add, 1, 2).ft().get();
printf("Result=%f\n", res.get()); // Prints 3.0
Tôi đang làm việc với một game có code named G4 cũng được vài năm rồi, trò chơi cung cấp cho player những chiếc máy tính mô phỏng nhỏ in-game mà thậm chí có thể code được. Điều này yêu cầu tôi phải cho nhiều server cùng chạy song song:
Gameplay Server(s)
VM Server(s) (mô phỏng máy tính ingame)
Để vẫn có thể mô phỏng máy tính ngay cả khi người chơi hiện không online
VM Disk Server(s)
Xử lý lưu trữ của máy tính ingame, như đĩa mềm hoặc ổ cứng.
Database server(s)
Login server(s)
Tất cả những server này cần trao đổi dữ liệu, vì vậy ta cần một RPC framework linh hoạt.
Ban đầu, giải pháp tôi tìm đến là tag method của một class với attribute cụ thể, rồi từ parser gốc Clang (clReflect) tạo bất cứ serialization cần thiết nào, và glue code.
Dù cách này vẫn áp dụng được, nhưng nhiều năm qua tôi vẫn luôn canh cánh làm cách nào tôi có thể dùng tính năng C++11/14 mới để tạo một minimal type safe C++ RPC framework, một phương pháp không cần bước code generation cho glue code, mà vẫn giữa một API chấp nhận được.
Với serialization của non-fundamental type, code genaration vẫn còn hữu dụng, vậy nên tôi không cần phải xác định thủ công làm sao để serialize tất cả các field của một struct/class cho trước. (dù khâu xác định thủ công này cũng chả phiền phức cho lắm).
RPC Parameters
Xét một function, để có type safe RPC call, ta phải làm được:
Xác định lúc compile nếu function là một RPC function hợp lệ (đúng số thông ố, đúng kiểu parameter,…)
Kiểm tra liệu các parameter được supply khớp (hoặc convert được) sang thứ mà function signature chỉ ra.
Serialize tất cả parameter.
Deserialize tất cả parameter.
Call những function mong muốn
Parameter Traits
Vấn đề đầu tiên bạn đối mặt là việc xác định kiểu parameter nào được chấp nhập. Một số RPC framework chỉ chấp nhật một số kiểu giới hạn, như Thrift. Hãy xem thử vấn đề.
Xét các function signature sau:
void func1(int a, float b, char c);
void func2(const char* a, std::string b, const std::string& c);
void func3(Foo a, Bar* b);
Vậy làm cách nào ta bắt compile kiểm tra theo parameter? Những kiểu fundamental type khá dễ và chắc chắn phải được framework hỗ trợ. Một bản copy dumb memory là đủ cho trường hợp như thế này, trừ khi bạn muốn cắt giảm số bit cần thiết để hy sinh một ít hiệu năng lấy bandwidth. Vậy chòn với các kiểu phức tạp như std::string, std::vector, hoặc chính class của mình? Còn pointer, reference, const reference, rvalue thì sao?
Ta có thể lấy một số ý tưởng từ những thứ C++ Standard Library đang làm trong type_traits header. Ta cần truy vấn một kiểu cho trước dựa theo tính chất RPC của nó. Hãy thử biến ý tưởng trên thành một template class `ParamTraits<T>`, với layout như sau:
Member constants
valid
true if T is valid for RPC parameters, false otherwise
Member types
store_type
Type dùng để giữ copy tạm thời cần cho deserializing
Member functions
write
Ghi parameter đến một stream
read
Đọc parameter vào một store_type
get
Xét một store_type parameter, kết quả nó trả có thể được chuyển đến RPC function làm parameter
Trong ví dụ này,hãy thực thi `ParamTraits<T>` cho kiểu thuật toán, trong trường hợp ta có stream class với method `write` và `read`:
`ParamTraits<T>` còn được sử dụng để kiểm tra nếu return type hợp lệ, và vì một void function hợp lệ, ta cần phải chuyên hóa `ParamTraits` cho void nữa.
Một điều khá kỳ lạ là khi chuyên hóa cho `void` là còn chỉ định một `store_type` nữa. Chúng ta không thể sử dụng nó để lưu trữ bất cư thứ gì, nhưng sẽ làm một vìa đoạn template code về sau dễ dàng hơn.
Với những ví dụ `ParamTraits`, reference không phải là RPC parameter hợp lệ. Trong thực tế, bạn ít nhất sẽ muốn cho phép const reference, đặc biệt với fundamental type. Bạn có thể thêm một tweak để kích hoạt hỗ trợ cho `const T&` cho bất kỳ T hợp lệ nào khi ứng dụng của bạn cần đến.
// Make "const T&" valid for any valid T
#define CZRPC_ALLOW_CONST_LVALUE_REFS \
namespace cz { \
namespace rpc { \
template <typename T> \
struct ParamTraits<const T&> : ParamTraits<T> { \
static_assert(ParamTraits<T>::valid, \
"Invalid RPC parameter type. Specialize ParamTraits if " \
"required."); \
}; \
} \
}
Bạn cũng có thể thực hiện những tweak tương tự để kích hoạt hỗ trợ `T&` hoặc `T&&` nếu cần thiết, mặc dù nếu function thay đổi đến những parameter này, những thay đổi đó sẽ mất đi.
Hãy thử thêm hỗ trợ cho các kiểu phức tạp như `std::vector<T>`. Để hỗ trợ `std::vector<T>`, ta buộc phải hỗ trợ thêm cả `T`:
namespace cz {
namespace rpc {
template <typename T>
struct ParamTraits<std::vector<T>> {
using store_type = std::vector<T>;
static constexpr bool valid = ParamTraits<T>::valid;
static_assert(ParamTraits<T>::valid == true,
"T is not valid RPC parameter type.");
// std::vector serialization is done by writing the vector size, followed by
// each element
template <typename S>
static void write(S& s, const std::vector<T>& v) {
int len = static_cast<int>(v.size());
s.write(&len, sizeof(len));
for (auto&& i : v) ParamTraits<T>::write(s, i);
}
template <typename S>
static void read(S& s, std::vector<T>& v) {
int len;
s.read(&len, sizeof(len));
v.clear();
while (len--) {
T i;
ParamTraits<T>::read(s, i);
v.push_back(std::move(i));
}
}
static std::vector<T>&& get(std::vector<T>&& v) {
return std::move(v);
}
};
} // namespace rpc
} // namespace cz
// A simple test
void testVector() {
TEST(ParamTraits<std::vector<int>>::valid); // true
// true if support for const refs was enabled
TEST(ParamTraits<const std::vector<int>&>::valid);
}
Để tiện hơn, chúng ta có thể dùng toán tử `<<` và `>>` với class `stream` (không thế hiện ở đây). Với những toán tử này, hãy đơn thuần call các function `ParamTraits<T> read` và `ParamTraits<T> write` tương ứng.
Giờ đây ta đã có thể kiểm tra xem một type nhất định có được phép cho RPC parameter hay không, chúng ta có thể build trên đó kiểm tra xem một function có được dùng cho RPC hay không. Cách này được thực thi với variadic template.
Trước hết hãy tạo một template cho ta biết nếu nhóm parameter có hợp lệ hay không.
namespace cz {
namespace rpc {
//
// Validate if all parameter types in a parameter pack can be used for RPC
// calls
//
template <typename... T>
struct ParamPack {
static constexpr bool valid = true;
};
template <typename First>
struct ParamPack<First> {
static constexpr bool valid = ParamTraits<First>::valid;
};
template <typename First, typename... Rest>
struct ParamPack<First, Rest...> {
static constexpr bool valid =
ParamTraits<First>::valid && ParamPack<Rest...>::valid;
}
} // namespace rpc
} // namespace cz
// Usage example:
void testParamPack() {
TEST(ParamPack<>::valid); // true (No parameters is a valid too)
TEST((ParamPack<int, double>::valid)); // true
TEST((ParamPack<int, int*>::valid)); // false
}
Using ParamPack, we can now create a FunctionTraits template to query a function's properties.
namespace cz {
namespace rpc {
template <class F>
struct FunctionTraits {};
// For free function pointers
template <class R, class... Args>
struct FunctionTraits<R (*)(Args...)> : public FunctionTraits<R(Args...)> {};
// For method pointers
template <class R, class C, class... Args>
struct FunctionTraits<R (C::*)(Args...)> : public FunctionTraits<R(Args...)> {
using class_type = C;
};
// For const method pointers
template <class R, class C, class... Args>
struct FunctionTraits<R (C::*)(Args...) const>
: public FunctionTraits<R(Args...)> {
using class_type = C;
};
template <class R, class... Args>
struct FunctionTraits<R(Args...)> {
// Tells if both the return type and parameters are valid for RPC calls
static constexpr bool valid =
ParamTraits<R>::valid && ParamPack<Args...>::valid;
using return_type = R;
// Number of parameters
static constexpr std::size_t arity = sizeof...(Args);
// A tuple that can store all parameters
using param_tuple = std::tuple<typename ParamTraits<Args>::store_type...>;
// Allows us to get the type of each parameter, given an index
template <std::size_t N>
struct argument {
static_assert(N < arity, "error: invalid parameter index.");
using type = typename std::tuple_element<N, std::tuple<Args...>>::type;
};
};
} // namespace rpc
} // namespace cz
// A simple test...
struct FuncTraitsTest {
void func1() const {}
void func2(int) {}
int func3(const std::vector<int>&) { return 0; }
int* func4() { return 0; }
};
void testFunctionTraits() {
TEST(FunctionTraits<decltype(&FuncTraitsTest::func1)>::valid); // true
TEST(FunctionTraits<decltype(&FuncTraitsTest::func2)>::valid); // true
TEST(FunctionTraits<decltype(&FuncTraitsTest::func3)>::valid); // true
TEST(FunctionTraits<decltype(&FuncTraitsTest::func4)>::valid); // false
}
`FunctionTraits` cho ta một vài tính chất sẽ được sử dụng sau. Lưu ý với ví dụ rằng `FunctionTraits::param_tuple` build trên `ParamTraits<T>::store_type`. Việc này cần thiết, vì ở một thời điểm nào đó, chung ta cần cách ể deserialize tất cả parameter thành một tuple trước khi call function.
Serialization
Vì chúng ta giờ đây có code cần thiết để truy vấn parameter, return type và validating function, chúng ta có thể gộp code lại để serialize một function call. Hơn nữa, đây chính là type safe. Nó sẽ không complie nếu nhập sai số hoặc kiểu parameter, hoặc nếu bản thân function không hợp lệ cho RPC (không hỗ trợ các return/parameter type).
namespace cz {
namespace rpc {
namespace details {
template <typename F, int N>
struct Parameters {
template <typename S>
static void serialize(S&) {}
template <typename S, typename First, typename... Rest>
static void serialize(S& s, First&& first, Rest&&... rest) {
using Traits =
ParamTraits<typename FunctionTraits<F>::template argument<N>::type>;
Traits::write(s, std::forward<First>(first));
Parameters<F, N + 1>::serialize(s, std::forward<Rest>(rest)...);
}
};
} // namespace details
template <typename F, typename... Args>
void serializeMethod(Stream& s, Args&&... args) {
using Traits = FunctionTraits<F>;
static_assert(Traits::valid,
"Function signature not valid for RPC calls. Check if "
"parameter types are valid");
static_assert(Traits::arity == sizeof...(Args),
"Invalid number of parameters for RPC call.");
details::Parameters<F, 0>::serialize(s, std::forward<Args>(args)...);
}
} // namespace rpc
} // namespace cz
//
// A simple test
//
struct SerializeTest {
void func1(int, const std::vector<int>) {}
void func2(int*) {}
};
void testSerializeCall() {
Stream s;
serializeMethod<decltype(&SerializeTest::func1)>(s, 1,
std::vector<int>{1, 2, 3});
// These fail to compile because of the wrong number of parameters
// serializeMethod<decltype(&SerializeTest::func1)>(s);
// serializeMethod<decltype(&SerializeTest::func1)>(s, 1);
// Doesn't compile because of wrong type of parameters
// serializeMethod<decltype(&SerializeTest::func1)>(s, 1, 2);
// Doesn't compile because the function can't be used for RPCs.
// int a;
// serializeMethod<decltype(&SerializeTest::func2)>(s, &a);
}
Deserialization
Như đã đề cập ở trên, `FunctionTraits<F>::param_tuple` là type `std::tuple` ta dùng được để giữ tất cả parameter của function. Để có thể sử dụng tuple này để deserialize parameter, chúng ta phải chuyên hóa `ParamTraits` cho các tuple. Bên cạnh đó, ta còn có thể sử dụng `std::tuple` cho RPC parameter.
namespace cz {
namespace rpc {
namespace details {
template <typename T, bool Done, int N>
struct Tuple {
template <typename S>
static void deserialize(S& s, T& v) {
s >> std::get<N>(v);
Tuple<T, N == std::tuple_size<T>::value - 1, N + 1>::deserialize(s, v);
}
template <typename S>
static void serialize(S& s, const T& v) {
s << std::get<N>(v);
Tuple<T, N == std::tuple_size<T>::value - 1, N + 1>::serialize(s, v);
}
};
template <typename T, int N>
struct Tuple<T, true, N> {
template <typename S>
static void deserialize(S&, T&) {}
template <typename S>
static void serialize(S&, const T&) {}
};
} // namespace details
template <typename... T>
struct ParamTraits<std::tuple<T...>> {
using tuple_type = std::tuple<T...>; // for internal use
using store_type = tuple_type;
static constexpr bool valid = ParamPack<T...>::valid;
static_assert(
ParamPack<T...>::valid == true,
"One or more tuple elements is not a valid RPC parameter type.");
template <typename S>
static void write(S& s, const tuple_type& v) {
details::Tuple<tuple_type, std::tuple_size<tuple_type>::value == 0,
0>::serialize(s, v);
}
template <typename S>
static void read(S& s, tuple_type& v) {
details::Tuple<tuple_type, std::tuple_size<tuple_type>::value == 0,
0>::deserialize(s, v);
}
static tuple_type&& get(tuple_type&& v) {
return std::move(v);
}
};
} // namespace rpc
} // namespace cz
// A simple test
void testDeserialization() {
Stream s;
serializeMethod<decltype(&SerializeTest::func1)>(s, 1,
std::vector<int>{1, 2});
// deserialize the parameters into a tuple.
// the tuple is of type std::tuple<int,std::vector<int>>
FunctionTraits<decltype(&SerializeTest::func1)>::param_tuple params;
s >> params;
};
Từ tuple đến function parameters
Sau khi deserialize tất cả parameter thành tuple, chúng ta giờ đâu phải tìm cách tháo tuble để call matching function. Một lần nữa, ta có thể dùng variadic template.
Vậy, giờ đây ta đã biết cách xác thực một function, serialize, deserialize, và call function đó. Vậy là đã xong bước code “level thấp” rồi. Lớp tiếp theo chúng ta sắp sửa phủ lên code này sẽ là RPC API thực sự.
The RPC API
Header
Header có chứa những thông tin sau:
Field
size
Tổng dung lượng (byte) của RPC. Có dung lượng là một phần của header sẽ đơn giản hóa mạnh mẽ, vì chúng ta có thể kiểm tra nếu ta nhận tất cả dữ liệu trước khi thử xử lý RPC.
counter
Số call. Mỗi lúc ta gọi RPC, một counter sẽ tăng và được chỉ định đến RPC call đó.
rpcid
The function to call
isReply
Nếu true, đây là reply đến RPC. Nếu false, đây là RPC call.
success
Chỉ áp dụng cho reply (isReply==true). Nếu true, call thành công và data là reply. Nấu false, data là exception information
Counter và rpcid hình thành một key xác định RPC call instance, cần khi ghép một incoming RPC reply đế RPC call gây ra nó.
// Small utility struct to make it easier to work with the RPC headers
struct Header {
enum {
kSizeBits = 32,
kRPCIdBits = 8,
kCounterBits = 22,
};
explicit Header() {
static_assert(sizeof(*this) == sizeof(uint64_t),
“Invalid size. Check the bitfields”);
all_ = 0;
}
struct Bits {
uint32_t size : kSizeBits;
unsigned counter : kCounterBits;
unsigned rpcid : kRPCIdBits;
unsigned isReply : 1; // Is it a reply to a RPC call ?
unsigned success : 1; // Was the RPC call a success ?
};
Chúng ta đã serialize và deserialize một RPC, nhưng không phải là cách map một RPC đã serialize đến đúng function bên phía server. Để giải quyết vấn đề này, chúng ta cần chỉ định một ID đến mỗi function. Client sẽ biết nó muốn call function nào, và điền đúng ID vào header. Server kiểm tra header, và khi biết được ID, nó sẽ gửi đến đúng handler. Hãy tạo một số ID cơ bản để xác định bảng phân phối tương tự.
namespace cz {
namespace rpc {
//
// Helper code to dispatch a call.
namespace details {
// Handle RPCs with return values
template <typename R>
struct CallHelper {
template <typename OBJ, typename F, typename P>
static void impl(OBJ& obj, F f, P&& params, Stream& out) {
out << callMethod(obj, f, std::move(params));
}
};
// Handle void RPCs
template <>
struct CallHelper<void> {
template <typename OBJ, typename F, typename P>
static void impl(OBJ& obj, F f, P&& params, Stream& out) {
callMethod(obj, f, std::move(params));
}
};
}
struct BaseRPCInfo {
BaseRPCInfo() {}
virtual ~BaseRPCInfo(){};
std::string name;
};
class BaseTable {
public:
BaseTable() {}
virtual ~BaseTable() {}
bool isValid(uint32_t rpcid) const {
return rpcid < m_rpcs.size();
}
protected:
std::vector<std::unique_ptr<BaseRPCInfo>> m_rpcs;
};
template <typename T>
class TableImpl : public BaseTable {
public:
using Type = T;
struct RPCInfo : public BaseRPCInfo {
std::function<void(Type&, Stream& in, Stream& out)> dispatcher;
};
template <typename F>
void registerRPC(uint32_t rpcid, const char* name, F f) {
assert(rpcid == m_rpcs.size());
auto info = std::make_unique<RPCInfo>();
info->name = name;
info->dispatcher = [f](Type& obj, Stream& in, Stream& out) {
using Traits = FunctionTraits<F>;
typename Traits::param_tuple params;
in >> params;
using R = typename Traits::return_type;
details::CallHelper<R>::impl(obj, f, std::move(params), out);
};
m_rpcs.push_back(std::move(info));
}
};
template <typename T>
class Table : public TableImpl<T> {
static_assert(sizeof(T) == 0, "RPC Table not specified for the type.");
};
} // namespace rpc
} // namespace cz
The Table template cần phải được chuyên hóa cho class mà ta muốn dùng cho RPC call. Giả sử ta có một Calculator class muốn dùng cho RPC calls:
class Calculator {
public:
double add(double a, double b) {
m_ans = a + b;
return m_ans;
}
double sub(double a, double b) {
m_ans = a - b;
return m_ans;
}
double ans() {
return m_ans;
}
private:
double m_ans = 0;
};
Ta có thể chuyên hóa Table template cho Calculator, để cả client và server đều có thể tham gia:
Với một ID,function `get` sẽ trả dispatcher về đúng method `Calculator`. Rồi ta có thể chuyển `Calculator` instance, input và output streams sang dispatcher để xử lý mọi thứ còn lại.
Quá trình chuyên hóa khá dài dòng và không thể tránh khỏi sai sót, vì enum và `registerRPC` call phải khớp. Nhưng chúng ta có thể rút ngắn rõ rệt quá trình với một vài macro. Đầu tiên, hãy xem thử một ví dụ dài dòng để thấy cách dùng bảng này:
void testCalculatorTable() {
// Both the client and server need to have access to the necessary table
using CalcT = Table<Calculator>;
//
// Client sends RPC
Stream toServer;
RPCHeader hdr;
hdr.bits.rpcid = (int)CalcT::RPCId::add;
toServer << hdr;
serializeMethod<decltype(&Calculator::add)>(toServer, 1.0, 9.0);
//
// Server receives RPC, and sends back a reply
Calculator calc; // object used to receive the RPCs
toServer >> hdr;
auto&& info = CalcT::get(hdr.bits.rpcid);
Stream toClient; // Stream for the reply
// Call the desired Calculator function.
info->dispatcher(calc, toServer, toClient);
//
// Client receives a reply
double r;
toClient >> r;
printf("%2.0f\n", r); // Will print "10"
}
Một lần nữa, ví dụ này khá dài dòng, chỉ để thể hiện code flow. Chúng ta sẽ có ví dụ tối ưu hơn sau.
Vậy, chúng ta đơn giản quá trình chuyên hóa bảng như thế nào? Nếu ta đặt gist của chuyên hóa bảng trong một header không được guard, bạn chỉ cần một vài define theo sau là một include của header (không guard) đó để tạo tương tự.
`RPCGenerate.h` là một header không được guard, trông như sau:
#ifndef RPCTABLE_CLASS
#error "Macro RPCTABLE_CLASS needs to be defined"
#endif
#ifndef RPCTABLE_CONTENTS
#error "Macro RPCTABLE_CONTENTS needs to be defined"
#endif
#define RPCTABLE_TOOMANYRPCS_STRINGIFY(arg) #arg
#define RPCTABLE_TOOMANYRPCS(arg) RPCTABLE_TOOMANYRPCS_STRINGIFY(arg)
template<> class cz::rpc::Table<RPCTABLE_CLASS> : cz::rpc::TableImpl<RPCTABLE_CLASS>
{
public:
using Type = RPCTABLE_CLASS;
#define REGISTERRPC(rpc) rpc,
enum class RPCId {
RPCTABLE_CONTENTS
NUMRPCS
};
Table()
{
static_assert((unsigned)((int)RPCId::NUMRPCS-1)<(1<<Header::kRPCIdBits),
RPCTABLE_TOOMANYRPCS(Too many RPCs registered for class RPCTABLE_CLASS));
#undef REGISTERRPC
#define REGISTERRPC(func) registerRPC((uint32_t)RPCId::func, #func, &Type::func);
RPCTABLE_CONTENTS
}
static const RPCInfo* get(uint32_t rpcid)
{
static Table<RPCTABLE_CLASS> tbl;
assert(tbl.isValid(rpcid));
return static_cast<RPCInfo*>(tbl.m_rpcs[rpcid].get());
}
};
#undef REGISTERRPC
#undef RPCTABLE_START
#undef RPCTABLE_END
#undef RPCTABLE_CLASS
#undef RPCTABLE_CONTENTS
#undef RPCTABLE_TOOMANYRPCS_STRINGIFY
#undef RPCTABLE_TOOMANYRPCS
Thêm vào đó, việc chuyên hóa bảng thế này giúp ta hỗ trợ inheritance dễ dàng hơn. Hãy tưởng tượng chúng ta có một `ScientificCalculator` kế thừa từ Calculator:
class ScientificCalculator : public Calculator {
public:
double sqrt(double a) {
return std::sqrt(a);
}
};
Khi đã define riêng biệt các content của Calculator, chúng ta có thể tái sử dụng define này:
// Separately define the Calculator contents so it can be reused
#define RPCTABLE_CALCULATOR_CONTENTS \
REGISTERRPC(add) \
REGISTERRPC(sub) \
REGISTERRPC(ans)
// Calculator table
#define RPCTABLE_CLASS Calculator
#define RPCTABLE_CONTENTS \
RPCTABLE_CALCULATOR_CONTENTS
#include "RPCGenerate.h"
// ScientificCalculator table
#define RPCTABLE_CLASS ScientificCalculator
#define RPCTABLE_CONTENTS \
RPCTABLE_CALCULATOR_CONTENTS \
REGISTERRPC(sqrt)
#include "RPCGenerate.h"
Transport
Chúng ta phải xác định dữ liệu được chuyển đổi như thế nào giữa client và server. Hãy đặt dữ liệu đó vào `Transport` interface class. Interface bị cố ý để rất đơn giản để ứng dụng xác định một custom transport. Tất cả chúng ta cần là method để gửi, nhận, và đóng.
namespace cz {
namespace rpc {
class Transport {
public:
virtual ~Transport() {}
// Send one single RPC
virtual void send(std::vector<char> data) = 0;
// Receive one single RPC
// dst : Will contain the data for one single RPC, or empty if no RPC
// available
// return: true if the transport is still alive, false if the transport
// closed
virtual bool receive(std::vector<char>& dst) = 0;
// Close connection to the peer
virtual void close() = 0;
};
} // namespace rpc
} // namespace cz
Result
Kết quả RPC phải trông như thế nào? Mỗi khi chúng ta tạo một RPC call, kết quả có thể đi theo 3 hình thức.
Form
Meaning
Valid
Nhận reply từ server với giá trị kết quả của RPC call
Aborted
Connection failed hoặc bị đóng, và chúng ta không nhận được giá trị kết quả
Exception
Nhận reply từ server với exception string (RPC call gây exception bên server side)
namespace cz {
namespace rpc {
class Exception : public std::exception {
public:
Exception(const std::string& msg) : std::exception(msg.c_str()) {}
};
template <typename T>
class Result {
public:
using Type = T;
Result() : m_state(State::Aborted) {}
explicit Result(Type&& val) : m_state(State::Valid), m_val(std::move(val)) {}
Result(Result&& other) {
moveFrom(std::move(other));
}
Result(const Result& other) {
copyFrom(other);
}
~Result() {
destroy();
}
Result& operator=(Result&& other) {
if (this == &other)
return *this;
destroy();
moveFrom(std::move(other));
return *this;
}
// Construction from an exception needs to be separate. so
// RPCReply<std::string> works.
// Otherwise we would have no way to tell if constructing from a value, or
// from an exception
static Result fromException(std::string ex) {
Result r;
r.m_state = State::Exception;
new (&r.m_ex) std::string(std::move(ex));
return r;
}
template <typename S>
static Result fromStream(S& s) {
Type v;
s >> v;
return Result(std::move(v));
};
bool isValid() const {
return m_state == State::Valid;
}
bool isException() const {
return m_state == State::Exception;
};
bool isAborted() const {
return m_state == State::Aborted;
}
T& get() {
if (!isValid())
throw Exception(isException() ? m_ex : "RPC reply was aborted");
return m_val;
}
const T& get() const {
if (!isValid())
throw Exception(isException() ? m_ex : "RPC reply was aborted");
return m_val;
}
const std::string& getException() {
assert(isException());
return m_ex;
};
private:
void destroy() {
if (m_state == State::Valid)
m_val.~Type();
else if (m_state == State::Exception) {
using String = std::string;
m_ex.~String();
}
m_state = State::Aborted;
}
void moveFrom(Result&& other) {
m_state = other.m_state;
if (m_state == State::Valid)
new (&m_val) Type(std::move(other.m_val));
else if (m_state == State::Exception)
new (&m_ex) std::string(std::move(other.m_ex));
}
void copyFrom(const Result& other) {
m_state = other.m_state;
if (m_state == State::Valid)
new (&m_val) Type(other.m_val);
else if (m_state == State::Exception)
new (&m_ex) std::string(other.m_ex);
}
enum class State { Valid, Aborted, Exception };
State m_state;
union {
Type m_val;
std::string m_ex;
};
};
// void specialization
template <>
class Result<void> {
public:
Result() : m_state(State::Aborted) {}
Result(Result&& other) {
moveFrom(std::move(other));
}
Result(const Result& other) {
copyFrom(other);
}
~Result() {
destroy();
}
Result& operator=(Result&& other) {
if (this == &other)
return *this;
destroy();
moveFrom(std::move(other));
return *this;
}
static Result fromException(std::string ex) {
Result r;
r.m_state = State::Exception;
new (&r.m_ex) std::string(std::move(ex));
return r;
}
template <typename S>
static Result fromStream(S& s) {
Result r;
r.m_state = State::Valid;
return r;
}
bool isValid() const {
return m_state == State::Valid;
}
bool isException() const {
return m_state == State::Exception;
};
bool isAborted() const {
return m_state == State::Aborted;
}
const std::string& getException() {
assert(isException());
return m_ex;
};
void get() const {
if (!isValid())
throw Exception(isException() ? m_ex : "RPC reply was aborted");
}
private:
void destroy() {
if (m_state == State::Exception) {
using String = std::string;
m_ex.~String();
}
m_state = State::Aborted;
}
void moveFrom(Result&& other) {
m_state = other.m_state;
if (m_state == State::Exception)
new (&m_ex) std::string(std::move(other.m_ex));
}
void copyFrom(const Result& other) {
m_state = other.m_state;
if (m_state == State::Exception)
new (&m_ex) std::string(other.m_ex);
}
enum class State { Valid, Aborted, Exception };
State m_state;
union {
bool m_dummy;
std::string m_ex;
};
};
} // namespace rpc
} // namespace cz
Chuyên hóa `Result<void>` rất cần thiết, vì trong trường hợp đó không có kết quả, nhưng caller vẫn muốn biết neus RPC call có được xử lý đúng cách hay không. Mới đầu, tôi từng xem xét sử dụng `Expected<T>` cho RPC reply. Nhưng `Expected<T>` về cơ bản có hai trạng thái (Value hoặc Exception), mà chúng ta lại cần tới 3 (Value, Exception, và Aborted). Ta hay cho rằng Aborted có thể được xem là exceptiom, nhưng từ góc nhìn của client, điều này không phải lúc nào cũng đúng. Trong một số trường hợp bạn sẽ muốn biết một RPC fail vì đóng kết nối, mà không phải vì server phản hồi exception.
Video: Sendo.vn xây dựng kiến trúc hệ thống mở rộng để đáp ứng tăng trưởng 10x mỗi năm như thế nào?
OutProcessor
Chúng ta cần phải theo dõi những RPC call đang diễn ra để user code nhận kết quả khi chúng đến. Ta có thể handle một kết quả theo hai cách. Thông qua handle không đồng bộ (tương tự Asio), hoặc với một future.
Ta cần hai class cho việc này: một outgoing processor và một wrapper cho một RPC call duy nhất. Một class khác (Connection) để buộc các outgoing processor và incoming processor với nhau. Class này sẽ được giới thiệu sau.
namespace cz {
namespace rpc {
class BaseOutProcessor {
public:
virtual ~BaseOutProcessor() {}
protected:
template <typename R>
friend class Call;
template <typename L, typename R>
friend struct Connection;
template <typename F, typename H>
void commit(Transport& transport, uint32_t rpcid, Stream& data,
H&& handler) {
std::unique_lock<std::mutex> lk(m_mtx);
Header hdr;
hdr.bits.size = data.writeSize();
hdr.bits.counter = ++m_replyIdCounter;
hdr.bits.rpcid = rpcid;
*reinterpret_cast<Header*>(data.ptr(0)) = hdr;
m_replies[hdr.key()] = [handler = std::move(handler)](Stream * in,
Header hdr) {
using R = typename ParamTraits<
typename FunctionTraits<F>::return_type>::store_type;
if (in) {
if (hdr.bits.success) {
handler(Result<R>::fromStream((*in)));
} else {
std::string str;
(*in) >> str;
handler(Result<R>::fromException(std::move(str)));
}
} else {
// if the stream is nullptr, it means the result is being aborted
handler(Result<R>());
}
};
lk.unlock();
transport.send(data.extract());
}
void processReply(Stream& in, Header hdr) {
std::function<void(Stream*, Header)> h;
{
std::unique_lock<std::mutex> lk(m_mtx);
auto it = m_replies.find(hdr.key());
assert(it != m_replies.end());
h = std::move(it->second);
m_replies.erase(it);
}
h(&in, hdr);
}
void abortReplies() {
decltype(m_replies) replies;
{
std::unique_lock<std::mutex> lk(m_mtx);
replies = std::move(m_replies);
}
for (auto&& r : replies) {
r.second(nullptr, Header());
}
};
std::mutex m_mtx;
uint32_t m_replyIdCounter = 0;
std::unordered_map<uint32_t, std::function<void(Stream*, Header)>>
m_replies;
};
template <typename T>
class OutProcessor : public BaseOutProcessor {
public:
using Type = T;
template <typename F, typename... Args>
auto call(Transport& transport, uint32_t rpcid, Args&&... args) {
using Traits = FunctionTraits<F>;
static_assert(
std::is_member_function_pointer<F>::value &&
std::is_base_of<typename Traits::class_type, Type>::value,
"Not a member function of the wrapped class");
Call<F> c(*this, transport, rpcid);
c.serializeParams(std::forward<Args>(args)...);
return std::move(c);
}
protected:
};
// Specialization for when there is no outgoing RPC calls
// If we have no outgoing RPC calls, receiving a reply is therefore an error.
template <>
class OutProcessor<void> {
public:
OutProcessor() {}
void processReply(Stream&, Header) {
assert(0 && "Incoming replies not allowed for OutProcessor<void>");
}
void abortReplies() {}
};
} // namespace rpc
} // namespace cz
Nên nhớ `OutProcessor<T>` không cần một reference/pointer đến object của T. Nó chỉ cần biết type mà ta gửi RPC đến, để biết được phải dùng `Table<T>`.
Đây là ví dụ sử dụng OutProcessor:
//
// "trp" is a transport that sends data to a "Calculator" server
void testOutProcessor(Transport& trp) {
// A processor that calls RPCs on a "Calculator" server
OutProcessor<Calculator> outPrc;
// Handle with an asynchronous handler
outPrc.call<decltype(&Calculator::add)>(
trp, (int)Table<Calculator>::RPCId::add, 1.0, 2.0)
.async([](Result<double> res) {
printf("%2.0f\n", res.get()); // prints '3'
});
// Handle with a future
Result<double> res = outPrc.call<decltype(&Calculator::add)>(
trp, (int)Table<Calculator>::RPCId::add, 1.0, 3.0)
.ft().get();
printf("%2.0f\n", res.get()); // prints '4'
}
Một lần nữa, hơi quá dài dòng, vì tôi chưa giới thiệu tất cả code để áp dụng tối ưu. Nhưng chí ít, ta đã thấy được interface `OutProcessor<T>` và `Call` làm việc như thế nào. Thực thi `std::future` build đơn giản trên thực thi không đồng bộ.
InProcessor
Giờ đây ta có thể gửi một RPC và đợi kết quả, hãy xem thử ta cần gì ở bên kia. Phải làm gì khi server nhận được một RPC call.
Hãy tạo class `InProcessor<T>`. Trái với `OutProcessor<T>`, `InProcessor<T>` cần phải giữ một reference tới một object thuộc kiểu T. Điều này sảy ra khi nhận được RPC, nó có thể gọi requested method lên object đó, và gửi kết quả trở lại client.
namespace cz {
namespace rpc {
class BaseInProcessor {
public:
virtual ~BaseInProcessor() {}
};
template <typename T>
class InProcessor : public BaseInProcessor {
public:
using Type = T;
InProcessor(Type* obj, bool doVoidReplies = true)
: m_obj(*obj), m_voidReplies(doVoidReplies) {}
void processCall(Transport& transport, Stream& in, Header hdr) {
Stream out;
// Reuse the header as the header for the reply, so we keep the counter
// and rpcid
hdr.bits.size = 0;
hdr.bits.isReply = true;
hdr.bits.success = true;
auto&& info = Table<Type>::get(hdr.bits.rpcid);
#if CZRPC_CATCH_EXCEPTIONS
try {
#endif
out << hdr; // Reserve space for the header
info->dispatcher(m_obj, in, out);
#if CZRPC_CATCH_EXCEPTIONS
} catch (std::exception& e) {
out.clear();
out << hdr; // Reserve space for the header
hdr.bits.success = false;
out << e.what();
}
#endif
if (m_voidReplies || (out.writeSize() > sizeof(hdr))) {
hdr.bits.size = out.writeSize();
*reinterpret_cast<Header*>(out.ptr(0)) = hdr;
transport.send(out.extract());
}
}
protected:
Type& m_obj;
bool m_voidReplies = false;
};
template <>
class InProcessor<void> {
public:
InProcessor(void*) {}
void processCall(Transport&, Stream&, Header) {
assert(0 && "Incoming RPC not allowed for void local type");
}
};
} // namespace rpc
} // namespace cz
Define `CZRPC_CATCH_EXCEPTIONS` cho phép chúng ta tweak nếu ta muốn exception phía server được chuyển sang phía client.
Nếu việc sử dụng `InProcessor<T>` (và `Table<T>`) cho phép call RPC lên object (không biết gì về RPC hoặc netwrk). Ví dụ như, hay xem xét giả dụ sau:
oid calculatorServer() {
// The object we want to use for RPC calls
Calculator calc;
// The server processor. It will call the appropriate methods on 'calc' when
// an RPC is received
InProcessor<Calculator> serverProcessor(&calc);
while (true) {
// calls to serverProcessor::processCall whenever there is data
}
}
Object `Calculator` được dùng cho RPC không biết gì về RPC. `InProcessor<Calculator>` sẽ đảm nhiệm mọi nhiệm vụ liên quan. Từ đó ta không thể sử dụng các class bên thức ba cho PRC. Trong một số trường hợp, chung ta muốn class dùng cho RPC biết biết về RPC và/hoặc network. Ví dụ như, nếu bạn đang tạo một chat system, bạn sẽ khiến client gửi message (RPC call) đến server. Server cần biết client được kết nối đến thứ gì, để có thể truyền phát message.
Connection
Ta giờ đây đã có thể gửi nhận RPC. dù API có hơi dài một chút. Những template class `OutProcessor<T>` và `InProcessor<T>` sẽ xử lý những sự kiện sảy ra với data tại cả hai đầu kết nối. Vậy, hiện ta cần chính điều này. Một `Connection` để buộc mọi thức cần để gửi nhận dữ liệu, và đơn giản là API, về một chỗ.
namespace cz {
namespace rpc {
struct BaseConnection {
virtual ~BaseConnection() {}
//! Process any incoming RPCs or replies
// Return true if the connection is still alive, false otherwise
virtual bool process() = 0;
};
template <typename LOCAL, typename REMOTE>
struct Connection : public BaseConnection {
using Local = LOCAL;
using Remote = REMOTE;
using ThisType = Connection<Local, Remote>;
Connection(Local* localObj, std::shared_ptr<Transport> transport)
: localPrc(localObj), transport(std::move(transport)) {}
template <typename F, typename... Args>
auto call(Transport& transport, uint32_t rpcid, Args&&... args) {
return remotePrc.template call<F>(transport, rpcid,
std::forward<Args>(args)...);
}
static ThisType* getCurrent() {
auto it = Callstack<ThisType>::begin();
return (*it) == nullptr ? nullptr : (*it)->getKey();
}
virtual bool process() override {
// Place a callstack marker, so other code can detect we are serving an
// RPC
typename Callstack<ThisType>::Context ctx(this);
std::vector<char> data;
while (true) {
if (!transport->receive(data)) {
// Transport is closed
remotePrc.abortReplies();
return false;
}
if (data.size() == 0)
return true; // No more pending data to process
Header hdr;
Stream in(std::move(data));
in >> hdr;
if (hdr.bits.isReply) {
remotePrc.processReply(in, hdr);
} else {
localPrc.processCall(*transport, in, hdr);
}
}
}
InProcessor<Local> localPrc;
OutProcessor<Remote> remotePrc;
std::shared_ptr<Transport> transport;
};
} // namespace rpc
} // namespace cz
Từ đây ta có output processor, the input processor, và the transport. Để user code có thể xác định liệu mó có đang phục vụ RPC không. nó sử dụng một class Callstack. Class cho phép tạo RPC/network aware code nếu cần thiết, giống server class.
Vậy, ta đơn giản hóa API bằng cách nào? Vì `Connection<T>` có mọi thứ ta cần, một macro dưới dạng paramether của connection object, một function name và parameter, xử lý mọi thứ, bao gồm cả type check để nó không compile nếu là call không hợp lệ.
Khi dùng marcro này, cú pháp RPC call sẽ trở nên cực kỳ đơn giản. Hãy xe, thử client code ví dụ dưới đây:
// Some class to use for RPC calls
class MagicSauce {
public:
int func1(int a, int b) {
return a + b;
}
int func2(int a, int b) {
return a + b;
}
};
// Define RPC table for MagicSauce
#define RPCTABLE_CLASS MagicSauce
#define RPCTABLE_CONTENTS REGISTERRPC(func1)
#include "RPCGenerate.h"
// 'trp' is a fully functional transport
void test_Connection(std::shared_ptr<Transport> trp) {
Connection<void, MagicSauce> con(nullptr, trp);
// Doesn't compile : Invalid number of parameters
// CZRPC_CALL(con, func1, 1);
// Doesn't compile : Wrong type of parameters
// CZRPC_CALL(con, func1, 1, "hello");
// Doesn't compile: func3 is not a MagicSauce method
// CZRPC_CALL(con, func3, 1, 2);
// Doesn't compile: func2 is a method of MagicSauce, but not registered as
// RPC
// CZRPC_CALL(con, func2, 1, 2);
// Compiles fine, since everything is valid
CZRPC_CALL(con, func1, 1, 2).async([](Result<int> res) {
printf("%d\n", res.get()); // print '3'
});
}
Bạn có để ý là `void` và `nullptr` được sử dụng khi tạo kết nối với `Connection<void`, `MagicSauce> con(nullptr, trp);`? Thao tác này sẽ điều tiết bidirectional RPC (server cũng có thể call RPC lên một client). Trong trường hợp này, chúng ta không trong đợi vào client side RPC, vậy nên client side `Connection` object không có một local object để call RPC.
Một ví dụ đơn giản (nhưng không hoạt động) của bidirectional RPC:
class ChatClient;
class ChatServer {
public:
// Called by clients to post new messages
void msg(const char* msg);
void addNewClient(std::shared_ptr<Transport> trp);
private:
// Connection specifies both a LOCAL, and REMOTE object types
std::vector<std::unique_ptr<Connection<ChatServer, ChatClient>>> m_clients;
};
#define RPCTABLE_CLASS ChatServer
#define RPCTABLE_CONTENTS REGISTERRPC(msg)
#include "RPCGenerate.h"
class ChatClient {
public:
void onMsg(const char* msg);
};
#define RPCTABLE_CLASS ChatClient
#define RPCTABLE_CONTENTS REGISTERRPC(onMsg)
#include "RPCGenerate.h"
void ChatServer::msg(const char* msg) {
// Simply broadcast the message to all clients
for (auto&& c : m_clients) {
CZRPC_CALL(*c, onMsg, msg);
}
}
void ChatServer::addNewClient(std::shared_ptr<Transport> trp) {
auto con = std::make_unique<Connection<ChatServer, ChatClient>>(this, trp);
m_clients.push_back(std::move(con));
}
void ChatClient::onMsg(const char* msg) {
printf("%s\n", msg);
}
void test_ChatServer() {
ChatServer server;
while (true) {
// Wait for connections, and call ChatServer::addClient
}
}
// 'trp' is some fully functional transport connected to the ChatServer
void test_ChatClient(std::shared_ptr<Transport> trp) {
ChatClient client;
// In this case, we have a client side object to answer RPCs
Connection<ChatClient, ChatServer> con(&client, trp);
while (true) {
// call the 'msg' RPC whenever the user types something, like this:
CZRPC_CALL(con, msg, "some message");
// The server will call our client 'onMsg' when other clients send a
// message
}
}
Các parameter của `Connection` template trên server và client được đảo ngược. Khi nhận được dữ liệu, object `Connection` sẽ forward xử lý đến `InProcessor` nếu là một incoming RPC call, hoặc đến `OutProcessor` nếu đó là reply đến outgoing RPC call trước đó. Data flow của bidirectional RPC trông như thế này:
Improvements
Có một số phần được cố ý không đưa vào framework, để từ đó ứng dụng có thể quyết định thế nào là tốt nhất. Ví dụ:
Transport initialization
Giao diện transport rất đơn giản, nên nó không quá chú trọng vào hướng khởi tạo cụ thể hoặc xác định incoming data. Việc cung cấp một transport hiệu quả đến class `Connecttion` hoàn toàn phụ thuộc vào ứng dụng. Đây cũng là lý do tại sao tôi tránh hiển thị transport initialization, vì tôi phải trình bày một transport implementation hoạt động hoàn chỉnh.
Tại thời điểm viết bài, repo mã nguồn có một transport implementation dùng Boost Asio (hoặc Asio biệt lập)
Xác định disconnection
Như với initialization, có những đoạn code để xử lý và xác định shutdown.Ứng dụng sẽ có nhiệm vụ quyết định thao tác như thế nào với bất cứ custom implementation nó cung cấp.
Flutter là mobile UI framework của Google để tạo ra các giao diện native chất lượng cao trên iOS và Android trong khoảng thời gian ngắn. Flutter hoạt động với source code có sẵn, được sử dụng bởi các nhà phát triển và các tổ chức trên khắp thế giới, đồng thời nó open-source và miễn phí.
Clip giới thiệu về Flutter:
Sự vượt trội của Flutter
Flutter là một công cụ mới được cung cấp bởi Google cho phép các nhà phát triển xây dựng các ứng dụng đa nền tảng có thể được thực hiện trong các hệ thống khác nhau chẳng hạn như Android hay iOS chỉ với một codebase chung.
Công cụ này được được xây dựng trong C và C ++ và cung cấp một cơ chế rendering 2D, một funtional-reactive framework là React-inspired, và một tập hợp các Material Design widget. Nó hiện đang được distribute bản alpha: version 0.0.20, tuy vậy giai đoạn đầu của nó đã được cho phép để tạo ra interfacing phức tạp, thực hiện kết nối mạng và thậm chí quản lý tập tin.
Cách tiếp cận Flutter’s là khác nhau từ các solution khác, ví dụ Cordova chạy trên một WebView là HTML, CSS và Javascript. Không giống như những công cụ này, nó chỉ sử dụng Dart như một ngôn ngữ lập trình duy nhất. Dart là khá dễ dàng để tìm hiểu và nếu bạn có kiến thức Java, 75% của công việc được gần như hoàn tất và làm quen với Dart sẽ chỉ mất một vài ngày.
Phát triển ứng dụng nhanh chóng: Tính năng hot reload của Flutter giúp bạn nhanh chóng và dễ dàng thử nghiệm, xây dựng giao diện người dùng, thêm tính năng và sửa lỗi nhanh hơn. Trải nghiệm tải lại lần thứ hai, mà không làm mất trạng thái, trên emulator, simulator và device cho iOS và Android.
UI đẹp và biểu cảm: Thỏa mãn người dùng của bạn với các widget built-in đẹp mắt của Flutter theo Material Design và Cupertino (iOS-flavor), các API chuyển động phong phú, scroll tự nhiên mượt mà và tự nhận thức được nền tảng.
Framework hiện đại và reactive: Dễ dàng tạo giao diện người dùng của bạn với framework hiện đại, reactive của Flutter và tập hợp các platform, layout và widget phong phú. Giải quyết các thách thức giao diện người dùng khó khăn của bạn với các API mạnh mẽ và linh hoạt cho 2D, animation, gesture, hiệu ứng và hơn thế nữa.
Truy cập các tính năng và SDK native: Làm cho ứng dụng của bạn trở nên sống động với API của platform, SDK của bên thứ ba và native code. Flutter cho phép bạn sử dụng lại mã Java, Swift và ObjC hiện tại của mình và truy cập các tính năng và SDK native trên iOS và Android.
Phát triển ứng dụng thống nhất: Flutter có các công cụ và thư viện để giúp bạn dễ dàng đưa ý tưởng của mình vào cuộc sống trên iOS và Android. Nếu bạn chưa có kinh nghiệm phát triển trên thiết bị di động, thì Flutter là một cách dễ dàng và nhanh chóng để xây dựng các ứng dụng di động tuyệt đẹp. Nếu bạn là một nhà phát triển iOS hoặc Android có kinh nghiệm, bạn có thể sử dụng Flutter cho các View của bạn và tận dụng nhiều code Java / Kotlin / ObjC / Swift hiện có của bạn.