Đầu tiên, nhắc tới Sharing là nhắc tới kỹ thuật Scaling. Thường sử dụng cho các hệ thống được thiết kế để scalable (có thể mở rộng một cách nhanh chóng).
Vậy Sharding là gì?
Sharding involves breaking up one’s data into two or more smaller chunks, called logical shards. The logical shards are then distributed across separate database nodes, referred to as physical shards, which can hold multiple logical shards. Despite this, the data held within all the shards collectively represent an entire logical dataset.
Shadring là kỹ thuật sử dụng để phân chia data thành 2 hoặc nhiều hơn các khối nhỏ (chunks), cũng được gọi là logical shards. Các khối logical shards này được phân chia và thuộc về các node database nhỏ. Giống như các khối vật lý, có thể được tồn tại trong đó các khối vật lý. Mặc dù vậy, dữ liệu được giữ trong tất cả các shards đại diện chung cho toàn bộ tập dữ liệu logic.
Má, định nghĩa tiếng việt viết ra khéo đọc không ai hiểu. Tốt nhất là anh em nên đọc tiếng anh. Túm cái váy lại thì với Dynamic Sharding, có 2 loại phổ biến nhất là Horizontal Scaling và Vertical Scaling. Đã được đề cập đầy đủ và chi tiết ở bài viết Toàn tập về thiết kế Scalable Web Application II
Hai kiểu Scaling chính (Horizontal và Vertical Scaling). Nguồn ảnh/ Source: digitalocean.com
2. Locator trong Dynamic Sharding
Dynamic Sharding lại gồm 2 từ. Dynamic và Sharding, từ Sharding thì đã nhắc tới ở phía trên. Còn Dynamic?. Nhắc tới Dynamic phải nhớ tới Locator.
Nếu không có Locator thì không có Dynamic. Nói thì hơi khó hiểu, xem thử ví dụ sau:
Giả sử ta có 3 Shards, mỗi Shards lại nắm một đoạn dữ liệu trong DB (tổng là 100). Thằng A thì giữ từ 1-30, thằng B thì giữ từ 30-60, thằng C thì giữ từ 60-100. Trường hợp client muốn request 35. Làm sao client biết để request tới B (vốn là thằng đang giữ từ 30-60).
Đấy là lúc thằng locator ra tay!
Locator sẽ phán định cho biết DB nào sẽ xử lý request tới từ Client.
Chính vì thằng Locator chịu trách nhiệm “điều đào”, ấy lộn “điều request” nên ta mới có cái chữ Dynamic trong khái niệm. Bất kì request nào từ phía client đều đi qua Locator, được Locator xem xét, phán định nên đi về “chốn nào”.
Dùng một đại ca như Locator anh em thấy có ưu nhược điểm gì không?. Quyền lực quá đôi khi cũng là một cái hại.
3. Điểm yếu, điểm mạnh của locator
Thành phần quan trọng nhất của Dynamic Sharding là locator. Locator như là “chị đại” trong giới giang hồ. Do tất cả các client request đến đều thông qua locator. Tất nhiên, luôn có điểm mạnh và điểm yếu của kiến trúc này.
Nói về điểm mạnh thì locator cho phép client request tới chính xác DB có chưa nội dung đang cần. Client A sẽ không đi lệch tới DB B để tìm nội dung mà nó cần. Với cách truy xuất này, thời gian để client A tới DB B được giảm đi đáng kể.
Tuy nhiên, do tất cả đều thông qua chị đại. Không tránh khỏi trường hợp chị đại “tèo” thì client cũng ra đi. Anh em giang hồ theo chân chị đại là DB cũng không người dẫn đầu. Như “rắn mất đầu”.
Luôn cần tới một Locator backup trong trường hợp locator chính gặp vấn đề.
Lúc này ta cần tới một Locator backup. Locator này cần đồng bộ với locator chính. Tuy nhiên trong các trường hợp Create hay Update ở DB. Locator này lại chưa đáp ứng được, lúc Locator chính sống lại cần synchronous lại ngay.
4. Khi nào thì nên sử dụng Dynamic Sharding
Do tính chất linh động khi sử dụng kiến trúc Dữ liệu phân tán (Distributed Database). Dynamic Sharding được sử dụng cho các kiến trúc lớn như Google Drive, Mega File. Mỗi file được chia thành các phần nhỏ và lưu trữ ở nhiều nơi.
Khi client yêu cầu nội dung file. Dynamic Sharding có thể phán định chính xác các phần nhỏ của file đang ở đâu, sau đó thực hiện nối ghép các file. Đem đến cho người dùng nội dung hoàn chỉnh.
Cách viết thông báo lỗi phù hợp để cải thiện trải nghiệm người dùng
Tác giả: Andrico Karoulla
Đã qua rồi cái thời những thông báo lỗi xuất hiện một cách chung chung và gần như vô ích. Nếu bạn quan tâm đến việc làm ra các sản phẩm hướng tới người dùng và giúp cho trải nghiệm của người dùng trở nên thú vị nhất có thể, thì đây chính là bài viết dành cho bạn. Như đã biết, thông báo lỗi đóng một vai trò quan trọng trong số những tác nhân ảnh hưởng đến trải nghiệm người dùng. Những thông báo lỗi dễ nhận diện và có ích chắc chắn sẽ giúp ích rất nhiều cho việc cải thiện những gì mà người dùng tiếp cận.
Viết thông báo lỗi như thế nào để tối ưu trải nghiệm người dùng?
1. Trạng thái hiện tại của thông báo lỗi
Trong một thế giới gọi là hoàn hảo, các thông báo lỗi sẽ là thứ dư thừa và người dùng có thể sử dụng bất kỳ thứ gì bạn đã xây dựng một cách dễ dàng. Nhưng, đáng tiếc là không có một thế giới hoàn hảo như thế, lỗi sẽ xảy ra và người dùng cuối chắc chắn sẽ đối mặt với chúng.
Những lỗi này có thể bắt nguồn từ:
Xác thực không thành công
Lỗi phía máy chủ
Giới hạn tỷ lệ
…
Và khi có sự cố, thông báo lỗi trên giao diện người dùng thường xuất hiện theo một trong hai cách:
Các lỗi chung chung không có thông tin có ý nghĩa, ví dụ: Something went wrong, please try again later
Thông báo quá cụ thể từ dấu vết ngăn xếp được gửi bởi máy chủ, ví dụ: Error 10x29183: line 26: error mapping Object -> Int32
Cả hai dạng thông báo này đều không hữu ích cho người dùng cuối. Đối với users, lỗi chung có thể tạo ra cảm giác bất lực và thất vọng. Nếu họ nhận được thông báo như vậy, họ hoàn toàn không thể làm gì và không có cách nào để biết tại sao lỗi lại xảy ra và làm thế nào (hoặc nếu) họ có thể giải quyết nó. Điều này có thể khiến website hoặc app của bạn mất lòng tin của người dùng cuối, mất khách hàng hoặc bị đánh giá một sao.
Mặt khác, thông báo lỗi quá cụ thể sẽ bị xem là một lỗ hổng về kỹ thuật của công ty bạn và người dùng cuối đương nhiên không nên nhìn thấy. Thứ hai, cũng là điểm đặc biệt, nếu lĩnh vực hoạt động của công ty bạn liên quan nhiều đến các trải nghiệm người dùng thì thông báo lỗi của phải mang tính con người và hướng đến dịch vụ nhiều hơn.
2. Tại sao nên build các thông báo lỗi có tính hỗ trợ cao hơn?
2.1. Giúp các developers làm việc dễ dàng hơn
Kiểm tra nhật ký người dùng mỗi ngày là việc làm khá tẻ nhạt. Nếu người dùng cuối báo cáo lỗi, điều quan trọng là họ phải cung cấp cho chúng ta nhiều thông tin hữu ích nhất có thể.
Một báo cáo từ một người dùng cho biết với mẫu như thế này:
Hi, I was using the app sometime last night updating my profile and all of a sudden it stopped working. The error said something about a validation error, but I don't know what that means
sẽ ít hữu ích hơn nhiều so với:
Hi, I was using the app sometime last night updating my profile and all of a sudden it stopped working. The error said "We had trouble updating your details. Your address must be located within the EU" but I live in England
Điều này giúp các lập trình viên phía sau tiết kiệm được rất nhiều thời gian và cắt giảm sự nghiên cứu để tìm ra chính xác lỗi đó là gì. Một thông báo lỗi rõ ràng và cụ thể cũng có thể giúp người dùng cuối hiểu được bản thân họ đã làm gì sai và có thể cho phép họ sửa chữa lỗi của mình.
2.2. Hoạt động của hệ thống được trơn tru hơn
Đối với những người làm việc trong các công ty lớn hơn, việc sao chép/nhắn tin có thể thuộc trách nhiệm của một bộ phận hoàn toàn riêng biệt. Càng có nhiều vị trí trong mã yêu cầu thay đổi bản sao, bản sao càng dễ trở nên không đồng bộ với các nguyên tắc thương hiệu của công ty.
Ngược lại, việc lưu giữ tất cả các thông báo lỗi của bạn trong một nguồn duy nhất giúp những người sở hữu bản sao tuân thủ các nguyên tắc thương hiệu đó dễ dàng hơn nhiều. Việc khắc phục sự cố với tin nhắn của bạn khi người dùng điền sai biểu mẫu, thiếu dữ liệu hoặc không có quyền cho một hành động cụ thể có thể tác động tích cực đến quá trình làm việc của team.
2.3. Trải nghiệm của người dùng cuối được thoải mái hơn
Bằng cách cung cấp thông báo lỗi lành mạnh, mong muốn lớn nhất là để không khiến người dùng cuối cảm thấy khó khăn trong quá trình sử dụng. Như đã mô tả trước đó, thông điệp của một thông báo lỗi được xem là hữu hiệu phải hướng đến dịch vụ. Chúng nên hướng dẫn người dùng cách hoàn thành quy trình làm việc, hoặc ít nhất là cho họ biết họ có thể đến đâu và nhận trợ giúp nếu vấn đề nằm ngoài tầm kiểm soát của họ.
Thông báo lỗi đóng một vai trò quan trọng trong việc ngăn chặn điều này, vì chúng có thể hoạt động như người kiểm soát cuối cùng giúp người dùng tránh được việc bị mắc kẹt hoặc thoát khỏi ứng dụng do thất vọng.
Nếu ai đó đang sử dụng sản phẩm của bạn cho mục đích giao dịch như mua vé máy bay hoặc mua sắm trực tuyến và họ bị dừng lại trong quá trình thực hiện một nhiệm vụ mà không có cách nào để tiếp tục, thì khả năng họ rời khỏi trang web của bạn cho một nhiệm vụ khác sẽ tăng vọt. Và cứ thế, việc đánh mất khách hàng sẽ còn kéo theo rất nhiều hệ lụy đáng lo khác.
3. Thông báo thế nào được xem là thông báo tốt?
Theo cuốn Microcopy: A complete guide, một thông báo lỗi hữu ích phải đáp ứng được một trong số các tiêu chuẩn sau:
Giải thích rõ ràng vấn đề
Nếu có thể, hãy cung cấp giải pháp để người dùng có thể hoàn tất quá trình
Hoặc hướng dẫn người dùng đến với trang web có thể giúp đỡ họ
Tạo một tin nhắn dễ hiểu nhất có thể
Ví dụ đơn giản như sau:
Chúng tôi đã giới hạn số lần bạn có thể đặt lại mật khẩu của mình mỗi giờ. Bạn có thể thử lại sau.
Vui lòng đăng nhập để xem hồ sơ này
Chúng tôi không thể tạo hồ sơ của bạn, chỉ cư dân Vương quốc Anh mới có thể sử dụng ứng dụng của chúng tôi.
4. Build một hệ thống báo lỗi hiệu quả bằng cách nào?
Có một open source gọi là sane-error-messages. Chạy công cụ này sẽ tạo ra một repo hoàn toàn mới được thiết kế để chứa thông báo lỗi mặc định. Bạn có thể điều chỉnh các giá trị mặc định, thêm hoặc xóa tin nhắn, sau đó xuất bản nó để sử dụng trong các ứng dụng dành cho khách hàng của mình.
sane-error-messages hoạt động bằng cách tổng hợp tất cả thông điệp của bạn vào một đối tượng JavaScript duy nhất. Key là một code lỗi và value là một thông báo tương ứng. Code lỗi phải giống với code bạn nhận được từ máy chủ của mình, chẳng hạn như POSTS_NOT_FOUND hoặc CONFLICTING_USER_RECORD. Repo thông báo lỗi cho thấy một chức năng nhận thông báo lỗi từ một code bị lỗi.
Nói một cách ngắn gọn:
Người dùng “xem tất cả các sản phẩm”
Giao diện người dùng thực hiện một yêu cầu mạng
Yêu cầu mạng không thành công và trả về mã lỗi “USER_NOT FOUND”
Giao diện người dùng yêu cầu thông báo lỗi tương ứng từ error-messages của bạn
Giao diện người dùng áp dụng bất kỳ thông tin ngữ cảnh có liên quan nào
Giao diện người dùng hiển thị thông tin này cho người dùng cuối
5. Cách thiết lập thông báo lỗi
Nếu bạn gặp bất kỳ sự cố nào trong quá trình hướng dẫn, bạn có thể gửi thông tin sự cố về GitHub.
Bắt đầu bằng cách chạy
yarn global add sane-error-message
sau đó
sane-error-messages create <dirName>
để mở đầu cho project. Làm vậy sẽ tạo ra một module hoàn toàn mới để bạn tùy chỉnh với các thông báo lỗi mặc định của mình. Module mới sử dụng ẩn tsdx để xử lý tất cả các tập lệnh quản lý module, chẳng hạn như chạy, xây dựng và thử nghiệm.
Tóm lại, nội dung làm việc sẽ phát triển giống như sau:
/* errorCodes.ts: The file that defines each error code like */constUSER_NOT_ADMIN='403_USER_NOT_ADMIN'/* defaultErrorMessages.ts: Maps each code to a default message */const errorCodes {// your codes and messages go here...[USER_NOT_ADMIN]:"We're afraid only administrators have access to "}/* ErrorMessages.ts: The class you'll use to instantiate your error messages object in the consuming project */classErrorMessages{// You can override default messages with more specific onesconstructor:(customErrorMessages: Partial<Record<string|number,string>>): ErrorMessages;// Pass through an error code to get your custom message
getErrorMessage:(code:string|number, fallbackMessage?:string):string;// Checks to see if the argument is a valid error code and acts as a guard for non-ErrorCode valuesisErrorCode(code:string|number):boolean;// Returns the errorCodes object with your custom messages
messages: Record<ErrorCode,string>}type ErrorCode = ValueOf<errorCodes>
6. Cách xử lý thông báo lỗi
Nếu bạn đã tạo một repo có tên custom-error-messages và xuất bản nó lên npm, bạn có thể sử dụng nó trong các ứng dụng của mình bằng cách làm như sau:
import{ ErrorMessages }from'custom-error-messages';const customErrorMessages ={'400_validation':'Please enter the fields in your form correctly',};// Initialise your errorMessages object with your custom messagesconst errorMessages =newErrorMessages(customErrorMessages);functionriskyFunction(){try{// Throws an error awaitboom();}catch(err){// Get the error code our server sent overconst{ code }= err;// Get the code's corresponding messageconst message = errorMessages.getErrorMessage(code);// Display the message to the clientdisplayNotification(message);}}
Sau đó, bạn có thể lấy tất cả các code bị lỗi mà phía máy chủ trả về và áp dụng các thông báo tương ứng cho chúng. Khi đã sẵn sàng, bạn có thể xuất bản công cụ của mình lên NPM, sau đó sử dụng nó từ các ứng dụng dành cho khách hàng của bạn.
Lỗi là một trong những vấn đề thường xuyên gặp khi phát triển web, app. Biết cách cải thiện và sửa chữa vấn đề chắc chắn sẽ giúp cho công việc của bạn hoạt động trơn tru và hiệu quả hơn.
Bài viết được sự cho phép của tác giả Tô Thị Vân Anh
TestNG là một nền tảng được sử dụng để giúp chúng ta thiết kế tự động test chức năng hoặc unit test trong Java. Eclipse là một trong những IDE rất phổ biến cho phát triển các test trong Java. Và để sử dụng testNG, không phải là bạn sẽ tải về và cài đặt như một cái tool, mà sẽ giống như một cái phần mềm nho nhỏ được cài đặt tích hợp trong Eclipse nha.
Ngày xưa còn chưa biết đến automation, hóng được testNG, thì cứ nghĩ nó là một cái tool độc lập phải cài vào máy mới dùng được. Giờ thì thông tin nhiều hơn rồi nên chắc không ai bị như mình ngày đấy đâu! hihi
Hôm nay thì mình sẽ hướng dẫn các bạn các bước để cài đặt testNG trong Eclipse nhé!
1. Mở Eclipse IDE. Chọn Help > Install new software… -> Trên màn hình sẽ hiển thị một cửa sổ có tên Install:
2. Nhấn nút Add, màn hình lúc này sẽ hiển thị một popup nho nhỏ, bạn sẽ điền thông tin này vào từng trường tương ứng nhé:
Name: TestNg ( Bạn có thể đặt tên tùy ý chỗ này)
Location: http://beust.com/eclipse/ (Link này thì lấy chính xác nhé! :D)
Nhấn OK
3. Check vào checkbox TestNG vừa hiển thị, sau đó nhấn Next
4. Check đồng ý vào điều khoản liên quan đến bản quyền
5. Lúc này quá trình download diễn ra, và TestNG sẽ được cài đặt sau đó
6. Sau khi quá trình cài đặt ở bước trên đã xong, sẽ có một cửa sổ hiển thị yêu cầu bạn sẽ khởi động lại Eclipse để cập nhật những cài đặt vừa rồi. Bạn chọn Yes để hoàn tất việc cài đặt TestNG.
Rất đơn giản đúng không nào!
Có một cách khác mà bạn cũng có thể sử dụng để cài đặt TestNG cho Eclipse đó là:
Click Help > Eclipse Marketplace…
Gõ TestNG và nhấn Go để tìm kiếm framework
Nhấn Install để cài đặt
Chờ cho đến khi cài đặt xong và khởi động lại TestNG là được
Hi vọng là với các bước đơn giản phía trên bạn có thể cài đặt TestNg một cách dễ dàng và nhanh chóng hơn.
Hãy nhớ rằng 1 observable sẽ không làm gì cho đến khi nó nhận được đăng ký(subscription). Một đăng ký kích hoạt nó phát ra các sự kiện, cho đến khi nó tạo ra 1 sự kiện .error hoặc .completed để kết thúc. Bạn có thể đăng ký nó và chấm dứt nó. Cùng xem ví dụ sau:
example(of: "dispose") {
// 1
let observable = Observable.of("A", "B", "C")
// 2
let subscription = observable.subscribe { event in
// 3
print(event)
}
}
Tạo 1 observable gồm các string
Đăng ký vào observable, phát ra các sự kiện
Hiển thị các sự kiện phát ra
Kết quả:
--- Example of: dispose ---
next(A)
next(B)
next(C)
completed
Để hủy 1 subscribe, ta thêm dispose() cho nó. Và nó ngừng phát ra sự kiện. Thêm đoạn code này vào cuối chương trình:
subscription.dispose()
Quản lý hủy đăng ký rất tẻ nhạt, cho nên RxSwift cung cấp cho chúng ta công cụ để hủy là hàm .disposed(by:), và sẽ tự động gọi dispose mỗi khi đăng ký bị hủy. Cùng xem đoạn code sau:
Đây là mẫu mà bạn sẽ sử dụng thường xuyên nhất, tạo và đăng ký 1 observable đồng thời thêm đăng ký vào xử lý disposeBag. Khi bạn quên đăng ký disposeBag cho nó có thể gây rò rỉ bộ nhớ. Tuy nhiên đừng lo lắng, trình biên dịch sẽ cảnh báo cho bạn.
Trong các ví dụ trước, bạn quan sát các sự kiện thông qua sự kiện .next. Cùng xem ví dụ sau:
Vì sự kiện complete đã phát ra cho nên sự kiện 3 không thể phát ra nữa. Điều gì sẽ xảy ra khi bạn thêm 1 sự kiện error cho nó? Cùng thêm đoạn code sau vào đầu file:
enum MyError: Error {
case anError
}
Và thêm đoạn code sau vào giữa .onNext và .onCompleted:
observer.onError(MyError.anError)
Kết quả như sau:
--- Example of: create ---
1
anError
Disposed
Điều gì sẽ xảy ra khi chúng ta không emit ra sự kiện onCompleted và .error cũng như không đăng ký với disposeBag? Sửa lại đoạn code như sau:
Machine learning là một chủ đề được nhắc đến rất nhiều trong thời gian trở lại đây bên cạnh trí tuệ nhân tạo, nó được ứng dụng cực kỳ nhiều ở thời điểm hiện tại trong hầu hết tất cả các lĩnh vực. Trong bài viết hôm nay, chúng ta sẽ cùng tìm hiểu xem machine learning là gì, các khái niệm cơ bản và vì sao nó lại được ứng dụng rỗng rãi như vậy?
Lưu ý trước khi đọc bài: mình mới vừa tìm hiểu về machine learning không lâu nên sai sót là điều không thể tránh khỏi. Các bạn đọc bài nếu thấy sai đừng quên góp ý dưới phần comment để mình hoàn thiện bài viết tốt hơn nha!
Machine learning là gì?
Tính đến thời điểm hiện tại, có rất nhiều định nghĩa về machine learning, nếu bạn nào từng google thì hẳn các bạn sẽ biết. Mình đã đọc và tổng hợp lại sau đó rút ra khái niệm như sau:
Machine learning (ML) hay máy học là một nhánh của trí tuệ nhân tạo (AI), nó là một lĩnh vực nghiên cứu cho phép máy tính có khả năng cải thiện chính bản thân chúng dựa trên dữ liệu mẫu (training data) hoặc dựa vào kinh nghiệm (những gì đã được học). Machine learning có thể tự dự đoán hoặc đưa ra quyết định mà không cần được lập trình cụ thể.
Bài toán machine learning thường được chia làm hai loại là dự đoán (prediction) và phân loại (classification). Các bài toán dự đoán như dự đoán giá nhà, giá xe… Các bài toán phân loại như nhận diện chữ viết tay, nhận diện đồ vật…
Machine learning workflow sẽ cho bạn thấy quy trình để làm việc với machine learning như thế nào. Hãy nhìn vào sơ đồ bên dưới:
Cụ thể từng bước trong machine learning workflow như sau như sau:
Data collection – thu thập dữ liệu: để máy tính có thể học được bạn cần có một bộ dữ liệu (dataset), bạn có thể tự thu thập chúng hoặc lấy các bộ dữ liệu đã được công bố trước đó. Lưu ý là bạn phải thu thập từ nguồn chính thống, có như vậy dữ liệu mới chính xác và máy có thể học một cách đúng đắng và đạt hiệu quả cao hơn.
Preprocessing – tiền xử lý: bước này dùng để chuẩn hóa dữ liệu, loại bỏ các thuộc tính không cần thiết, gán nhãn dữ liệu, mã hóa một số đặc trưng, trích xuất đặc trưng, rút gọn dữ liệu nhưng vẫn đảm bảo kết quả… Bước này tốn thời gian nhất tỉ lệ thuận với số lượng dữ liệu bạn có. Bước 1 và 2 thường chiếm hơn 70% tổng thời gian thực hiện.
Training model – huấn luyện mô hình: bước này là bước bạn huấn luyện cho mô hình hay chính là cho nó học trên dữ liệu bạn đã thu thập và xử lý ở hai bước đầu.
Evaluating model – đánh giá mô hình: sau khi đã huấn luyện mô hình xong, chúng ta cần dùng các độ đo để đánh giá mô hình, tùy vào từng độ đo khác nhau mà mô hình cũng được đánh giá tốt hay không khác nhau. Độ chính xác của mô hình đạt trên 80% được cho là tốt.
Improve – cải thiện: sau khi đã đánh giá mô hình, các mô hình đạt độ chính xác không tốt thì cần được train lại, chúng ta sẽ lặp lại từ bước 3, cho đến khi đạt độ chính xác như kỳ vọng. Tổng thời gian của 3 bước cuối rơi vào khoảng 30% tổng thời gian thực hiện.
Có rất nhiều cách phân loại machine learning, thông thường thì machine learning sẽ được phân làm hai loại chính sau:
Supervised learning: học có giám sát
Unsupervised learning: học không giám sát
Ngoài ra, machine learning còn có thể phân làm các loại sau:
Semi-supervised learning: học bán giám sát
Deep learning: học sâu (về một vấn đề nào đó)
Reinforce learning: học củng cố/tăng cường
Mình sẽ chỉ đề cập đến cách phân loại phổ biến nhất là phân làm hai nhóm: học có giám sát và học không giám sát.
Supervised learning
Supervised learning là việc cho máy tính học trên dữ liệu đã được gán nhãn (label), hay nói cách khác, với mỗi đầu vào Xi, chúng ta sẽ có nhãn Yi tương ứng.
Unsupervised learning
Unsupervised learning là cho máy tính học trên dữ liệu mà không được gán nhãn, các thuật toán machine learning sẽ tìm ra sự tương quan dữ liệu, mô hình hóa dữ liệu hay chính là làm cho máy tính có kiến thức, hiểu về dữ liệu, từ đó chúng có thể phân loại các dữ liệu về sau thành các nhóm, lớp (clustering) giống nhau mà chúng đã được học hoặc giảm số chiều dữ liệu (dimension reduction).
Môt số khái niệm cơ bản
Dataset (còn gọi là data corpus hay data stock): là tập dữ liệu ở dạng nguyên thủy chưa qua xử lý mà bạn đã thu thập được ở bước data collection. Một dataset sẽ bao gồm nhiều data point.
Data point: là điểm dữ liệu, mỗi điểm dữ liệu biểu diễn cho một quan sát. Mỗi data point có nhiều đặc trưng hay thuộc tính khác nhau, được chia làm hai loại: dữ liệu số (numerical) và dữ liệu không phải số (ví dụ như chuỗi) (non-numerical/categorical). Data point được biểu diễn thành dòng tương ứng, mỗi dòng có thể có 1 hoặc nhiều dữ liệu (chính là các đặc trưng).
Training data và test data: dataset thường sẽ được chia làm 2 tập này, training data dùng để huấn luyện cho mô hình, test data dùng để dự đoán kết quả và đánh giá mô hình. Có bài toán người ta sẽ cho sẵn hai tập này thì bạn không cần phải chia nữa, đối với bài toán chỉ cho mỗi dataset thôi thì phải chia ra. Thường tỷ lệ giữa tập train và test sẽ là 8/2.
Features vector: là vector đặc trưng, mỗi vector này sẽ biểu diễn cho một điểm dữ liệu trong dataset. Mỗi vector có n chiều biểu diễn các đặc trưng của điểm dữ liệu, mỗi đặc trưng là một chiều và phải là dữ liệu số. Các mô hình chỉ có thể huấn luyện được từ các vector đặc trưng này, do đó dataset cần phải chuyển về dạng một tập các vector đặc trưng (features vectors).
Model: là các mô hình được dùng để training trên một training data theo thuật toán của mô hình đó. Sau đó mô hình có thể dự đoán hoặc đưa ra các quyết định dựa trên những gì chúng đã được học.
Ứng dụng của Machine learning
Machine learning được ứng dụng cực kỳ nhiều trong đời sống hiện nay trong mọi lĩnh vực:
Tài chính – ngân hàng
Sinh học
Nông nghiệp
Tìm kiếm, trích xuất thông tin
Tự động hóa
Robotics
Hóa học
Mạng máy tính
Khoa học vũ trụ
Quảng cáo
Xử lý ngôn ngữ tự nhiên
Thị giác máy tính
Và còn rất rất nhiều lĩnh vực mà machine learning có thể được áp dụng, machine learning tỏ ra cực kỳ hiệu quả, hơn hẳn con người trong cụ thể các lĩnh vực mà chúng được áp dụng.
Ví dụ đơn giản như dự báo thời tiết, người ta sẽ dùng các phép tính và những quan sát, ghi nhận về thời tiết trong quá khứ để dự báo về thời tiết của những ngày kế tiếp. Tuy nhiên sẽ thế nào nếu như có cực kỳ nhiều quan sát được thực hiện, có thể lên đến hàng triệu, hàng tỉ quan sát, lúc đó con người không thể nào thực hiện được việc tính toán trên dữ liệu lớn như vậy. Hơn nữa, việc tính toán với dữ liệu lớn như vậy có thể gặp sai sót và dẫn đến kết quả dự đoán bị sai.
Khi này, việc áp dụng machine learning vào để cho máy tính học các quan sát được ghi nhận trong quá khứ, chúng có thể dự đoán được thời tiết trong tương lai với độ chính xác cao hơn rất nhiều so với con người dự đoán.
Chính vì sự phổ biến và hiệu quả của machine learning, việc bạn biết và học về machine learning chắc chắn là một lợi thế lớn trong thời đại công nghệ 4.0 như ngày nay.
Tổng kết
Vậy là trong bài này, mình đã cùng các bạn tìm hiểu qua về machine learning là gì, các khái niệm cơ bản và ứng dụng của nó. Nếu như bạn thấy bài viết này hay hoặc có đóng góp về bài viết, đừng quên bình luận phía bên dưới để giúp mình hoàn thiện bài viết tốt hơn nha.
Odoo is a suite of web based open source business apps. The main Odoo Apps include an Open Source CRM, Website Builder, eCommerce, Warehouse Management, Project Management, Billing & Accounting, Point of Sale, Human Resources, Marketing, Manufacturing, Purchase Management, … Odoo Apps can be used as stand-alone applications, but they also integrate seamlessly so you get a full-featured Open Source ERP when you install several Apps.
Nó là một bộ các web app phục vụ các công việc trong kinh doanh như quản lý quan hệ khách hàng, quản lý dự án, tính tiền/kế toán, tuyển dụng… đầy đủ đến mức nó là một giải pháp “ERP” cho doanh nghiệp. Odoo là tên mới từ bản 8.0 (giờ đang là 10.0) của phần mềm OpenERP.
Đứng ở góc độ của lập trình viên, việc phát triển một “module/addon” cho Odoo không có gì quá khác biệt hay cao siêu so với viết một web app bằng bất cứ framework nào (Django, Flask…), ngoài chuyện clone 1.6GB git repo.
Cài đặt và chạy thử
Code Python cài từ source có một số thư viện yêu cầu lập trình viên phải cài thêm các thư viện C (bằng apt trên Ubuntu):
Mặc dù dùng qweb template engine, Odoo vẫn có vài chỗ dùng jinja2 hay Mako template engine:
[2] although it uses a few others, either for historical reasons or because they remain better fits for the use case. Odoo 9.0 still depends on Jinja and Mako.
Sau khi cài
Giao diện quản lý dự án
Cảm nhận chung là Odoo vẫn chậm như thời OpenERP, khá ngốn RAM, 180MB cho process Python chạy Odoo, chưa làm gì cả + một đống process chạy postgres.
Khi vận hành trang tổng hợp việc Python của PyMi, những tin tuyển lập trình viên Odoo hiện lên hàng ngày, lặp đi lặp lại, với mức lương phổ biến trong khoảng 6-10 triệu.
Các nhà tuyển dụng thì lại bắt buộc lập trình viên phải có sẵn kinh nghiệm với Odoo… NOOOOOO!
Chẳng có lập trình viên web Python nào đầu tư vào 1 framework không dùng ở đâu ngoài để phát triển chính Odoo module/addon, họ học Django, Flask. Một lập trình viên Django hay Flask có thể viết Odoo webapp chỉ sau vài tiếng chuyển đổi những kiến thức tương tự sang những thư viện của Odoo framework.
Tại sao anh đòi hỏi thứ không ai có, mà lại trả lương thấp?
Theo như kết quả nghiên cứu của Stack Overflow trong năm 2020, JavaScript là ngôn ngữ lập trình phổ biến nhất trên thế giới, với hơn 63% các nhà phát triển đã và đang sử dụng nó. JavaScript có kho lưu trữ gói mã nguồn mở lớn nhất trên thế giới (npm) và cũng có nhiều khung và thư viện khác nhau được xây dựng cùng với nó. JavaScript rất dễ bắt đầu vì nó không yêu cầu bất kỳ cài đặt nào để bắt đầu coding.
JavaScript có thể làm nhiều việc hơn so với những gì bạn biết
Phát triển Front-end Web
Front-end web là một hình thức phát triển giao diện người dùng liên quan mà người dùng có thể xem và tương tác được. Có 3 ngôn ngữ chính hiện nay có thể dùng để phát triển front-end: HTML, CSS và JavaScript.
HTML (Ngôn ngữ đánh dấu siêu văn bản) liên quan đến cấu trúc và nội dung của trang web, CSS (Trang tính kiểu xếp tầng) xử lý kiểu dáng.
Và cuối cùng, JavaScript làm cho trang web mang các hiệu ứng động, dễ nhìn hơn. Và điều này có thể được thực hiện theo nhiều cách khác nhau, chẳng hạn như:
Thuyết trình
Bạn có thể tạo băng chuyền và thanh trượt trên trang web một cách dễ dàng bằng cách sử dụng một số thư viện JavaScript như Reveal JS, Swiper JS và Owl JS
Với các thư viện này, bạn có thể tùy chỉnh cách trình bày trang web của mình một cách nhanh chóng và không quá phiền phức.
Một trong những cách bạn có thể làm cho trang web của mình trở nên sinh động và hấp dẫn hơn là sử dụng hình ảnh động. Bạn có thể sử dụng chúng trên một trang web theo nhiều cách khác nhau, chẳng hạn như thông qua các chuyển động, sự biến đổi của các đối tượng và rất nhiều điều tuyệt vời khác.
Có một số thư viện ảnh gif để bạn lựa chọn, chẳng hạn như Anime JS, Greensock JS, Mo JS và Animate On Scroll. Đây là tất cả các thư viện được xây dựng bằng ngôn ngữ JavaScript giúp tạo hoạt ảnh của bạn dễ dàng hơn bằng cách sử dụng các khối mã nhỏ hơn.
Phát triển back-end liên quan đến phần phía sau của trang web mà người dùng không tương tác trực quan. Đây còn được gọi là phát triển ở phía máy chủ. Phát triển back-end liên quan đến việc tạo cơ sở dữ liệu, API, máy chủ tệp, dịch vụ đám mây, v.v.
Và với việc tạo ra Node.js, một công cụ thời gian chạy JavaScript, giờ đây bạn cũng có thể sử dụng JavaScript để phát triển web back-end. Các khung JavaScript khác nhau đã được xây dựng để đơn giản hóa quá trình phát triển web back-end. Một số trong số đó bao gồm:
Express Js: Express là một Web Application framework nổi tiếng nhất dựa trên Node.js. Nó chủ yếu được sử dụng để phát triển Web Applications và API REST.
Fastify: Đây là một Server-Side Web Framework tối giản, tập trung nhiều vào trải nghiệm của nhà phát triển và hiệu suất ứng dụng.
Koa: Đây là một framework trung gian hiện đại và mạnh mẽ cho các Web Applications và APIs.
Game là một trong những dự án thú vị nhất mà bạn có thể tạo, vì cả bạn và người dùng cuối đều có thể tận hưởng chúng. Có nhiều cách khác nhau để sáng tạo game mới với ngôn ngữ JavaScript, tùy thuộc vào độ phức tạp của trò chơi.
Để xây dựng các game có thiết kế phức tạp, cũng như các trò chơi mang tính hiệu quả hơn và nhanh hơn, bạn có thể sử dụng công cụ trò chơi JavaScript để làm cho quá trình này dễ dàng hơn. Một số engines này là Phaser Js, Blyon Js và Lime Js có thể giúp bạn làm việc dễ dàng hơn.
Phát triển ứng dụng dành cho thiết bị di động đề cập đến quá trình viết phần mềm hoạt động trên thiết bị di động. Nó liên quan đến việc phát triển các ứng dụng có thể được sử dụng trên thiết bị di động.
Bạn có thể sử dụng JavaScript để phát triển các mobile app. Các khung và thư viện Javascript khác nhau đã được xây dựng để tạo các ứng dụng gốc, đa nền tảng và kết hợp bằng JavaScript. Một số trong số đó là:
React Native: React Native là một framework mã nguồn mở có thể được sử dụng để xây dựng các cross-platform native apps – ứng dụng gốc đa nền tảng. React Native sử dụng phong cách lập trình khai báo và các thành phần có thể sử dụng lại cho giao diện người dùng.
NativeScript: NativeScript là một trong những framework đa nền tảng nổi tiếng nhất giúp bạn phát triển các mobile app cho các nền tảng Android và iOS. Vì nó là một khuôn khổ mã nguồn mở, các nhà phát triển có thể điều chỉnh giao diện người dùng cho nhiều màn hình và thiết bị, đồng thời cũng sử dụng các biến phụ thuộc khác nhau.
Ionic: Ionic là một JavaScript framework phổ biến khác để xây dựng các ứng dụng kết hợp. Đối với các nhà phát triển quen thuộc với công nghệ web và phát triển ứng dụng web, việc hiểu cấu trúc của một ứng dụng Ionic rất đơn giản.
Việc chọn JavaScript framework phù hợp cho dự án của bạn phụ thuộc vào ứng dụng bạn đang xây dựng và cũng như nền tảng mà nó dành cho. Vì vậy, hãy chắc chắn chọn một cái dựa trên trường hợp sử dụng của bạn nếu bạn có thể.
JavaScript là một ngôn ngữ đa dụng và có thể được ứng dụng trong nhiều lĩnh vực công nghệ khác nhau. Cá nhân tôi rất đề cao nó và thật sự khuyên bạn nên học JavaScript như một ngôn ngữ lập trình nếu bạn quan tâm đến bất kỳ lĩnh vực nào mà tôi đã chia sẻ ở trên.
Phỏng dịch dựa trên bài viết gốc được đăng tải tại freecodecamp.org
Bài viết được sự cho phép của tác giả Lê Xuân Quỳnh
Vậy là bạn đã đọc qua về phần detail của khóa học rồi đúng không? Nếu chưa hãy đọc nó tại menu lập trình IOS nhé. Dù sao, chúng ta nên nhắc lại mục đích của hàng loạt bài viết liên quan tới Rx và MVVM trước. Để chúng ta biết chúng ta sẽ đạt được gì sau khi học.
Vậy là có tổng 10 thư mục. Chúng ta hãy tìm hiểu lần lượt từng thư mục nhé
1. Thư mục Application
Thư mục này có 3 file:
File AppDelegate.swift:
Hãy để ý dòng code này:
let libsManager = LibsManager.shared
Ở đây tác giả tạo 1 singleton có tên là LibsManager. Nếu ai chưa biết về singleton, thì nôm na nó là 1 lớp được khởi tạo duy nhất 1 lần, và cho dù có gọi hàm tạo đi bao lần nữa nó cũng chỉ tạo có 1 lần thôi. Người ta hay dùng singleton cho những thứ chỉ được tạo 1 lần duy nhất, như quản lý AppDelegate, loading app… Ai chưa hiểu hãy giành 30 phút để google singleton là gì?
Vậy lớp LibsManager dùng để làm gì? Chúng ta hãy vào class này xem nhé:
Để ý đoạn code này:
if UserDefaults.standard.object(forKey: Configs.UserDefaultsKeys.bannersEnabled) == nil {
bannersEnabled.accept(true)
}
lúc mới chạy app lần đầu
Bạn chạy app lần đầu, hình ảnh này sẽ xuất hiện. Còn các lần tiếp theo, nó không xuất hiện nữa. Và chính dòng code trên điều khiển việc đó.
Bắt đầu mù tịt vì đụng đến Rx rồi. Bắt đầu khó nha. Đến đây thì chúng ta bắt buộc phải đọc về Rx để hiểu nó đang làm gì. Vậy thì hãy để ý tới biến bannersEnabled. Nó được khởi tạo dạng Rx chứ không phải biến thông thường bằng câu lệnh:
let bannersEnabled = BehaviorRelay(value: UserDefaults.standard.bool(forKey: Configs.UserDefaultsKeys.bannersEnabled))
Chúng ta thấy, BehaviorRelay là 1 hàm, có value phía trong là 1 giá trị kiểu boolean. Đọc về help của Xcode như sau:
Summary
BehaviorRelay is a wrapper for BehaviorSubject.
Discussion
Unlike BehaviorSubject it can’t terminate with error or completed.
Đến đây thì đọc không hiểu mô tê gì cả. Bắt buộc chúng ta phải đọc sample ở đâu đó trên mạng. OK, vậy chúng ta cùng tìm hiểu nhé.
Sau đó các bạn mở file RxSample.xcworkspace và xem code trong file ViewController
let bag = DisposeBag() //0
let bannersEnabled = BehaviorRelay<String>(value: "code")//1
bannersEnabled.accept("toan")//2
bannersEnabled.accept("bug")//3
let subscriber = bannersEnabled.subscribe { (event) in
print("event = \(event)")
} //4
bannersEnabled.accept("viet")//5
bannersEnabled.accept("nam")//6
subscriber.disposed(by: bag)//7
Giải thích từng dòng:
Dòng 1: Các bạn khởi tạo 1 biến bannersEnabled có kiểu là BehaviorRelay. Đúng ra cái này không phải biến thông thường như các bạn đã quen, là nó có giá trị kiểu số 1,2 hay là kiểu string abc.. Mà thằng này có 1 đặc điểm là biến có thẻ phát ra 1 giá trị nào đó theo thời gian thực, cụ thể mình cho nó phát ra được các giá trị string. Giá trị khởi đầu là “code”
Dòng 2, 3: Mình đang ném 2 cái giá trị string “toan” và “bug” vào biến này để nó phát ra – nó như 1 cái đài phát thanh đang nói ra 2 câu đó.
Dòng 4: Mình khởi tạo biến subscriber, biến này dùng để lắng nghe các sự kiện phát ra từ biến bannersEnabled, giống như bạn chọn đúng kênh của đài phát thanh để nghe âm thanh trên kênh. này. Và câu lệnh print để hiển thị những gì mà nó nghe được.
Dòng 5, 6: tương tự dòng 2,3 mình lại phát ra 2 câu nói “viet” và “nam”.
Dòng 7: Các bạn hiểu nôm na là quy tắc dọn dẹp bộ nhớ tự động. Nó dựa vào biến dòng số 0 để dọn dẹp lại, và nôm na nó bắt buộc luôn khi các bạn khởi tạo các subscriber thì đều phải nhờ ông bag này dọn dẹp bộ nhớ khi không dùng nữa. Hiểu thế cho đơn giản.
Tại sao chúng ta chỉ nghe được 3 câu trên. Đơn giản do thằng bannersEnabled nó bắt đầu nghe tính từ thời điểm cách thời điểm nó đăng ký trước đó 1 tín hiệu, nghĩa là bắt đầu nghe từ từ bug trở đi về sau.
Vậy làm sao ta chỉ muốn nghe mỗi 2 từ viet nam thôi, cách đơn giản là thêm dòng lệnh này thay thế:
let subscriber = bannersEnabled.skip(1).subscribe { (event) in
print("event = \(event)")
}
skip(n) ở đây nghĩa là bỏ đi n giá trị phát ra tính từ thời điểm trước đăng ký 1 sự kiện cho đến về sau. Các bạn có thể thay đổi con số để loại bỏ. Ví dụ skip(2) thì nó bỏ từ bug và từ viet, còn nghe được mỗi từ nam.
Ồ! Vậy bạn đã hiểu các biến trên làm gì chưa? Bọn nó phát ra sự kiến, xử lý sự kiện trong hàm subscribe.
Quay trở lại hàm trong dự án của chúng ta, đoạn code trên:
let bannersEnabled = BehaviorRelay(value: UserDefaults.standard.bool(forKey: Configs.UserDefaultsKeys.bannersEnabled))//0
if UserDefaults.standard.object(forKey: Configs.UserDefaultsKeys.bannersEnabled) == nil {//1
bannersEnabled.accept(true)//2
}
bannersEnabled.skip(1).subscribe(onNext: { (enabled) in
UserDefaults.standard.set(enabled, forKey: Configs.UserDefaultsKeys.bannersEnabled)//3
analytics.set(.adsEnabled(value: enabled))//4
}).disposed(by: rx.disposeBag)//5
Dòng 1: lệnh if nếu như kiểm tra cái bannersEnabled chưa có giá trị(app chưa được mở bao giờ) thì gán giá trị bannersEnabled.accept(true) để phát ra tín hiệu rằng nó chưa mở banner bao giờ(dòng 2).
Dòng 3: Nó đăng ký 1 subscriber và bỏ đi 1 skip để loại bỏ giá trị khởi tao ban đầu(dòng 0), chỉ bắt đầu nghe ngóng từ dòng số 2 trở đi.
Vậy khi dòng 2 phát ra sự kiện thì các dòng 3, 4 được thực thi, và nếu mình đoán không nhầm thì nó làm 2 hành động: 1 là gán giá trị để bật banner lên, và chắc chắn ở đâu đó có 1 thằng lắng nghe khi giá trị này thay đổi, để hiển thị UI như hình trên. 2 là nó bật quảng cáo lên, và cũng tương tự, ở đâu đó nhiều thằng subscriber cũng lắng nghe sự thay đổi của biến này để hiển thị UI quảng cáo(adsEnabled nghĩa là bật quảng cáo, từ ads là quảng cáo, enabled là bật lên).
Vậy chúng ta đã hiểu hết logic đống loằng ngoằng trên rồi đúng không? Các bạn bắt đầu hình dung được là Rx sẽ là công cụ giúp chúng ta phát tín hiệu và lắng nghe tín hiệu. Vậy nó có ưu điểm gì:
Ví dụ khi chúng ta bật ads lên, thì mọi UI ở khắp mọi nơi khi hiển thị cho người dùng không cần quan tâm bạn thay đổi ở màn hình nào, đều đồng bộ được giá trị. Nếu bạn làm UIKit thuần, bạn sẽ phải vất vả dùng NSNotificationCenter hay là Protocol hay là closure function để báo hiệu giá trị thay đổi cho các màn khác. Và giả sử bạn có 5 cái màn khác nhau, sub view có, parrent view có, thì câu chuyện đồng bộ hóa dữ liệu trở nên khủng khiếp biết nhường nào
OK, Rx đáng để học đúng không nào? Vậy chúng ta cùng xem các đoạn code khác nhé.
CocoaLumberjack is a fast & simple, yet powerful & flexible logging framework for Mac and iOS.
Vậy nó là 1 thư viện log, chức năng là ghi lại những hoạt động của người dùng trong ứng dụng. Ví dụ người dùng bấm nút, chuyển màn hình, gõ text… bất cứ action nào trên ứng dụng của mình. Điều này nhằm cho developer đánh giá được người dùng hay xem phần nào, màn nào bị crash không, … rất nhiều thứ hay ho mà chúng ta có thể thống kê được qua thư viện này. Nhằm cải thiện app tốt hơn hay triển khai các hoạt động marketing cho app…
setupAnalytics là gì? Ở đây chúng ta đang nói đến Firebase của Google. Nó có nhiều chức năng lắm, và cụ thể là làm server, realtime, bắn notification cho app, tracking app, quảng cáo… Rất nhiều tính năng mà Google cung cấp cho chúng ta. Và tất nhiên như thông lệ, Google vẫn hay bài cho dùng thử giới hạn, dùng quá thì phải pay Đúng là ông lớn nên biết làm kinh tế quá ha.
Cụ thể nó như nào, chúng ta sẽ nghiên cứ 1 topic sâu về Firebase sau các bạn nhé.
setupAds là gì? cái tên tiếng Anh cũng nói rằng nó là cài đặt cho phần quảng cáo trong app rồi đúng không. Và ở đây chúng ta dùng quảng cáo của Google.
setupTheme – phần này khá mới trong thời gian gần đây, khi mà các ứng dụng bắt đầu quan tâm tới thị giác của người dùng nhiều hơn. Làm sao mà họ dùng app đỡ hại mắt, và dùng trong đêm tối sẽ theo 1 chế độ màu khác với dùng ban ngày. Người ta đẻ ra dark-mode và light-mode, chính ông lớn apple cũng chơi cuộc chơi này rồi. Nếu bạn đang cầm trên tay 1 thiết bị IOS 13, bạn sẽ hiểu được vào ban đêm, nhiều ứng dụng tự nhiên chuyển màu tối, đễ đơ hại mắt hơn. Hay thậm chí facebook cũng đã bật chế độ này trên appweb của họ rồi. Nó rất nhiều thứ hay ho để học, và trong ứng dụng này chúng ta chỉ cần bấm nút switch đơn giản để chuyển qua lại giữa 2 chế độ.
setupKafkaRefresh – Cái này là gì? Thư viện ở đây:
Như readme mà họ viết, thì nó đơn giản là dạng loading khi bạn vuốt xuống ở table để cập nhật data mới, hay gọi là pull to refresh mà các bạn hay thấy trên nhiều ứng dụng. Trông cũng cool ngầu đấy chứ:
FLEX (Flipboard Explorer) is a set of in-app debugging and exploration tools for iOS development. When presented, FLEX shows a toolbar that lives in a window above your application. From this toolbar, you can view and modify nearly every piece of state in your running application.
Chi tiết, bạn đọc readme tiếng Anh, còn ai không thích tiếng anh thì mình tìm thấy 1 bài viết khá đầy đủ ở đây. Nôm na nó là thư viện cho phép xem thông tin mọi thứ của app mà ta đang làm, bao gồm view, thông số biến, hàm, log hay database…
setupKeyboardManager – nghe tên cũng biết là quản lý bàn phím rồi đúng không?
Như readme viết, thường khi chúng ta build ứng dụng sẽ hay gặp tình trạng bàn phím che mất chữ, không nhìn thấy được. Và thư viện này giúp chúng ta giải quyết vấn đề đó mà không phải viết bất kỳ 1 dòng code nào. Nghe ra gì phết. Okie vậy chúng ta cứ dùng thôi.
setupActivityView – vào phần chi tiết chúng ta thấy 1 đối tượng NVActivityIndicatorView.
Vậy là chúng ta đã dạo qua 1 vòng về file LibsManager – quản lý toàn bộ library của App. Theo mình đây là cách rất thông minh để có thể tập trung lại các thư viện, dễ quản lý và dễ sửa lỗi khi cần.
2. Thư mục Managers
Chúng ta cùng nghiên cứu về cách quản lý các thư viện của tác giả ở bài viết tiếp theo.
Trong lập trình C/C++, Enum hay Enumeration là kiểu dữ liệu cố định, chỉ cho phép biến nhận số số giá trị nhất định nào đó.
Các giá trị enum có thể coi là một hằng số. Việc sử dụng enum giúp đảm bảo giá trị các biến chỉ nhận các giá trị mong đợi.
Ví dụ: Mình định nghĩa kiểu Enum là dayOfWeek và chỉ nhận các giá trị là ngày trong tuần (từ thứ 2 đến chủ nhật). Mỗi lần có tính toán tới ngày trong tuần thì mình dùng dayOfWeek sẽ giúp tránh các giá trị nằm ngoài khoảng từ thứ hai đến chủ nhât.
Trường hợp bạn không truyền giá trị cho các trạng thái trong enum thì nó sẽ tự nhận các giá trị tăng dần từ 0. Hoặc tăng dần theo giá trị của trạng thái trước đó.
Sự khác biệt map và weakmap luôn là một phần kiến thức cần nắm vững đối với lập trình viên Javascript. Nắm rõ sự khác biệt còn phải hiểu lúc nào nên sử dụng loại nào nữa. Đấy, vô vàn vấn đề cần tìm hiểu.
Tuy nhiên, khó cũng phải hiểu. Vì Data Structure không phải muốn dùng sao cũng được. Ở các System lớn, chỉ cần thay đổi một kiểu của DS thôi cũng gây ra hàng tá vấn đề.
Dữ liệu lớn, request xử lý hàng đống. Memory là thứ cần kiểm soát chặt chẽ để đảm bảo hệ thống luôn vận hành trơn tru. Kieblog hân hạnh giới thiệu bài viết sự khác biệt map và weakmap. Bắt đầu ngay thôi nào!
The Map object holds key-value pairs and remembers the original insertion order of the keys. Any value (both objects and primitive values) may be used as either a key or a value.
Đối tượng Map lưu cặp key-value (khoá – giá trị) theo thứ tự chèn vào của khoá. Bất kỳ giá trị nào (cả đối tượng (objects) và primitive values) đều có thể sử dụng làm key hoặc value.
WeakMap thì bản thân bao gồm 2 chữ. Weak và Map, hẳn là phải có cái gì đó “yếu hơn” rồi.
The WeakMap object is a collection of key/value pairs in which the keys are weakly referenced. The keys must be objects and the values can be arbitrary values.
Đối tượng WeakMap là collection của các cặp key/value với các key được tham chiếu yếu ớt. Các key phải là đối tượng và các value có thể là bất kỳ giá trị nào.
Cả Map và WeakMap đều cho phép lưu trữ theo kiểu key-value. Key và uniques và một loạt Value đi kèm. Tuy nhiên vẫn tồn tại một số sự khác biệt map và weakmap.
Weak không có nghĩa là yếu. Chỉ “lỏng lẻo” hơn một vài điểm thôi
2. Sự khác biệt Map và Weak Map
2.1 Key Store
Cùng là cơ chế lưu trữ key-value. Nhưng có sự khác biệt rõ ràng về tiêu chuẩn của key.
A WeakMap accepts only objects as keys whereas a Map,in addition to objects, accepts primitive datatype such as strings, numbers etc.
WeakMap chỉ chấp nhận kiểu objects đóng vai trò như là key. Trong khi Map, ngoài kiểu Objects, nó còn chấp nhận cả các kiểu nguyên thủy như Strings, Numbers,…
JavaScript
map.set(44, 12);
// Lỗi
weakmap.set(44, 13);
// Lỗi. Không thể tạo WeakMap từ 2D array.
var map_1 = new WeakMap([[1, 2], [4, 5]]);
Mà cái thằng WeakMap này cũng dị. Mấy function hay methods dùng được với map thì không dùng được với Weakmap.
JavaScript
console.log(weakmap.size); //undefined
//loop through the keys in an map
for(var i of map)
{
console.log(i);
}
//loop through the keys in an weakmap doesn't work
for(var i of weakmap)
{
console.log(i);
}
//delete all keys
map.clear();
weakmap.clear(); //but this works
Ngoài chuyện Primitive Type thì còn một vấn đề khác nữa là Memory Leak. Cái này là “cốt lõi”. Có nó mới có chữ Weak trong WeakMap. Khác biệt cốt lõi nhất!
Memory thì từ xưa đến nay đã luôn là vấn đề cố hữu của Map. Một Map được tạo ra cũng đồng nghĩa với một phần memory được cấp phát. Cấp thì dễ, nhưng đôi khi mấy anh dev lại quên lấy lại phần đó (mặc dù đã sử dụng xong Map).
Sự khác biệt giữa Map và WeakMap lại là vấn đề này. Đây cũng là điểm khác biệt cần nắm rõ để có lựa chọn sáng suốt khi lựa chọn Map hay WeakMap.
Để dễ hiểu hơn thì ta có thể xem xét ví dụ sau:
JavaScript
var map = new Map();
var weakmap = new WeakMap();
(function(){
var a = {x: 12};
var b = {y: 12};
map.set(a, 1);
weakmap.set(b, 2);
})()
Xét như ví dụ phía trên, function tóm gọn lại từ dòng 4 đến dòng 10. Tuy nhiên để access được b = {y: 12}; bên ngoài function scope thực sự là không thể.
Mặc dù cả Map và Weakmap đều là global access variable. Tuy nhiên cơ chế của WeakMap cho phép Garbage collector xóa pointer tới b của chính nó. Bản thân chữa weak mang nghĩa là yếu, liên kết của weak map tới chính đứa con mà nó set vào là liên kết lỏng lẻo.
So “Map” can cause more garbages in memory. We can say that “Map” references are strong pointer whereas “WeakMap” references are weak pointers.
Chính vì vậy Map có thể chiếm dụng khá nhiều về mặt memory. Cũng có thể nói “map” tham chiếu mạnh hơn so với “WeakMap”. Tham chiếu ở đây được hiểu là tham chiếu tới memory được cấp phát.
3. Lúc nào nên xài cái gì?
Không phải ngẫu nhiên mà Javascript ES6 bổ sung thêm WeakMap. Rõ ràng Map luôn có sự bất tiện khi sử dụng.
Rõ ràng có sự bất tiện khi sử dụng Map
Vấn đề memory có được dọn dẹp hay không đã là sự khác biệt map và weakmap. Vậy lúc nào ta nên dùng WeakMap?
Đầu tiên, ta cần một Map để chứa key-value. Tuy nhiên thời gian sử dụng chỉ gói gọn trong một vài dòng code. Tính toán, mapping xong lại không xài tới Map nữa -> Ưu tiên sử dụng WeakMap.
Keeping private data about a specific object and only giving access to it to people with a reference to the Map. A more ad-hoc approach is coming with the private-symbols proposal but that’s a long time from now. Đảm bảo an toàn cho dữ liệu, chỉ cho phép access đối với các đối tượng có tham chiếu và cần sử dụng tới WeakMap.
Chắc sẽ còn một vài trường hợp khác WeakMap trở nên hữu ích. Tui sẽ bổ sung cho mấy ông sau.
Chúng mình xin chia sẻ với bạn kỹ thuật bảo mật tiên tiến, mạnh mẽ dành cho Java web sử dụng Spring Security. Bài viết hội tụ các best-practice, các kỹ thuật tối tân nhất hiện nay. Nội dung sẽ hay và khó.
Công nghệ sử dụng (technology-stack)
Spring Security
Spring Boot
Spring Web MVC
Gradle (build system)
Thymeleaf (template engine)
Công cụ sử dụng
IntelliJ IDEA 2016.1.3
Apache Tomcat 8.0.35
Trước khi bắt tay thực hiện, Bạn nên đọc các bài viết trên SmartJob:
Giới thiệu Spring Boot (để hiểu về Spring Boot)
Kiểm tra tính hợp lệ của dữ liệu đầu vào form Spring Web MVC bởi Hibernate Validator (để hiểu về luồng đi của Spring Web MVC)
Spring Security [1] là bộ khung bảo mật ứng dụng Java web cung cấp cơ chế cấp phép quyền (authorization) và xác thực người dùng (authentication). Sức mạnh của Spring Security thể hiện ở sự dễ dùng, dễ cấu hình, khả năng mở rộng, và khả năng bảo mật ứng dụng mạnh mẽ. Spring Security chống được các kỹ thuật hacking tinh vi:
Session fixation (Tấn công chiếm quyền điều khiển session của người dùng) [2]
Clickjacking (click chuột tự động, ví dụ click vào nút Like Facebook mà không xin phép người dùng)
CSRF (Cross-site request forgery: Tạo truy vấn (request) giả mạo truyền từ trang này sang trang khác)
Mục tiêu
Chúng ta sẽ xây dựng ứng dụng Java web có trang chủ, trang đăng nhập, khu vực bảo mật (chỉ truy cập được sau khi người dùng hợp lệ đăng nhập). Cấu trúc project sau khi hoàn tất sẽ như thế này
Bạn sẽ phải tạo/chỉnh sửa 7 file:
Application.java
MvcConfig.java
WebSecurityConfig.java
dang_nhap.html
khu_vuc_bao_mat.html
trang_chu.html
build.gradle
Sử dụng IntelliJ IDEA để khởi tạo project:
Bộ khung project được dựng trên nền Spring Boot. Bấm chọn Spring Initializr, rồi bấm Next.
Bạn điền thông tin, các tùy chọn vào 10 mục như ảnh chụp màn hình. Bước thứ 11 là bấm nút Next
Chọn và nhặt vào thư viện cần dùng. 3 thành phần tham gia mở rộng ứng dụng Spring Boot là:
Security
Web
Thymeleaf
Phiên bản Spring Boot ổn định, mới nhất tại thời điểm viết bài là 1.3.5.RELEASE
Đặt tên project, chọn vị trí lưu mã nguồn project
Cấu hình cho Gradle build system
InteliJ IDEA và Spring Initializer tự động sinh ra cho bạn cấu trúc thư mục của project. Tìm file build.gradle sửa lại nội dung như sau:
Dòng 10, 29 cho phép chúng ta đóng gói ứng dụng Spring Boot thành file WAR truyền thống. (Nếu để theo mặc định, Spring Boot sẽ đóng gói ứng dụng web thành file JAR thực thi dùng Apache Tomcat nhúng, run trực tiếp, rất đặc sắc. Tuy nhiên theo cách này việc deploy sẽ quá đơn giản đến mức khiến bạn đọc khó hiểu, tại sao không có file WAR mà ứng dụng Java web vẫn chạy).
Tạo thêm thư mục java trong thư mục src/main, rồi tạo thêm package vn.smartjob.demo_spring.demo_security . Tạo class Application.java
package vn.smartjob.demo_spring.demo_security;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
/**
* Điểm đi vào của ứng dụng Spring Boot.
*/
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Application.class);
}
public static void main(String[] args) throws Throwable {
SpringApplication.run(Application.class, args);
}
}
Mã nguồn dưới đây thực chất đã chuyển đổi (convert) ứng dụng Spring Boot nguyên bản (tạo ra file JAR có thể run được) thành ứng dụng web Java truyền thống (đóng gói thành file WAR) bằng cách:
– Dòng 12, class Application được extends từ class org.springframework.boot.context.web.SpringBootServletInitializer
– Dòng 14 đến 17, phải Override method configure để đóng gói ứng dụng Spring Boot thành file WAR truyền thống.
Tạo thêm thư mục resources bên trong thư mục src\main. Tạo thư mục templates bên trong thư mục resources.. Sau đó tạo 3 tập tin giao diện (sử dụng Thymeleaf template engine). Các file giao diện sử dụng Thymeleaf. Ở đâu Thymeleaf xử lý (render) ra giao diện, sẽ sử dụng HTML element mà trong đó chứa thuộc tính th:____ . Các thẻ security chèn vào trong giao diện sẽ do Spring Security xử lý.
File trang_chu.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://wwww.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Trang chủ</title>
</head>
<body>
<h1>Ứng dụng minh họa Spring Security</h1>
<p>Bấm vào <a th:href="@{/hello}">link này</a> để đăng nhập.</p>
</body>
</html>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Đây là khu vực bảo mật</title>
</head>
<body>
<h2>Đây là khu vực chỉ dành cho thành viên đã đăng nhập</h2>
Bí quyết công nghệ sử dụng trong ứng dụng này:<br/>
<ol>
<li>Spring Web MVC</li>
<li>Spring Boot</li>
<li>Spring Security</li>
</ol>
<h1 th:inline="text">Xin chào bạn [[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Đăng xuất"/>
</form>
</body>
</html>
File MvcConfig.java . 4 dòng code từ 12 đến 15, là việc ánh xạ (mapping) đường dẫn truy cập với file giao diện tương ứng (đó chính là tên các file html không bao gồm đuôi file).
package vn.smartjob.demo_spring.demo_security;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/home").setViewName("trang_chu");
registry.addViewController("/").setViewName("trang_chu");
registry.addViewController("/hello").setViewName("khu_vuc_bao_mat");
registry.addViewController("/login").setViewName("dang_nhap");
}
}
File WebSecurityConfig.java . Cấu hình bảo mật sử dụng hoàn toàn bằng method và class Java. Bạn để ý bên trên class có annotation @Configuration và @EnableWebSecurity để ứng dụng nhận diện thông số bảo mật nằm trong đoạn mã nguồn này. Để cho đơn giản, chúng ta cấp phép 1 người dùng có tên smartjob cùng mật khẩu 4321 . Chúng ta có thể cấu hình các quy tắc bảo mật tinh tế sau này trong các ví dụ sau.
package vn.smartjob.demo_spring.demo_security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* Cấu hình bảo mật
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* Cấu hình xác thực người dùng (authentication).
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
/**
* Cấu hình cấp phép (quyền) người dùng (authorization).
*
* @param auth
* @throws Exception
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("smartjob").password("4321").roles("USER");
}
}
Dòng 26: Cấp phép truy cập, khởi tạo luồng lo-gic bảo mật.
Dòng 27: Cho phép tất cả người dùng (đã đăng nhập, hay chưa đăng nhập) đều được truy cập trang chủ.
Dòng 30: Nếu người dùng có nhu cầu, đưa người dùng đến trang Đăng nhập.
Dòng 31: Tất cả mọi người dùng đều có quyền tiếp cận (truy cập) trang Đăng nhập.
Dòng 34: Sau khi đăng nhập, người dùng đã được xác thực có thể Đăng xuất.
Dòng 48: Cấp phép người dùng có tên đăng nhập smartjob, mật khẩu 4321, với vai trò (role) là người dùng thông thường (USER). Trên một hệ thống sẽ có nhiều role khác nhau.
Chạy ứng dụng trên Apache Tomcat 8
Chúng ta cố tình nhập sai tên đăng nhập hoặc mật khẩu để kiểm tra thông báo lỗi và kiểm tra khả năng ngăn chặn của Spring Security:
Khi nhập đúng tên đăng nhập: smartjob và mật khẩu: 4321 thì người dùng sẽ truy cập được khu vực bảo mật:
Đăng nhập thành công, người dùng truy cập được khu vực đặc quyền riêng. Thậm chí Trình duyệt web còn hỏi bạn có muốn lưu tên đăng nhập, mật khẩu ứng dụng trong trình duyệt hay không. Để sử dụng tính năng đăng xuất (do Spring Security cung cấp), bạn bấm nút Đăng xuất để kiểm tra việc vận hành của việc xóa thông tin người dùng trên trình duyệt.
Mẹo: Trong quá trình phát triển ứng dụng, có thể bạn phải xóa thông tin đăng nhập lưu trong trình duyệt đi để test khả năng bảo mật. Bạn làm như sau: Trình duyệt Google Chrome, chọn Setting, gõ từ khóa tìm kiếm pri (privacy)
Chọn Content settings… rồi tìm
Chọn All cookies and site data…
Nhớ bấm nút X rồi sau đó Done để xóa thông tin bảo mật lưu trên trình duyệt, nhằm mục đích chuẩn bị môi trường test ứng dụng Spring Security.
Người dùng cuối thường sẽ không phân biệt được những điểm giống và khác nhau của website và web application. Họ chỉ việc nhập URL và sử dụng kết quả tìm kiếm là được. Nhưng với các dev, bạn chính là người build nên các nền tảng này cho người dùng sử dụng. Do đó việc nắm bắt được sự khác nhau này chắc chắn sẽ giúp ích rất nhiều cho công việc.
Web Application còn hay được gọi tắt là web app là một phần của phần mềm có thể được truy cập bởi trình duyệt. Nói cách khác, web app là website có chức năng và các yếu tố tương tác.
Các web app có khả năng tùy biến cực cao và có thể thực hiện nhiều tác vụ và chức năng khác nhau. Chúng thường phức tạp hơn và khó xây dựng hơn, đồng thời chúng cần một đội ngũ phát triển phần mềm có kinh nghiệm để tạo ra chúng.
Một số web apps phổ biến hiện nay như Twitter, Facebook, Gmail, Adobe CC và Youtube.
Web Application là gì?
Đặc điểm của Web Application
Đa nền tảng
Dễ dàng test với các bài kiểm tra automated
Được lưu trữ trên đám mây
Bạn sử dụng Web Application để làm gì?
Có thể sử dụng trên mọi nền tảng vì chúng hỗ trợ tất cả các trình duyệt hiện đại
Bạn không cần phê duyệt từ cửa hàng ứng dụng để có một web app
Người dùng có thể truy cập chúng bất kỳ lúc nào, từ bất kỳ đâu
Có thể sử dụng trên cả thiết bị di động hoặc máy tính để bàn để truy cập dữ liệu
Dễ hiểu hơn vì sử dụng cùng một bộ mã trong toàn bộ ứng dụng
Website là một tập hợp các trang web có liên quan chứa hình ảnh, văn bản, âm thanh, video và hơn thế nữa. Nó có thể bao gồm một trang hoặc nhiều trang và nó cung cấp cả nội dung trực quan và văn bản. Có rất nhiều loại website khác nhau hiện tại trang web giáo dục, cộng đồng, tìm kiếm, viết blog,…
Một số website phổ biến như Wikipedia, Google, Amazon và Craigslist.
Website là gì
Đặc điểm của một Website
Thân thiện với người dùng
Có thể dễ dàng tìm kiếm bằng công cụ tìm kiếm
Hiển thị nội dung chất lượng
Có một bố cục dễ điều hướng
Khi nào bạn cần dùng đến website?
Bạn có thể cần một trang web để giới thiệu sản phẩm của mình
Trang web giúp bạn thiết lập thương hiệu cho doanh nghiệp
Giúp tạo ra bằng chứng xã hội để người khác có thể thấy những gì bạn đã và đang làm
Sử dụng nó để quảng cáo và nâng cao nhận thức về thương hiệu của mình
Sự khác biệt chính giữa Website và Web Application
Sự tương tác của người dùng
Website cung cấp nội dung văn bản và hình ảnh mà người dùng có thể xem và đọc, nhưng điều này không ảnh hưởng đến hoạt động của trang web.
Với web app, người dùng không chỉ xem nội dung trên trang mà còn thao tác dữ liệu. Người dùng có thể tương tác từng người một bằng cách điền vào biểu mẫu hoặc cung cấp dữ liệu cần thiết để tương tác với ứng dụng.
Vấn đề xác thực
Xác thực không phải lúc nào cũng cần thiết cho các website dựa trên thông tin. Người dùng có thể được yêu cầu đăng ký nhận các bản cập nhật thường xuyên để truy cập các tùy chọn bổ sung, và thế là xong.
Các web app cần xác thực vì chúng cung cấp phạm vi tùy chọn và chức năng/tương tác rộng hơn nhiều so với một trang web. Điều này có nghĩa là bạn phải có tên người dùng và mật khẩu để truy cập vào tài khoản của mình.
Một số khác biệt cơ bản giữa website với web application
Tasks và sự linh hoạt
Một website sẽ chỉ hiển thị dữ liệu và thông tin được thu thập trên một trang cụ thể khi người dùng đã tìm kiếm.
Trong một web app, các chức năng cao hơn và phức tạp hơn so với các chức năng của một trang web.
Mục đích sáng tạo
Một website chủ yếu bao gồm nội dung tĩnh. Điều này có nghĩa là thông tin có thể truy cập công khai cho tất cả khách truy cập.
Web app được thiết kế để tương tác với người dùng cuối. Điều này có nghĩa là nếu không có thông tin đăng nhập bắt buộc, bạn có thể không truy cập được vào bất kỳ dữ liệu nào.
Khi xử lý một website, những thay đổi nhỏ không bao giờ yêu cầu biên dịch lại và triển khai đầy đủ. Bạn chỉ cần cập nhật mã HTML và mọi thứ sẽ được cập nhật.
Trong khi với web app, bạn cần phải biên dịch lại và triển khai lại ứng dụng bất cứ khi nào bạn thực hiện thay đổi.
Phát triển một website là một quá trình tương đối đơn giản. Nhưng việc tạo một web app đòi hỏi kiến thức sâu hơn, nhiều kinh nghiệm hơn và lập kế hoạch nhiều hơn. Do đó, nắm rõ sự khác biệt của chúng giúp bạn hiểu rõ mình cần làm gì và phát triển như thế nào.
Bài viết được sự cho phép của tác giả Kien Dang Chung
Video trong bài viết
Chúng ta có quy trình 3 bước để máy có thể học nhưng trước khi đi đến phần thuật toán, tôi muốn giới thiệu với các bạn một chút về môi trường chúng ta sẽ thực hành viết code Python.
Jupyter Notebook là công cụ thực hiện code Python, không những thế bạn có thể đưa vào văn bản, hình ảnh giúp cho các đoạn code dễ hiểu và có thể chia sẻ với mọi người.
Phần văn bản của Jupyter Notebook có thể đưa vào nội dung rất phong phú với các cú pháp Markdown và LaTex:
Markdown là một cú pháp sẽ được phân tích cú pháp thành HTML, nó có thể đưa vào tất các các phần tử của HTML, do đó Notebook của bạn sẽ không khác gì một trang web cả.
LaTex là công cụ đánh dấu và hiển thị cho các công thức toán học và khoa học. Bạn thử xem hai hàm số này, cái nào dễ nhìn hơn: y = x^2 + 2x + 1 và y=x2+2x+1y=x2+2x+1.
Trang web bạn đang xem All Laravel được chúng tôi xây dựng trên nền tảng OctoberCMS cũng đang sử dụng các công cụ như Markdown và LaTex để biên soạn nội dung các bài viết mà bạn đang xem.
1.1 Cú pháp Markdown
Có khá nhiều cú pháp trong Markdown nhưng tôi sẽ giới thiệu sơ qua một số cú pháp hay dùng nhất:
Tiêu đề: Trong HTML chúng ta có các thẻ tiêu đề từ H1 đến H6 thì trong Markdown, chúng ta thực hiện định dạng một câu thành tiêu đề bằng dấu # ở trước. Ví dụ:
# Đây là tiêu đề H1
nó sẽ tương ứng với
<h1>Đây là tiêu đề H1</h1>
Muốn có tiêu đề H2 chúng ta sử dụng hai dấu ##, H3 tương ứng với ###…
Định dạng văn bản: Một số định dạng phổ biến như viết đậm, viết nghiêng trong Markdown sẽ như sau:
**Đoạn này chữ đậm**
nó sẽ tương ứng với
<strong>Đoạn này chữ đậm</strong>
*Đoạn này chữ nghiêng*
nó sẽ tương ứng với
<em>Đoạn này chữ đậm</em>
Danh sách: Có hai loại danh sách là danh sách có thứ tự với số ở đầu (ol) và danh sách không có thứ tự (ul). Trong Markdown các danh sách được viết khá dễ dàng:
* Python
* C#
* PHP
Tương đương với
<ul><li>Python</li><li>C#</li><li>PHP</li></ul>
Kết quả thực tế như sau:
Python
C#
PHP
Tương tự với danh sách có thứ tự, chúng ta chỉ cần viết như khi đánh văn bản hay dùng:
1. Python
2. C#
3. PHP
Tương đương với
<ol><li>Python</li><li>C#</li><li>PHP</li></ol>
Kết quả thực tế như sau:
Python
C#
PHP
Có rất nhiều các cú pháp trong Markdown, bạn có thể tìm hiểu thêm trên mạng với từ khóa Markdown syntax, ở đây tôi chỉ giới thiệu để bạn hiểu được Markdown là gì? Bản chất Markdown là một ngôn ngữ đánh dấu trung gian, nó khá tiện dụng khi viết nội dung đặc biệt với các tài liệu khoa học. Các hệ thống hỗ trợ Markdown sẽ thực hiện theo thứ tự như sau:
Markdown -> HTML -> Hiển thị nội dung cho người dùng.
1.2 Cú pháp LaTex
LaTex nhận dạng nội dung bên trong một đoạn văn bản là công thức hay không thông qua thẻ $ và $$.
Với $, nội dung bên trong sẽ là biểu thức nằm trên cùng một dòng với văn bản ở ngoài.
Với $$ nội dung bên trong là biểu thức sẽ được trình bày ở giữa một dòng mới.
Ví dụ: Phương trình hàm parabol y=3x2+2x+1y=3×2+2x+1 là dạng cùng dòng (inline) với thẻ $ trong LaTex. Nhưng biểu thức sau đây lại sử dụng $$:
∑i=0ni2=(n2+n)(2n+1)6∑i=0ni2=(n2+n)(2n+1)6
Chỉ số trên và chỉ số dưới: sử dụng _ cho chỉ số dưới và ^ cho chỉ số trên. Ví dụ: $x^2$ sẽ có kết quả là x2x2, $n_i$ sẽ có kết quả là nini và kết hợp cả hai $\theta_0^{(i)}$ sẽ cho kết quả là θ(i)0θ0(i). Các biểu thức toán sẽ rất đẹp phải không bạn.
Tổng, tích phân: Với tổng chúng ta sử dụng \sum, tích phân là \int với các chỉ số trên và chỉ số dưới giống như ở phần trên. Ví dụ: $\sum_{i=0}^ni^2$ cho kết quả ∑ni=0i2∑i=0ni2. Ví dụ khác về tích phân $$\int_0^{\frac{\pi}{4}}\sqrt{1-x^2}dx$$ cho kết quả
∫π401−x2−−−−−√dx∫0π41−x2dx
Phân số: Phân số trong LaTex sử dụng cú pháp $\frac{a}{b}$, ví dụ $\frac{(n^2+n)(2n+1)}{6}$ cho kết quả (n2+n)(2n+1)6(n2+n)(2n+1)6.
Trên đây là một số cú pháp cơ bản khi biểu diễn các biểu thức toán học với LaTex, bạn có thể xem nhiều các công thức toán học khác có thể được biểu diễn bằng LaTex ở đây.
1.3 Đệ nhất lẩu Jupyter, Markdown và LaTex
Với sự kết hợp của Markdown và LaTex chúng ta có thể biểu diễn các thuật toán với các biểu thức toán học kết hợp với các nội dung diễn giải tuyệt vời. Không chỉ có vậy, Jupyter Notebook là một tài liệu có chứa cả các cell có code Python có thể thực thi được, bạn thấy đấy, giờ đây chúng ta chỉ cần 1 chỗ vừa có thể xem các diễn giải các công thức và dưới đó là các phần code thực hiện và xem các kết quả chương trình đưa ra. Có thể nói ĐỆ NHẤT LẨU JML (Jupyter-Markdown-LaTex) đã mang lại cho chúng ta những lồi nẩu cực cực cực … cực kỳ hấp dẫn.
2. Vẽ đồ thị trong Python
Các cụ nhà ta có câu:
Trăm nghe không bằng một thấy.
Cho thấy tầm quan trọng của việc biểu diễn dữ liệu bằng các biểu đồ, đồ thị. Trong phần này, chúng ta sẽ thực hiện vẽ đồ thị các hàm số trong Python, nó giúp cho các thuật toán lằng nhằng có thể được diễn tả bằng hình ảnh một cách dễ hiểu nhất.
Trong hệ sinh thái Python, có rất nhiều các thư viện bên thứ 3 được phát triển dưới dạng mã nguồn mở, rất phổ biến và cực hữu ích khi làm việc với Python. Trong phần này tôi giới thiệu với bạn hai thư viện được dùng khá nhiều trong Khóa học Machine Learning này đó là Numpy và Matplotlib.
Numpy: giúp thao tác với các mảng đa chiều, sinh dữ liệu và thực hiện các phép biến đổi dữ liệu dễ dàng hơn.
Matplotlib: thư viện giúp bạn mô phỏng dữ liệu thành các biểu đồ, đồ thị, thư viện không thể thiếu khi bạn thực hiện phân tích dữ liệu.
Nói thì nghe cao siêu vậy thôi, chúng ta cùng bắt tay vào một ví dụ vẽ đồ thị, bạn sẽ thấy các thư viện này rất dễ sử dụng. Trong bài trước, bạn đã nghe đến khái niệm về hàm chi phí hay hàm mất mát (từ giờ tôi sẽ chỉ dùng tên gọi Hàm chi phí), đó là cơ sở để tính toán các sai lệch khi đưa ra các giá trị khởi tạo trong bước 1 của Quy trình 3 bước trong Machine Learning. Chúng ta cần vẽ đồ thị của hàm chi phí để có thể tìm được các điểm cực tiểu mà tại đó là lời giải của thuật toán Grandient Descent.
2.1 Phương thức plot()
Giả sử chúng ta có một hàm chi phí y=x2+x+1y=x2+x+1, chúng ta sẽ vẽ hàm chi phí này để có thể tìm hiểu về điểm cực tiểu, nơi mà chi phí thấp nhất (Chi phí – cost, ở đây không có nghĩa là mất mát về tiền, chi phí ở đây với ý nghĩa là …). Bắt đầu thôi:
Đầu tiên, chúng ta thực hiện import các thư viện cần thiết:
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
Hai dòng đầu tiên là import các thư viện matplotlib và numpy, dòng %matplotlib inline là một câu lệnh của Jupyter Notebook, nó giúp vẽ các đồ thị bên trong các cell. Tiếp đó, để vẽ được hàm y=x2+x+1y=x2+x+1 chúng ta sẽ sử dụng phương thức plot() của pyplot là một module trong matplotlib mà ta đã đặt tên ngắn gọn là plt.
Phương thức plot() giúp vẽ một danh sách các điểm thành một đồ thị, phương thức này có nhiều tham số nhưng chúng ta chỉ quan tâm đến tham số x và y là hai danh sách (Python List) với giá trị là tọa độ x và y.
Chú ý, phương thức plot() trả về một đối tượng Python do đó để thực sự vẽ ra đồ thị, chúng ta cần gọi phương thức show().
2.2 Tạo dữ liệu mẫu với numpy
Bạn thấy đấy, chúng ta có 3 điểm là (0,1) (2,8) và (7,9), ở đây chúng ta đưa hết giá trị trên tọa độ x vào biến x_list và trên tọa độ y vào y_list. plot() đã vẽ ra ba điểm này và giữa hai điểm liên tiếp nó vẽ một đường thẳng. Ok, vậy chúng ta có thể vẽ phương trình y=x2+x+1y=x2+x+1 như thế nào? Chẳng lẽ chúng ta phải tự tạo ra dữ liệu là danh sách các tọa độ x, y này, như thế thì thật mất nhiều công sức, chưa kể là giữa hai điểm ở đây là một đường thẳng, như vậy đồ thị vẽ ra cũng không trơn tru vì chỉ là một đường nối các điểm với nhau.
Không có vấn đề gì, chúng ta vẫn còn Numpy, bạn nhớ chứ, chúng ta đã import thư viện này vào nhưng chưa sử dụng đến. Trong numpy có phương thức linspace() cho phép tạo ra một mảng số với số lượng phần tử cho trước và nằm trong một phạm vi cho trước.
x_1 = np.linspace(start=-3, stop=3, num=100)
Dòng code này sẽ tạo ra một mảng gồm 100 phần tử chạy trong khoảng [-3,3] với giá trị các phần tử cách đều nhau.
2.3 Vẽ đồ thị hàm parabol
Ok, chúng ta đã tạo ra được dữ liệu mẫu cho trục x trong khoảng từ [-3, 3], giờ là lúc cần định nghĩa hàm y=x2+x+1y=x2+x+1 trong Python và tính ra các giá trị cho trục y tương ứng với các giá trị trục x.
def f(x):return x**2+ x +1
y_1 =f(x_1)
Kết quả ta có biến y_1 là một mảng các giá trị hàm số y=x2+x+1y=x2+x+1 tương ứng với x tại các giá trị trong mảng x_1.
Ok, dữ liệu cho các trục x, y đã chuẩn bị xong, giờ chúng ta sẽ vẽ đồ thị y=x2+x+1y=x2+x+1.
plt.plot(x_1, y_1)
plt.show()
Kết quả chúng ta có đồ thị y=x2+x+1y=x2+x+1 như hình dưới đây:
Đồ thị đã rất đẹp, nhưng chúng ta vẫn cần định dạng lại chút, ví dụ thêm nhãn cho các trục tọa độ, giới hạn tọa độ hiển thị:
Trong đoạn code trên, xlim() và ylim() để giới hạn vùng tọa độ sẽ hiển thị trên trục x và y, xlabel() và ylabel() để gán nhãn cho các trục tọa độ, kết quả chúng ta được đồ thị mới như sau:
2.4 Vài lời tâm sự
Đồ thị hàm parabol y=x2+x+1y=x2+x+1 rất đẹp được vẽ lên từ 100 điểm nối với nhau. Chúng ta thấy đường parabol rất trơn tru vì trong một khoảng ngắn có tới 100 điểm thì các điểm gần như sát với nhau do đó chúng ta không cảm nhận được những đoạn thẳng nối từng hai điểm liên tiếp.
Bạn sẽ đặt câu hỏi, tại sao tôi chọn khoảng [-3, 3] để vẽ đồ thị, thực ra khi vẽ chúng ta sẽ phải dò dẫm chút, đưa ra một khoảng bất kỳ và chỉnh dần về khoảng sao cho đồ thị có những điểm đặc biệt nằm chọn trong đó. Khi vẽ bạn cũng chọn số điểm vừa phải, nếu thấy chưa trơn tru thì tăng dần lên tránh việc máy tính phải tính toán nhiều, đó là thói quen cần thiết của những người làm về khoa học dữ liệu.
Một thực tế nữa là khi làm việc với data science, chúng ta chỉ luôn tính xấp xỉ chứ không thể có những con số tuyệt đối như khi học toán học. Những con số xấp xỉ đã có một giá trị rất cao mặc dù nếu khi thi toán, giải kết quả xấp xỉ là bạn bị điểm 0 liền. Ví dụ, khi tính toán một phương án đầu tư, chỉ cần cho kết quả cỡ 60-70% là đã coi như 100% chứ không như trong toán, lúc nào cũng phải 1+1=21+1=2.
3. Bài tập
Nhân tiện về chủ đề vẽ đồ thị và khảo sát hàm số, một chủ đề rất thích hợp với chương trình toán trong Phổ thông trung học. Mình đưa ra một số bài tập ở đây để mọi người vẽ và hồi tưởng về một thời mài đít quần trên ghế nhà trường :D.
Bài tập: Soạn một notebook trên Jupyter bao gồm cả đề bài, vẽ đồ thị với Python.
Cho đồ thị hàm số y=14x3−32x2+5y=14×3−32×2+5
Khảo sát sự biến thiên, vẽ đồ thị đã cho.
Tìm giá trị của m để phương trình x3−6x2+m=0x3−6×2+m=0 có 3 nghiệm phân biệt.
Hehe, giải kiểu gì cũng được nhé, miễn là ra kết quả, kể cả kết quả gần đúng. Lời giải có trong bài tiếp theo nhé.
Dead Letter là một tin nhắn không thể gửi đến người nhận. Dead Letter Queue (DLQ), là hàng đợi chứa tin nhắn chưa được gửi, không thể được gửi đến đích của chúng vì lý do này hay lý do khác.
Trong hàng đợi tin nhắn, DLQ là một dịch vụ được cài đặt để lưu trữ các tin nhắn đáp ứng một hoặc nhiều sự kiện sau:
Tin nhắn bị từ chối (rejected) bởi một Queue Exchange.
Message hết hạn (expire) do Time to live (TTL).
Vượt quá giới hạn chiều dài hàng đợi (length limit).
Đoạn code trên đơn giản tạo một Exchange mới gọi là gpcoder.exchange.name và Exchange mới này là Dead Letter Exchange cho hàng đợi mới được tạo. Lưu ý rằng Exchange không phải được khai báo khi hàng đợi được khai báo, nhưng nó phải tồn tại tại thời điểm các Message được Dead Letter, nếu không thì các Message sẽ bị hủy bỏ.
Theo mặc định RabbitMQ sẽ lấy khoá định tuyến từ Message ban đầu được gửi đến Exchange. Chúng ta cũng có thể chỉ định một khóa định tuyến sẽ được sử dụng khi Dead Letter Message nếu muốn.
Dead-lettered message được định tuyến tới Dead Letter Exchange theo thứ tự ưu tiên sau:
Theo khoá định tuyến được chỉ định cho hàng đợi Dead Letter Message được chỉ định.
Theo khoá định tuyến ban đầu Message được publish.
Ví dụ: Nếu một Message ban đầu được publish đến Exchange với khoá định tuyến foo. Sau đó Message này bị reject và nó trở thành một Dead Letter Message. Và nó được publish đến Dead Letter Exchange với khoá định tuyến foo. Giả sử chúng ta đã chỉ định một khóa định tuyến sẽ được sử dụng khi Dead Letter Message là bar, lúc này Message được publish đến Dead Letter Exchange với khoá định tuyến bar.
Dead-lettered message được re-published với tính năng publisher confirm được bật mặc định để đảm bảo rằng Dead-letter queue phải gửi xác nhận Message (ack) đã được lưu trữ trước khi nó bị xoá ở Queue gốc.
Message bị thay đổi như thế nào khi chuyển sang Dead-Lettered Message?
Header bị thay đổi:
Exchange name bị thay thế bởi Dead-letter exchange name.
Routing key có thể bị bởi một Routing key khác.
Nếu điều trên xảy ra, thì CC và BCC header cũng sẽ bị remove.
Thêm header x-death, với một mảng các giá trị:
queue: tên của Queue mà Message được publish trước khi nó trở thành Dead-Lettered Message.
reason: lý do xảy ra Dead-Lettered Message. Có thể là: rejected, expired, maxlen.
time: thời gian Message bị dead lettered.
exchange: tên Exchange mà Message được publish, nó có thể là dead letter exchange nếu Message bị dead lettered nhiều lần.
routing-keys: là routing key (bao gồm CC keys nhưng không bao gồm BCC keys) của Message được publish.
count: số lần mà Message bị dead-lettered trong Queue này vì lý do này.
original-expiration: nếu Message bị dead-lettered vì lý do TTL, nào là giá trị của thuộc tính expiration ban đầu của Message. Thuộc tính expiration sẽ bị remove từ dead-lettering message để ngăn việc expiring lần nữa trong Queue mà nó được định tuyến đến.
3 header được thêm cho mỗi dead-lettering event đầu tiên:
x-first-death-reason
x-first-death-queue
x-first-death-exchange
Chúng có cùng các giá trị như reason, queue, và exchange của event xảy ra Dead-Lettered ban đầu. Sau khi thêm, các header này không bao giờ được sửa đổi.
Lưu ý rằng: mảng giá trị x-death được sắp xếp gần đây nhất trước, do đó, Dead-Lettered gần đây nhất sẽ được ghi lại trong mục đầu tiên.
Ví dụ sử dụng Dead Letter Exchange trong RabbitMQ
Trong ví dụ này tôi sử dụng Dead Letter Exchange để mô phỏng trường hợp Retry xử lý sau mỗi 300 millisecond nếu ngay lần nhận Message đó không thể xử lý thành công.
Tạo một WorkQueue và bind đến WorkExchange.
Tạo một RetryQueue và bind đến RetryExchange.
Gán agruments: x-dead-letter-exchange đến WorkExchange
Gán agruments: x-message-ttl là 300 ms.
Producer publish một Message đến WorkExchange. Sau đó, WorkExchange sẽ chuyển Message đến WorkQueue.
Consumer nhận Message từ WorkQueue và cố gắng xử lý nó.
Trường hợp xử lý thất bại, Consumer sẽ publish Message đó đến RetryExchange. Sau đó, RetryExchange sẽ chuyển Message đến RetryQueue.
Message sẽ lưu tại RetryQueue trong 300 ms.
Khi Message bị expire, nó sẽ được chuyển đến WorkExchange và được chuyển đến WorkQueue.
Khi đó Consumer có thể nhận lại Message từ WorkQueue và xử lý lại.
Như bạn thấy, Message được tự động chuyển từ WorkQueue sang RetryQueue sau mỗi 300 ms nhờ vào Dead Letter Message. Đây là ví dụ cho trường hợp một Message hết hạn (expire) do Time to live (TTL).
Tương tự các bạn có thể set agruments là agruments.put(“x-max-length”, 10) để test trường hợp số lượng Message trong Queue vượt quá giới hạn chiều dài hàng đợi (length limit).
Ở bài viết trước, ta đã phân tích về Load Balancer và bài toán “Server nào sẽ handle request nào?“, ở bài viết này, ta sẽ bàn tiếp về một khía cạnh khác của Load Balancer là Stateful App Servers.
Qua bài viết, phần nào ta đã hiểu được rằng tại sao App Servers không hề quan tâm tới User State. Tuy nhiên, với những Servers được thiết kế Stateful App Servers, bằng cách nào ta có thể áp dụng Load Balancer thành công?.
Bản thân nó luôn đối đãi như nhau với các request từ phía client. Công bằng và văn minh vcl.
Sự công bằng này đảm bảo cho mô hình Load Balancer hoạt động hiệu quả, server nào trống sẽ được phân công làm nhiệm vụ ngay. Nếu một server quan tâm tới user state, nó sẽ bị kẹt không biết tới khi nào (ví dụ như một chị đi shopping 3 tiếng mới thêm một món vào giỏ hàng). Server nếu hold cứ hold mãi không biết tới lúc nào.
Bản thân kiến trúc Load Balancer cũng cần giải pháp để kết nối những công việc còn “chưa xong” ở phía client. Kiến trúc này thường được biết tới như Stateful App Servers. Lúc này mỗi machine trong kiến trúc balancer sẽ giữ một số thông tin phía client. Bản thân những thông tin này cho phép Server có thể tùy ý tracking client đã làm việc với server nào
Những yếu tố có thể giúp việc stateful dễ dàng hơn:
Với những application đã được thiết kế theo hướng Stateful App Servers thì điều quan tâm tới state hiện tại của user để có cách handle hợp lý. Trường hợp có Load Balancer, bằng cách nào ta có thể đáp ứng được “stateful”, mà vẫn đảm bảo “load balancer” thành công.
2.1 IP Address
IP address là một cách khả phố biến để phán định xem server nào sẽ tiếp tục xử lý request từ phía client. Tracking IP sẽ được thực hiện ngay từ lần đầu tiên User request tới server.
Ví dụ client A send request tới Server B. Ở các request tiếp theo, khi server phát hiện địa chỉ IP nó đã handle trước đó. Sử dụng IP Address thường đi kèm thêm lớp Network Load Balancer, có trách nhiệm phân giải IP, block các IP đến từ khu vực không cho phép thiết lập trước đó.
Kiến trúc Application Load Balancer với IP Address. Nguồn ảnh / Source: aws.amazon.com
Sử dụng IP Address cũng là giải pháp thường biết tới với tên “Layer 4”. Bản thân Layer 4 ở đây là Transport Layer trong kiến trúc 7 tầng của mạng máy tính (7 layers OSI Model). Sử dụng IP Address cũng là biện pháp khá phổ biến cho các Stateful App Servers
2.2 Web Application
NgoàimLayer 4, ta cũng có thể sử dụng một số kỹ thuật thường được sử dụng ở phía Application như:
Session Id
Session Id SSL
Cookies
để phán định server sẽ handle request còn dang dở. Session và Cookies từ đâu đã là thành phần không thể thiếu của Stateful App Servers. Tuy nhiên để handle session trong kiến trúc Load Balancer, ta cần biết tới một khái niệm mới là Sticky Session.
Với Session stickiness, request được điều phối đi đúng server để tiếp tục handle
Session stickiness, a.k.a., session persistence, is a process in which a load balancer creates an affinity between a client and a specific network server for the duration of a session, (i.e., the time a specific IP spends on a website). Using sticky sessions can help improve user experience and optimize network resource usage.
Session Stickiness là quá trình load balancer khởi tạo mối quan hệ giữa client và server trong thời gian của một phiên (session). Sử dụng sticky session cải thiện performance và user experienced, tối ưu tài nguyên server được sử dụng.
Dựa vào session id được cấp phát, Session stickiness sẽ lựa chọn đúng Server đã handle các request còn dang dở. Ở các request tiếp theo, nó cũng điều động các request đi đúng hướng, trỏ về đúng server mục tiêu.
Không những thay đổi lớn về performance, không thể chê được vào đâu khi bổ sung hàng loạt tính năng mới, nhưng kích thước của Vue 3 cũng chỉ bằng Vue 2 (12KB).
Cải tiến đáng giá được nhắc tới trong bài viết này bao gồm:
Composition API
Multiple root elements
Suspense
Multiple v-models
Sơ bộ anh em có thể xem qua video này để nắm bắt thêm Vue 3 có gì mới nha!.
2. Composition API
Kể từ khi Vue 2 nổi lên như là ngôi sáng sáng giữa bầu trời, rất nhiều dự án lớn đã sử dụng Vue. Một khi dự án lớn (cỡ vài trăm component), vấn đề tái sử dụng (reusable code) và scability (mở rộng) trở nên quan trọng hơn bao giờ hết.
Ok, Composition API là câu trả lời đầu tiên cho Vue 3 có gì mới?. Vậy Composition API là gì?
Composition API as a reactive API coupled with the ability to register lifecycle hooks using globally imported functions.
Composition API là thành phần của Reactive API, với khả năng đăng kí các lifecycle hooks sử dụng các import global cho functions.
Quất thêm cái nữa khó hiểu hơn:
The main point is that the Composition API provides a different way to manage reactivity at all points in an application, without compromising organization and readability.
Điểm mấu chốt mà Composition API cung cấp là một “cách khác” để quản lý reactivity tất cả các điểm trong application, mà không ảnh hưởng tới tổ chức code và khả năng đọc hiểu.
Vãi nồi khó hiểu, đọc vậy mông lung như một trò đùa.
2.1 Dễ hiểu hơn được không?
Thực chất Composition API cho phép ta đóng gói một loạt các method trong life cycle. Ví dụ, trước kia ở Vue 2, created tính toán, chuẩn bị dữ liệu cho object A, watch theo dõi object A khi thay đổi, … Vô vàn thứ cần làm với A. Source code trở nên mất kiểm soát, khó hiểu nếu bạn không phải là người viết ra component đó.
Các vùng có màu khác nhau thì độc lập với nhau. Nội đủ hết method trong life cycle cũng đã là nhiều với component.
Chính bởi khi có quá nhiều Component và nhiều logic phức tạp. Khó maintain và scale, nên ta cần tới Composition API.
Với Composition API, tất cả các đoạn code xử lý liên quan tới Object A có thể được gom lại duy nhất ở setup.
Tuy nhiên, đừng quên là Vue 3 vẫn hỗ trợ Options API, nên nếu muốn, không có vấn đề gì khi cứ viết các method theo Life Cycle của Vue nha. Với Options API, cùng lướt qua component vô cùng đơn giản với Options API.
Because the component instance is not yet created when setup is executed, there is no this inside a setup option. This means, with the exception of props, you won’t be able to access any properties declared in the component – local state, computed properties or methods.
Bởi vì Component instance chưa hề được tạo ra khi method setup được thực thi, nên this chắc chắn không work. Ngoại trừ cái thằng props ra, tuyệt nhiên ta không thể access tới bất cứ properties nào khai báo trong component, local state, computed properties hoặc methods cũng không là ngoại lệ.
3. Multiple root elements
Multi root elements ở đây được hiểu là component không cần phải thuộc về duy nhất một node cha nữa. Câu trả lời thứ 2 cho Vue 3 có cái gì mới xem ra dễ hiểu hơn nhiều!.
Bai bai cái lỗi khó chịu quỷ tha ma bắt này. Bắt đầu từ Vue 3, một component có thể có nhiều hơn một node cha.
Tha hồ, mặc sức mà tạo node con trong template với Vue 3. Việc bỏ đi một node cha cũng bớt phiền phức khi làm việc với css. Trước đây 2 tag p nếu bắt buộc chung nhau 1 div phải viết css parent child mệt mỏi. Tuy nhiên, giờ mạnh ai người đó xài css của mình.
Suspense là cái tiến mới ở Vue 3 liên quan tới Asynchorous (Bất đồng bộ). Trước đây khi làm việc với API, do việc xử lý call API là bất đồng bộ, nên cần chờ tới khi có response trả về mới thực hiện render.
Thông thường sử dụng v-if, tuy nhiên với Suspense, mọi việc trở nên đơn giản hơn nhiều.
Suspense is a special component that renders a fallback content instead of your component until a condition is met.
Suspense là component dặc biệt render nội dung dự phòng thay vì component chính (lúc này chưa có data), cho tới khi condition (điều kiện để render component) được xác lập.
Sử dụng Suspense cũng khá đơn giản, chỉ cần lưu ý tới 2 từ khóa default và fallback. Cùng xem xét ví dụ sau:
Vue.js Component
<template>
<Suspense>
// Sau khi đã có response từ API, nội dung template này sẽ render sau.
<template #default>
<div v-for="item in articleList" :key="item.id">
<article>
<h2>{{ item.title }}</h2>
<p>{{ item.body }}</p>
</article>
</div>
</template>
// Trước khi có data từ API, nội dung trong template này sẽ tải trước
<template #fallback>
Articles loading...
</template>
</Suspense>
</template>
<script>
import axios from 'axios'
export default {
async setup() {
let articleList = await axios
.get('https://jsonplaceholder.typicode.com/posts')
.then(response => {
console.log(response)
return response.data
})
return {
articleList
}
}
}
</script>
5. Multiple V-models
Làm việc với Vue 2 chắc chắn đã biết tới v-models. V-models là two way binding. Dữ liệu binding 2 chiều, thường được sử dụng với form trên component.
Hữu dụng là thế, tuy nhiên Vue 2 chỉ cho phép một component có duy nhất một v-models. Vue 3 thì khác, cho phép không giới hạn số lượng v-model trong component.
Vue.js Component
<template>
<Suspense>
// Sau khi đã có response từ API, nội dung template này sẽ render sau.
<template #default>
<div v-for="item in articleList" :key="item.id">
<article>
<h2>{{ item.title }}</h2>
<p>{{ item.body }}</p>
</article>
</div>
</template>
// Trước khi có data từ API, nội dung trong template này sẽ tải trước
<template #fallback>
Articles loading...
</template>
</Suspense>
</template>
<script>
import axios from 'axios'
export default {
async setup() {
let articleList = await axios
.get('https://jsonplaceholder.typicode.com/posts')
.then(response => {
console.log(response)
return response.data
})
return {
articleList
}
}
}
</script>
Đến thời điểm hiện nay, tôi thường yêu cầu các dự án của mình phải có CI. Nếu viết bằng script như nodejs thì CI sẽ có nhiệm vụ check syntax, để các thành viên trong nhóm cùng một kiểu viết giống nhau, và chạy các mức test khác nhau như unit test, CDC test.
Điểu tuyệt vời của gitlab là nó đi kèm với bộ CI miễn phí cho cả dự án private (yay). Blog này sẽ trình bày chiến lược để thiết lập ENV test trên Gitlab CI để build docker image, và upload nó lên gitlab registry.
Là nơi chương trình test của bạn sẽ được chạy. Bạn có thể có một PC riêng biệt cho việc build và test. Gitlab cung cấp một chương trình khá tiện https://gitlab.com/gitlab-org/gitlab-ci-multi-runner để thiết lập runner cục bộ. Cá nhân tôi chỉ sử dụng multi runner này khi chương trình cần tài nguyên lớn.
Shared runner chạy trên docker, nên có thể khai báo các docker có sẵn khá tiện (yay).
.gitlab-ci.yml
Là file cấu hình cho gitlab CI. Dưới đây là một ví dụ trong đó khai báo một trạng thái của test pipeline là unit test. Phần sau dựa trên giả thuyết tôi đang xây dựng một web app với nodejs, và chạy unit test với npm run test.
image: node:boron-alpine
stages:
- unit_test
unit_test_job:
stage: unit_test
script:
- npm install
- npm run test
Với mỗi git push lên CI được trigger. CI sẽ pull về docker image node:boron-alpine giống với docker chạy trong staging và production environment của web app này. Stages khai báo các bước của CI. Trong trường hợp này, tôi chỉ có một bước duy nhất là unit test khai báo trong unit_test_job.
Nhưng tôi tin vào 12factor, và tôi muốn có quá trình build trước. CI hay staging hay production sẽ khác nhau ở ENV truyền vào docker như trình bày ở https://12factor.net/build-release-run. Tôi sẽ tạo một job để tạo artifact, và chạy test trên artifact này.
image: docker:1.12
services:
- docker:dind
stages:
- build
variables:
CONTAINER_TEST_IMAGE: registry.gitlab.com/company/project:$CI_BUILD_REF_NAME
before_script:
- docker info
build_image_job:
stage: build
script:
- docker build -t $CONTAINER_TEST_IMAGE .
- docker run $CONTAINER_TEST_IMAGE npm run test
Setup này sử dụng docker in docker (dind) để build docker image trong một docker (nơi CI đang chạy). Đều này đạt được do shared runner chạy với privileged enable.
Như vậy lúc này CI sẽ tạo một docker có tên biến CONTAINER_TEST_IMAGE , và chạy test trong image này.
Tôi muốn tiến thêm một bước nữa, pipeline bao gồm: build, test, và release. Quá trình release xảy ra khi tôi tag git tag -a version, nó sẽ tạo ra tag latest và upload lên gitlab registry.
Khoa, gitlab registry? Đúng vậy, gitlab cho phép bạn lưu các image lên host của họ. Bạn sẽ có CI, và registry (miễn phí).
Gitlab rất hào phóng khi cung cấp đầy đủ các công cụ và dịch vụ để bạn có thể xây dựng một quá trình CI khá hoàn chính, từ việc chạy các test đến việc lưu trữ các image của bạn.
Hy vọng bạn sẽ tìm thấy sự hữu dụng trong ecosystem của gitlab.
Bài viết được sự cho phép của tác giả Nguyễn Việt Hưng
Julia là ngôn ngữ lập trình mới, trông rất giống code Python, chạy nhanh hơn Python3 ít nhất 2 lần và có nhiều tính năng hấp dẫn. Bạn không cần phải từ bỏ Python hay ngôn ngữ XYZ để học Julia, Julia chỉ đơn giản là một bông hoa đẹp mà ai thích… thì múc.
Julia là ngôn ngữ lập trình “làm gì cũng được”, nhưng được tập trung vào lĩnh vực tính toán khoa học và muốn thế chỗ Python (nhưng đường còn dài, và còn nhiều hơn chông gai).
Tại thời điểm viết bài (2019 tháng 4), Julia ở bản 1.1.0 – đã ổn định hơn trước rất nhiều, ít bug hơn, chạy nhanh hơn.
Từ Python ngó sang
Julia trông rất giống Python, cách dùng các học, cách code cũng tương tự, nên nếu biết Python thì việc viết được code để làm công việc thường ngày (của 1 DevOps) chỉ trong vòng vài tiếng.
REPL
Gõ lệnh trực tiếp bằng lệnh julia trên Linux/MacOS
julia>1+12###### gõ ? xong , biến từ julia> thành help?>help?>sumsearch:sumsum!summarycumsumcumsum!isnumericVersionNumberissubnormalget_zero_subnormals...sum(itr)Returnsthesumofallelementsinacollection.
Màu mè (syntax highlight) cũng tự có sẵn rồi.
Chạy file code
File code Julia có đuôi .jl, ví dụ hello.jl. Chạy file code bằng cách gõ lệnh: julia hello.jl
Cài đặt package
Để cài các package trên mạng, sử dụng package manager CÓ SẴN của Julia. Chuyện về virtualenv tạm chưa bàn tới ở đây vì khá rõ ràng là không cần thiết (cài package không cần gõ sudo gì cả).
Việc cài đặt package trong Julia thực hiện hơi khác so với Python Pip hay NodeJS NPM một chút. Sẽ không có câu lệnh nào để gõ cả, không có chương trình thêm nào hết. Julia thực hiện cài đặt package khi một đoạn code Julia được chạy (gọi function thực hiện cài đặt).
|> gọi là Piping operator, lấy đầu ra của function này làm đầu vào cho function khác. Giúp việc goi funciton liên tục (và đọc nó) dễ dàng hơn. Giống UNIX command.
Nhanh
Julia thường nhanh hơn Python ít nhất 2 lần
Không quan trọng indentation
Dù bạn thụt ra thụt vào 2 3 4 5 ký tự, hay dùng tab đều không quan trọng. Chuyện này vốn gây đau đầu cho không ít người dùng Python.
Julia dùng từ end để kết thúc if hay for, nên không cần thiết sử dụng dấu cách hay tab để thụt dòng. Thậm chí có thể dùng ; để phân cách các phần, và viết trên 1 dòng (bạn KHÔNG THỂ làm điều này với Python khi có for):
Một điều gây khó chịu với người mới code Python là phần kết thúc của range không được tính. Tức nếu viết range(1,1000) thì chỉ có từ 1 đến 999. Trong đầu luôn phải nhớ bớt đi 1. Julia 1:999 nghĩa là 1 đến 999, không thêm bớt gì.
Do chữ Đ trong Unicode UTF-8 được biểu diễn bằng 2 byte, ta chỉ có thể truy cập được index 1, không truy cập được index 2. Và chữ i ngay sau Đ sẽ là index 3.
Exception
TBD
Chú ý
Julia khởi động mất 0.2-0.4 giây, Python khởi động nhanh gấp 10.
String trong Julia phải dùng double quote "", single quote '' dành cho ký tự (Char).
Nối string dùng * chứ không phải +.
Sẽ không có method gắn liền vào các object như string hay list trong Python, thay vào đó là các function có sẵn (thường không cần import, gọi là trong Base)
Ví dụ 1 chương trình CLI nhận argument, gọi HTTP với JSON
importHTTPimportJSONusingDocOpt# import docopt functionfunctionmain()doc="""Fist Julia program which makes HTTP requests to httpbin endpoint Usage: main.jl request <endpoint> Options: -h --help Show this screen. --version Show version. """args=docopt(doc,version=v"0.0.1")endpoint=args["<endpoint>"]url="https://httpbin.org/$endpoint"println("Accessing $url")resp=HTTP.get(url)d=JSON.Parser.parse(String(resp.body))println("My IP is "*d["origin"])println("Now also send post")r=HTTP.request("POST","https://httpbin.org/post",["Content-Type"=>"application/json"],JSON.json(Dict('a'=>2,'b'=>3)))data=JSON.Parser.parse(String(r.body))open("/tmp/data.json","w")dofwrite(f,JSON.json(data["json"]))endopen("/tmp/data.json","r")dofd=JSON.Parser.parse(String(read(f)))println(d)endopen(`ls -l`)doioforlineineachline(io)if!isa(match(r".*\.jl",line),Nothing)println(line)endendendendmain()
Chạy
$ julia main.jl -h
Fist Julia program which makes HTTP requests to httpbin endpoint
Usage:
main.jl request <endpoint>
Options:
-h --help Show this screen.
--version Show version.
$ julia main.jl request ip
Accessing https://httpbin.org/ip
My IP is 3.117.2.254, 3.117.2.254
Now also send post
Dict{String,Any}("b"=>3,"a"=>2)
-rw-r--r-- 1 viethung.nguyen viethung.nguyen 1189 Apr 1208:38 main.jl
Code hoàn toàn tương đương với code Python.
Kết luận
Còn chờ gì nữa? Làm tí Julia thôi.
Phần tiếp theo (nếu có) sẽ đi vào các tính năng của Julia sử dụng trong tính toán khoa học.
Bài viết được sự cho phép của tác giả Trần Hữu Cương
Trong lập trình, việc xử lý các giá trị đúng/sai là điều thường xuyên phải thực hiện, đặc biệt trong các câu lệnh điều kiện, vòng lặp và kiểm tra logic. Kiểu dữ liệu Boolean được sử dụng để biểu diễn hai trạng thái logic cơ bản này. Cùng tìm hiểu về Bool trong C/C++ được sử dụng như thế nào, cú pháp ra sao trong bài viết của TopDev.
Bool trong C/C++ là gì?
Trong C++, kiểu dữ liệu boolean được sử dụng để biểu diễn hai trạng thái logic: đúng hoặc sai. Kiểu dữ liệu này được gọi là bool, và nó có thể chứa một trong hai giá trị:
true: Đại diện cho giá trị đúng.
false: Đại diện cho giá trị sai.
Kiểu dữ liệu bool được sử dụng rộng rãi trong các câu lệnh điều kiện như if, while, for, và các phép toán logic khác. Khi một giá trị bool được sử dụng trong điều kiện, giá trị true được coi là 1 và false được coi là 0.
Ban đầu, ngôn ngữ C không hỗ trợ kiểu bool, mà nó dùng số integer để biểu thị true/false (0 tức là false, khác 0 tức là true). Bắt đầu từ phiên bản C99 standard for C language thì mới bắt đầu hỗ trợ kiểu bool.
Thư viện <stdbool.h> là một phần của C99, cung cấp định nghĩa cho kiểu bool, cũng như các hằng số true và false. Thư viện này thường được sử dụng trong C hơn là trong C++.
Với cách này ta sử dụng kiểu bool của C (với C++ thì mặc định không cần khai báo thư viện <stdbool.h>)
Ví dụ:
#include<stdio.h>#include <stdbool.h>intmain(){bool value = false;if(value){ // tương đương với kiểm tra value == trueprintf("value is true"); }else{printf("value is false");}return0; }
Nếu bạn không muốn sử dụng kiểu bool tích hợp sẵn, bạn có thể tự định nghĩa nó bằng cách sử dụng enum (liệt kê). Điều này giúp bạn tạo ra kiểu dữ liệu bool của riêng mình với các giá trị true và false.
#include<stdio.h>typedef enum {false,true} bool;int main(){ bool value =false;if(value){ // tuong duong v?i ki?m tra value == true printf("value is true");}else{ printf("value is false");}return0;}
Cách 3: Định nghĩa kiểu bool với integer
Một cách khác để mô phỏng kiểu bool là sử dụng một số nguyên (integer). Trong cách này, bạn quy ước rằng 0 đại diện cho false và 1 (hoặc bất kỳ giá trị khác 0) đại diện cho true.
typedefintbool;
#define true 1#define false 0intmain(){
bool isTrue = true; // isTrue = 1bool isFalse = false; // isFalse = 0if (isTrue) {
// Thực hiện một số hành động khi isTrue là true
}
return0;
}
Cách 4: Khai báo hằng số true/false
Bạn cũng có thể tự định nghĩa các hằng số true và false mà không cần phải tạo kiểu dữ liệu mới. Điều này thường được sử dụng trong các ngôn ngữ hoặc môi trường không hỗ trợ kiểu bool một cách tự nhiên.
#define true 1#define false 0intmain(){
int isTrue = true; // isTrue = 1int isFalse = false; // isFalse = 0if (isTrue) {
// Thực hiện một số hành động khi isTrue là true
}
return0;
}
Cách 1 chỉ chạy được từ phiên bản C99 standard for C language. Tuy nhiên hầu hết các bản compiler C/C++ đều đã update cho nên các bạn nên dùng cách này. Cách 2,3,4: thì hoạt động giống nhau.
Nhìn chung thì kiểu bool vẫn là kiểu integer, nên cho dù dùng cách nào thì bạn vẫn có thể gán bool bằng một giá trị integer bất kì (0 tức là false, khác 0 tức là true).
Các ứng dụng thực tiễn của Bool C++
Kiểu dữ liệu boolean có rất nhiều ứng dụng trong lập trình, từ việc kiểm tra điều kiện trong câu lệnh if, vòng lặp while, for, đến việc sử dụng trong các phép toán logic phức tạp.
Kiểm tra điều kiện trong câu lệnh if
Câu lệnh if là một trong những câu lệnh điều kiện phổ biến nhất trong lập trình, và boolean đóng vai trò quan trọng trong việc xác định các điều kiện đúng hoặc sai.
Ví dụ:
#include<iostream>intmain(){
bool isLoggedIn = true;
if (isLoggedIn) {
std::cout << "User is logged in." << std::endl;
} else {
std::cout << "User is not logged in." << std::endl;
}
return0;
}
Sử dụng trong vòng lặp while
Vòng lặp while sử dụng boolean để xác định khi nào vòng lặp sẽ tiếp tục chạy và khi nào sẽ dừng.
Boolean cũng được sử dụng trong các phép toán logic, như AND (&&), OR (||), và NOT (!), giúp thực hiện các kiểm tra điều kiện phức tạp.
Ví dụ:
#include<iostream>intmain(){
bool isAdmin = true;
bool isLoggedIn = true;
if (isAdmin && isLoggedIn) {
std::cout << "User is an admin and is logged in." << std::endl;
} else {
std::cout << "Access denied." << std::endl;
}
return0;
}
Kiểu dữ liệu boolean là một phần quan trọng và không thể thiếu trong lập trình, giúp lập trình viên thực hiện các kiểm tra điều kiện và các phép toán logic một cách dễ dàng và hiệu quả. Dù là trong ngôn ngữ C với sự thiếu hụt ban đầu của kiểu boolean hay trong C++ với kiểu dữ liệu boolean tích hợp sẵn, việc hiểu và sử dụng đúng cách kiểu boolean là kỹ năng quan trọng mà mỗi lập trình viên cần nắm vững.
Qua bài viết này, hy vọng bạn đã có cái nhìn sâu hơn về cách sử dụng kiểu boolean trong C và C++, cũng như biết cách áp dụng nó vào các tình huống thực tế trong lập trình.