Home Blog Page 106

Top 10 Hệ quản trị cơ sở dữ liệu (DBMS) phổ biến

Danh sách 10 HỆ QUẢN TRỊ CƠ SỞ DỮ LIỆU tốt nhất !

Chào các bạn, đã từ lâu hệ quản trị cơ sở dữ liệu (Database Management System – DBMS) là một phần không thể thiếu trong các hệ thống thông tin có nhu cầu quản lý và trao đổi dữ liệu. Thậm chí là việc sử dụng hệ quản trị CSDL tốt và đúng cách còn rất quan trọng trong quá trình phát triển và mở rộng hệ thống.

Vậy nên, ở trong bài viết này TopDev sẽ cùng với các bạn tìm hiểu về hệ quản trị cơ sở dữ liệu và TOP 10 HỆ QUẢN TRỊ CƠ SỞ DỮ LIỆU đang được sử dụng rộng rãi nhất tính đến thời điểm hiện tại nhé!

Hệ quản trị cơ sở dữ liệu là gì?

Hệ quản trị cơ sở dữ liệu là gì?
Hệ quản trị cơ sở dữ liệu là gì?

Hệ quản trị cơ sở dữ liệu – Database Management System (DBMS) là một hệ thống được sử dụng để quản lý và điều hành cơ sở dữ liệu (CSDL). Bạn có thể thao tác thêm mới, sửa, xóa và truy vấn dữ liệu một cách hiệu quả và an toàn. DBMS đảm bảo dữ liệu được tổ chức tốt và dễ dàng truy xuất, đồng thời bảo mật dữ liệu và duy trì tính nhất quán.

Một số ứng dụng thực tiễn của DBMS trong doanh nghiệp

  • Quản lý Khách hàng (CRM – Customer Relationship Management): Các hệ thống CRM sử dụng DBMS để lưu trữ thông tin về khách hàng và tương tác của họ với công ty. Ví dụ như Salesforce sử dụng hệ quản trị cơ sở dữ liệu để lưu trữ thông tin khách hàng, lịch sử mua hàng, và dữ liệu liên quan để quản lý mối quan hệ khách hàng hiệu quả.
  • Hệ thống Quản lý Nhân sự (HRMS – Human Resource Management System): Các hệ thống quản lý nhân sự sử dụng DBMS để lưu trữ thông tin về nhân viên, bảng lương, quản lý thời gian làm việc và các dữ liệu nhân sự khác. Ví dụ như SAP ERP HR sử dụng hệ quản trị CSDL để quản lý thông tin nhân viên và quá trình tuyển dụng.
  • Hệ thống Quản lý Sản xuất và Tài nguyên (ERP – Enterprise Resource Planning): Các hệ thống ERP sử dụng DBMS để quản lý dữ liệu về quá trình sản xuất, vận hành kinh doanh, lưu trữ dữ liệu tài chính, và quản lý lô hàng. Ví dụ như Oracle ERP sử dụng hệ quản trị cơ sở dữ liệu để tích hợp các dữ liệu từ các phòng ban khác nhau của công ty.
  • Hệ thống Quản lý Kho (WMS – Warehouse Management System): Các hệ thống quản lý kho sử dụng DBMS để quản lý thông tin về vị trí hàng hóa trong kho, lượng tồn kho, và quản lý điều hành các quá trình nhập xuất hàng hóa. Ví dụ như Manhattan Associates sử dụng hệ quản trị cơ sở dữ liệu để quản lý và tối ưu hóa quá trình kho hàng.
  • Hệ thống Quản lý Tài chính (Financial Management System): Các hệ thống quản lý tài chính sử dụng DBMS để lưu trữ và quản lý dữ liệu tài chính, báo cáo tài chính, và quản lý chi phí. Ví dụ như QuickBooks sử dụng hệ quản trị cơ sở dữ liệu để lưu trữ thông tin tài chính và quản lý kế toán.

Cấu trúc của hệ quản trị cơ sở dữ liệu

Cấu trúc của một hệ quản trị CSDL bao gồm các thành phần chính sau đây:

Cấu trúc của hệ quản trị cơ sở dữ liệu
Cấu trúc của hệ quản trị cơ sở dữ liệu

Core DBMS Engine

Là trung tâm của hệ thống DBMS, chịu trách nhiệm xử lý các yêu cầu từ người dùng và quản lý dữ liệu. Nó bao gồm các module để phân tích và thực hiện các truy vấn SQL, xử lý các giao dịch, quản lý bộ nhớ và đọc/ghi dữ liệu vào bộ nhớ hoặc ổ đĩa.

Cơ sở dữ liệu (Database)

  • Bảng (Tables): Đây là thành phần chính trong cơ sở dữ liệu quan hệ (Relational Database). Mỗi bảng chứa dữ liệu có cấu trúc, được tổ chức thành các cột (fields) và hàng (rows). Mỗi cột đại diện cho một thuộc tính riêng biệt và mỗi hàng đại diện cho một bản ghi (record) cụ thể.
  • Mối quan hệ (Relationships): Đây là cách mà các bảng trong cơ sở dữ liệu liên kết với nhau thông qua các khóa ngoại (foreign keys) và khóa chính (primary keys). Mối quan hệ giúp cho việc truy xuất và thao tác dữ liệu giữa các bảng trở nên hiệu quả và logic.

Hệ quản trị cơ sở dữ liệu (DBMS)

  • Trình quản lý cơ sở dữ liệu (Database Manager): Là thành phần chịu trách nhiệm quản lý cơ sở dữ liệu, bao gồm các dịch vụ như lưu trữ, truy xuất, cập nhật, và bảo vệ dữ liệu. Trình quản lý cơ sở dữ liệu có thể hỗ trợ nhiều loại cơ sở dữ liệu khác nhau như quan hệ (Relational), đối tượng (Object), và các dạng khác.
  • Trình quản lý bảo mật (Security Manager): Đảm bảo rằng chỉ những người được cấp quyền mới có thể truy cập vào dữ liệu và thực hiện các thao tác. Quản lý bảo mật bao gồm xác thực người dùng, kiểm soát truy cập và mã hóa dữ liệu.
  • Trình quản lý dữ liệu (Data Manager): Xử lý các yêu cầu truy xuất dữ liệu từ người dùng và ứng dụng, đảm bảo rằng các truy vấn SQL được thực thi một cách hiệu quả và chính xác.
  • Trình điều khiển cơ sở dữ liệu (Database Driver): Là thành phần trung gian giúp các ứng dụng kết nối và giao tiếp với cơ sở dữ liệu. Điều khiển cơ sở dữ liệu thực hiện việc chuyển đổi giữa dữ liệu được lưu trữ trong cơ sở dữ liệu và dữ liệu được xử lý bởi ứng dụng.

Vùng lưu trữ (Storage Area)

  • File dữ liệu (Data Files): Đây là các tập tin vật lý hoặc phân vùng trên đĩa cứng được DBMS sử dụng để lưu trữ dữ liệu. Các bảng và dữ liệu trong cơ sở dữ liệu được lưu trữ trong các file này theo cấu trúc cụ thể của DBMS.
  • File nhật ký (Log Files): Được sử dụng để ghi lại các thay đổi dữ liệu và các hoạt động quản lý của hệ thống. Log files quan trọng trong việc phục hồi dữ liệu sau khi xảy ra sự cố và đảm bảo tính toàn vẹn của dữ liệu.

Người Sử Dụng (Users)

  • Người quản trị hệ thống (Database Administrators): Cài đặt và duy trì hệ thống cơ sở dữ liệu, quản lý bảo mật, và tối ưu hóa hiệu suất.
  • Lập trình viên (Developers): Phát triển ứng dụng và truy xuất dữ liệu từ cơ sở dữ liệu bằng các ngôn ngữ lập trình và truy vấn SQL.
  • Người dùng cuối (End Users): Truy xuất và thao tác với dữ liệu thông qua các ứng dụng được xây dựng trên nền tảng cơ sở dữ liệu.

Ứng Dụng (Applications)

  • Ứng dụng kinh doanh (Business Applications): Sử dụng dữ liệu từ cơ sở dữ liệu để hỗ trợ các quyết định kinh doanh và quản lý hoạt động.
  • Hệ thống phân tích dữ liệu (Analytics Systems): Phân tích và xử lý dữ liệu từ cơ sở dữ liệu để đưa ra các báo cáo, dự đoán và thống kê.

DBMS hoạt động ra sao?

DBMS hoạt động ra sao?
Hệ quản trị cơ sở dữ liệu hoạt động ra sao?
  1. Lưu trữ dữ liệu: DBMS quản lý cách dữ liệu được tổ chức và lưu trữ trong cơ sở dữ liệu. Dữ liệu thường được tổ chức dưới dạng bảng, trong đó mỗi bảng có các cột và hàng.
  2. Truy xuất dữ liệu: Người dùng có thể truy xuất dữ liệu từ cơ sở dữ liệu bằng các truy vấn SQL (Structured Query Language) để lấy thông tin cụ thể hoặc thực hiện tính toán trên dữ liệu.
  3. Cập nhật dữ liệu: DBMS cho phép người dùng cập nhật dữ liệu bằng cách thêm mới, sửa đổi hoặc xóa bỏ các bản ghi trong cơ sở dữ liệu.
  4. Quản lý bảo mật: DBMS bảo vệ dữ liệu bằng cách thiết lập quyền truy cập, mã hóa dữ liệu và xác thực người dùng.
  5. Quản lý hiệu suất: DBMS quản lý tối ưu hóa hiệu suất bằng cách tổ chức dữ liệu và tối ưu hóa các truy vấn để đảm bảo thời gian phản hồi nhanh nhất.

Ví dụ về hoạt động của DBMS:

Giả định: Bạn là một quản lý của một cửa hàng bán lẻ và bạn sử dụng một hệ thống quản lý cơ sở dữ liệu để lưu trữ thông tin về các sản phẩm, khách hàng và đơn đặt hàng.

1. Lưu trữ dữ liệu:

  • Cơ sở dữ liệu: Hệ thống DBMS của bạn chứa các bảng dữ liệu, bao gồm bảng sản phẩm, bảng khách hàng và bảng đơn đặt hàng.
  • Bảng sản phẩm: Lưu trữ thông tin về các sản phẩm như mã sản phẩm, tên sản phẩm, giá bán, số lượng tồn kho, v.v.
  • Bảng khách hàng: Lưu thông tin cá nhân của khách hàng như tên, địa chỉ, email, số điện thoại, v.v.
  • Bảng đơn đặt hàng: Lưu thông tin về các đơn đặt hàng bao gồm ngày đặt hàng, thông tin khách hàng, danh sách các sản phẩm được đặt hàng, v.v.

2. Truy xuất dữ liệu:

  • Người dùng: Bạn, nhân viên bán hàng, muốn xem thông tin chi tiết về một sản phẩm cụ thể.
  • Truy vấn SQL: Bạn sử dụng câu lệnh SQL để truy xuất dữ liệu từ bảng sản phẩm: SELECT * FROM Products WHERE ProductID = 'P001';
  • DBMS: Hệ thống DBMS phân tích và thực thi câu lệnh SQL này.
  • Kết quả: Hệ thống trả về thông tin chi tiết về sản phẩm có mã P001, bao gồm tên sản phẩm, giá bán, số lượng tồn kho, v.v.

3. Cập nhật dữ liệu:

  • Người dùng: Bạn muốn cập nhật số lượng tồn kho của sản phẩm P001 sau khi bán hàng.
  • Câu lệnh SQL: Bạn sử dụng câu lệnh để cập nhật dữ liệu: UPDATE Products SET StockQuantity = StockQuantity - 1 WHERE ProductID = 'P001';
  • DBMS: Hệ thống DBMS xác nhận và thực hiện cập nhật dữ liệu trong bảng sản phẩm.
  • Kết quả: Số lượng tồn kho của sản phẩm P001 giảm đi 1 đơn vị sau khi cập nhật thành công.

4. Bảo vệ dữ liệu:

  • Quản trị viên hệ thống: Đảm bảo rằng chỉ những người có quyền truy cập được phép truy xuất và cập nhật dữ liệu trong cơ sở dữ liệu.
  • DBMS: Hệ thống DBMS quản lý các quyền truy cập và đảm bảo tính bảo mật của dữ liệu bằng cách sử dụng các cơ chế xác thực và phân quyền.

5. Quản lý giao dịch:

  • Người dùng: Bạn nhận được nhiều đơn đặt hàng cùng một lúc và muốn đảm bảo rằng mỗi đơn đặt hàng được xử lý một cách đáng tin cậy.
  • DBMS: Hệ thống DBMS quản lý các giao dịch bằng cách thực hiện các thao tác đọc và ghi dữ liệu một cách nhất quán và đồng bộ.
  • Ghi nhật ký (Log): Hệ thống DBMS lưu trữ các hoạt động ghi nhật ký để đảm bảo có thể phục hồi dữ liệu trong trường hợp có lỗi xảy ra.

6. Sao lưu và phục hồi dữ liệu:

  • Quản trị viên hệ thống: Thực hiện sao lưu định kỳ của cơ sở dữ liệu để đảm bảo an toàn dữ liệu.
  • DBMS: Cung cấp các công cụ để sao lưu và phục hồi dữ liệu khi cần thiết, đảm bảo tính toàn vẹn của dữ liệu.

Phân loại hệ quản trị cơ sở dữ liệu

Hệ quản trị cơ sở dữ liệu có thể được phân loại dựa trên nhiều tiêu chí khác nhau, bao gồm cấu trúc dữ liệu, kiểu mối quan hệ, và mục đích sử dụng.

1. Theo cấu trúc dữ liệu

  • Hệ quản trị cơ sở dữ liệu quan hệ (Relational Database Management System – RDBMS): Lưu trữ dữ liệu trong các bảng có các mối quan hệ được xác định trước, dựa trên lý thuyết mối quan hệ. Ví dụ: MySQL, PostgreSQL, Oracle Database.
  • Hệ quản trị cơ sở dữ liệu phi quan hệ (Non-relational DBMS): Lưu trữ dữ liệu theo các cấu trúc dữ liệu phi quan hệ, chẳng hạn như các tài liệu, cặp khóa-giá trị, hoặc đồ thị. Ví dụ: MongoDB (tài liệu), Redis (cặp khóa-giá trị), Neo4j (đồ thị).

2. Theo mục đích sử dụng:

  • OLTP (Online Transaction Processing): Dùng để xử lý các giao dịch trực tuyến, đảm bảo hiệu suất cao và thời gian phản hồi nhanh. Ví dụ: Oracle Database, MySQL.
  • OLAP (Online Analytical Processing): Dùng để phân tích và truy vấn dữ liệu phức tạp, hỗ trợ các hoạt động phân tích kinh doanh và báo cáo. Ví dụ: Microsoft SQL Server, PostgreSQL.

3. Theo phương pháp lưu trữ:

  • Hệ quản trị cơ sở dữ liệu dạng dòng (Row-oriented DBMS): Lưu trữ dữ liệu dưới dạng dòng, thích hợp cho các ứng dụng thường xuyên truy cập vào toàn bộ dữ liệu của từng bản ghi. Ví dụ: PostgreSQL, Oracle Database.
  • Hệ quản trị cơ sở dữ liệu dạng cột (Column-oriented DBMS): Lưu trữ dữ liệu dưới dạng cột, phù hợp cho các ứng dụng cần truy vấn dữ liệu một cách phân tích và thống kê. Ví dụ: Apache Cassandra, Google Bigtable.

4. Theo tính năng đặc biệt:

  • Hệ quản trị cơ sở dữ liệu nhớ đệm (In-memory DBMS): Lưu trữ và xử lý dữ liệu trực tiếp trong bộ nhớ, giúp tăng tốc độ truy xuất dữ liệu. Ví dụ: Redis, Memcached.
  • Hệ quản trị cơ sở dữ liệu đám mây (Cloud DBMS): Cung cấp dịch vụ quản trị cơ sở dữ liệu trên nền tảng đám mây, giúp giảm chi phí vận hành và quản lý cơ sở dữ liệu. Ví dụ: Amazon RDS, Microsoft Azure SQL Database.

Top 10 hệ quản trị cơ sở dữ liệu phổ biến

#1. MySQL

MySQL - Hệ quản trị cơ sở dữ liệu phổ biến nhất hiện nay
MySQL – Hệ quản trị cơ sở dữ liệu phổ biến nhất hiện nay

Được phát hành lần đầu tiên vào năm 1995 và MySQL đang được phát triển bởi tập đoàn Oracle, có thể nói MySQL là một trong những hệ quản trị cơ sở dữ liệu được sử dụng phổ biến nhất hiện nay.

MySQL được viết bởi ngôn ngữ C/C++ nên có hiệu năng cao, dễ sử dụng, có tính khả chuyển và hỗ trợ nhiều nền tảng hệ điều hành (Windows, Linux, MacOS).

MySQL cũng tương thích với nhiều ngôn ngữ lập trình phổ biến như Java, Python, NodeJS…

MySQL được xây dựng theo kiến trúc Client-Server, bao gồm một máy chủ đa luồng hỗ trợ nhiều máy khách khác nhau.

Các bạn có thể đọc thêm tài liệu về MySQL tại đây: https://www.mysql.com/ hoặc https://www.mysqltutorial.org/

#2. MariaDB

Một trong các hệ quản trị cơ sở dữ liệu phổ biến hiện nay là MariaDB
Một trong các hệ quản trị cơ sở dữ liệu phổ biến hiện nay là MariaDB

MariaDB thực chất là một nhánh được tách ra từ quá trình phát triển MySQL với mục đích phi thương mại, có nghĩa là nó sẽ hoàn toàn miễn phí cho người sử dụng.

Cũng như MySQL thì MariaDB được viết bằng ngôn ngữ C/C++, Perl nhưng được tối ưu khá nhiều về mặt hiệu năng truy vấn dữ liệu.

Hiện tại thì MariaDB cũng hỗ trợ hầu hết các hệ điều hành phổ biến như Windows, Linux và MacOS… Nên anh em lập trình không phải lo về vấn đề môi trường để sử dụng MariaDB nha.

Do hoàn toàn miễn phí nên các bạn có thể tham khảo mã nguồn mở của MariaDB tại đây: https://github.com/MariaDB/server

Ngoài ra thì các bạn cũng có thể đọc thêm về các bài viết hướng dẫn của MariaDB tại đây nữa nhé: https://www.mariadbtutorial.com/

Xem thêm: Điểm khác biệt của MariaDB và MySQL

#3. Oracle

một số hệ quản trị cơ sở dữ liệu hiện nay

Là một hệ quản trị cơ sở dữ liệu đa mô hình do công ty phần mềm thứ 2 thế giới là Oracle xây dựng và phát triển.

Tất nhiên là chúng ta phải trả phí để có thể sử dụng được hệ quản trị CSDL này, thậm chí là chi phí khá đắt đối với các hệ thống lớn.

Oracle được viết bằng ngôn ngữ C/C++, Assembly nên cũng cho hiệu năng rất cao. Và tất nhiên thì Oracle cũng hỗ trợ hầu hết các nền tảng hệ điều hành hiện nay như Windows, Linux, MacOS

Oracle database thường được sử dụng để chạy các công việc liên quan đến xử lý giao dịch trực tuyến (OLTP), kho dữ liệu (DW) hoặc là hỗn hợp (OLTP và DW).

Các bạn có thể mua các gói dịch vụ tại các nhà cung cấp được Oracle ủy quyền.

Về tài liệu tham khảo thì các bạn có thể xem các hướng dẫn về Oracle tại đây: https://www.oracletutorial.com/

#4. MongoDB

mongoDB - Hệ quản trị cở dữ liệu phi quan hệ (NoSQL)
mongoDB – Hệ quản trị cở dữ liệu phi quan hệ (NoSQL)

Ở trên thì chúng ta đã đề cập đến 3 kiểu CƠ SỞ DỮ LIỆU CÓ QUAN HỆ, tiếp theo, chúng ta sẽ đến với MongoDB – đây là một hệ quản trị cở dữ liệu phi quan hệ (NoSQL)

Hiện tại thì MongoDB cũng là một hệ quản trị cơ sở dữ liệu mã nguồn mở, tức là nó miễn phí. Các bạn có thể tham khảo mã nguồn của MongoDB tại đây: https://github.com/mongodb/mongo

MongoDB được viết bằng nhiều ngôn ngữ lập trình khác nhau như C/C++, Go, JavaScript, Python và cũng hỗ trợ trên hầu hết các nền tảng hệ điều hành (Windows, Linux, MacOS..).

Đặc điểm của HỆ QUẢN TRỊ DỮ LIỆU PHI QUAN HỆ là dữ liệu được lưu lại dưới dạng JSON (JavaScript Object Notation) và gần như là các các bản ghi không nhất thiết phải giống nhau về cấu trúc.

Các bạn có thể đọc thêm tài liệu về MongoDB tại đây: https://www.mongodb.com/3

#5. PostgreSQL

các hệ qtcsdl phổ biến hiện nay là

Tiếp tục với một hệ quản trị cơ sở dữ liệu có quan hệ mã nguồn mở đó là PostgreSQL.

PostgreSQL được phát triển bởi khoa điện toán của trường đại học California tại Berkeley. PostgreSQL mở đầu nhiều khái niệm quan trọng cho các hệ quản trị cơ sở dữ liệu thương mại sau này mới có.

Được viết hoàn toàn bằng ngôn ngữ lập trình C nên tốc độ cũng như hiệu năng của PostgreSQL là rất tốt.

Đồng thời thì nó cũng hỗ trợ nhiều nền tảng như Windows, Linux, MacOS nên PostgreSQL đang ngày càng trở nên phổ biến hơn.

Với ưu điểm hỗ trợ nhiều truy vấn phức tạp thì PostgreSQL cũng đang là một ứng cử viên rất tiềm năng cho các hệ thống lớn sau này.

Các bạn có thể đọc thêm tài liệu về PostgreSQL tại đây: https://www.postgresqltutorial.com/

#6. Microsoft SQL Server

một số hệ quản trị cơ sở dữ liệu
Microsoft SQL Server DBMS

Là một hệ quản trị cơ sở dữ liệu quan hệ được xây dựng và phát triển bởi Microsoft, với phiên bản đầu tiên ra đời vào năm 1989 (SQL Server 1.0) đến nay đã là phiên bản SQL Server 2019, và cũng là phiên bản ổn định nhất.

Microsoft SQL Server được viết bằng ngôn ngữ C/C++ nên hiệu năng cũng như tốc độ truy vấn rất tốt.

Ban đầu thì Microsoft SQL Server chỉ hỗ trợ hệ điều hành Windows, nhưng sau này nó đã được phát triển để hỗ trợ trên hầu hết các nền tảng hệ điều hành Linux và MacOS.

Tuy không phải là một hệ quản trị dữ liệu mã nguồn mở, song Microsoft SQL được đánh giá khá cao là “tiền nào của nấy”.

Các bạn có thể download Microsoft SQL Server tại đây https://www.microsoft.com/en-us/sql-server/sql-server-downloads

#7. Redis

các hệ quản trị csdl

Redis (viết tắt của cụm từ REmote DIctionary Server) là một mã nguồn mở được sử dụng để lưu trữ dữ liệu có cấu trúc, có thể được sử dụng như một Database, bộ nhớ cache hoặc là một Message Broker.

Redis lưu trữ dữ liệu dưới dạng Key-Value, hỗ trợ việc sắp xếp, query, backup dữ liệu lên đĩa cứng để cho phép bạn có thể khôi phục hệ thống khi gặp sự cố.

Một vài kiểu dữ liệu trong Redis như String, List, Set, Hash…

Có thể nói Redis là một sự lựa chọn tuyệt vời khi cần đến một Server lưu trữ dữ liệu đòi hỏi tính mở rộng cao, chia sẻ nhiều tiến trình, nhiều ứng dụng và nhiều server khác nhau.

#8. Elasticsearch

một số hệ quản trị csdl hiện nay

Chính xác thì Elasticsearch là một công cụ tìm kiếm mã nguồn mở. Elasticsearch cung cấp bộ máy tìm kiếm dạng phân tán, có đầy đủ công cụ với giao diện web HTTP hỗ trợ dữ liệu kiểu JSON.

Elasticsearch được viết bằng ngôn ngữ lập trình Java và nó được ra đời vào năm 2016. Các bạn có thể tham khảo mã nguồn của Elasticsearch tại đây: https://github.com/elastic/elasticsearch

Hiện tại thì Elasticsearch hỗ trợ hầu hết các hệ điều hành Window, Linux, MacOS

Các bạn có thể download Elasticsearch tại đây nhé: https://www.elastic.co/downloads/

#9. Firebase

phần mềm hệ quản trị cơ sở dữ liệu
phần mềm hệ quản trị cơ sở dữ liệu Firebase

Được xây dựng và phát triển bởi Google, Firebase là một dịch vụ cơ sở dữ liệu dựa trên nền tảng công nghệ điện toán đám mây.

Sự ra đời của Firebase với mục đích hỗ trợ cho các lập trình giảm thiểu thao tác với cơ sở dữ liệu, từ đó tập trung vào việc phát triển ứng dụng hơn.

Với hệ thống server mạnh mẽ, sự tiện lợi cũng như việc bảo mật cực tốt đã giúp Firebase trở thành một trong những cái tên được nhiều nhà phát triển lựa chọn để xây dựng các hệ thống lớn.

Có thể kể đến một vài ưu điểm của Firebase như sau:

  • Chỉ việc tạo tài khoản và sử dụng.
  • Tốc độ phát triển nhanh.
  • Được hậu thuẫn bởi ông lớn Google.
  • Hỗ trợ các công nghệ mới (Machine Learning (học máy), AI)
  • Khả năng realtime (thời gian thực).
  • Tự động sao lưu vào khôi phục…

#10. SQLite

các hệ qtcsdl phổ biến hiện nay là?

SQLite là một hệ thống cơ sở dữ liệu quan hệ nhỏ, nó có đặc điểm là có thể tích hợp vào bên trong các trình ứng dụng khác.

SQLite được viết và phát triển bởi D. Richard Hipp sử dụng ngôn ngữ lập trình C, với mục đích không cần yêu cầu quản trị cơ sở dữ liệu mà vẫn có thể vận hành ứng dụng.

Chúng ta có thể liên kết SQLite tĩnh hoặc động tới ứng dụng, chỉ khoảng 400KB với cấu hình đầy đủ hoặc 259KB nếu bỏ qua một số tính năng tùy chọn thì SQLite thực sự rất nhẹ để tích hợp vào ứng dụng.

Các bạn có thể tham khảo các bài hướng dẫn về SQLite tại đây: https://www.sqlitetutorial.net/

Vậy là trong bài viết này mình đã cùng với các bạn điểm qua khái niệm Hệ quản trị CSDL và top 10 DBMS được sử dụng phổ biến nhất hiện nay rồi nhé.

Ngoài những hệ quản trị cơ sở dữ liệu mình đã liệt kê bên trên ra, nếu bạn còn biết thêm DBMS nào khác thì có thể comment ở phần bình luận để anh em cùng tìm hiểu nha.

Nguồn tham khảo: blogchiasekienthuc.com

Xem thêm Việc làm it cho các Developer hấp dẫn trên TopDev

Viết React Code sạch hơn như thế nào? (Phần 1)

clean react code
Viết React Code sạch hơn như thế nào? (Phần 1)

Tác giả: Reed Barger

Là một React developer, bất cứ ai đều muốn có thể viết ra những code sạch hơn, đơn giản và dễ đọc nhất có thể. Bài viết dưới đây sẽ cung cấp cho các React devs những cách hiệu quả nhất để clean code.

clean code react
Clean code React như thế nào?

1. Sử dụng JSX shorthands

Làm cách nào để bạn chuyển một giá trị true cho một giá trị đã cho? Trong ví dụ bên dưới đang sử dụng showTitle để hiển thị tiêu đề ứng dụng trong thành phần Navbar.

// src/App.js

export default function App() {
  return (
    <main>
      <Navbar showTitle={true} />
    </main>
  );
}

function Navbar({ showTitle }) {
  return (
    <div>
      {showTitle && <h1>My Special App</h1>}
    </div>
  )
}

Chúng ta có cần đặt thành showTitle Boolean một cách rõ ràng true không? Không! Một cách viết nhanh cần nhớ là bất kỳ phần mềm hỗ trợ nào được cung cấp trên một thành phần đều có giá trị mặc định là true.

Vì vậy, nếu thêm phần hỗ trợ showTitle trên Navbar, phần tử tiêu đề sẽ được hiển thị:

// src/App.js

export default function App() {
  return (
    <main>
      <Navbar showTitle />
    </main>
  );
}

function Navbar({ showTitle }) {
  return (
    <div>
      {showTitle && <h1>My Special App</h1>} // title shown!
    </div>
  )
}

Một cách viết tắt hữu ích khác cần ghi nhớ liên quan đến việc chuyển các chuỗi props. Khi truyền một giá trị prop là một chuỗi, bạn không cần phải đặt nó trong dấu ngoặc nhọn. Nếu đang đặt tiêu đề cho Navbar của mình, với title prop, chúng ta chỉ có thể đưa giá trị của nó vào dấu ngoặc kép:

// src/App.js

export default function App() {
  return (
    <main>
      <Navbar title="My Special App" />
    </main>
  );
}

function Navbar({ title }) {
  return (
    <div>
      <h1>{title}</h1>
    </div>
  )
}

2. Di chuyển những code không liên quan vào một file chung

Cách dễ nhất và quan trọng nhất để viết code React sạch hơn là làm tốt việc trừu tượng hóa code thành các thành phần React riêng biệt. Ứng dụng đang hiển thị một thành phần Navbar. Chúng ta đang lặp lại một loạt các bài đăng có .map() và hiển thị tiêu đề của chúng trên trang.

// src/App.js

export default function App() {
  const posts = [
    {
      id: 1,
      title: "How to Build YouTube with React"
    },
    {
      id: 2,
      title: "How to Write Your First React Hook"
    }
  ];

  return (
    <main>
      <Navbar title="My Special App" />
      <ul>
        {posts.map(post => (
          <li key={post.id}>
            {post.title}
          </li>
        ))}
      </ul>
    </main>
  );
}

function Navbar({ title }) {
  return (
    <div>
      <h1>{title}</h1>
    </div>
  );
}

Làm thế nào để có thể viết những code này sạch hơn? Tại sao không tóm tắt những đoạn code đang lặp lại – các bài đăng – và hiển thị chúng trong một thành phần riêng biệt, được gọi là FeaturedPosts.

// src/App.js

export default function App() {
 return (
    <main>
      <Navbar title="My Special App" />
      <FeaturedPosts />
    </main>
  );
}

function Navbar({ title }) {
  return (
    <div>
      <h1>{title}</h1>
    </div>
  );
}

function FeaturedPosts() {
  const posts = [
    {
      id: 1,
      title: "How to Build YouTube with React"
    },
    {
      id: 2,
      title: "How to Write Your First React Hook"
    }
  ];

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

3. Tạo các file riêng biệt cho từng thành phần

Tương tự như cách trừu tượng hóa code thành các phần riêng biệt để làm cho ứng dụng dễ đọc hơn, để làm cho các file ứng dụng dễ đọc hơn, chúng ta có thể đặt từng thành phần vào mỗi file riêng biệt. Điều này có nghĩa là mỗi file chỉ chịu trách nhiệm cho một thành phần và không có sự nhầm lẫn thành phần đến từ đâu nếu chúng ta muốn sử dụng lại nó trên ứng dụng của mình.

// src/App.js
import Navbar from './components/Navbar.js';
import FeaturedPosts from './components/FeaturedPosts.js';

export default function App() {
  return (
    <main>
      <Navbar title="My Special App" />
      <FeaturedPosts />
    </main>
  );
}
// src/components/Navbar.js

export default function Navbar({ title }) {
  return (
    <div>
      <h1>{title}</h1>
    </div>
  );
}
// src/components/FeaturedPosts.js

export default function FeaturedPosts() {
  const posts = [
    {
      id: 1,
      title: "How to Build YouTube with React"
    },
    {
      id: 2,
      title: "How to Write Your First React Hook"
    }
  ];

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

Ngoài ra, bằng cách chia nhỏ thành từng file riêng biệt như thế này sẽ tránh việc khiến một file trở nên quá cồng kềnh.

  3 bước tối ưu hiệu năng React App bằng các API mới của React
  5 sai lầm thường thấy khi viết react component

4. Chuyển chức năng được chia sẻ vào React hooks

Xem xét các component FeaturedPosts, thay vì hiển thị dữ liệu bài đăng tĩnh, nếu bạn muốn tìm nạp dữ liệu bài đăng của mình từ một API. Bạn có thể xem kết quả bên dưới cho điều đó

// src/components/FeaturedPosts.js

import React from 'react';

export default function FeaturedPosts() {
  const [posts, setPosts] = React.useState([]);  	
    
  React.useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts')
      .then(res => res.json())
      .then(data => setPosts(data));
  }, []);

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

Tuy nhiên, điều gì sẽ xảy ra nếu thực hiện yêu cầu này đối với dữ liệu trên nhiều thành phần?

Giả sử ngoài một FeaturedPosts component, bạn muốn tạo một thành phần chỉ có tên là Posts với dữ liệu cũng giống như vậy. Chúng tôi sẽ phải sao chép logic đã sử dụng để tìm nạp dữ liệu của mình và paste vào trong thành phần đó.

Để tránh phải làm điều đó, tại sao chúng ta không sử dụng một React hook mới mà chúng ta có thể gọi useFetchPosts

// src/hooks/useFetchPosts.js

import React from 'react';

export default function useFetchPosts() {
  const [posts, setPosts] = React.useState([]);  	
    
  React.useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts')
      .then(res => res.json())
      .then(data => setPosts(data));
  }, []);

  return posts;
}

Khi đã tạo hook này trong một file ‘hooks’ chuyên biệt, bạn có thể sử dụng lại nó trong bất kỳ thành phần nào

// src/components/FeaturedPosts.js

import useFetchPosts from '../hooks/useFetchPosts.js';

export default function FeaturedPosts() {
  const posts = useFetchPosts()

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

Bài viết được phỏng dịch theo bài viết gốc tại freecodecamp.org

Có thể bạn quan tâm:

Xem thêm Việc làm Developer hấp dẫn trên TopDev

Cấu trúc dữ liệu từ điển Dictionary trong Python

Cấu trúc dữ liệu từ điển Dictionary trong Python

Bài viết được sự cho phép của tác giả Kien Dang Chung

Trong Python có tới 4 kiểu cấu trúc dữ liệu là List, Tuple, Set và Dictionary. Trong các bài trước chúng ta đã lần lượt làm quen với các kiểu dữ liệu này và còn lại Dictionary, một cấu trúc rất hay dùng trong Python. Trong bài viết này, cùng TopDev tìm hiểu về cấu trúc dữ liệu từ điển Dictionary trong Python.

Từ điển – Dictionary trong Python

Từ điển dữ liệu (Dictionary) còn được gọi là mảng liên kết (associative array) trong một số ngôn ngữ lập trình, là một dạng danh sách như bạn đã được tìm hiểu. Có một điểm khác là các phần tử trong danh sách được truy xuất thông qua vị trí thì phần tử trong từ điển được truy xuất qua khóa (key). Bạn có thể định nghĩa khóa này, nó có thể là một chuỗi hoặc số nhưng nó phải là duy nhất trong từ điển.

Tại sao vậy? Chúng ta liên tưởng đến nhưng tình huống thực tế như, hai người có cùng một số điện thoại, vậy khi gọi đến biết ai là người nghe máy. Hai người có cùng một số tài khoản, vậy biết chuyển khoản cho ai bây giờ? Chính vì vậy, khóa (key) trong từ điển phải là duy nhất.

Vậy kiểu dữ liệu Dict trong Python là gì?

Kiểu dữ liệu Dict (viết tắt của Dictionary) trong Python là một kiểu dữ liệu lưu trữ các cặp key-value, tương tự như List và Tuple. Tuy nhiên, các giá trị trong Dict không được sắp xếp theo một trật tự cụ thể nào.

Từ điển trong Python cũng có thể tưởng tượng giống như một cuốn từ điển Anh – Việt chẳng hạn. Bạn muốn tra từ “python” – tương ứng với khóa trong Dictionary, bạn sẽ có được phần diễn giải chính là giá trị trong Dictionary. Vậy từ điển bao gồm rất nhiều các phần tử mà mỗi phần tử đi theo cặp khóa – giá trị (key-value).

  Biến và kiểu dữ liệu cơ bản trong Python

Cách sử dụng Dictionary trong Python

Khai báo Dictionary

Dictionary trong Python được khai báo với cặp dấu ngoặc nhọn {} bao ngoài giống như với Set. Bên trong các phần tử theo cặp (khóa và giá trị) được phân tách bởi dấu phẩy, phân tách giữa khóa và giá trị của từng phần tử bởi dấu hai chấm. Cú pháp chung như sau:

dictionary_name = {key_1: value_1, key_2: value_2, ..., key_n: value_n}

Ví dụ:

friend_ages = {"Rolf": 24, "Adam": 30, "Anne": 27}
print(friend_ages["Adam"]) # Kết quả là 30

Chúng ta cũng có thể sử dụng hàm có sẵn dict() để khai báo một Dictionary.

friend_ages = dict({"Rolf": 24, "Adam": 30, "Anne": 27})
# Hoặc
friend_ages = dict([("Rolf", 24), ("Adam", 30), ("Anne", 27)])

Chú ý:

  • Khóa của Dictionary có thể là chuỗi hoặc số, khóa này là duy nhất ở từng cấp.
  • Giá trị của Dictionary có thể chuỗi, số hoặc các cấu trúc List, Tuple, Set hoặc thậm chí là Dictionary.

Một ví dụ khai báo Dictionary với giá trị phức tạp:

friends = {
  "Rolf": {
    "phone": "09382993433",
    "address": "Boston, America",
    "age": 30
  },
  "Bob": {
    "phone": "03099483832",
    "address": "London, England",
    "age": 33
  },
  "Anne": {
    "phone": "00393982224",
    "address": "Texas, America",
    "age": 25
  }
}

Truy xuất giá trị

Dictionary có thể truy xuất giá trị từng phần tử thông qua các khóa theo cú pháp:

dictionary_name[key_x]

Ví dụ, trong từ điển friends ở trên, chúng ta có thể truy xuất thông tin (giá trị) của Bob (khóa) như sau:

print(friends["Bob"]) # Kết quả là {'phone': '03099483832', 'address': 'London, England', 'age': 33}

Chú ý, mỗi phần tử trong Dictionary friends này lại là một Dictionary nên chúng ta có thể truy xuất tiếp đến tuổi tuổi của Bob như sau:

print(friends["Bob"]["age"]) # Kết quả là 33

Khi truy xuất đến các phần tử trong Dictionary, nếu khóa không tồn tại, chương trình sẽ báo lỗi KeyError.

print(friends["FirebirD"]) # Kết quả lỗi KeyError: "FirebirD"

Do vậy trước khi truy xuất giá trị một phần tử trong từ điển, chúng ta cần kiểm tra xem khóa có tồn tại hay không với toán tử in.

print("FirebirD" in friends) # Kết quả là False, FirebirD không có trong danh sách friends.

>>> Xem thêm: Kiểu dữ liệu chuỗi và định dạng chuỗi trong Python

Thay đổi giá trị

Dictionary trong Python có thể thay đổi thêm bớt các phần tử, thay đổi giá trị của các phần tử hiện có.

Thêm và cập nhật phần tử

Dictionary truy xuất thông qua khóa, do vậy khi gán giá trị cho một phần tử:

  • Khóa tồn tại thì giá trị phần tử được cập nhật.
  • Khóa không tồn tại thì Dictionary được thêm một phần tử có khóa và giá trị trong câu lệnh.
friend_ages = {"Rolf": 24, "Adam": 30, "Anne": 27}

# Thay đổi giá trị
friend_ages["Adam"] = 31

# Thêm một phần tử
friend_ages["Firebird"] = 37

print(friends) # Kết quả là {'Rolf': 24, 'Adam': 31, 'Anne': 27, 'Firebird': 37}

Ngoài ra chúng ta có thể sử dụng phương thức .update() để thêm hoặc chỉnh sửa giá trị nhiều phần tử vào từ điển. Trong ví dụ tiếp theo, chúng ta vừa thực hiện sửa tuổi của Rolf thành 25 và thêm một phần tử mới vào từ điển.

friend_ages = {"Rolf": 24, "Adam": 30, "Anne": 27}
new_friend_ages = {"Rolf": 25, "FirebirD": 37}
friend_ages.update(new_friend_ages)
print(friend_ages) # Kết quả là {'Rolf': 25, 'Adam': 30, 'Anne': 27, 'FirebirD': 37}

Xóa phần tử khỏi từ điển

Cũng giống như List, Dictionary có rất nhiều các phương thức có sẵn để có thể xóa một phần tử khỏi từ điển như .clear(), .pop(), .popitem() hoặc sử dụng từ khóa del.

.clear() Xóa toàn bộ các phần tử khỏi từ điển.

friend_ages = {"Rolf": 24, "Adam": 30, "Anne": 27}
friend_ages.clear()
print(friend_ages) # Kết quả là {}

.popitem() Xóa một phần tử ngẫu nhiên

.pop() Xóa một phần tử với khóa cho trước khỏi từ điển.

friend_ages = {"Rolf": 24, "Adam": 30, "Anne": 27}
friend_ages.pop("Adam")
print(friend_ages) # Kết quả là {'Rolf': 24, 'Anne': 27}

del Xóa một phần tử hoặc xóa hẳn biến chứa Dictionary.

friend_ages = {"Rolf": 24, "Adam": 30, "Anne": 27}
del friend_ages["Adam"]
print(friend_ages) # Kết quả là {'Rolf': 24, 'Anne': 27}

del friend_ages
print(friend_ages) # Kết quả lỗi NameError: name 'friend_ages' is not defined

Một số phương thức trong Dictionary

Phương thức .copy()

Phương thức này trả về một bản copy riêng biệt. Chú ý, phương thức .copy() có điểm khác so với gán biến trong Python. Chúng ta cùng xem ví dụ sau:

friend_ages = {"Rolf": 24, "Adam": 30, "Anne": 27}
copy_friend_ages = friend_ages.copy()
new_friend_ages = friend_ages

friend_ages.clear()

print(copy_friend_ages) # Kết quả là {'Rolf': 24, 'Adam': 30, 'Anne': 27}
print(new_friend_ages) # Kết quả là {}

Như vậy, với phương thức .copy() toàn bộ bộ nhớ liên quan đến biến friend_ages được copy sang một vùng khác trong bộ nhớ, do vậy biến cũ thay đổi thì biến mới không thay đổi do tham chiếu đến vùng nhớ khác trên bộ nhớ. Còn khi gán giá trị bằng dấu =, biến đó vẫn tham chiếu đến vùng nhớ của biến cũ, nên khi biến cũ thay đổi, biến mới cũng thay đổi theo.

Phương thức .get()

Trả về giá trị một phần tử trong Dictionary với một khóa cho trước, nó giống với truy xuất giá trị tại mục 1.2 nhưng có một khác biệt nhỏ, nếu khóa không tồn tại, chương trình sẽ không báo lỗi mà trả về giá trị None.

friend_ages = {"Rolf": 24, "Adam": 30, "Anne": 27}

print(friend_ages.get("FirebirD")) # Kết quả là None
print(friend_ages.get("Rolf")) # Kết quả là 24
print(friend_ages["FirebirD"]) # Kết quả lỗi KeyError: 'FirebirD'

Phương thức .keys() và .values()

Hai phương thức này trả về danh sách khóa và danh sách các giá trị của từ điển. Ví dụ:

friend_ages = {"Rolf": 24, "Adam": 30, "Anne": 27}

print(friend_ages.keys())
# Kết quả dict_keys(['Rolf', 'Adam', 'Anne'])

print(friend_ages.values())
# Kết quả dict_values([24, 30, 27])

So sánh List, Tuple, Set và Dictionary

Bạn đã được giới thiệu 4 cấu trúc dữ liệu có trong Python là List, Tuple, Set và Dictionary, có vẻ như hơi nhiều thứ rắm rối. Trong từng bài viết, chúng ta đã có những so sánh hai khái niệm một với nhau, giờ là lúc chúng ta tổng kết lại toàn bộ những gì đã học về 4 cấu trúc dữ liệu này:

Cách khai báo:

  • [value_1, value_2, …, value_n]: Khai báo danh sách (List)
  • (value_1, value_2, …, value_n): Khai báo Tuple
  • {value_1, value_2, …, value_n}: Khai báo tập hợp (Set)
  • {key_1:value_1, key_2:value_2, …, key_n:value_n}: Khai báo từ điển (Dictionary)

Sau khi làm quen với 4 cấu trúc List, Tuple, Set và Dictionary, có lẽ bạn sẽ rất băn khoăn: Nên lựa chọn cấu trúc nào cho phù hợp với chương trình của bạn? Trước khi đến với câu trả lời, tôi khuyên bạn hãy đọc thật kỹ lại kiến thức từng cấu trúc này:

Sau đó, chúng ta cùng đúc kết để có được câu trả lời cho: Khi nào dùng List, Tuple, Set hay Dictionary trong Python?

  • Sử dụng List khi muốn có dữ liệu truy xuất theo thứ tự (vị trí của phần tử tăng dần từ 0), dữ liệu List ở dạng đơn giản và thay đổi thường xuyên.
  • Sử dụng Set khi muốn dữ liệu là duy nhất và dễ dàng thực hiện các phép toán tập hợp như phép giao, hợp, trừ.
  • Sử dụng Tuple khi dữ liệu không thay đổi và cần tốc độ xử lý.
  • Sử dụng Dictionary khi dữ liệu thay đổi liên tục, có sự liên kết logic giữa khóa và giá trị và một điểm nhấn quan trọng là muốn truy xuất dữ liệu nhanh thông qua khóa.

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

Xem thêm Việc làm python lương cao hấp dẫn trên TopDev

Đừng sudo, nếu không biết virtualenv

Đừng sudo, nếu không biết virtualenv

Bài viết được sự cho phép của tác giả Nguyễn Việt Hưng

  • “Mình không cài bằng pip được…”
  • “chạy sudo pip … nhé “

Đó là những câu hỏi và trả lời thường xuất hiện, khi người ta nói về pip. Đây là vấn đề chung với những người dùng không hiểu về phân quyền trên UNIX và thường sẽ làm mọi thứ trở nên rắc rối thêm bằng cách chạy sudo.

  Hướng dẫn cài đặt VirtualBox trên Ubuntu chi tiết nhất
  JavaScript Runtime Environment là gì?

sudo – câu lệnh thần thánh

Sudo XKCD

Một mẩu truyện nhỏ cho thấy sức mạnh của sudo, khi ta nói mà đối phương không nghe, thêm sudo vào đầu là mọi thứ đều sẽ hoạt động.

sudo – SUperuser DO

Trên các hệ điều hành *NIX có chia ra nhiều user để nhiều người dùng chung một máy tính. Trong đó, mọi máy tính luôn có user root – user có quyền năng tối thượng, thường được gọi là quyền admin hay superuser, có thể thực hiện bất kỳ thay đổi gì trên máy tính. Do có quyền năng vô hạn như vậy, nên khi thực hiện việc gì cũng ẩn chứa nguy hiểm, một câu lệnh gõ nhầm cũng có thể phá huỷ hệ thống, và nếu như ai cũng là root thì hệ thống sẽ loạn, người dùng không hiểu biết có thể cài đặt các loại virus/malware lên máy tính (hi Windows).

Để giải quyết vấn đền này, người ta sản xuất ra phần mềm tên “sudo”, cho phép cấp quyền cho các user nhất định. Với sự tồn tại của sudo, người ta thay đổi cách dùng máy tính:

  • tất cả mọi người đều có tài khoản riêng của mình
  • cấp quyền root cho một số user cụ thể
  • user được cấp quyền có thể chạy câu lệnh như user root.

Khi nào cần quyền root

Mỗi user trên *NIX được cấp một thư mục gọi là thư mục HOME. Có thể truy cập thư mục này bằng:

cd ~

hay

cd $HOME

Thư mục này thường có đường dẫn /home/yourname/. và mặc định thuộc toàn quyền sử dụng của user , thích tạo/sửa/xoá file nào thì tuỳ ý.

Hệ thống có thư mục đặc biệt /tmp, ai cũng đều có thể ghi vào (nhưng người này không thể can thiệp vào file/thư mục của người kia) – và các file trong này sẽ tự bị xoá mỗi khi máy tắt.

Còn lại, các thư mục khác trên máy tính chỉ có root mới có thể thay đổi (/etc, /log, /var, /usr, /bin …), khi sử dụng các phần mềm thực hiện thay đổi ảnh hưởng đến cả hệ thống, ví dụ dùng apt-get để cài package, lý do ta cần chạy sudo vì:

  • apt-get sẽ ghi file vào các thư mục nói trên. Ví dụ khi cài package nginx, các file sau sẽ được tạo ra:
$ dpkg -L nginx | grep nginx
/var/cache/nginx
/var/log/nginx
/usr/lib/nginx
/usr/lib/nginx/modules
/usr/share/doc/nginx
/usr/share/doc/nginx/CHANGES.ru.gz
/usr/share/doc/nginx/changelog.Debian.gz
/usr/share/doc/nginx/changelog.gz
/usr/share/doc/nginx/copyright
/usr/share/doc/nginx/README
/usr/share/man/man8/nginx.8.gz
/usr/share/lintian/overrides/nginx
/usr/share/nginx
/usr/share/nginx/html
/usr/share/nginx/html/index.html
/usr/share/nginx/html/50x.html
/usr/sbin/nginx-debug
/usr/sbin/nginx
/etc/init.d/nginx-debug
/etc/init.d/nginx
/etc/logrotate.d/nginx
/etc/nginx
/etc/nginx/koi-win
/etc/nginx/fastcgi_params
/etc/nginx/uwsgi_params
/etc/nginx/mime.types
/etc/nginx/koi-utf
/etc/nginx/nginx.conf
/etc/nginx/conf.d
/etc/nginx/conf.d/default.conf
/etc/nginx/scgi_params
/etc/nginx/win-utf
/etc/default/nginx-debug
/etc/default/nginx
/etc/nginx/modules
...

Chỉ user root mới có quyền ghi vào /etc/ /bin … đó là lý do cần thêm sudo khi người dùng với user của họ chạy câu lệnh apt-get.

Nếu ta cấu hình cho apt-get không ghi vào các thư mục trên, ta hoàn toàn có thể chạy apt-get để cài đặt package trên thư mục home của mình. Các chương trình được cài xong sẽ chỉ mình mình dùng được, do user khác không truy cập được vào các file này.

sudo pip

Khi dùng pip cài ipython (không dùng virtualenv), đây là nơi ipython sẽ được cài:

root@hvn:~# pip show ipython
Name: ipython
Version: 6.1.0
Summary: IPython: Productive Interactive Computing
Home-page: https://ipython.org
Author: The IPython Development Team
Author-email: ipython-dev@python.org
License: BSD
Location: /usr/local/lib/python3.5/dist-packages
Requires: prompt-toolkit, setuptools, pygments, simplegeneric, jedi, pexpect, traitlets, pickleshare, decorator

/usr/local là thư mục chỉ root mới được quyền ghi, vì vậy bạn không thể chạy pip install PACKAGE, bởi user thông thường không có quyền (permission denied) ghi vào /usr

$ pip install phg
..,
PermissionError: [Errno 13] Permission denied: '/usr/local/lib/python3.5/dist-packages/phg-1.1.0.dist-info'

Mô-tuýp truyền thống lại xảy ra:

  • “Mình không cài được …”
  • “sudo pip …”

Cách làm này không sai, nhưng thiếu hiểu biết:

  • nó cài đặt package lên thư mục hệ thống /usr/local/lib, khi có nhu cầu sử dụng 2 phiên bản của cùng 1 phần mềm, ta không thể gỡ một bản đi và giữ lại bản kia (VD: django 1.7 và django 1.11)
  • không phải code nào cài từ pip cũng an toàn, khi chạy với user root cài một package chứa code phá hoại, nó có thể huỷ diệt cả hệ thống (vì user root có quyền làm mọi thứ).

Giải pháp là virtualenv!

Virtualenv

virtualenv là một phần mềm viết bằng Python, giúp tạo ra các “môi trường ảo” để tách biệt các môi trường của các project khác nhau.

Mỗi “môi trường ảo” về bản chất chỉ là một thư mục, với một vài thay đổi khiến cho khi ta dùng pip để cài package, các package sẽ được cài vào thư mục này. Điều này giải quyết các vấn đề nói trên:

  • tách biệt các môi trường giữa các project, 2 project có thể dùng 2 phiên bản khác nhau của cùng 1 phần mềm.
  • người dùng có thể tạo môi trường ảo trong thư mục $HOME của mình, không cần chạy sudo – không ảnh hưởng đến hệ thống.

virtualenv hoạt động được là nhờ vào cách mà Python thực hiện import. Luật import của Python, khi ta import spam:

  • Tìm các builtin module xem có cái nào tên là spam không. Danh sách các builtin module được chứa trong sys:
 python -c 'import sys; print(sys.builtin_module_names)'
('__builtin__', '__main__', '_ast', '_bisect', '_codecs', '_collections', '_functools', '_heapq', '_io', '_locale', '_md5', '_random', '_sha', '_sha256', '_sha512', '_socket', '_sre', '_struct', '_symtable', '_warnings', '_weakref', 'array', 'binascii', 'cPickle', 'cStringIO', 'cmath', 'datetime', 'errno', 'exceptions', 'fcntl', 'gc', 'grp', 'imp', 'itertools', 'marshal', 'math', 'operator', 'posix', 'pwd', 'select', 'signal', 'spwd', 'strop', 'sys', 'syslog', 'thread', 'time', 'unicodedata', 'xxsubtype', 'zipimport', 'zlib')
  • Nếu không thấy, tìm lần lượt trong các thư mục chứa trong list : sys.path, cho đến khi tìm thấy file spam.py.
  • Nếu vẫn không thấy, raise Exception ImportError.

Khi ta bật (activate) một virtualenv lên (source), thực tế ta thay đổi sys.path, nhét đường dẫn đến “môi trường ảo” hiện tại lên đầu sys.path, vì vậy khi thực hiện import, Python sẽ tìm trong “môi trường ảo” trước.

Đọc kỹ những dòng sau để thấy sự ảnh hưởng của virtualenv với Python sys.path, khi bật virtualenv, đường dẫn tới virtualenv sẽ xuất hiện trong sys.path, tức module / code nào nằm trong thư mục /home/hvn/myvenv/lib/python3.6/site-packages đều có thể được import.

$ python3 -m venv myvenv
$ source myvenv/bin/activate
(myvenv) $ python3 -c 'import sys; print(sys.path)'
['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/home/hvn/myvenv/lib/python3.6/site-packages']
(myvenv) $ deactivate
$ python3 -c 'import sys; print(sys.path)'
['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages']

Với virtualenv, ta sẽ không bao giờ phải sudo pip nữa, mỗi project sẽ có một môi trường riêng biệt, không ai ảnh hưởng tới ai. Và chỉ gõ sudo pip khi cần thay đổi 1 package trên toàn hệ thống.

Sử dụng pip trong venv

Cho python3.6 trở lên, ví dụ trên Ubuntu 18.04, xem tài liệu cho lệnh dùng trên Windows.

$ python3 -m venv myvenv
$ source myvenv/bin/activate
(myvenv) $ python3 -m pip install django
Collecting django
  Cache entry deserialization failed, entry ignored
  Downloading https://files.pythonhosted.org/packages/5c/63/6d7efecbf3f06db8c6577950a24a191e55cadf7cda4d7fe6976206c886dd/Django-3.0.7-py3-none-any.whl (7.5MB)
    100% |████████████████████████████████| 7.5MB 206kB/s
Collecting pytz (from django)
  Using cached https://files.pythonhosted.org/packages/4f/a4/879454d49688e2fad93e59d7d4efda580b783c745fd2ec2a3adf87b0808d/pytz-2020.1-py2.py3-none-any.whl
Collecting sqlparse>=0.2.2 (from django)
  Using cached https://files.pythonhosted.org/packages/85/ee/6e821932f413a5c4b76be9c5936e313e4fc626b33f16e027866e1d60f588/sqlparse-0.3.1-py2.py3-none-any.whl
Collecting asgiref~=3.2 (from django)
  Cache entry deserialization failed, entry ignored
  Downloading https://files.pythonhosted.org/packages/68/00/25013f7310a56d17e1ab6fd885d5c1f216b7123b550d295c93f8e29d372a/asgiref-3.2.7-py2.py3-none-any.whl
Installing collected packages: pytz, sqlparse, asgiref, django
Successfully installed asgiref-3.2.7 django-3.0.7 pytz-2020.1 sqlparse-0.3.1
(myvenv) $ python
Python 3.6.9 (default, Apr 18 2020, 01:56:04)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> django.__version__
'3.0.7'
>>> quit()

$ python3 -m pip freeze
asgiref==3.2.7
Django==3.0.7
pkg-resources==0.0.0
pyfml==0.0.0
pytz==2020.1
sqlparse==0.3.1

Kết luận

sudo là sức mạnh, đừng chỉ vì quá mạnh mà thích làm gì cũng được 🙄

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

Có thể bạn quan tâm:

Xem thêm Việc làm Developer hấp dẫn trên TopDev

Sửa lỗi jenkins No Java executable found in current PATH

Sửa lỗi jenkins No Java executable found in current PATH

Bài viết được sự cho phép của tác giả Trần Hữu Cương

Sửa lỗi jenkins No Java executable found in current PATH

Đây là lỗi khi bạn start service jenkins. Nguyên nhân là do jenkins không tìm thấy Java Path.

  Cài Plugin cho Jenkins, Cài Maven Plugin cho Jenkins
  Hướng dẫn cấu hình JDK (Java) cho Jenkins

Sửa lỗi jenkins "No Java executable found in current PATH".

Có 2 nguyên nhân:

  • Trường hợp 1: Do bạn chưa cài Java trên máy tính or chưa cấu hình environment cho Java
  • Trường hợp 2: Đã cài Java và cấu hình environment nhưng jenkins không nhận Java PATH (thường là bị trên ubuntu do bạn cài JDK từ oracle bằng tay)

Sửa lỗi jenkins No Java executable found in current PATH.

Trường hợp 1:

Bạn cài đặt Java và cấu hình environment bằng lệnh:

sudo apt install openjdk-8-jre

Hoặc tải file tar.gz rồi cài đặt bằng tay như tại đây.

Trường hợp 2:

Mở file cấu hình jenkins bằng lệnh sau:

sudo vi /etc/init.d/jenkins

Sửa dòng thêm folder bin trong thư mục java của bạn vào sau biến PATH, nếu bạn cài open jdk thì nó sẽ có dạng /usr/lib/jvm/java-8-openjdk-amd64/bin/, còn trên máy mình cài java ở folder /opt/java/jdk1.8.0_261/bin nên mình sẽ sửa thành như sau:

PATH=/bin:/usr/bin:/sbin:/usr/sbin:/opt/java/jdk1.8.0_261/bin

Khởi động lại jenkins:

sudo service jenkins start
sudo service jenkins status

Sửa lỗi jenkins No Java executable found in current PATH

Reference: https://stackoverflow.com/questions/39621263/jenkins-fails-when-running-service-start-jenkins

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

Có thể bạn quan tâm:

Xem thêm Tuyển dụng Java hấp dẫn trên TopDev

Deadlock in Java – something you should know 1

Deadlock in Java

Bài viết được sự cho phép của tác giả Kiên Nguyễn

Nếu bạn đã có kinh nghiệm hoặc đã tiếp xúc qua với Java Concurrency, hẳn không còn xa lạ gì với khái niệm DeadLock. Tuy nhiên, bài viết này vẫn cung cấp một cái nhìn tổng quan, dễ hiểu hơn về DeadLock.

Dành cho những ai đang muốn tìm hiểu về deadlock. Bắt đầu ngay thôi nào!

  Sử dụng Dead Letter Exchange trong RabbitMQ
  Giới thiệu Temporal Dead Zone trong Javascript

1. DeadLock là gì?

Deadlock is a situation where a set of processes are blocked because each process is holding a resource and waiting for another resource acquired by some other process.

Deadlock là tình trạng các process block lẫn nhau do process này thì chờ tài nguyên kia, tài nguyên kia thì đang được một process khác nữa giữ lấy

Dead – Chết, nghe thôi đã thấy lạnh. Lock – Khóa, khóa lại trong nhau mà chết là từ chính xác dùng để diễn tả về Dead Lock

Deadlock lock and waitVòng lặp không lối thoát – Nguồn ảnh / Source: geeksforgeeks.org

Xem tin tuyển dụng Java mới nhất trên TopDev

2. Xảy ra lúc nào?

Tuy vậy, có phải lúc nào cũng xảy ra deadlock hay không?. Câu trả lời là không. Ở một số trường hợp liệt kê dưới đây sẽ xảy ra:

  • Mutual exclusion – Loại trừ nhau
  • Lock & wait – Chờ rồi tèo
  • No preemption – Không ưu tiên
  • Circular wait – Chờ nhau theo vòng tròn

2.1 Mutual Exclusion

Loại trừ lẫn nhau chính xác là khi hai hay nhiều thread cần sử dụng chung một resource. Nhiều connection đòi thực thi trên một table là ví dụ điển hình.

Một connection đều yêu cầu được ghi record mới vào table, nhưng table thì đang được lock bởi thread khác. Deadlock coming

2.2 Lock and wait

Khóa và chờ cũng gần giống như loại trừ lẫn nhau. Trường hợp này là một thread cứ chày cối dữ lấy tài nguyên (resource) không buông. Các thread khác thì cứ chờ.

Chờ hoài không thấy, chờ người nơi ấy. Dẫn tới chờ rồi chết cả đám

2.3 No preemption

Không có quyền ưu tiên. Trường hợp này được hiểu là resource A đang rất cần resource, nhưng nó không có quyền lấy resource từ thằng B đang giữ nó. Hai thằng giành nhau cũng xảy ra DeadLock

2.4 Circular Wait

Circular Wait là chờ vòng tròn, lặp trong vô vọng. Thằng T1 thì cần tài nguyên R2, T2 thì cần tài nguyên R3, T3 thì cần tài nguyên R1 của T1.

Nhức cả cái đầu nhưng xoay mòng mòng thành một cái vòng chờ nhau không lối thoát

Circular Wait, đợi nhau trong vòng tròn vô tận. Nguồn ảnh/ Source: The Robert C. Martin Clean Code Collection

3. Tổng kết

Deadlock là nỗi sợ to lớn với dân lập trình multi thread, concurency. Mỗi khi nó coming, y như cái tên của nó, chỉ có chết. Nhưng biết thì sẽ luôn có cách để phòng tránh.

Ở phần hai của series bài viết về deadlock sẽ là cách phòng tránh đối với từng loại. Đón đọc nha!.

4. Tham khảo

Wish you have best of luck. Have a good day!. Happy coding!

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

Có thể bạn quan tâm:

Xem thêm Tuyển lập trình Java hấp dẫn trên TopDev

5 tips giúp lập trình viên TẬP TRUNG hơn khi NGỒI CODE

Bài viết được sự cho phép của tác giả Kiên Nguyễn

Chào anh em, thực ra dù anh em làm gì, trong lĩnh vực nào đi chăng nữa thì việc “tập trung” gần như là một trong những điều kiện tiên quyết để anh em nâng cao hiệu suất làm việc.

  "Code dễ đọc" là như thế nào?
  3 tips làm việc với JavaScript giúp bạn tiết kiệm thời gian

Với anh em làm lập trình thì sự tập trung lại càng cần thiết hơn nữa, vì bản chất của công việc này sẽ đòi hỏi phải sử dụng trí não rất nhiều.

5 tips giúp lập trình viên TẬP TRUNG hơn khi NGỒI CODE

Vậy một câu hỏi đặt ra là làm thế nào để tập trung ngồi code được đây? Vâng, và trong bài viết này mình sẽ chia sẻ với anh em làm lập trình một vài tips mà mình đã áp dụng và mình thấy nó khá là hữu ích và hiệu quả đối với cá nhân mình.

Anh em có thể tham khảo, nếu thấy phù hợp thì có thể áp dụng cho mình đã nâng cao năng suất làm việc lên nhé. Điều này không những tốt cho cả công việc của công ty, mà còn tốt cho cả anh em nữa

#1. Dừng việc nghe nhạc trên Youtube

Nhiều anh em làm lập trình khi ngồi code thường có thói quen nghe nhạc và mình cũng là một trong số đó.

Việc nghe nhạc nào thì tùy vào sở thích của mỗi người, nhưng mình tin chắc rất nhiều anh em thường nghe nhạc trên Youtube là chủ yếu.

5 tips giúp lập trình viên TẬP TRUNG hơn khi NGỒI CODE

Tại sao anh em lại thích nghe nhạc trên Youtube? Câu trả lời đơn giản là trên Youtube nhạc nào mà chả có. Từ nhạc trẻ, nhạc vàng, nhạc đỏ… cho đến rap đều có hết, mà nó lại còn miễn phí nữa. Cứ cắm tai nghe vào là nghe thôi ^^!

Mình không thể phủ định được việc nghe nhạc trên Youtube thực sự rất tiện và hay nữa. Nhưng cái gì cũng có hai mặt của nó.

Bản chất Youtube là nền tảng chia sẻ video, vì vậy có những lúc đang nghe thì tự nhiên thấy bài nhạc bạn nghe bị dừng hoặc bị chèn quảng cáo rất khó chịu.

Ok, bạn có thể cài adblock nhưng cứ mỗi lần lướt ra Youtube để nghe nhạc mình cá là nhiều anh em sẽ “lang thang” thêm vài một video nữa rồi mới sực nhớ ra: “Ơ, đang giờ làm việc mà!”

Vì vậy nếu có điều kiện anh em có thể chọn nghe nhạc theo cách khác, ví dụ như nghe trên spotify hoặc các app nghe nhạc để tránh việc sao nhãng nhé.

Hoặc anh em cũng có thể tải một vài bài nhạc không lời mà anh em yêu thích về máy tính và sau đó bật để nghe khi code. Rất tuyệt vời đấy

#2. Hãy nhớ, 50 phút giải lao một lần !

Đừng quá để ý đến con số 50 phút nha các bạn !

Thực ra, khả năng tập trung của mỗi người là khác nhau, có người ngồi cả ngày được , nhưng có người chỉ ngồi được 20-30 phút là đã cảm thấy khá oải rồi.

5 tips giúp lập trình viên TẬP TRUNG hơn khi NGỒI CODE

Khi làm lập trình, không chỉ bộ não phải hoạt động, mà bạn phải nhìn màn hình liên tục. Điều này nhiều khi khiến bạn mỏi mắt chứ chưa muốn nói là hoa mắt nữa.

Chính vì như vậy mà các bạn nên đưa ra một khoảng thời gian mà các bạn thấy phù hợp để tập trung làm việc trong khoảng thời gian đó. Sau đó nghỉ ngơi một chút rồi lại làm tiếp.

Đừng cố làm việc khi cảm thấy đầu óc đang bị căng thẳng, bởi thời gian đó bạn cũng không làm việc gì nên hồn được đâu. Hãy nghỉ ngơi 1 lát để lấy lại “phong độ” nhé !

Để đưa ra được một con số thích hợp (ví dụ 30′, 40’, 50’ hay là 1 tiếng đồng hồ) thì các bạn phải tự thử nghiệm xem sao.

Tức là bạn hãy thử nghiệm xem bạn có thể TẬP TRUNG LÀM VIỆC được tối đa trong vòng bao lâu, rồi lấy đó làm “con số tập trung” của bạn. Cái này tùy vào khả năng và cơ địa của mỗi người nên mình không thể fix cứng được nha các bạn !

#3. Úp điện thoại, hoặc không để điện thoại gần người

Nếu anh em để điện thoại cạnh người rồi còn bật Wi-Fi nữa thì thôi rồi, tập xác định ! Thông báo cứ ting ting liên tục thì làm sao mà tập trung nổi.

5 tips giúp lập trình viên TẬP TRUNG hơn khi NGỒI CODE

Tất nhiên là không đến mức phải tắt máy hay tắt nguồn, vì nhiều khi có những cuộc gọi quan trọng nên nếu anh em tắt nguồn thì sẽ lỡ mất.

Với mình thì mình thường tắt Wi-Fi đi, lật úp điện thoại xuống bàn và để ở bên trái hoặc phía sau Laptop của mình. Bạn cũng có thể kích hoạt chế độ KHÔNG LÀM PHIỀN trên điện thoại để làm việc hiệu quả hơn.

Làm như vậy sẽ hạn chế việc mình để ý chiếc điện thoại trong khi nếu có cuộc gọi thì mình vẫn không bị lỡ mất.

Mình biết có nhiều anh em kiểu cứ 10-15 phút lại phải cầm điện thoại lên lướt lướt mới chịu được. Nó thành thói quen rồi nên để khắc phục được thì chẳng còn cách nào khác là rèn luyện thôi.

#4. Tạo danh sách công việc phải làm hàng ngày

Mình nghĩ đây là việc quan trọng nhất nếu anh em muốn tập trung thực sự, vì đơn giản chỉ khi lên kế hoạch rồi thì anh em mới định hình được mình phải làm gì, làm khi nào và làm trong bao lâu.

5 tips giúp lập trình viên TẬP TRUNG hơn khi NGỒI CODE

Thực tế khi đi làm thì anh em sẽ được phân làm theo Task, tức là các đầu việc gần như đã rõ ràng rồi. Việc của anh em chỉ là ngồi lại xem cái nào làm trước, cái nào làm sao và làm trong bao lâu mà thôi.

Ngoài ra anh em nào còn có kế hoạch khác (chẳng hạn như ngồi học công nghệ mới, viết tài liệu, họp với sếp…) thì anh em cũng phải sắp xếp sao cho hợp lý.

Khi tất cả nằm trong kế hoạch rồi thì cứ thế mà triển thôi. Mình tin chắc nếu tạo được todo-list như vậy thì anh em sẽ làm việc cực kỳ hiệu quả luôn.

Còn việc tạo todo list sao cho hiệu quả thì mình nghĩ anh em nên tạo theo thứ tự ưu tiên (ưu tiên về mức độ cần hoàn thành của task, ưu tiên những việc phải giải quyết ngay trong ngày).

Với mình, mình luôn làm những công việc “khó nhai” nhất trước, vì thời điểm đầu ngày sẽ là thời điểm dễ tập trung cao độ nhất, sau khi đã hoàn thành việc khó nhất trong ngày rồi thì những việc tiếp theo cứ thong thả mà làm thôi

Nói chung là phải có mục tiêu và kế hoạch rõ ràng, anh em phải định hình được các công việc cần phải làm trong ngày thì mọi việc diễn ra một cách đúng lộ trình hơn…

#5. Kiếm một chỗ ngồi thật thoải mái

5 tips giúp lập trình viên TẬP TRUNG hơn khi NGỒI CODE

Chắc có lẽ, chẳng mấy anh em nghĩ đến việc kiếm một chỗ ngồi thoải mái đâu nhỉ, vì những yếu tố ngoại cảnh như thế ít ai để ý.

Vậy thế nào là một chỗ ngồi thoải mái, theo mình trước tiên phải có cái ghế dễ ngồi đã (ghế có thể thay đổi chiều cao, có tựa lưng chẳng hạn).

Sau đó là đến bàn làm việc, bàn làm việc thì không nên cao quá và phải chắc chắn, cứng cáp (không bị ọt ẹt) và không có mùi gỗ ép.

Vị trí ngồi nên tránh chỗ mọi người đi lại nhiều vì nhiều người đi qua có thói quen tiện tay vỗ vai người ngồi, rất là phiền.

Ngoài ra thì bạn cũng nên để ý đến cả hướng nắng và vị trí điều hòa nữa nhé, vì ngồi sát tường – đúng hướng nắng hoặc đối điện điều hòa thì đều không tốt và dễ khiến bạn có cảm giác khó chịu, gây mất tập trung.

Những yếu tố tuy nhỏ nhặt như vậy thôi nhưng nó có tác động rất lớn đến sự tập trung của anh em đấy !

#6. Lời Kết

Trên đây là 5 kinh nghiệm mà cá nhân mình đang áp dụng để có thể tập trung hơn khi ngồi code, hi vọng nó sẽ giúp ích cho anh em phần nào, chứ thực ra việc tập trung được hay không vẫn là do chính bản thân mỗi người quyết định.

Các yếu tố bên ngoài dù bạn có làm tốt đến đâu mà bản thân bạn không thể tập trung được (không có sự chủ động) thì cũng chẳng có tác dụng gì cả đâu.

CTV: Nguyễn Đức Cảnh – Bài viết gốc được đăng tải tại blogchiasekienthuc.com

Có thể bạn quan tâm:

Truy cập ngay việc làm IT đãi ngộ tốt trên TopDev

Giao thức trong kiểm thử API

Giao thức trong kiểm thử API

Bài viết được sự cho phép của vntesters.com

Bài trước mình đã giải thích vai trò của API đối với client và server, bài này sẽ nói về cách mà 2 thằng đó nói chuyện với nhau (gọi đại khái là 2 cái máy tính cho đơn giản).

  10+ tools và extensions tuyệt vời cho GraphQL APIs
  3 bước tối ưu hiệu năng React App bằng các API mới của React

Protocol là gì?

Giả sử: Có 2 người A và B nói chuyện với nhau qua điện thoại, nếu người A hỏi 1 câu rồi im lặng, người B sẽ biết rằng người A đang chờ đợi câu trả lời và đến lượt người B nói. Hai chiếc máy tính cũng giao tiếp 1 cách lịch sự như vậy và được mô tả với cái thuật ngữ “Protocol” – giao thức.
Giao thức chính là những luật lệ được chấp thuận để 2 cái máy tính có thể nói chuyện với nhau.
Tuy nhiên, luật lệ này chặt chẽ hơn rất nhiều so với giao tiếp giữa người với người. Máy tính sẽ không thông minh để có thể nhận biết 2 câu “A là chồng B” hay “B là vợ A” có cùng ý nghĩa. Để 2 máy tính giao tiếp hiệu quả, server phải biết chính xác cách mà client sắp xếp cái message nó gửi lên như thế nào

Chúng ta đã từng nghe đến những Protocol cho những mục đích khác nhau, ví dụ như Mail có POP hay IMAP, message có XMPP, Kết nối thiết bị: Bluetooth. Trong web thì Protocol chính là HTTP – HyperText Transfer Protocol, vì sự phổ biến của nó mà hầu hết các công ty chọn nó là giao thức cho các API.
Lưu ý: API có thể viết trên nền Protocol khác, ví dụ như SOAP. Nhưng trong khuôn khổ bài viết này, mình chỉ giới thiệu về HTTP.

HTTP hoạt động như thế nào?

Cuộc sống của HTTP xoay quanh cái vòng luẩn quẩn: Request và Response. Client gửi request, server gửi lại response là liệu server có thể làm được cái client muốn hay ko. Và API được xây dựng trên chính 2 thành phần: Request và Reponse. Trước tiên, ta phải hiểu cấu trúc của mỗi thành phần.

Giao thức trong kiểm thử API

Request

Một cái request đúng chuẩn cần có 4 thứ:

  1. URL
  2. Method
  3. Headers
  4. Body

OK, bây giờ săm soi từng thứ một.

URL là 1 cái địa chỉ duy nhất cho 1 thứ (dùng danh từ), có thể là web page, image,hoặc video. API mở rộng cái ý tưởng gốc của URL cho những thứ khác, ví dụ: customers, products. Và như thế client dễ dàng cho server biết cái nó muốn là cái gì, những cái này còn được gọi chung là “resources” – nguồn lực.

Method: là cái hành động client muốn tác động lên “resources”, và nó thường là động từ. Có 4 loại Method hay được dùng:
– GET: Yêu cầu server đưa lại resource: Hãy tưởng tượng ra cái cảnh vào fb, tay vuốt new feeds.
– POST: Yêu cầu server cho tạo ra 1 resource mới. Ví dụ: đăng ký 1 chuyến đi ở GrabBike.
– PUT: Yêu cầu server cho sửa / thêm vào resource đã có trên hệ thống. Ví dụ: Edit 1 post ở trên fb.
– DELETE: Yêu cầu server cho xóa 1 resourse. Cái này chắc chả cần ví dụ.

Headers: nơi chứa các thông tin cần thiết của 1 request nhưng end-users không biết có sự tồn tại của nó. Ví dụ: độ dài của request body, thời gian gửi request, loại thiết bị đang sử dụng, loại định dạng cái response mà client có đọc được…

Body: nơi chứa thông tin mà client sẽ điền. Giả sử bạn đặt 1 cái bánh pizza, thì thông tin ở phần body sẽ là: Loại bánh pizza, kích cỡ, số lượng đặt.

Response

Sau khi nhận được request từ phía client, server sẽ xử lý cái request đó và gửi ngược lại cho client 1 cái response. Cấu trúc của 1 response tương đối giống phần request nhưng Status code sẽ thay thế cho URL và Method. Tóm lại, nó có cầu trúc 3 phần:

  1. Status code
  2. Headers
  3. Body

Giao thức trong kiểm thử API

Status code là những con số có 3 chữ số và có duy nhất 1 ý nghĩa. Chắc các bạn cũng không còn lạ lẫm với những Error “404 Not Found” hoặc “503 Service Unavailable”. Full list có ở đây. Phần Header và body tương đối giống với request.

Hôm nay tạm vậy đã, hôm sau sẽ tiếp tục.

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

Có thể bạn quan tâm:

Xem thêm Việc làm Developer hấp dẫn trên TopDev

Lead Engineer trông như thế nào?

Lead Engineer trông như thế nào?

Bài viết được sự cho phép của tác giả Lê Chí Dũng

Gần đây, anh PM cũ đã hỏi tôi về cách tôi nhìn nhận một người nào đó đủ tiêu chuẩn trở thành Lead Engineer / Tech Lead tương lai. Đây là điều mà tôi chia sẻ và với phản hồi tích cực từ nội bộ, tôi nghĩ nó có thể hữu ích cho các Manager, CTO, Tech Lead, Team lead hay engineer, v.v…

Mặc dù cách đặt title, level và expectations ở mỗi công ty có thể hơi khác một chút, nhưng tôi hy vọng rằng điều này sẽ giúp các engineer phát triển hơn nữa mà tôi đã may mắn được trãi nghiệm.

  Biến Git và GitHub trở thành công cụ đắc lực cho Software Engineer
  Cách Engineer Nhật Bản thực hiện test như thế nào

Tôi nhìn vào năng lực của Lead Engineer được thể hiện trên một số khía cạnh. Lead Engineer có khả năng và kỹ năng của một Senior Engineer. Nhưng hơn thế

  • Hỗ trợ cố vấn, cung cấp hướng dẫn memo, workflow, architect, … để đưa công việc vào guồn trong giai đoạn đầu của quá trình phát triển product / services
  • Hỗ trợ trong kế hoạch phát triển sản phẩm và quy trình.
  • Hỗ trợ team phản hồi với các bên khác liên quan về techical, system.
  • Thảo luận và hỗ trợ các PM, team lead để đảm bảo rằng dev team và technical được lựa chọn phù hợp với target của dự án.

Năng lực của Lead Engineer

Technical

Scope

  • Họ hiểu rõ về các source code, architect, design pattern, các domain liên quan, cách chúng hoạt động, v.v… của một product hoặc 2–5 services (thay đổi tùy theo size S – M – L -XL hé).
  • Họ có khả năng build architect một product / services để đạt được yêu cầuncần thiết cho business.
  • Họ tham gia vào các cuộc trao đổi phát triển sản phẩm để đưa ra các quyết định kỹ thuật tốt với mức độ hiểu biết sâu về tác động của các quyết định đó. Về cơ bản, họ có thể đảm nhận vai trò ra quyết định với khả năng phán đoán hiệu quả dựa trên sự hiểu biết sâu sắc về một product / services và cách chúng sẽ hoạt động.

Hoạt động kỹ thuật

  • Họ có hiểu biết sâu và ngày càng cải tiến hiệu quả cho source code theo quy mô. Mặc dù có thể thay đổi tùy theo bối cảnh của product / services, nhóm hoặc tổ chức, nhưng họ hiểu và áp dụng các quy trình, nguyên tắc để kiểm tra, theo dõi, ghi nhật ký, giám sát và đánh giá tình trạng liên tục develop của product / services một cách hiệu quả.

Con người / Quản lý dự án

Scope

  • Liên quan đến phạm vi giám sát kỹ thuật ở trên, Lead Engineer cũng có khả năng phân chia task hiệu quả thành các phần khả thi cho 3 developer trở lên. Như dựng cấu trúc lớn và chia thành từng phần nhỏ.
  • Xác định phạm vi và trình tự develop một cách hiệu quả bao gồm việc tạo các estimate task thành các phần nhỏ đủ nhỏ để ước tính có hiệu quả, cân nhắc xem ai có thể hoàn thành công việc và thời gian thực hiện công việc đó theo các ràng buộc khác của nhóm.
  • Việc xác định phạm vi này có thể sẽ được phối hợp với các developer khác và team lead. Tuy nhiên, Lead Engineer có kinh nghiệm và khả năng thể hiện trong việc chia task nhỏ, rủi ro từ thành phần khác, xác định các trở ngại tiềm ẩn, v.v… để đảm bảo rằng nó được chia nhỏ một cách hiệu quả và dự án tổng thể là được hiểu một cách rõ ràng.

Estimate

  • Họ thường đạt 70–80% (trong vòng 150% so với ước tính ban đầu) trong việc estimate chính xác Stories/Tasks của họ và hỗ trợ những người khác trong nhóm của họ hoàn thành điều đó.
  • Họ hiểu ticket xác định đúng trông như thế nào, họ viết chúng ra sao để dễ maintain, dễ hiểu khi ticket do một thành viên khác trong nhóm viết cần clear hơn.
  • Các ước tính của họ được hoàn thành một cách nhất quán, đáp ứng các tiêu chí đã xác định DOD (Define Of Done) của team. Điều này bao gồm việc xem xét tất cả các phần của quy trình để đạt được “Hoàn thành”, không chỉ đơn thuần là viết code.
    Dí dụ: Research, Collaboration, Develop, Viết test code, Self check, Deploy, v.v.

Cải tiến team

Cải tiến liên tục

  • Họ luôn đưa ra các ý tưởng để nâng cao hiệu quả công việc của team / department của họ. Về cơ bản, họ đang xem xét mọi thứ một cách tổng thể và xác định các hạn chế, chẳng hạn như họ đang đưa ra các đề xuất về quy trình review, cải thiện các test cases, v.v…
  • Họ có sự hiểu biết ngày càng sâu sắc về product / services, users và business mà nó hoạt động.

Dẫn dắt

  • Họ thể hiện khả năng để những người khác đến với họ một cách tự tin vì họ được biết đến là những người mà những người khác có thể đến để có được cái nhìn sâu sắc hơn, rõ ràng hơn, v.v… về vấn đề nào đó.
  • Họ dẫn dắt những người khác một cách hiệu quả thông qua các cuộc họp, sharing và bất kỳ quy trình nào cần thiết để hoàn thành các mục tiêu của nhóm.

Giao tiếp

  • Họ giao tiếp hiệu quả với những người trong và ngoài nhóm. Đây không phải là năng lực cốt lõi duy nhất, nhưng nó cho phép họ cải thiện hiệu quả team, project, product,… Giao tiếp tốt và rõ ràng, phát triển mối quan hệ với những người trong toàn công ty, cập nhật rõ ràng về các project khác để so sánh project của họ.

Chắc chắn có những năng lực khác cũng quan trọng mà ai đó ở cấp độ này cần: Tư duy về product, Interview, Design Focus, Business Orientation, v.v. và những điều này có thể cực kỳ mạnh mẽ đối với phát triển của các cá nhân.

Tuy nhiên, khi tôi nghĩ về những năng lực cốt lõi mà tôi mong đợi Lead Engineer sẽ được thể hiện. Đây là những điều mà tôi thường xuyên nghĩ đến để giúp các senior engineer tiềm năng thành công và phát triển lên Lead Engineer tại một công ty phần mềm chuyên nghiệp.

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

Có thể bạn quan tâm:

Xem thêm Việc làm Developer hấp dẫn trên TopDev

Toàn tập về thiết kế Scalable Web Application II

Bài viết được sự cho phép của tác giả Kiên Nguyễn

Scalable Web Application luôn là điểm quan trọng để đánh giá sự thành công của một web applications. Không còn như xưa, số lương request ít ỏi, nâng ram, nâng chip.

Các ứng dụng ngày nay có thể phải giải quyết hàng tỷ (billion) request hàng ngày. Cùng tìm hiểu cách giải quyết vấn đề qua bài viết dưới đây

18 Web Developer Jobs tốt nhất từ các công ty HOT

1. Giải pháp nào để có Scalable

Với các ứng dụng lớn hiện nay, có Scale được hay không trở thành một câu hỏi lớn. Do nhược điểm hiện hữu về xử lý có hạn của cấu trúc Single Server. Scalable Web Application trở thành bài toán về mặt kiến trúc Architecture.

Toàn tập về thiết kế Scalable Web Application II
Nguồn ảnh / Source: re-cycledair.com

Cần có những giải pháp cụ thể để đảm bảo performance, availability cho ứng dụng. Ngoài ra, khi ứng dụng mở rộng, việc scale là dễ dàng và hiệu quả.

2. Vertical Scaling và Horizontal Scaling

Với một số điểm yếu rõ ràng của kiến trúc Single Server truyền thống. Giải pháp và yêu cầu đặt ra là phải có một mô hình cụ thể cho Scalable Web Application.

Mở rộng theo kiểu Scaling cũng có hai loại Vertical Scaling (theo chiều dọc) và Horizontal Scaling (theo chiều ngang).

2.1 Vertical Scaling

Scale theo chiều dọc được hiểu là

Vertical scaling refers to adding more resources (CPU/RAM/DISK) to your server (database or application server is still remains one) as on demand.

Vertical scaling hiểu như là thêm tài nguyên bổ sung (CPU/RAM/DISK) cho server (hệ cơ sở dữ liệu hoặc server ứng dụng vẫn ở một máy)

Vertical Scaling thường được áp dụng cho các công ty quy mô nhỏ. Các ứng nhỏ khi người dùng tăng đều lên qua thời gian cũng có thể scale theo phương thức này.

Toàn tập về thiết kế Scalable Web Application II
Kiến trúc Single Server thường được scale theo hướng Vertical Scaling

Tuy nhiên, với các ứng dụng có lượt request tới nhiều. Chỉ Single Server là không đủ, rất có thể dẫn tới hiện tượng nghẽn cổ chai. Quá nhiều request nhưng chỉ có một cổng vào.

Nếu chỉ nâng cấp phần cứng, chưa thể có một thiết kế tốt cho Scalable Web Application.

Hơn nữa, Single Server có thể chỉ giải quyết tối đa trong một lúc một số lượng nhất định request. Để improve performance và đảm bảo A trong CAP Theorem. Ta sẽ tìm hiểu kiểu scale tiếp theo

  10 kênh Youtube học lập trình không thể bỏ qua dành cho Junior Web Developer / Designer
  10 trang web hàng đầu để tìm hiểu WordPress

2.2 Horizontal Scaling

Horizontal là theo chiều dọc. Vậy Scalable Web Application được scale theo chiều dọc như thế nào?

Đầu tiên, định nghĩa.

Horizontal Scaling is a must use technology – whenever a high availability of (server) services are required. Scaling horizontally involves adding more processing units or phyiscal machines to your server or database.

Scaling theo chiều dọc nên là scale về công nghê, đảm bảo tính luôn luôn sẵn sàng cho services là điều bắt buộc. Scaling horizontally thường là thêm vào các đơn vị xử lý tính toán hoặc thiết bị phần cứng cho phía server hoặc database

Toàn tập về thiết kế Scalable Web Application II

Về mặt lý thuyết, Horizontal Scaling có thể add thêm nhiều máy vật lý, tuy nhiên một máy vật lý không quá lớn. Các database này được lưu trữ theo kiểu Distributed Storage (lưu trữ phân tán).

3. Phân tích các thành phần của Scalable Web Application

Để có thiết kế tốt và đảm bảo Scalable Web Application, ta cần hiểu rõ từng thành phần trong thiết kế. Tham khảo hình vẽ phía dưới.

  • Client có thể lên tới hàng triệu. Vài triệu request trong một giây đối với các services lớn như Google, Facebook, Amazon
  • CDN là Content Delivery Network, giúp tăng tốc dịch vụ, cải thiện performance. Bạn nào chưa biết có thể tham khảo qua tại đây.
  • Load Balancer giúp cân bằng tải cho phía services, giàn đều số lượng xử lý. Tránh services thì quá nhiều request để handle. Servies lại không có request nào. Kieblog sẽ có bài viết riêng về Load Balancer sau.
  • App server là phần xử lý chung, sẽ phán định xem phần nội dung cần response cho client là lấy từ Memory Cached hay sẽ gọi Workers
  • Job Queue là nơi xử lý tuần tự, FIFO (First in, First out). Request nào tới trước sẽ được xử lý trước, tới sau xử lý sau.
  • Workers là phần xử lý chính của Services, thực hiện tính toán, xử lý, kiểm tra.
  • Distributed Database là hệ cơ sở dữ liệu phân tán (theo mô hình Horizontal Scaling). Có thể tham khảo thêm bài viết về Distributed Cached ở đây

4. Tổng kết

Tùy thuộc vào hệ thống, mục đích và số lượng request phải handle mà bản thân Software Engineer phải lựa chọn kiến trúc phù hợp. Horizontal và Vertical cũng là lý thuyết phụ.

Tuy nhiên, hiểu biết về scale và scaling là chưa bao giờ thừa. Không ai lại muốn viết ứng dụng mà không có khả năng scale hoặc scale rất khó khăn.

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

Có thể bạn quan tâm:

Xem thêm it jobs for Developer hấp dẫn trên TopDev

5 lỗi sai các Web Developers newbie thường mắc phải và cách khắc phục

sai lầm của web developers
5 lỗi sai các Web Developers newbie thường mắc phải và cách khắc phục

Tác giả: Dave Gray

Mục tiêu của bài viết này không phải để khiến bạn cảm thấy xấu hổ hay khó chịu mà là để những người mới bắt đầu có thể tránh việc mắc các sai sót. Sai lầm của Web Developers có thể khiến bạn mất nhiều thời gian và công sức không đáng có.

sai lầm của web developers
5 sai lầm các Web Developers nên tránh

Thứ 5: Thêm dấu cách vào tên file

Bạn có thể lưu tệp HTML của mình với tên “my cool page.html”, nhưng những khoảng trống giữa các từ là một sai lầm.

Địa chỉ web (hay còn gọi là URL) không được có khoảng trắng.

Nếu bạn tải tệp này vào trình duyệt của mình, bạn sẽ thấy “my%20cool%20page.html” trên thanh địa chỉ của trình duyệt. Dấu cách phải được mã hóa vì chúng không được phép trong URL.

Nếu bạn muốn thấy sự phân tách giữa các từ trong tên tệp của mình, hãy sử dụng dấu gạch dưới (my_cool_page.html) hoặc dấu gạch ngang (my-cool-page.html).

Là người mới bắt đầu, bạn có thể không quá lo lắng về việc tối ưu hóa công cụ tìm kiếm (SEO), nhưng Google đã từng đề cập rằng họ thích dấu gạch ngang trong tên tệp hơn dấu gạch dưới.

  10 trang web hàng đầu để tìm hiểu WordPress
  10 tip tối ưu code trên JavaScript mà web developer nào cũng nên biết

Thứ 4: Bỏ qua cAsE sEnSiTiViTy

Nếu bạn đang sử dụng Windows cho công việc của mình, bạn có thể không nhận thấy sự cố khi sử dụng chữ thường và chữ in hoa không nhất quán. Đây cũng là một sai lầm.

Giả sử bạn đã tạo một thư mục CSS có tên “Css” và một tệp bên trong nó có tên “Main.css”. Nhưng trong mã của bạn, bạn liên kết đến nó như thế này:

<link rel="stylesheet" href="css/main.css">

Khi bạn đang thực hiện project của mình sẽ không có vấn đề gì xảy ra. Nhưng khi bạn tải dự án của mình lên một máy chủ web thì sẽ không có CSS ​​nào được áp dụng cả.

Nhiều máy chủ web có một số phiên bản Linux hoặc Unix chạy thay vì Windows. Bạn có thể đã nghe nói về ngăn xếp LAMP. Linux là chữ L trong LAMP.

Các hệ thống này phân biệt chữ hoa chữ thường.

Do đó, cách tốt nhất là sử dụng tên tệp viết thường và tên thư mục mọi lúc, trừ khi có quy ước đặt tên cụ thể sử dụng chữ cái viết hoa. Tại thời điểm đó, tên tệp sẽ vẫn luôn nhất quán. Và sự nhất quán là thứ sẽ ngăn chặn sai lầm này.

Xem thêm Trước khi trở thành Web Developer mình đã phải bỏ lỡ những điều gì?

Thứ 3: Không hiểu Đường dẫn tệp

Đa phần các sinh viên không hiểu cách liên kết các tệp trong các thư mục khác nhau thường kết xuất tất cả các tệp của họ trong thư mục gốc để truy cập chúng. Đây là một sai lầm dẫn đến một file tree không được sắp xếp.

Không lâu sau khi bạn bắt đầu học HTML, bạn bắt đầu học cách liên kết với các tệp HTML và CSS khác.

Điều này khá đơn giản khi các file nằm trong cùng một thư mục. Ngay cả trong ví dụ trên, chúng tôi chỉ xem bên trong thư mục CSS cho tệp main.css. Nó bắt đầu trở nên phức tạp hơn khi chúng ta cần truy cập một thư mục thay vì (hoặc trước đó) đi xuống một thư mục.

Trong ví dụ dưới đây, chúng tôi đang đặt hình nền cho một trang web trong file main.css của chúng tôi. File main.css nằm trong thư mục CSS. Chúng tôi đang liên kết đến một hình ảnh trong thư mục img.

body {
background-image: url("../img/moon.png");
}

Cả hai thư mục này đều nằm trong thư mục gốc. Do đó, cần đi lên và ra khỏi thư mục CSS và sau đó xuống thư mục img. Chúng tôi đi lên một thư mục có hai dấu chấm: “..”

Từ đó, chúng tôi đi xuống thư mục img để liên kết đến tệp moon.png.

Nếu chúng ta cần truy cập hai thư mục, đường dẫn tệp sẽ bắt đầu như sau: “../../”

Hãy nhớ rằng, một dấu chấm cho biết thư mục bạn đang ở. Hai dấu chấm cho biết thư mục phía trên nơi bạn hiện đang ở.

lưu ý web developers

Thứ 2: Không đặt tên cho index trang mặc định

Đặt tên trang mặc định của bạn không phải là “index” là một sai lầm. Máy chủ web tìm kiếm một tệp chỉ mục. Khi bạn đang làm việc với HTML, bạn nên có một tệp index.html. Tệp này sẽ tải theo mặc định mà không hiển thị tên tệp ở cuối URL.

Đó là lý do tại sao bạn có thể truy cập dot com hoặc địa chỉ web khác yêu thích của mình và không thấy “/index.html” sau “.com” của chúng. Tệp chỉ mục tải theo mặc định.

Đúng là, trang web yêu thích của bạn có thể sử dụng nhiều hơn HTML, nhưng khái niệm này được chuyển sang các công nghệ khác như PHP (index.php), React (index.js),…

Khi bạn tiếp tục tìm hiểu, bạn sẽ thấy một số nhà phát triển chọn các tên tệp khác khi sử dụng các công nghệ khác, nhưng là người mới bắt đầu, hãy gắn bó với chỉ mục.

  10 tips để trở thành Java Developer xịn hơn

Thứ 1: Không dành thời gian nghỉ ngơi

Là một giảng viên, tôi đã nhận được nhiều email từ các sinh viên than phiền về việc họ thất vọng như thế nào khi không làm được việc. Họ đã đổ nhiều thời gian cho dự án mà vẫn không thể tìm ra lỗi.

Thông thường, vấn đề là tag hoặc biến sai chính tả, thiếu dấu chấm phẩy hoặc lỗi cú pháp nhỏ khác. Điều này xảy ra với tất cả chúng ta!

Sau khi nhìn chằm chằm vào mã trong một khoảng thời gian dài, tầm nhìn của chúng ta bị mờ đi, bộ não của chúng ta hoạt động khó khăn và những gì có thể dễ dàng nhìn thấy bằng đôi mắt mới trở nên không thể.

Đừng cảm thấy tồi tệ. Đừng tự trách mình. Hãy dành một ít thời gian để nghỉ ngơi. Đi dạo. Uống một ít cà phê. Hay đánh một giấc… Bất cứ điều gì có thể giúp bạn thoát khỏi sự lo lắng và trả lại cho bạn đôi mắt tươi tắn và đầu óc minh mẫn trở lại.

Thực sự, sai lầm này không chỉ dành cho người mới bắt đầu. Nó có thể xảy ra với bất cứ ai. Hãy quay lại làm việc khi bạn đã trở nên fresh hơn và mọi thứ chắc chắn sẽ rõ ràng cũng như dễ chịu hơn!

Bài viết được phỏng dịch theo bài viết gốc tại freecodecamp.org

Có thể bạn quan tâm:

Xem thêm Việc làm web Developer hấp dẫn trên TopDev

Todo App Flutter – Real Code

Todo App Flutter – Real Code

Bài viết được sự cho phép của tác giả Khiêm Lê

Todo App Flutter

Todo App Flutter là một ứng dụng giúp chúng ta có thể lưu lại những công việc cần làm, tránh việc chúng ta quên đi sau một thời gian. Todo app là một ứng dụng khá đơn gian mà ai học qua lập trình di động đều biết và code khi mới bắt đầu, hôm nay chúng ta sẽ cùng thực hiện điều đó.

  Biết chọn gì đây? Flutter, React Native hay Xamarin?
  Custom page transition – Flutter

Trong bài này sẽ có các phần sau:

  • Thiết kế giao diện ứng dụng
  • Thiết lập sqlite database
  • Viết code thực thi

Tạo project named Todo và bắt đầu với phần đầu tiên nào!

Thiết kế giao diện

Ý tưởng ứng dụng như sau: màn hình chính sẽ có một ListView hiện ra tất cả các task, mỗi item thì sẽ có một trailing là một button, nhấn vào sẽ hiện ra PopupMenu có hai tùy chọn là Edit và Delete. Nhấn vào Delete sẽ hiện một AlertDialog xác nhận xóa task đó. Nhấn vào Edit sẽ cho phép mình sửa task đó. Một FAB nhấn vào sẽ đưa mình đến màn hình thêm task. Màn hình thêm task đơn giản chỉ có một TextField để nhập task, một nút save phía trên thanh AppBar. Ok, bắt tay vào code nào.

Màn hình chính

Đầu tiên mình tạo một folder đặt tên là screens nằm trong folder lib. Tiếp theo, tạo một file main_screen.dart – đây chính là file màn hình chính của mình. Trong màn hình chính, mình sẽ có một ListView, một FAB, và một cái AppBar hiện tên ứng dụng. Vậy chúng ta sẽ có code sau:

import 'package:flutter/material.dart';

// Vì sau này mình sẽ lấy dữ liệu từ Database đổ vào ListView
// nên dùng StatefulWidget để có thể thay đổi được UI
class MainScreen extends StatefulWidget {
  // Mình đặt id để xíu nữa mình dùng trong routes
  static const id = 'main_screen';

  
  _MainScreenState createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
  
  Widget build(BuildContext context) {
    return Scaffold(
      // Một cái AppBar đơn giản hiển thị tên app
      appBar: AppBar(
        title: Text('Todo App'),
      ),
      // FAB sẽ là biểu tượng Add (ý là add task vào ý)
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        child: Icon(Icons.add),
      ),
      body: ListView.builder(
        // Mình demo với 9 item nha
        // Phần sau mình sẽ lấy data từ database sau
        itemCount: 9,
        itemBuilder: (context, index) {
          // Mỗi item là một ListTile
          return ListTile(
            title: Text('Task $index'),
          );
        },
      ),
    );
  }
}

Mình muốn là mỗi item có trailing là một button, khi nhấn vào nó sẽ show một PopupMenu, mình sẽ chọn sử dụng Widget PopupMenuItem. Mình muốn menu có hai item là Edit và Delete, khi nhấn vào Edit sẽ đưa mình đến màn hình Edit, khi nhấn Delete thì sẽ hiển thị AlertDialog xác nhận. Code của mình như sau:

// ...
          return ListTile(
            title: Text('Task $index'),
            trailing: PopupMenuButton(
              onSelected: (i) {
                if (i == 0) {
                  // Code chuyển sang màn hình edit
                } else if (i == 1) {
                  // Hiện dialog
                  showDialog(
                    context: context,
                    builder: (context) {
                      return AlertDialog(
                        title: Text('Confirm your deletion'),
                        content: Text(
                            'This task will be deleted permanently. Do you want to do it?'),
                        actions: <Widget>[
                          // Nút hủy, nhấn vào chỉ pop cái dialog đi thôi không làm gì thêm
                          FlatButton(
                            onPressed: () {
                              Navigator.pop(context);
                            },
                            child: Text('CANCEL'),
                          ),
                          FlatButton(
                            onPressed: () {
                              // Xóa task...
                              Navigator.pop(context);
                            },
                            child: Text(
                              'DELETE',
                              style: TextStyle(color: Colors.red),
                            ),
                          ),
                        ],
                      );
                    },
                  );
                }
              },
              itemBuilder: (context) {
                return [
                  PopupMenuItem(
                    value: 0,
                    child: Text('Edit'),
                  ),
                  PopupMenuItem(
                    value: 1,
                    child: Text('Delete'),
                  ),
                ];
              },
            ),
          );
// ...

Màn hình thêm task

Trong thư mục screens, mình tạo một file mới tên là add_task_screen.dart. Trong màn hình này, mình muốn trên AppBar có một IconButton save, nút back cũng sẽ được mình Override (mình sẽ giải thích phần này sau). Code của mình như sau:

import 'package:flutter/material.dart';

class AddTaskScreen extends StatefulWidget {
  // Mình đặt id dùng trong routes
  static const id = 'add_task_screen';

  
  _AddTaskScreenState createState() => _AddTaskScreenState();
}

class _AddTaskScreenState extends State<AddTaskScreen> {
  final _taskController = TextEditingController();
  bool _inSync = false;
  String _taskError;

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Add task'),
        backgroundColor: Colors.white,
        leading: IconButton(
          icon: Icon(Icons.arrow_back),
          // Nút back trên appbar sẽ không nhấn được khi đang lưu dữ liệu
          onPressed: !_inSync
              ? () {
                  Navigator.pop(context);
                }
              : null,
        ),
        actions: <Widget>[
          // Tương tự, như nút back tránh trường hợp user nhấn 2 lần
          !_inSync
              ? IconButton(
                  icon: Icon(Icons.done),
                  onPressed: () {
                    
                  },
                )
              : Icon(Icons.refresh),
        ],
        elevation: 0.0,
        textTheme: TextTheme(
          title: Theme.of(context).textTheme.title,
        ),
        iconTheme: IconThemeData(
          color: Colors.black87,
        ),
      ),
      body: WillPopScope(
        // Ngăn nút người dùng nhấn back trên android khi đang lưu dữ liệu
        onWillPop: () async {
          if (!_inSync) return true;
          return false;
        },
        child: Padding(
          padding: EdgeInsets.all(16.0),
          child: TextField(
            controller: _taskController,
            decoration: InputDecoration(
              labelText: 'Task',
              errorText: _taskError,
              border: OutlineInputBorder(),
            ),
          ),
        ),
      ),
    );
  }
}

Giờ đến lượt file main.dart, chúng ta cần phải thêm các màn hình này vào để navigate giữa chúng. File main.dart như sau:

import 'package:flutter/material.dart';

// import screens
import 'screens/main_screen.dart';
import 'screens/add_task_screen.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: MainScreen.id,
      routes: {
        MainScreen.id: (_) => MainScreen(),
        AddTaskScreen.id: (_) => AddTaskScreen(),
      },
    );
  }
}

Giờ chúng ta sẽ sửa lại file main_screen.dart, chúng ta sẽ bắt sự kiện onPress FAB thì đi sang màn hình add task. Code sửa lại như sau:

import 'package:flutter/material.dart';

// import screens
import 'add_task_screen.dart';

// ...
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.pushNamed(context, AddTaskScreen.id);
        },
        child: Icon(Icons.add),
      ),
// ...

Giờ bạn có thể run app để check thử. Chúng ta sẽ chuyển sang phần tiếp theo là thiết lập sqlite database.

Thiết lập SQLite database

Đầu tiên, tạo một folder mới trong folder lib và đặt tên là models. Trong folder models, bạn tạo một file mới có tên là task.dart, đây sẽ là model data của mình. Code như sau:

class Task {
  // Task đơn giản chỉ cần 1 id và task
  final int id;
  final String task;
  // constructor
  Task({this.id, this.task});

  // function chuyển properties của class Task sang Map để lưu trong database
  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'task': task,
    };
  }
}

Giờ chúng ta đã có model Task, tiếp theo chúng ta cần phải lưu trữ data trong database. Tạo folder có tên database trong folder lib, trong folder database tạo một file mới có tên là tasks_db.dart. Trước khi code trong file này, mình cần phải thêm 2 dependencies là path và sqflite và file pubspec.yaml:

// ...
dependencies:
  flutter:
    sdk: flutter
  path:
  sqflite:
// ...

Nhớ chạy lệnh “flutter pub get” để lấy dependencies nha. Tiếp tục với file tasks_db.dart, mình sẽ có code sau:

import 'dart:async';

import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';

// import Task model
import '../models/task.dart';

class TasksDB {
  Database _database;

  // Mình để các biến final để sau này chỉ cần đổi một chỗ thôi cho tiện
  final String kTableName = 'tasks';
  final String kId = 'id';
  final String kTask = 'task';

  // Hàm mở database
  Future _openDB() async {
    // openDatabase được cung cấp bởi sqflite
    _database = await openDatabase(
      // lấy đường dẫn database, tasks.db là tên file do mình đặt
      join(await getDatabasesPath(), 'tasks.db'),
      onCreate: (db, version) {
        // Truy vấn tạo table khi database được tạo
        return db.execute(
            'CREATE TABLE $kTableName($kId INTEGER PRIMARY KEY AUTOINCREMENT, $kTask TEXT)');
      },
      // Phiên bản của database
      version: 1,
    );
  }

  // Thêm task vào database
  Future insert(Task task) async {
    // Phải chờ mở database trước khi thao tác tiếp
    await _openDB();
    // Thêm task sau khi đã được convert sang Map vào table kTableName
    await _database.insert(kTableName, task.toMap());
    print('Task inserted');
  }

  // Hàm cập nhật task
  Future update(Task task) async {
    await _openDB();
    // Cập nhật lại task tại record có id là id của task truyền vào
    await _database.update(
      kTableName,
      task.toMap(),
      where: '$kId = ?',
      whereArgs: [task.id],
    );
    print('Task updated');
  }

  // Xóa task
  Future delete(int id) async {
    await _openDB();
    // Xóa task có id là id được truyền vào
    print((await _database.delete(
      kTableName,
      where: '$kId = ?',
      whereArgs: [id],
    )));
    print('Task deleted');
  }

  // Lấy toàn bộ task trong database
  Future<List<Task>> getTasks() async {
    await _openDB();
    // Query toàn bộ table kTableName về một List<Map>
    List<Map<String, dynamic>> maps = await _database.query(kTableName);
    // Chuyển List<Map> về dạng List<Task> và return về List đó
    return List.generate(
        maps.length,
        (i) => Task(
              id: maps[i][kId],
              task: maps[i][kTask],
            ));
  }
}

Vậy là chúng ta đã thiết lập xong database. Giờ chúng ta sẽ thực hiện nối UI và code thực thi lại với nhau.

Viết code thực thi

Chúng ta sẽ bắt đầu với file add_task_screen.dart trước. Sẽ có một sự thay đổi lớn ở đoạn này, mình sẽ giải thích trong code. Đoạn code nào được add comment “// new” là mới thêm vào.

import 'package:flutter/material.dart';

import '../database/tasks_db.dart'; // new
import '../models/task.dart'; // new

class AddTaskScreen extends StatefulWidget {
  static const id = 'add_task_screen';

  final Task task; // new

  AddTaskScreen(this.task); // new

  
  _AddTaskScreenState createState() => _AddTaskScreenState();
}

class _AddTaskScreenState extends State<AddTaskScreen> {
  final _taskController = TextEditingController();
  bool _inSync = false;
  String _taskError;

   // new
  void initState() { // new
    Task task = widget.task; // new
    // Nếu có task được truyền qua màn hình add, tức là đang chỉnh sửa task
    if (task != null) { // new
      // Thực hiện gán task vào TextField
      _taskController.text = task.task; // new
    } // new
    super.initState(); // new
  } // new

  void addTask() async { // new
    // Kiểm tra TextField xem có trống hay không
    if (_taskController.text.isEmpty) { // new
      setState(() { // new
        _taskError = 'Please enter this field'; // new
      }); // new
      return null; // new
    } // new
    setState(() { // new
      _taskError = null; // new
      _inSync = true; // new
    }); // new
    final db = TasksDB(); // new
    final task = Task( // new
      task: _taskController.text.trim(), // new
    ); // new
    // insert task vào database
    await db.insert(task); // new
    setState(() { // new
      _inSync = false; // new
    }); // new
    // Trở về màn hình chính với giá trị trả về là true
    Navigator.pop(context, true); // new
  } // new

  void updateTask() async { // new
    if (_taskController.text.isEmpty) { // new
      setState(() { // new
        _taskError = 'Please enter this field'; // new
      }); // new
      return null; // new
    } // new
    setState(() { // new
      _taskError = null; // new
      _inSync = true; // new
    }); // new
    final db = TasksDB(); // new
    // Update task với giá trị mới ở record có id là id của task truyền vào
    final task = Task( // new
      id: widget.task.id, // new
      task: _taskController.text.trim(), // new
    ); // new
    await db.update(task); // new
    setState(() { // new
      _inSync = false; // new
    }); // new
    Navigator.pop(context, true); // new
  } // new

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Add task'),
        backgroundColor: Colors.white,
        leading: IconButton(
          icon: Icon(Icons.arrow_back),
          onPressed: !_inSync
              ? () {
                  Navigator.pop(context);
                }
              : null,
        ),
        actions: <Widget>[
          !_inSync
              ? IconButton(
                  icon: Icon(Icons.done),
                  onPressed: () {
                    // Nếu như có truyền vào task tức là mình update
                    // nếu không thì add task
                    widget.task == null ? addTask() : updateTask(); // new
                  },
                )
              : Icon(Icons.refresh),
        ],
        elevation: 0.0,
        textTheme: TextTheme(
          title: Theme.of(context).textTheme.title,
        ),
        iconTheme: IconThemeData(
          color: Colors.black87,
        ),
      ),
      body: WillPopScope(
        onWillPop: () async {
          if (!_inSync) return true;
          return false;
        },
        child: Padding(
          padding: EdgeInsets.all(16.0),
          child: TextField(
            controller: _taskController,
            decoration: InputDecoration(
              labelText: 'Task',
              errorText: _taskError,
              border: OutlineInputBorder(),
            ),
          ),
        ),
      ),
    );
  }
}

Giờ là đến file main_screen.dart chúng ta có code như sau:

import 'package:flutter/material.dart';

import '../database/tasks_db.dart'; // new
import '../models/task.dart'; // new

// import screens
import 'add_task_screen.dart';

class MainScreen extends StatefulWidget {
  static const id = 'main_screen';

  
  _MainScreenState createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
  List<Task> tasks = []; // new

  Future getTasks() async { // new
    // Lấy tất cả task và gán vào list tasks
    final db = TasksDB(); // new
    tasks = await db.getTasks(); // new
    setState(() {}); // new
  } // new

  Future deleteTask(int id) async { // new
    // Xóa task ở record có id là id được truyền vào
    final db = TasksDB(); // new
    await db.delete(id); // new
    tasks = await db.getTasks(); // new
    await getTasks(); // new
    setState(() {}); // new
  } // new

   // new
  void initState() { // new
    getTasks(); // new
    super.initState(); // new
  } // new

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Todo App'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          // Navigate sang màn hình add task và chờ kết quả trả về
          final result = await Navigator.pushNamed(context, AddTaskScreen.id); // Edited
          // Nếu kết quả trả về là true tức là có thêm task nên ta sẽ cập nhật lại list tasks
          if (result == true) getTasks(); // new
        },
        child: Icon(Icons.add),
      ),
      body: ListView.builder(
        itemCount: tasks.length, // Edited
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(tasks[index].task), // Edited
            trailing: PopupMenuButton(
              onSelected: (i) async {
                if (i == 0) {
                  // Tương tự như FAB add task, ta chờ xem có update task thì up
                  // lại list tasks
                  final result = await Navigator.pushNamed( // Edited
                    context,  // new
                    AddTaskScreen.id,  // new
                    // truyền task qua màn hình add task để edit
                    arguments: tasks[index],  // new
                  );  // new
                  if (result == true) getTasks(); // new
                } else if (i == 1) {
                  showDialog(
                    context: context,
                    builder: (context) {
                      return AlertDialog(
                        title: Text('Confirm your deletion'),
                        content: Text(
                            'This task will be deleted permanently. Do you want to do it?'),
                        actions: <Widget>[
                          FlatButton(
                            onPressed: () {
                              Navigator.pop(context);
                            },
                            child: Text('CANCEL'),
                          ),
                          FlatButton(
                            onPressed: () {
                              // delete task có id  là id của item hiện tại
                              deleteTask(tasks[index].id); // new
                              Navigator.pop(context);
                            },
                            child: Text(
                              'DELETE',
                              style: TextStyle(color: Colors.red),
                            ),
                          ),
                        ],
                      );
                    },
                  );
                }
              },
              itemBuilder: (context) {
                return [
                  PopupMenuItem(
                    value: 0,
                    child: Text('Edit'),
                  ),
                  PopupMenuItem(
                    value: 1,
                    child: Text('Delete'),
                  ),
                ];
              },
            ),
          );
        },
      ),
    );
  }
}

Chúng ta đã xong 2 file screen rồi, nhưng nếu bạn để ý bạn sẽ thấy, mình sử dụng Constructor để nhận dữ liệu, vậy làm sao có thể dùng thuộc tính arguments để truyền dữ liệu? Chúng ta sẽ chỉnh sửa lại file main.dart để hoàn thành việc đó. Ta sẽ có code như sau:

      routes: {
        MainScreen.id: (_) => MainScreen(),
        AddTaskScreen.id: (_) => AddTaskScreen(), // Xóa dòng này đi
      },
      // Thêm đoạn code bên dưới vào
      onGenerateRoute: (settings) {
        // Nếu Navigator được gọi và màn hình đến là AddTaskScreen
        if (settings.name == AddTaskScreen.id) {
          return MaterialPageRoute(
            builder: (context) {
              // Nếu có dữ liệu truyền vào thì đưa qua constructor
              if (settings.arguments != null) {
                Task task = settings.arguments;
                return AddTaskScreen(task);
              }
              // default là null
              return AddTaskScreen(null);
            },
          );
        }
        return null;
      },

Tổng kết

Vậy là chúng ta đã viết được một Todo App Flutter đơn giản rồi. Mình đã upload toàn bộ Source code lên github rồi.

Vậy là trong bài này, mình đã code xong app Todo sử dụng Flutter và các plugin Flutter như path, sqflite. Hy vọng bài viết này sẽ có ích cho các bạn, nếu bạn thấy hay có thể share để mọi người cùng đọc. Cảm ơn các bạn đã đọc bài viết của mình!

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

Có thể bạn quan tâm:

Xem thêm Việc làm Developer hấp dẫn trên TopDev

Chạy Python web app

Chạy Python web app

Bài viết được sự cho phép của tác giả Nguyễn Việt Hưng

Làm một trang web bằng Python là việc cực kỳ đơn giản, 5-7 dòng code với Flask framework, bạn đã sẵn sàng để khoe website “hello world” với cả trái đất này.

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello PyMi.vn!"

Nhưng sẵn sàng khoe không có nghĩa là đã khoe được. Bạn cần phải tiếp tục vật lộn để mang tuyệt tác ấy đến cho cả thế giới này được biết.

Làm thế nào để cho cả “cộng đồng” cùng xem được website của bạn? Nên nhớ, để website chạy được, phải có một cái máy nào đó luôn chạy code Python mà bạn đã viết. Tự nguyện xung phong bật máy tính của mình 24/7 là một lựa chọn dũng cảm, nhưng nhiều khó khăn và rắc rối và tốn tiền. Vì vậy, tìm một “cái máy khác” hay các dịch vụ hosting/VPS/server để chạy thì khỏe người hơn.

Những người dùng đến từ thế giới PHP sẽ đi tìm ngay Python free hosting, nhưng kết quả sẽ không có gì dùng được, đây không phải PHP. Nên từ khóa “free hosting” sẽ không giúp gì cho bạn cả, trong Python cũng chẳng ai nhắc tới khái niệm này.

Tài liệu của Flask – phần hướng dẫn “triển khai” (deployment) có liệt kê vài lựa chọn phổ biến. Trong đó, đáng kể – tiện lợi – và đơn giản nhất là chạy miễn phí với tài khỏan Heroku – có thể chạy 1 website miễn phí, không yêu cầu thẻ tín dụng hay thanh toán – đủ để bạn chạy 1 trang web Python nhỏ.

Dù Heroku đã có hướng dẫn rất chi tiết với một Django app, bài này sẽ thực hiện deploy một website Flask đơn giản lên Heroku.

  Python: Sự khác nhau giữa List và Tuple?

Tạo tài khoản Heroku miễn phí

Vào trang heroku.com rồi chọn “Sign Up”, điền thông tin, Heroku sẽ gửi mail, mở email và xác nhận.

Chạy Python web app

Cài đặt câu lệnh “heroku”, git

Tải chương trình “heroku” theo hướng dẫn ở https://devcenter.heroku.com/articles/getting-started-with-python#set-up , mỗi hệ điều hành sẽ có cách cài riêng.

Git là chương trình để quản lý code của bạn (và chia sẻ với người khác). Thay vì phải nén các file code vào zip, copy qua lại, dùng FTP như thời năm 2000, thì ngày nay người ta sử dụng git để “copy” code về hay “đưa” code đi. Cài đặt git nằm ngoài phạm vi bài viết này, xem thêm tại vinagit.

Tạo một “heroku app”

Gõ lệnh sau tại thư mục chứa code

$ heroku create
Creating app... done, ⬢ glacial-lowlands-37256
https://glacial-lowlands-37256.herokuapp.com/ | https://git.heroku.com/glacial-lowlands-37256.git

Heroku sẽ yêu cầu bạn nhập tài khỏan, rồi tạo một “app” cho bạn, đồng thời thiết lập git để sẵn sàng “đẩy code lên Heroku”

Các file cần có:

  • requirements.txt – chứa danh sách các thư viện cần cho chương trình. Trong ví dụ này có 2 dòng, flask là web framework ta dùng ở đây, và gunicorn – là chương trình server sẽ chạy code Python.
    flask==0.12.2
    gunicorn
    
  • Procfile – chứa câu lệnh mà heroku sẽ chạy. Trong ví dụ này, heroku sẽ chạy app Flask bằng lệnh gunicorn
    # Procfile
    web: gunicorn app:app --log-file -
    
  • Code Flask webapp nằm trong file app.py hiển thị dòng chữ “Hello PyMi.vn!” khi người dùng vào trang chủ
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello PyMi.vn!"

Xem đầy đủ code tại pymivn/flask_heroku.

  Selenium WebDriver trên Python

Đưa code lên heroku

Sau khi đã add hết các file cần thiết vào git, gõ lệnh để đưa code (push) lên Heroku,

$ git push heroku master
Counting objects: 8, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (8/8), 701 bytes | 0 bytes/s, done.
Total 8 (delta 1), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Python app detected
remote: -----> Installing python-3.6.2
remote: -----> Installing pip
remote: -----> Installing requirements with pip
remote:        Collecting flask==0.12.2 (from -r /tmp/build_332af31aead5e8121044856aecc9b1ca/requirements.txt (line 1))
remote:          Downloading Flask-0.12.2-py2.py3-none-any.whl (83kB)
remote:        Collecting gunicorn (from -r /tmp/build_332af31aead5e8121044856aecc9b1ca/requirements.txt (line 2))
remote:          Downloading gunicorn-19.7.1-py2.py3-none-any.whl (111kB)
remote:        Collecting itsdangerous>=0.21 (from flask==0.12.2->-r /tmp/build_332af31aead5e8121044856aecc9b1ca/requirements.txt (line 1))
remote:          Downloading itsdangerous-0.24.tar.gz (46kB)
remote:        Collecting Werkzeug>=0.7 (from flask==0.12.2->-r /tmp/build_332af31aead5e8121044856aecc9b1ca/requirements.txt (line 1))
remote:          Downloading Werkzeug-0.12.2-py2.py3-none-any.whl (312kB)
remote:        Collecting Jinja2>=2.4 (from flask==0.12.2->-r /tmp/build_332af31aead5e8121044856aecc9b1ca/requirements.txt (line 1))
remote:          Downloading Jinja2-2.9.6-py2.py3-none-any.whl (340kB)
remote:        Collecting click>=2.0 (from flask==0.12.2->-r /tmp/build_332af31aead5e8121044856aecc9b1ca/requirements.txt (line 1))
remote:          Downloading click-6.7-py2.py3-none-any.whl (71kB)
remote:        Collecting MarkupSafe>=0.23 (from Jinja2>=2.4->flask==0.12.2->-r /tmp/build_332af31aead5e8121044856aecc9b1ca/requirements.txt (line 1))
remote:          Downloading MarkupSafe-1.0.tar.gz
remote:        Installing collected packages: itsdangerous, Werkzeug, MarkupSafe, Jinja2, click, flask, gunicorn
remote:          Running setup.py install for itsdangerous: started
remote:            Running setup.py install for itsdangerous: finished with status 'done'
remote:          Running setup.py install for MarkupSafe: started
remote:            Running setup.py install for MarkupSafe: finished with status 'done'
remote:        Successfully installed Jinja2-2.9.6 MarkupSafe-1.0 Werkzeug-0.12.2 click-6.7 flask-0.12.2 gunicorn-19.7.1 itsdangerous-0.24
remote:
remote: -----> Discovering process types
remote:        Procfile declares types -> web
remote:
remote: -----> Compressing...
remote:        Done: 42.8M
remote: -----> Launching...
remote:        Released v3
remote:        https://glacial-lowlands-37256.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/glacial-lowlands-37256.git
 * [new branch]      master -> master

Heroku sẽ tự động cài đặt và chạy code, nó sẽ in ra đường dẫn đến trang web lên màn hình https://glacial-lowlands-37256.herokuapp.com/

hoặc nếu không thấy thì gõ:

heroku open

Câu lệnh sẽ tự mở trang web tại địa chỉ https://glacial-lowlands-37256.herokuapp.com/

Xem kết quả

Chạy Python web app

Chú ý

  • SQlite sẽ không dùng được trên Heroku do Heroku sử dụng công nghệ container (như Docker), mỗi lần deploy lại code, các file được sinh ra sẽ bị xóa hết. File SQLite database sẽ bị xóa sau mỗi lần như vậy.
  • Thay vì dùng SQLite, hãy dùng PostgreSQL. Heroku hỗ trợ 1 free database PostgreSQL – database opensource xịn nhất thế giới, xem thêm tại bài hướng dẫn của Heroku.

Tổng kết

Bài này hướng dẫn chạy một Flask web app đơn giản trên Heroku, cho cả thế giới thấy sản phẩm của bạn.

  • Tài khỏan miễn phí của heroku cho phép bạn chạy 1 website nhỏ 550 tiếng “Dynos” mỗi tháng – nói nôm na thì nếu không có nhiều người vào, thời lượng này sẽ đủ chạy cả tháng – và miễn phí. Bao giờ đòi thu tiền Heroku sẽ báo, đến lúc ấy hãy phải lo.
  • Mỗi tài khoản sẽ chỉ được giới hạn miễn phí 550 giờ một tháng (~ 22 ngày CPU chạy liên tục), nên nói chung thì sẽ chỉ chạy được 1 website / tài khoản.
  • Khi “chạy thật”, hầu hết các Python web app sẽ được chạy trên các “máy ảo”/”server”/”cloud” riêng, người dùng sẽ tự lo cài đặt database, tạo virtualenv, cài requirements và chạy code. Heroku là dịch vụ rất tiện, nhưng rất đắt khi website bắt đầu lớn lên.

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

Có thể bạn quan tâm:

Xem thêm tìm việc python hấp dẫn trên TopDev

Hướng dẫn cài đặt, lập trình Python trên Ubuntu (Linux)

Hướng dẫn cài đặt, lập trình Python trên Ubuntu (Linux).

Bài viết được sự cho phép của tác giả Trần Hữu Cương

Hướng dẫn cài đặt, lập trình Python trên Ubuntu (Linux).

Cài Python qua repository

Đây là cách đơn giản nhất, các bạn không cần phải lên trang chủ python để tải file, giải nén…

Chỉ cần chạy lệnh là được.

B1: Update lại repository

sudo apt-get update

B2: Cài python

sudo apt-get install python

Mặc định nó sẽ cài cho bạn 2 bản python là 2.x và 3.x

Để kiểm tra version của python sau khi cài đặt các bạn dùng lệnh sau:

Với python 2.x: python hoặc python -V

Hướng dẫn cài đặt, lập trình Python trên Ubuntu (Linux)

Với python 3.x: python3 hoặc python3 -V

Hướng dẫn cài đặt, lập trình Python trên Ubuntu (Linux)

  Học Python: Từ Zero đến Hero (phần 1)

Demo:

Chạy lệnh python và nhập print('hello') sau đó ấn enter sẽ thấy nó in ra dòng hello

Hướng dẫn cài đặt, lập trình Python trên Ubuntu (Linux)

  Chuyển đổi hệ cơ số trong Python

Cài đặt pip cho python

pip (python package manager) là một trình quản lý module, thư viện của python.

Ví dụ bạn muốn thực hiện kết nối tới database bằng python thì bạn phải có module mysqlclient (trong java thì là thư viện jdbc). Nhưng khi cài python nó chưa có sẵn module đó, để cài module mysqlclient ta cài qua pip bằng lệnh pip install mysqlclient

Thông thường các bản python mới sẽ tích hợp sẵn pip. Để kiểm tra pip đã cài chưa, các bạn dùng lệnh pip –version với python 2.x hoặc pip3 –version với python 3.x

Hướng dẫn cài đặt, lập trình Python trên Ubuntu (Linux)

Trường hợp python của bạn chưa tích hợp sẵn pip thì bạn có thể cài đặt pip bằng lệnh sau:

sudo apt-get install pip

hoặc

sudo apt-get install pip3

References:

https://docs.python.org/3/

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

Có thể bạn quan tâm:

Xem thêm việc làm developer hấp dẫn trên TopDev

Cho phép tùy chọn Giao diện trong Spring Web MVC framework

Cho phép tùy chọn Giao diện trong Spring Web MVC framework

Bài viết được sự cho phép của tác giả Trần Thị Thu Hà

Để tăng độ hài lòng của khách truy cập, trang web có thêm tùy chọn Giao diện theo ý muốn của người dùng. Trong bài viết này, chúng mình sẽ chia sẻ với bạn cách xây dựng một ứng dụng Spring Web MVC như thế.

  Giải thích mô hình MVC thông qua … cốc trà đá
  Google AMP là gì ? Cài đặt AMP cho website asp.net mvc

Bạn cần khởi tạo project bằng Maven archetype có tên maven-archetype-webapp, nếu bạn chưa rõ cách khởi tạo hãy xem bài viết Kỹ thuật Autowiring sử dụng annotation trong Spring Framework. Cấu trúc thư mục của ứng dụng sẽ như sau:

Cho phép tùy chọn Giao diện trong Spring Web MVC framework

Chúng ta sẽ tạo 14 files mã nguồn bao gồm:

  1. JobController.java
  2. Job.java
  3. beach.properties
  4. classic.properties
  5. modern.properties
  6. beach.css
  7. classic.css
  8. modern.css
  9. addjob.jsp
  10. resultJob.jsp
  11. springmvc-servlet.xml
  12. web.xml
  13. index.jsp
  14. pom.xml

Sau khi Maven tự động tạo cho bạn một số thư mục có sẵn, bạn cần tạo thêm thư mục java, css, pages như trong ảnh chụp màn hình. Sửa lại file pom.xml có nội dung như sau:

Ứng dụng Spring Web MVC cần tối thiểu các dependencies là: spring-core, spring-beans, spring-context, spring-web, spring-webmvc. Chúng ta bổ sung thêm thư viện JUnit để sau này viết Unit test, bổ sung thêm hibernate-validator để validate dữ liệu nhập vào qua form. Tất cả các dependency đều được khai báo 3 tham số GAV (GroupId – ArtifactId – Version). Khi khao báo version cho dependency, chúng ta sử dụng cách tổng quát hóa, khai báo số phiên bản qua properties trong pom.xml, đây là best practice, giả sử khi Spring Framework phát hành phiên bản 4.3.0.RELEASE, thì chúng ta chỉ cần thay đổi giá trị phiên bản này trong 1 vị trí chứ không phải 5 vị trí:

Là một ứng dụng web, nên trước khi xây dựng ứng dụng, bạn cần để ý đến file web.xml (deployment descriptor file). Từ dòng 7 – 18 là nội dung filter, filter này đảm bảo cho việc truyền dữ liệu ký tự Unicode tiếng Việt được đúng. Nếu bỏ qua thiết lập này, ứng dụng của bạn sẽ bị lỗi Font tiếng Việt. Filter có tên encodingFilter  này tác động đến tất cả các đường dẫn ứng dụng (được mô tả bằng <urlpatter>/*</urlpattern> ). Dòng 26 và 31 là đặc biệt quan trọng, để Spring Web MVC xử lý luồng đi của ứng dụng web.

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/webapp_3_1.xsd"
version="3.1">

<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.mvc</url-pattern>
</servlet-mapping>

</web-app>

Tạo thực thể Job (công việc). Các quy tắc kiểm tra tính hợp lệ dữ liệu đầu vào được thiết lập bằng annotation: @Size, @NotNull. Hibernate Validator phụ trách việc này.Một công việc có 8 thuộc tính là:

  1. title: Tiêu đề công việc
  2. company: Công ty tuyển dụng
  3. companyAddress: Địa chỉ công ty, nơi làm việc
  4. content: Chi tiết về yêu cầu tuyển dụng, chế độ đãi ngộ, thời gian làm việc, v.v..
  5. startDate: Ngày đăng tin
  6. endDate: Ngày gỡ tin tuyển dụng
  7. salary: Tiền lương
  8. createDate: Thời điểm tạo bản tin tuyển dụng

Controller:

package vn.smartjob.demospring.controller;

import vn.smartjob.demospring.domain.Job;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class JobController {

@RequestMapping(value = "/form")
public ModelAndView job() {
return new ModelAndView("addJob", "job", new Job());
}

@RequestMapping(value = "/result", method = RequestMethod.POST)
public ModelAndView processJob(Job job, BindingResult result) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("j", job);
if (result.hasErrors()) {
modelAndView.setViewName("addJob");
} else {
modelAndView.setViewName("resultJob");
}
return modelAndView;
}

}

Cấu hình bean cho Spring Framework. Nghiệp vụ tùy chọn giao diện (theme) của người dùng được xử lý ở dòng thứ 24-26.

Class  org.springframework.web.servlet.theme.ThemeChangeInterceptor chịu trách nhiệm về việc này. Giao diện (theme) mặc định là modern (Hiện đại), là giao diện xuất hiện khi người dùng chưa đưa ra tùy chọn riêng cho mình.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

<context:component-scan base-package="vn.smartjob.demospring"/>
<context:annotation-config/>
<mvc:annotation-driven/>

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>

<bean id="themeSource" class="org.springframework.ui.context.support.ResourceBundleThemeSource"/>

<bean id="themeResolver" class="org.springframework.web.servlet.theme.SessionThemeResolver">
<property name="defaultThemeName" value="modern"/>
</bean>

<mvc:interceptors>
<bean id="themeChangeInterceptor" class="org.springframework.web.servlet.theme.ThemeChangeInterceptor">
<property name="paramName" value="theme"/>
</bean>
</mvc:interceptors>

</beans>


Chúng ta sẽ tạo 3 giao diện có tên là:

  • Classic: Cổ điển
  • Modern: Hiện đại
  • Beach: Bờ biển

3 tập tin css tương ứng là:

File classic.css

body {
background-color: #ffcab3;
color: #ffe48e;
}

.error {
background-color: gainsboro;
}
table{
width: 600px;
background-color: #2898ff;
}
td{
width: 300px;
}

File modern.css

body {
background-color: white;
color: black;
}

.error {
background-color: lime;
}
table{
width: 600px;
background-color: darkcyan;
}
td{
width: 300px;
}

File beach.css

body {
background-color: #28e2ff;
color: green;
}

.error {
background-color: beige;
}
table{
width: 600px;
background-color: lightblue;
}
td{
width: 300px;
}

Tạo 3 tập tin *.properties nằm trong thư mục resources:

File classic.properties

File modern.properties

File beach.properties

Cần 2 trang JSP, một trang để thêm công việc mới và một trang để hiển thị kết quả:

Trang thêm mới công việc addJob.jsp . Bạn phải nhớ khai báo thư viện tag sử dụng như ở dòng 2 và dòng 3. Hãy để ý vào dòng 8, 13 và 14. Khi người dùng bấm chọn Giao diện, thì đường dẫn đến file css giao diện thay đổi, được xử lý bởi tag <spring> .

<%@ page contentType="text/html;charset=UTF-8" %>
<%@taglib prefix="mvc" uri="http://www.springframework.org/tags/form" %>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<html>
<head>
<title>Thêm công việc mới</title>
<link rel="stylesheet" href="<spring:theme code="style"/>" type="text/css"/>
</head>
<body>

<h2>Thêm công việc mới</h2>
Giao diện: <a href="?theme=classic">Cổ điển</a> - <a href="?theme=modern">Hiện đại</a> - <a href="?theme=beach">Bờ
biển</a><br/>

<mvc:form modelAttribute="job" action="result.mvc">
<table>
<tr>
<td><mvc:label path="title">Tiêu đề công việc</mvc:label></td>
<td><mvc:input path="title" cssErrorClass="error"/></td>
<td><mvc:errors path="title"/></td>
</tr>
<tr>
<td><mvc:label path="company">Công ty</mvc:label></td>
<td><mvc:input path="company" cssErrorClass="error"/></td>
<td><mvc:errors path="company"/></td>
</tr>
<tr>
<td><mvc:label path="companyAddress">Địa chỉ làm việc</mvc:label></td>
<td><mvc:input path="companyAddress" cssErrorClass="error"/></td>
<td><mvc:errors path="companyAddress"/></td>
<td></td>
</tr>
<tr>
<td><mvc:label path="content">Nội dung tuyển dụng</mvc:label></td>
<td><mvc:textarea path="content" cssErrorClass="error"/></td>
<td><mvc:errors path="content"/></td>
</tr>
<tr>
<td><mvc:label path="salary">Mức lương</mvc:label></td>
<td><mvc:input path="salary" cssErrorClass="error"/></td>
<td><mvc:errors path="salary"/></td>
</tr>
<tr>
<td><mvc:label path="startDate">Ngày bắt đầu đăng tin (mm/dd/yyyy)</mvc:label></td>
<td><mvc:input path="startDate" cssErrorClass="error"/></td>
<td><mvc:errors path="startDate"/></td>
</tr>
<tr>
<td><mvc:label path="endDate">Ngày gỡ bỏ tin (mm/dd/yyyy)</mvc:label></td>
<td><mvc:input path="endDate" cssErrorClass="error"/></td>
<td><mvc:errors path="endDate"/></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Gửi">
</td>
</tr>
</table>
</mvc:form>

</body>
</html>

Trang hiển thị kết quả resultJob.jsp

Trang chủ index.jsp sẽ trỏ về trang thêm mới công việc

Đến nay chúng ta đã tạo đủ 14 file và project đã hoàn tất, chạy ứng dụng trên Apache Tomcat để thấy kết quả:

Giao diện mặc định là Hiện đại (Modern) được thiết lập ở dòng 25 trong file springmvc-servlet.xml

Cho phép tùy chọn Giao diện trong Spring Web MVC framework

Bấm chọn và đổi giao diện, ta có giao diện Cổ điển (classic):

Giao diện Bờ biển (beach):

Cho phép tùy chọn Giao diện trong Spring Web MVC framework

Sau khi bấm nút Gửi, nếu dữ liệu hợp lệ, sẽ trả về trang kết quả:

Cho phép tùy chọn Giao diện trong Spring Web MVC framework

(Nội dung xuất hiện trong tất cả ảnh chụp màn hình chỉ mang tính chất minh họa)

Tải về mã nguồn từ server SmartJob: theme hoặc GitHub: https://github.com/SmartJobVN/spring_theme

Đỗ Như Vý – Bài viết gốc được đăng tải tại smartjob.vn

Có thể bạn quan tâm:

Xem thêm Jobs Developer hấp dẫn trên TopDev

Bảng băm trong C++

https://topdev.vn/blog/system-design-co-ban-consistent-hashing/

Bài viết được sự cho phép của tác giả Khiêm Lê

Bảng băm là gì?

Bảng băm hay HashTable là một cấu trúc mà khi người dùng thực hiện truy xuất một phần tử qua khóa thì nó sẽ được ánh xạ vào thông qua hàm băm (Hash function).

  1001 Tips: Con trỏ và hàm (Pointer & Function) trong C++
  C Token là gì? Cú pháp trong lập trình C/C++

Quá trình ánh xạ khóa vào bảng băm được thực hiện thông qua hàm băm (Hashing). Một bảng băm tốt cần phải có hàm băm tốt. Bảng băm là một mảng có M vị trí được đánh số từ 0 đến M – 1.

Bảng băm

Có rất nhiều cách cài đặt kết nối của bảng băm như trực tiếp, dò tuyến tính, dò bậc hai, băm kép… Trong bài viết này mình sẽ giới thiệu đến các bạn phương pháp kết nối dò trực tiếp. Nhưng trước tiên, ta cần tìm hiểu hàm băm trước đã vì như mình đã nói, một bảng băm tốt khi nó có hàm băm tốt.

Trước khi vào bài, phương pháp kết nối trực tiếp là phương pháp sử dụng danh sách liên kết đơn, do đó, bạn nào chưa biết gì về danh sách liên kết đơn thì hãy xem lại bài Danh sách liên kết đơn trong C++ để hiểu rõ hơn nha.

Hàm băm

Hàm băm hay là Hash function là hàm thực hiện việc ánh xạ khóa k nào đó vào trong bảng băm (h(k)). Một hàm băm tốt thỏa mản các tiêu chí sau:

  • Tốc độ tính toán nhanh
  • Các khóa được phân bố đều trong bảng
  • Ít xảy ra đụng độ

Mình sẽ giới thiệu đến các bạn các phép băm thường được sử dụng nhất là phương pháp chia và nhân.

Đối với phương pháp chia, mình sẽ ánh xạ khóa theo hàm h(k) = k % M, với k là khóa và M là kích thước của bảng băm.

Đối với phương pháp nhân, hàm ánh xạ h(k) = M * (k*A % 1), với k là khóa, M là kích thước bảng băm và A là số thực 0 < A < 1. Theo phương pháp nhân này, sự hiệu quả phụ thuộc vào việc lựa chọn A, theo như nhà khoa học máy tính Knuth, chọn A = (sqrt(5) – 1) / 2 là hiệu quả nhất (xấp xỉ 0.618033987).

Thông thường, mình sử dụng phương pháp chia cho dễ cài đặt. Tuy nhiên, không thể nào tránh khỏi đụng độ dù có dùng hàm băm nào đi nữa, do đó, chúng ta cần giải quyết đụng độ.

Giải quyết đụng độ

Đối với việc sử dụng phương pháp kết nối trực tiếp, các phần tử bị đụng độ sẽ được thêm vào danh sách liên kết tại h(k) trong bảng băm.

Giải quyết đụng độ bảng băm

Như bạn có thể thấy trong hình, các khóa như 7, 17 đụng độ nhau thì chúng sẽ được thêm vào danh sách liên kết ở h(k) = M. Tương tự các khóa 4, 19 cũng bị đụng và được thêm vào danh sách liên kết ở h(k) = 2…

Bây giờ chúng ta hãy cùng bắt đầu cài đặt bảng băm vào trong trong C++ nha.

Cấu trúc một nút trong bảng băm

Như đã nói, phương pháp kết nối trực tiếp dùng danh sách liên kết đơn, các phần tử bị đụng độ tại phần tử i trong bảng băm thì sẽ được thêm vào danh sách liên kết đơn tại i trong bảng băm. Do đó, một phần tử trong bảng băm có cấu trúc như một nút trong danh sách liên kết đơn.

struct Node
{
    int key;
    Node* next;
};

Cấu trúc bảng băm và hàm khởi tạo

Một bảng băm là một mảng chứa các nút, giả sử mình có 100 phần tử, vậy mình sẽ định nghĩa một HashTable như sau:

#define M 100

typedef Node *HashTable[M];

Như vậy, chúng ta có thể khai báo một bảng băm như sau:

HashTable mHashTable;

Các bạn có thể dễ dàng thấy một nút trong bảng là một con trỏ trỏ đến một Node, như vậy, chúng ta cần phải khởi tạo chúng bằng NULL để tránh gặp lỗi. Mình sẽ có hàm khởi tạo bảng như sau:

void InitHashTable(HashTable &HT)
{
    for (int i = 0; i < M; i++)
        HT[i] = NULL;
}

Hàm băm

Như đã nói ở trên, để đơn giản mình sẽ sử dụng hàm băm theo phép chia:

int Hash(int k)
{
    return k % M;
}

Thêm một nút vào bảng băm

Để thêm một nút, ta cần xác định vị trí sẽ thêm qua hàm băm h(k), sau đó thêm vào danh sách liên kết ở vị trí h(k) đó. Việc đụng độ sẽ được giải quyết do nếu đụng độ thì khóa sẽ được tự thêm vào sau danh sách liên kết đơn. Mình sẽ có hàm thêm như sau:

void InsertNode(HashTable &HT, int k)
{
    int i = Hash(k);
    AddTail(HT[i], k);
}

Hàm AddTail thì trong danh sách liên kết đơn, mình đã có bài viết về nó rồi, các bạn có thể đọc lại.

void AddTail(Node *&l, int k)
{
    Node *newNode = new Node{k, NULL};
    if (l == NULL)
    {
        l = newNode;
    }
    else
    {
        Node* p = l;
        while (p != NULL && p->next != NULL)
            p = p->next;
        p->next = newNode;
    }
}

Tìm kiếm một khóa trong bảng băm

Để tìm kiếm một khóa trong bảng băm, ta cũng thực hiện xác định vị trí h(k), sau đó ta thực hiện tìm kiếm trong danh sách liên kết tại vị trí h(k) trong bảng băm.

Node *SearchNode(HashTable HT, int k)
{
    int i = Hash(k);
    Node *p = HT[i];
    while (p != NULL && p->key != k)
        p = p->next;
    if (p == NULL)
        return NULL;
    return p;
}

Xóa một nút ra khỏi bảng băm

Để xóa một phần tử ra khỏi bảng băm, đầu tiên ta cũng phải xác định h(k), sau đó tìm xem nó nằm ở đâu trong danh sách liên kết đơn tại vị trí h(k) đó rồi thực hiện xóa nó đi.

void DeleteNode(HashTable &HT, int k)
{
    int i = Hash(k);
    Node *p = HT[i];
    Node *q = p;
    while (p != NULL && p->key != k)
    {
        q = p; // Lưu lại địa chỉ của phần tử trước đó
        p = p->next;
    }
    if (p == NULL)
        cout << k << " not found!" << endl;
    else if (p == HT[i])
        DeleteHead(HT[i]); // Nút cần xóa là phần tử đầu của DSLK
    else
        DeleteAfter(q); // Xóa nút sau nút q
}

Hai hàm DeleteHead và DeleteAfter cũng đã được mình trình bày trong bài Danh sách liên kết đơn trong C++ rồi nên mình sẽ không giả thích gì thêm.

void DeleteHead(Node *&l)
{
    if (l != NULL)
    {
        Node *p = l;
        l = l->next;
        delete p;
    }
}

void DeleteAfter(Node *&q)
{
    Node *p = q->next;
    if (p != NULL)
    {
        q->next = p->next;
        delete p;
    }
}

Duyệt qua bảng băm

Duyệt qua bảng băm rất đơn giản, bạn chỉ cần duyệt qua mảng, mỗi phần tử của mảng là một danh sách liên kết đơn, vậy thì duyệt danh sách liên kết đơn nữa là xong.

void Traverse(Node *p) // duyệt DSLK
{
    while (p != NULL)
    {
        cout << p->key << ' ';
        p = p->next;
    }
    cout << endl;
}

void TraverseHashTable(HashTable HT)
{
    for (int i = 0; i < M; i++)
    {
        cout << "Bucket " << i << ": ";
        Traverse(HT[i]);
    }
}

Lưu ý về bảng băm

Đối với dữ liệu lớn, việc cấp phát một mảng quá lớn sẽ gây lãng phí bộ nhớ không đáng có, tuy nhiên, việc M lớn đảm bảo việc đụng độ ít xảy ra do các khóa phân bố đều. Ngược lại, nếu M nhỏ để tiết kiệm bộ nhớ, việc này sẽ làm giảm hiệu suất của bảng băm do việc đụng độ xảy ra với tần suất cao hơn.

Do vậy, khi thao tác với bảng băm, các bạn cần phải cân nhắc giữa hiệu suất và dung lượng lưu trữ.

Tổng kết

Như vậy là trong bài viết này, mình đã giới thiệu đến các bạn về bảng băm trong C++, cách cài đặt bảng băm bằng phương thức kết nối trực tiếp dùng danh sách liên kết đơn. Nếu các bạn có bất kỳ ý kiến, đóng góp nào, đừng ngần ngại comment phía bên dưới bài viết nha. Cảm ơn các bạn đã theo dõi bài viết!

Source code

#include <iostream>
using namespace std;

#define M 10

struct Node
{
    int key;
    Node *next;
};

typedef Node *HashTable[M];

void InitHashTable(HashTable &HT)
{
    for (int i = 0; i < M; i++)
        HT[i] = NULL;
}

int Hash(int k)
{
    return k % M;
}

void AddTail(Node *&l, int k)
{
    Node *newNode = new Node{k, NULL};
    if (l == NULL)
    {
        l = newNode;
    }
    else
    {
        Node* p = l;
        while (p != NULL && p->next != NULL)
            p = p->next;
        p->next = newNode;
    }
}

void InsertNode(HashTable &HT, int k)
{
    int i = Hash(k);
    AddTail(HT[i], k);
}

void DeleteHead(Node *&l)
{
    if (l != NULL)
    {
        Node *p = l;
        l = l->next;
        delete p;
    }
}

void DeleteAfter(Node *&q)
{
    Node *p = q->next;
    if (p != NULL)
    {
        q->next = p->next;
        delete p;
    }
}

void DeleteNode(HashTable &HT, int k)
{
    int i = Hash(k);
    Node *p = HT[i];
    Node *q = p;
    while (p != NULL && p->key != k)
    {
        q = p;
        p = p->next;
    }
    if (p == NULL)
        cout << k << " not found!" << endl;
    else if (p == HT[i])
        DeleteHead(HT[i]);
    else
        DeleteAfter(q);
}

Node *SearchNode(HashTable HT, int k)
{
    int i = Hash(k);
    Node *p = HT[i];
    while (p != NULL && p->key != k)
        p = p->next;
    if (p == NULL)
        return NULL;
    return p;
}

void Traverse(Node *p)
{
    while (p != NULL)
    {
        cout << p->key << ' ';
        p = p->next;
    }
    cout << endl;
}

void TraverseHashTable(HashTable HT)
{
    for (int i = 0; i < M; i++)
    {
        cout << "Bucket " << i << ": ";
        Traverse(HT[i]);
    }
}

int main()
{
    HashTable mHashTable;
    InitHashTable(mHashTable);

    InsertNode(mHashTable, 0);
    InsertNode(mHashTable, 1);
    InsertNode(mHashTable, 2);
    InsertNode(mHashTable, 3);
    InsertNode(mHashTable, 10);
    InsertNode(mHashTable, 13);
    InsertNode(mHashTable, 9);
    InsertNode(mHashTable, 11);

    cout << "HashTable:\n";
    TraverseHashTable(mHashTable);

    DeleteNode(mHashTable, 3);
    DeleteNode(mHashTable, 13);
    DeleteNode(mHashTable, 9);
    cout << "HashTable after Delete:\n";
    TraverseHashTable(mHashTable);

    Node *result = SearchNode(mHashTable, 10);
    if (result == NULL)
        cout << "Not found!";
    else
        cout << "Found!";

    std::cout << std::endl;
    system("pause");
    return 0;
}
Bài viết gốc được đăng tải tại khiemle.dev
Có thể bạn quan tâm:
Xem thêm TOP It jobs for Developer hấp dẫn trên TopDev

Quy trình 3 bước trong Machine Learning và hàm chi phí

Quy trình 3 bước trong Machine Learning và hàm chi phí

Bài viết được sự cho phép của tác giả Kien Dang Chung

Video trong bài viết

Trong Phần 1: Dự đoán doanh thu phim với Hồi quy tuyến tính, chúng ta đã được làm quen với Machine Learning và thuật toán Hồi quy tuyến tính (Linear Regression). Các bài học ở phần 1 mới chỉ mang tính chất giới thiệu, để bạn đọc có thể hiểu sơ lược xem Machine Learning là gì? khi áp dụng vào các bài toán thực tế nó đem lại kết quả ra sao?

  9 hiểu lầm "ngớ ngẩn" về machine learning
  Chia sẻ cơ bản sử dụng machine learning để giải quyết bài toán.

Quy trình 3 bước trong Machine Learning

Bạn có nhớ trong bài toán dự đoán doanh thu bằng Linear Regression chúng ta đã sử dụng thuật toán được cài đặt sẵn trong thư viện Scikit-learn. Nhưng chúng ta không đi sâu vào Machine learning diễn ra như thế nào?

Theo hoạt động học tập thông thường, chúng ta cần đưa dữ liệu vào máy tính và nó có thể trả lời các câu hỏi của chúng ta. Cách máy tính học là chúng nhìn ra các mối quan hệ trong dữ liệu. Quay lại với Linear Regression, chúng ta tìm hiểu xem máy đã học như thế nào?

Trước hết, chúng ta cùng đến với quy trình 3 bước trong Machine Learning và sau đó sẽ áp dụng vào một thuật toán cụ thể.

Quy trình 3 bước trong Machine Learning và hàm chi phí

  • Bước 1: Đưa ra dự đoán (đưa ra một giá trị bất kỳ đầu tiên).
  • Bước 2: Tính toán các sai lệch dựa trên dự đoán và dữ liệu đầu vào
  • Bước 3: Máy học, điều chỉnh dự đoán ban đầu và quay về với bước 1.

Phần tiếp theo có liên quan đến Phần 1: Dự đoán doanh thu phim với Linear Regression, bạn nên xem phần 1 trước khi bắt đầu bài viết này vì có rất nhiều thông tin liên quan.

Quay lại với bài toán dự đoán bằng Linear Regression, chương trình cần học và đưa ra các giá thị θ0θ0 và θ1θ1 tối ưu. Bước 1, nó sẽ dự đoán giá trị θ0θ0 và θ1θ1 ví dụ θ00θ00 và θ01θ10. Sau đó, tại bước 2, nó thực hiện tính toán sai lệch. Ở đây, trong thuật toán Linear Regression chúng ta sử dụng Tổng bình phương độ lệch để làm tiêu chí đánh giá xem dự đoán ở bước 1 là đã đúng hay chưa?

TngBìnhPhươngĐLch(RSS)=(y(1)hθ(x(1)))2+(y(2)hθ(x(2)))2+...+(y(n)hθ(x(n)))2=i=nn(y(i)hθ(x(i)))2TổngBìnhPhươngĐộLệch(RSS)=(y(1)−hθ(x(1)))2+(y(2)−hθ(x(2)))2+…+(y(n)−hθ(x(n)))2=∑i=nn(y(i)−hθ(x(i)))2

Với đường thẳng cần dự đoán là:

hθ(x)=θ0+θ1xhθ(x)=θ0+θ1x

Trong đó, θ0θ0 là điểm cắt với trục y, θ1θ1 là độ dốc của đường thẳng. x(i)x(i)y(i)y(i) là ngân sách và doanh thu của phim ii trong tập dữ liệu ban đầu.

Ở bước thứ 3, nó so sánh RSS của lần tính toán trước với lần tính toán này xem cái nào tốt hơn, nếu đã đủ tốt với một độ chính xác yêu cầu, máy tính dừng lại, nếu chưa nó điều chỉnh các giá trị dự đoán θ0θ0 và θ1θ1 và quay lại bước 1.

Chúng ta đã coi RSS là độ lớn sai lệch tính toán trong Linear Regression. Một phần rất rộng trong Machine Learning là tối ưu hóa để tìm ra các giá trị nhỏ nhất, ví dụ như ở bài toán trên chúng ta cần tìm RSS nhỏ nhất và như vậy với θ0θ0 và θ1θ1 có được, chúng ta có được đường thẳng nói lên mối quan hệ giữa ngân sách và doanh thu phim. Trong Machine learning các hàm này gọi là hàm chi phí (cost function) hoặc hàm mất mát (lost function).

Có thể bạn chưa thật sự hiểu về quy trình 3 bước trong machine learning này nhưng cũng đừng lo lắng, khi bắt đầu vào thiết kế thuật toán và viết code Python bạn sẽ hiểu thôi. Tôi đảm bảo đấy.

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

Có thể bạn quan tâm:

Xem thêm Tuyển dụng Machine Learning hấp dẫn trên TopDev

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Bài viết được sự cho phép của tác giả Trần Hữu Cương

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ).

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Để thực hiện viết code Python trên intellij IDEA ta cần cài đặt plugin Python.

File/Setting (hoặc ấn tổ hợp phím Ctrl + Alt + S)

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Chọn menu Plugins, và gõ python để tìm kiếm plugin Pytthon.

Hiện tại máy mình chưa cài plugin python nên mình sẽ cần search nó ở repositories.

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Chọn Plugin Python và Install.

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Sau khi cài đặt thành công thì khởi động lại IntelliJ để nó nhận plugin vừa cài đặt

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Như vậy là bạn đã cài đặt thành công plugin python để lập trình python trên IntelliJ.

  Selenium WebDriver trên Python

Ví dụ Python Hello World trên IntelliJ

Chọn File/New/Project

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Chọn Python. Phần Project SDK thì các bạn trỏ tới file python.exe trong folder python mà bạn đã cài đặt. (Ví dụ máy bạn cài cả bản python 3 và python 2 thì bạn có thể lựa chọn bản mà mình muốn)

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Kết quả:

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

  Tổng hợp các Module Python "khủng" mà bạn có thể đã bỏ qua

Tạo file hello.py với nội dung print("hell world)

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Chạy file hello.py trên bằng cách click chuột phải vào file hello.py và chọn Run (hoặc Ctrl+Shift+F10)

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

Kết quả:

Lập trình Python trên IntelliJ IDEA (code Python trên IntelliJ)

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

Có thể bạn quan tâm:

Xem thêm Việc làm python hấp dẫn trên TopDev

Column Oriented Storage – The hidden thing 1

Column Oriented Storage

Bài viết được sự cho phép của tác giả Kiên Nguyễn

Từ trước tới nay, làm với với Database là nhắc tới row (dòng), cũng có làm việc với column nhưng lưu trữ theo hướng column (Column Oriented Storage) thì chưa nghe bao giờ.

Đây, bắt đầu ngay xem tại sao ta lại cần biết về cái kiểu này.

  Khái niệm về OOD – Object oriented design
  Lập trình theo kiểu Aspect Oriented Programming (AOP) sử dụng Spring Framework

Một ngày đẹp trời, sếp đưa cho bạn một table với vài tỷ dòng dữ liệu. Nhắc lại là tỷ (trillion) nha. Sếp bảo lấy ra mấy dòng trong tháng 12 năm 2005. Lúc này, bất cứ thao tác nhỏ nào với DB cũng trở thành vấn đề lớn.

Column Oriented StorageKhông thiếu những câu hỏi thao tác, speeding up các table dữ liêu lớn

Rồi đây, cùng xem cái thằng quỷ Column Oriented Storage có gì khác?

1. Column Oriented Storage là gì?

Ý tưởng vô cùng đơn giản

The idea behind column oriented storage is simple: don’t store all the values from one row together, but store all the values from each column together instead

Ý tưởng phía sau column oriented storage vô cùng đơn giản: đừng lưu trữ tất cả dữ liệu trên một dòng, thay vào đó lưu trữ với nhau trên một cột

Nếu data trong mỗi column được lưu trữ trong một file?. Lúc này, khi cần truy xuất, chỉ cần đọc và parse nội dụng file sẽ có data. Tiết kiệm được rất nhiều thời gian và cải thiện rõ rệt về performance.

Column Oriented StorageNguồn ảnh / Source: Designing Data-Intensive Applications

Ở table phía trên, thay vì lưu trữ theo hướng row (hàng) ở phía trên, ta sử dụng column. Cột data_key, product_sk, tất cả chuyển qua lưu trữ theo dòng. Đó là bước đầu tiên trong Column Oriented Storage.

Cũng có thể nhớ dựa vào tên, Oriented: hướng về, thiên về. Nên Column Oriented Storage là lưu trữ theo hướng column (cột).

2. Nhanh hơn chỗ nào?

Column Oriented Storage
Column Oriented Storage
Column Oriented Storage

4. Tham khảo

Đã tìm hiểu về Column Oriented thì ngại ngùng gì mà không xúc luôn Database Clustering ha.

Thanks for read. Have a great day!. Happy coding!

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

Có thể bạn quan tâm:

Xem thêm Việc làm Developer hấp dẫn trên TopDev

In ra màn hình số 1 – ngôn ngữ nào nhẹ nhất?

In ra màn hình số 1 - ngôn ngữ nào nhẹ nhất?

Bài viết được sự cho phép của tác giả Nguyễn Việt Hưng

Nếu chỉ cần in ra mà hình số 1, ngôn ngữ lập trình nào sẽ chạy tốn ít RAM nhất?

Thí nghiệm sau sẽ cho ta thấy các chương trình cần bao nhiêu RAM để chạy và in ra số 1, kết thúc dòng bằng một dấu xuống dòng (newline \n).

Ta dùng chương trình /usr/bin/time để có cả thông số về RAM (RSS) thay vì chỉ gõ time – lệnh builtin của bash.

  10 lý do cho thấy tại sao bạn nên theo học ngôn ngữ lập trình Java
  4 ngôn ngữ phát triển game indie phổ biến

bash4.3

$ /usr/bin/time bash -c 'echo 1'

1

0.00user 0.00system 0:00.00elapsed 100%CPU (0avgtext+0avgdata 3064maxresident)k

0inputs+0outputs (0major+136minor)pagefaults 0swaps

dash

$ /usr/bin/time /bin/dash -c 'echo 1'
1
0.00user 0.00system 0:00.00elapsed ?%CPU (0avgtext+0avgdata 1492maxresident)k
0inputs+0outputs (0major+67minor)pagefaults 0swaps

mawk

$ /usr/bin/time mawk 'BEGIN {print 1}'

1

0.00user 0.00system 0:00.00elapsed 100%CPU (0avgtext+0avgdata 1932maxresident)k

0inputs+0outputs (0major+86minor)pagefaults 0swaps

gawk 4.1.3

$ /usr/bin/time gawk 'BEGIN {print 1}'

1

0.00user 0.00system 0:00.00elapsed 42%CPU (0avgtext+0avgdata 3632maxresident)k

552inputs+0outputs (2major+173minor)pagefaults 0swaps

Python3.5.2

$ /usr/bin/time python3 -c 'print(1)'

1

0.03user 0.01system 0:00.05elapsed 98%CPU (0avgtext+0avgdata 9292maxresident)k

0inputs+0outputs (0major+1100minor)pagefaults 0swaps

Python2.7.12

$ /usr/bin/time python2 -c 'print(1)'

1

0.01user 0.00system 0:00.01elapsed 94%CPU (0avgtext+0avgdata 6640maxresident)k

0inputs+0outputs (0major+819minor)pagefaults 0swaps

Ruby 2.3.1

$ /usr/bin/time ruby -e 'puts 1'
1
0.03user 0.01system 0:00.04elapsed 100%CPU (0avgtext+0avgdata 10336maxresident)k
0inputs+0outputs (0major+1595minor)pagefaults 0swaps

Perl v5.22.1

$ /usr/bin/time perl -e 'print "1\n"'

1

0.00user 0.00system 0:00.00elapsed 100%CPU (0avgtext+0avgdata 4144maxresident)k

0inputs+0outputs (0major+176minor)pagefaults 0swaps

NodeJS 6.11.2

$ /usr/bin/time node -p '1'

1

0.09user 0.00system 0:00.10elapsed 100%CPU (0avgtext+0avgdata 24080maxresident)k

0inputs+0outputs (0major+2920minor)pagefaults 0swaps

Elixir 1.6.3

$ /usr/bin/time elixir -e 'IO.puts 1'

1

0.17user 0.04system 0:00.16elapsed 137%CPU (0avgtext+0avgdata 29268maxresident)k

16inputs+0outputs (1major+7837minor)pagefaults 0swaps

Guile 2.0.11

$ /usr/bin/time guile -c '(display 1) (newline)'

1

0.02user 0.00system 0:00.01elapsed 115%CPU (0avgtext+0avgdata 7720maxresident)k

0inputs+0outputs (0major+976minor)pagefaults 0swaps

printf 8.25 – chương trình viết bằng C

$ /usr/bin/time printf '1'

10.00user 0.00system 0:00.00elapsed 100%CPU (0avgtext+0avgdata 1844maxresident)k

0inputs+0outputs (0major+72minor)pagefaults 0swaps

Làm lại toàn bộ bằng Python

Tạo một dictionary, chứa các tên ngôn ngữ – kèm với câu lệnh sẽ dùng để thử nghiệm

import shlex
import subprocess


programs = {
    'bash': 'bash -c "echo 1"',
    'dash': 'dash -c "echo 1"',
    'mawk': "mawk 'BEGIN {print 1}'",
    'gawk': "gawk 'BEGIN {print 1}'",
    'python2.7': 'python2 -c "print(1)"',
    'python3.5': 'python3 -c "print(1)"',
    'ruby2.3': 'ruby -e "puts 1"',
    'node6': 'node -p 1',
    'perl5': "perl -e \"print '1\n'\"",
    'elixir': 'elixir -e "IO.puts 1"',
    'guile': 'guile -c "(display 1) (newline)"',
    'printf': 'printf "1\n"'
}


import re

def get_rss_from_stderr(stderr):
    match = re.compile('(?P<rss>\d+)maxresident').search(err.decode('utf-8'))
    return int(match.group('rss'))


import pandas as pd

languages = []
rsses = []
for language, cmd in programs.items():
    cmd = ['/usr/bin/time'] + shlex.split(cmd)
    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = process.communicate()
    assert out.decode('utf-8') == '1\n'
    rss = get_rss_from_stderr(err)
    print(language, rss)
    languages.append(language)
    rsses.append(rss)

df = pd.DataFrame(index=languages, data=rsses, columns=['RSS'])

df_sorted_rss = df.sort_values(by='RSS')

# run in Jupyter
from matplotlib import pyplot as plt
import matplotlib
matplotlib.style.use('fivethirtyeight')

%matplotlib inline
df_sorted_rss.plot.bar(title='RSS size in byte when print 1');

Dynamic typing languages

Kết luận

Khoan vội kết luận rằng cả thế giới nên chuyển hết về dùng dash cho đỡ tốn RAM hay xa lánh Elixir vì nó chạy tới 30 MB RAM để in số 1 ra màn hình.

Sử dụng đúng công cụ, cho đúng vấn đề thích hợp mới là điều quan trọng.

  • dash được viết ra để dùng làm shell (sh) cho Ubuntu, nó cần nhẹ, dù trả giá bằng việc không nhiều tính năng, và chỉ là một shell scripting language
  • bash là shell được dùng phổ biến nhất thế giới
  • awk là ngôn ngữ lập trình đã gắn liền với lịch sử của thế giới UNIX, rất tiện để tính toán dữ liệu cột / hàng. Với AWK cùng các CLI tool truyền thống, người ta có thể giải quyết bài toán “big data” nhanh gấp 200 lần dùng Hadoop (EMR)
  • mawk nhanh hơn gawk
  • perl5 luôn có chỗ đứng của nó, nó vẫn ở trên máy Ubuntu dù chưa bao giờ mình chủ ý cài
  • guile là extension language chính thức được lựa chọn bởi GNU, nó chính là ngôn ngữ scheme được mang vào ứng dụng thực tế
  • Python – ngôn ngữ dễ đọc, phổ biến nhất thế giới, đã đẩy ngành tính toán khoa học từ bộ môn trong phòng thí nghiệm trở thành ngành hot nhất trái đất. PS: Python khi bật lên đã dùng ~ 8MB RAM.
  • Ruby còn dùng nhiều RAM hơn nữa, dù về mặt tính năng thường được so ngang với Python, nhưng mặt ứng dụng vào khoa học thì là vực so với trời Python.
  • NodeJS là một JavaScript engine chuyển biến cả ngành web, cũng chưa rõ sao nó ngốn gần 25MB để bật lên, có khi đó vốn là bản chất của JavaScript.
  • Elixir không chỉ bật lên một interpreter như bash, mà đó là cả hệ thống phân tán – chạy máy ảo BEAM của Erlang, nên con số 30MB thậm chí còn là hơi … ít.

Bài này không xét các ngôn ngữ mà phải viết thành file, compile mới chạy, vì tác giả quá lười.

PS: học hết đống trên ở đây

Hết.

Bài viết gốc được đăng tải tại pp.pymi.vn

Có thể bạn quan tâm:

Xem thêm Việc làm Developer hấp dẫn trên TopDev