Là một kỹ sư kiểm thử phần mềm chuyên nghiệp, bạn nên biết sơ lược lịch sử về công nghệ phần mềm, bởi vì kiểm thử phần mềm gắn liền với tất cả các giai đoạn của công nghệ phần mềm. Công nghệ phần mềm đã phát triển qua 4 giai đoạn: thập niên 50-60, giữa thập niên 60 đến cuối những năm 70, giữa thập niên 70 đến giữa những năm 80, và giữa thập niên 80 đến hiện tại. Mỗi giai đoạn đều có những đặc trưng riêng biệt của nó, nhưng trong những năm qua phần mềm đã tăng lên về số lượng và độ phức tạp. Một số vấn đề phổ biến với gần như tất cả các giai đoạn và sẽ được thảo luận dưới đây.
Cuộc khủng hoảng phần mềm những năm 1960 khi những lý do chính cho tình hình này là việc ứng dụng kỹ thuật phần mềm vào thực tiễn ít được chấp nhận. Trong giai đoạn đầu của công nghệ phần mềm, có rất nhiều sự quan tâm đến máy tính, rất nhiều đoạn mã lệnh được viết nhưng không được công bố một cách chuẩn mực. Sau đó, vào đầu thập niên 70 rất nhiều các chương trình máy tính bắt đầu ngừng hoạt động và người ta mất niềm tin, như vậy, một cuộc khủng hoảng đã được công bố. Vì nhiều lý do dẫn đến cuộc khủng hoảng bao gồm:
– Phần cứng tiến bộ nhanh hơn khả năng xây dựng phần mềm cho phần cứng này.
– Khả năng xây dựng ứng dụng không theo kịp với nhu cầu.
– Tăng cường sự phụ thuộc vào phần mềm.
– Cuộc đấu tranh để xây dựng phần mềm đáng tin cậy và chất lượng cao.
– Thiết kế yếu kém và các nguồn tài nguyên không tương xứng.
Dự án phần mềm cơ bản được coi là thất bại nếu dự án được chấm dứt vì chi phí (vượt quá 50% dự toán) hoặc ngày phát hành quá trễ so với dự định. Một số ví dụ về những thất bại bao gồm sự thất bại của hệ thống kiểm soát không lưu, thất bại của phần mềm y tế, và thất bại trong phần mềm viễn thông. Lý do chính cho những thất bại được đề cập ở trên là do áp dụng công nghệ phần mềm không tốt. Một số cách thức ứng dụng công nghệ phần mềm tồi tệ nhất bao gồm:
– Không có phần mềm đo lường dữ liệu quá khứ.
– Từ chối dự toán chi phí chính xác.
– Không sử dụng các công cụ tính toán và lập kế hoạch tự động.
– Gây áp lực quá mức, tiến độ bất hợp lý và không hiểu rõ các yêu cầu của người sử dụng.
– Thất bại trong việc theo dõi tiến độ và thực hiện quản lý rủi ro.
– Không sử dụng đánh giá thiết kế và kiểm tra mã.
Để tránh những thất bại và cải thiện kết quả, điều cần thiết là sự hiểu biết tốt hơn về quy trình, các kỹ thuật ước lượng tốt hơn cho chi phí, thời gian và các biện pháp đo lường chất lượng. Hiện nay, một số lượng lớn các vấn đề tồn tại do một quy trình phần mềm hỗn loạn và sự thành công không thường xuyên phụ thuộc vào nỗ lực cá nhân. Vì vậy để dự án phần mềm thành công, tập trung vào quy trình là cần thiết. Sự tập trung này sẽ giúp ích trong việc dự đoán kết quả, xu hướng dự án và đặc điểm của dự án. Các quy trình đã được xác định và thông qua cần phải được quản lý tốt và sau đó nó sẽ phát huy tác dụng.
Từ đây chúng ta kết luận rằng quy trình tốt có thể cứu dự án phần mềm khỏi thất bại. Cần lưu ý rằng quy trình một mình nó không thể giúp chúng ta tránh được tất cả các vấn đề, cần thay đổi quy trình để có thể thích ứng với những nhu cầu khác nhau, hoàn cảnh khác nhau. Vì vậy để thực hiện một sản phẩm thành công, một sự kết hợp giữa quy trình và công nghệ là cần thiết.
Sau khi nói về quy trình phần mềm tổng thể, việc xác định vai trò của kiểm thử phần mềm là quan trọng trong việc sản xuất phần mềm chất lượng. Định nghĩa thử nghiệm như sau: “Kiểm thử hay Thử nghiệm – Một phương pháp xác minh bằng cách sử dụng tập hợp những điều kiện có kiểm soát với mục đích tìm kiếm lỗi. Đây là phương pháp được ưa chuộng nhất để xác minh các yêu cầu chức năng và hiệu suất. Kết quả thử nghiệm là tài liệu chứng minh rằng các yêu cầu đã được đáp ứng và có thể được lặp đi lặp lại. Các dữ liệu kết quả có thể được xem xét bởi tất cả các bên có liên quan để xác nhận”
Có thể có nhiều định nghĩa về kiểm thử phần mềm và theo thời gian, nhưng tốt nhất hãy bắt đầu bằng cách định nghĩa kiểm thử và sau đó bổ sung tùy thuộc vào các yêu cầu hoặc nhu cầu.
Trong các bài viết trước, chúng ta đã cùng tìm hiểu về Direct Exchange, Fanout Exchange và Topic Exchange. Trong bài này, tôi sẽ giới thiệu với các bạn một loại exchange rất mạnh mẽ khác của RabbitM là Headers Exchange.
Header exchange (amq.headers) được thiết kế để định tuyến với nhiều thuộc tính, để dàng thực hiện dưới dạng header của message hơn là routing key. Header exchange bỏ đi routing key mà thay vào đó định tuyến dựa trên header của message. Trường hợp này, broker cần một hoặc nhiều thông tin từ application developer, cụ thể là, nên quan tâm đến những tin nhắn với tiêu đề nào phù hợp hoặc tất cả chúng.
Headers Exchange rất giống với Topic Exchange, nhưng nó định tuyến dựa trên các giá trị header thay vì routing key.
Một Message được coi là phù hợp nếu giá trị của header bằng với giá trị được chỉ định khi ràng buộc.
Flow của một Message trong Headers Exchange như sau:
Một hoặc nhiều Queue được tạo và binding tới một Headers Exchange sử dụng các header property (H).
Một Producer sẽ tạo một Message với các header property (MH) và publish tới Exchange.
Một Message được Exchange chuyển đến Queue nếu Header H match với Header MH.
Consumer đăng ký tới Queue để nhận Message.
Có 2 loại matching được sử dụng để kiểm tra một Header của binding queue có match với một header từ message đến:
any: tương tự như logic OR, được biểu diễn trong các ràng buộc header property là {“x-match“, “any“, …} . Nghĩa là, một Message được gửi tới Exchange phải chứa ít nhất một trong các header mà Queue được liên kết, sau đó Message sẽ được chuyển đến Queue.
all: tương tự như logic AND, được biểu diễn trong các ràng buộc header property là {“x-match“, “and“, …} . Nghĩa là, các Message có tất cả các header được liệt kê của nó sẽ được chuyển tiếp đến Queue.
Ví dụ Topic Exchange trong RabbitMQ
Trong ví dụ này, tôi tạo một Headers Exchange có tên GPCoderHeadersExchange, tạo 3 Queue binding tới Headers Exchange này:
QDeveloper : Queue này sẽ nhận tất cả message có header là {“dev”, “Developer Channel”} hoặc {“general”, “General Channel”}.
QManager : Queue này nhận tất cả message có header là {“dev”, “Developer Channel”} hoặc {“general”, “General Channel”} hoặc {“manager”, “Manager Channel”}.
QPublished : Queue này nhận tất cả message có header là {“dev”, “Developer Channel”} và {“access”, “publish”}.
Một số class của chương trình:
ConnectionManager : hỗ trợ tạo Connection đến RabbitMQ.
HeadersExchangeChannel : class util hỗ trợ tạo Echange, Queue, binding Queue đến Exchange, publish/ subscribe message, …
Constant : định nghĩa constant chứa các thông tin về tên Exchange, Queue.
Producer: để gửi Message đến Exchange.
Consumer: để nhận Message từ Queue.
App: giả lập việc gửi nhận Message thông qua Headers Exchange của RabbitMQ.
ConnectionManager.java
package com.gpcoder.headersexchange;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ConnectionManager {
private ConnectionManager() {
super();
}
public static Connection createConnection() throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
return factory.newConnection();
}
}
package com.gpcoder.headersexchange;
public final class Constant {
// Exchange
public static final String EXCHANGE_NAME = "GPCoderHeadersExchange";
// Queue
public static final String DEV_QUEUE_NAME = "QDeveloper";
public static final String MANAGER_QUEUE_NAME = "QManager";
public static final String PUBLISHED_QUEUE_NAME = "QPublished";
private Constant() {
super();
}
}
Producer.java
package com.gpcoder.headersexchange;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import static com.gpcoder.headersexchange.Constant.*;
public class Producer {
private HeadersExchangeChannel channel;
public void start() throws IOException, TimeoutException {
// Create connection
Connection connection = ConnectionManager.createConnection();
// Create channel
channel = new HeadersExchangeChannel(connection, EXCHANGE_NAME);
// Create headers exchange
channel.declareExchange();
// Create headers
Map<String, Object> devHeaders = new HashMap<>();
devHeaders.put("x-match", "any"); // Match any of the header
devHeaders.put("dev", "Developer Channel");
devHeaders.put("general", "General Channel");
Map<String, Object> managerHeaders = new HashMap<>();
managerHeaders.put("x-match", "any"); // Match any of the header
managerHeaders.put("dev", "Developer Channel");
managerHeaders.put("manager", "Manager Channel");
managerHeaders.put("general", "General Channel");
Map<String, Object> publishedHeaders = new HashMap<>();
publishedHeaders.put("x-match", "all"); // Match all of the header
publishedHeaders.put("general", "General Channel");
publishedHeaders.put("access", "publish");
// Create queues
channel.declareQueues(DEV_QUEUE_NAME, MANAGER_QUEUE_NAME, PUBLISHED_QUEUE_NAME);
// Binding queues with headers
channel.performQueueBinding(DEV_QUEUE_NAME, devHeaders);
channel.performQueueBinding(MANAGER_QUEUE_NAME, managerHeaders);
channel.performQueueBinding(PUBLISHED_QUEUE_NAME, publishedHeaders);
}
public void send(String message, Map<String, Object> headers) throws IOException {
// Send message
channel.publishMessage(message, headers);
}
}
Consumer.java
package com.gpcoder.headersexchange;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import static com.gpcoder.headersexchange.Constant.*;
public class Consumer {
private HeadersExchangeChannel channel;
public void start() throws IOException, TimeoutException {
// Create connection
Connection connection = ConnectionManager.createConnection();
// Create channel
channel = new HeadersExchangeChannel(connection, EXCHANGE_NAME);
// Create headers exchange
channel.declareExchange();
// Create headers
Map<String, Object> devHeaders = new HashMap<>();
devHeaders.put("x-match", "any"); // Match any of the header
devHeaders.put("dev", "Developer Channel");
devHeaders.put("general", "General Channel");
Map<String, Object> managerHeaders = new HashMap<>();
managerHeaders.put("x-match", "any"); // Match any of the header
managerHeaders.put("dev", "Developer Channel");
managerHeaders.put("manager", "Manager Channel");
managerHeaders.put("general", "General Channel");
Map<String, Object> publishedHeaders = new HashMap<>();
publishedHeaders.put("x-match", "all"); // Match all of the header
publishedHeaders.put("general", "General Channel");
publishedHeaders.put("access", "publish");
// Create queues
channel.declareQueues(DEV_QUEUE_NAME, MANAGER_QUEUE_NAME, PUBLISHED_QUEUE_NAME);
// Binding queues with headers
channel.performQueueBinding(DEV_QUEUE_NAME, devHeaders);
channel.performQueueBinding(MANAGER_QUEUE_NAME, managerHeaders);
channel.performQueueBinding(PUBLISHED_QUEUE_NAME, publishedHeaders);
}
public void subscribe() throws IOException {
// Subscribe message
channel.subscribeMessage(DEV_QUEUE_NAME);
channel.subscribeMessage(MANAGER_QUEUE_NAME);
channel.subscribeMessage(PUBLISHED_QUEUE_NAME);
}
}
App.java
package com.gpcoder.headersexchange;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
public class App {
public static void main(String[] args) throws IOException, TimeoutException {
// Create producers, queues and binding queues to Headers Exchange
Producer producer = new Producer();
producer.start();
// Publish some messages
Map<String, Object> devHeader = new HashMap<>();
devHeader.put("dev", "Developer Channel");
producer.send("[1] Developer message", devHeader);
Map<String, Object> managerHeader = new HashMap<>();
managerHeader.put("manager", "Manager Channel");
producer.send("[2] Manager message", managerHeader);
Map<String, Object> generalHeader = new HashMap<>();
generalHeader.put("general", "General Channel");
producer.send("[3] General message", generalHeader);
Map<String, Object> publishedHeader = new HashMap<>();
publishedHeader.put("general", "General Channel");
publishedHeader.put("access", "publish");
producer.send("[4] Published message", publishedHeader);
// Create consumers, queues and binding queues to Headers Exchange
Consumer consumer = new Consumer();
consumer.start();
consumer.subscribe();
}
}
developer có thể nhận bất kỳ Message nào có header là {“dev”, “Developer Channel”} hoặc {“general”, “General Channel”}.
manager có thể nhận bất kỳ Message nào có header là {“dev”, “Developer Channel”} hoặc {“general”, “General Channel”} hoặc {“manager”, “Manager Channel”}.
customer có thể nhận bất kỳ Message nào có header là {“dev”, “Developer Channel”} và {“access”, “publish”}.
Infrastructure as Code – xu hướng quản lý của doanh nghiệp
Trong kỷ nguyên công nghệ điện toán đám mây, doanh nghiệp bắt đầu phát triển, quản lý và vận hành cơ sở hạ tầng công nghệ với hệ thống máy chủ trên cloud, kho lưu trữ dữ liệu hoặc các ứng dụng IoT. Bên cạnh đó, doanh nghiệp cần phải liên tục ra mắt, nâng cấp các sản phẩm, dịch vụ trên thị trường với tốc độ nhanh chóng nhưng vẫn phải đảm bảo việc tối ưu số lượng nhân sự, giảm chi phí. Điều này tạo ra thách thức cho bộ phận IT về việc triển khai hệ thống nhanh với hạ tầng co giãn linh hoạt, đáp ứng quá trình vận hành, mở rộng và phát triển hạ tầng mới.
Sự ra đời của Infrastructure as Code đã giải quyết những khó khăn trên của doanh nghiệp bằng cách tự động hoá quá trình cấu hình cơ sở hạ tầng, giúp doanh nghiệp triển khai hệ thống nhanh nhất. Một trong những công cụ phổ biến nhất của Infrastructure as Code có thể nhắc đến là Terraform, một công cụ nguồn mở giúp xây dựng, chỉnh sửa và versioning cho cơ sở hạ tầng một cách an toàn và hiệu quả nhất.
Lợi ích của Terraform
Terraform vừa để quản lý các đơn vị cung cấp dịch vụ cloud như AWS, Azure, Google Cloud …, kể cả VNG Cloud và vừa được dùng trong nội bộ. Terraform quản lý đa dạng các hệ thống hạ tầng, từ compute instance, storage, networking đến DNS hay các giải pháp SaaS….
Với Terraform, doanh nghiệp có thể rút ngắn thời gian điều chỉnh, triển khai các hệ thống công nghệ, dễ dàng mở rộng quy mô máy chủ. Sử dụng Terraform còn giúp doanh nghiệp tăng khả năng giám sát toàn bộ hệ thống, đảm bảo an toàn cơ sở hạ tầng.
VNG Cloud là một trong những đơn vị đầu tiên tích hợp thành công Terraform tại Việt Nam, giúp doanh nghiệp có thể khởi tạo và quản lý network, server nhanh chóng và tối ưu nhất.
Hướng dẫn tích hợp Terraform vào hệ thống vServer
Chúng ta hãy tìm hiểu một ví dụ đơn giản, sử dụng Terraform để khởi tạo và quản lý đồng thời nhiều vServer cùng một lúc trên nền tảng VNG Cloud
Các bước cần thực hiện như sau:
Bước 1: Cài đặt Terraform bạn có thể theo hướng dẫn chi tiết tại đây, sau khi chọn đúng hướng dẫn cài theo hệ điều hành, bạn sẽ có thể gõ câu lệnh terraform –help để kiểm tra đã cài đặt thành công
Bước 2: Tải các example file với tên main.tf và variables.tf từ VNG Cloud repo hoặc bạn có thể tự viết những file này về một thư mục trên máy ví dụ như terraform-vngloud. Terraform sử dụng các file có đuôi .tf để bạn thiết lập các mô tả cho hạ tầng của mình,
main.tf: file này chứa các đoạn mã để tạo tài nguyên theo mong muốn, ví dụ đoạn mã dưới đây sẽ giúp Terraform khởi tạo vServer Instance:
variables.tf: file này chứa các biến được sử dụng trong file main.tf ở phía trên, bạn cần thay đổi các thông tin này ứng với nhu cầu của bạn, các thông tin như: cấu hình server, cấu hình disk, hệ điều hành, ssh key,…đồng thời bạn có thể chỉ định số lượng vServer cần khởi tạo ở biến server_count, ví dụ ở đây chúng ta chỉ định 10 vServer sẽ được khởi tạo cùng lúc.
variable “image_name” {
type = string
default = “1-Ubuntu-18.04×64”
}
variable “flavor_zone_name” {
type = string
default = “General v1 Instances”
}
variable “flavor_name” {
type = string
default = “v1.small1x1.b100”
}
variable “volume_type_name” {
type = string
default = “SSD-IOPS3000”
}
variable “root_disk_size” {
type = number
default = 20
}
variable “data_disk_size” {
type = number
default = 50
}
variable “server_count” {
type = number
default = 10
}
Bước 3: Sau quá trình cấu hình các thông tin ở tf file, bạn cần chạy lệnh terraform init để terraform khởi tạo và tải VNG Cloud provider về, đồng thời thiết lập các thông tin cần thiết
Bước 4: Chạy lệnh terraform plan để kiểm tra và hiển thị trước những tài nguyên sẽ được tạo hoặc có sự thay đổi
Bước 5: Chạy lệnh terraform apply để thực hiện việc khởi tạo vServer với những tf file đặc tả trên thông qua Terraform. Chúc mừng bạn khởi tạo thành công 10 vServer với Terraform
Để xem chi tiết hơn hướng dẫn này bạn có thể xem tại đây
Để giúp doanh nghiệp triển khai hệ thống, dịch vụ trên nền tảng VNG Cloud nhanh chóng bằng công cụ Terraform, doanh nghiệp/khách hàng vui lòng để lại thông tin tại đây, các chuyên gia của VNG Cloud sẽ liên hệ trong thời gian sớm nhất.
Trong các bài viết trước, chúng ta đã cùng tìm hiểu về Direct Exchange và Fanout Exchange. Trong bài này, tôi sẽ giới thiệu với các bạn một loại exchange khác là Topic Exchange.
Topic exchange (amq.topic) định tuyến message tới một hoặc nhiều queue dựa trên sự trùng khớp giữa routing key và pattern. Topic exchange được sử dụng để thực hiện định tuyến thông điệp multicast. Loại Exchange này thường được sử dụng để thực hiện các biến thể của Pub/Sub pattern.
Ví dụ một vài trường hợp sử dụng:
Phân phối dữ liệu liên quan đến vị trí địa lý cụ thể.
Xử lý tác vụ nền được thực hiện bởi nhiều workers, mỗi công việc có khả năng xử lý các nhóm tác vụ cụ thể.
Cập nhật tin tức liên quan đến một category hoặc gắn tag.
Điều phối các dịch vụ của các loại khác nhau trong cloud.
Một topic exchange sẽ sử dụng wildcard để gắn routing key với một routing pattern khai báo trong binding. Consumer có thể đăng ký những topic mà nó quan tâm.
Routing Key trong Topic Exchange:
Một Routing Key trong Topic Exchange phải bao gồm 0 hoặc nhiều từ phân cách bởi dấu chấm (.).
Routing Key trong Topic Exchange còn gọi là Routing Pattern.
Routing Pattern tương tự như Regular expression, nhưng chỉ các wildcard *, . và # được sử phép.
Ý nghĩa các wildcard được sử dụng là:
* : có nghĩa là chính xác một từ được phép.
# : có nghĩa là 0 hoặc nhiều số từ được phép.
. : có nghĩa là dấu phân cách từ. Nhiều từ chính được phân tách bằng dấu phân cách dấu chấm.
Ví dụ:
java.* : được đăng ký bởi tất cả những key với pattern bắt đầu bằng java và theo sau là chính xác một từ bất kỳ.
Những key sau là hợp lệ: java.core, java.gpcoder.
Những key sau là không hợp lệ: java, java.core.gpcoder.
java.*.gpcoder : được đăng ký bởi tất cả những key với pattern bắt đầu bằng java, theo sau là chính xác một từ bất kỳ và kết thúc là gpcoder.
Những key sau là hợp lệ: java.core.gpcoder, java.collection.gpcoder.
Những key sau là không hợp lệ: java.gpcoder, java.core.variable.gpcoder.
java.# : được đăng ký bởi tất cả các key bắt đầu với java.
Những key sau là hợp lệ: java, java.gpcoder, java.core.gpcoder.
Những key sau là không hợp lệ: core.java, core-java.com.
java.#.gpcoder : được đăng ký bởi tất cả những key với pattern bắt đầu bằng java và kết thúc là gpcoder.
Những key sau là hợp lệ: java.gpcoder, java.core.gpcoder, java.collection.map.gpcoder.
Những key sau là không hợp lệ: java.gpcoder.com, java.core.gpcoder.com.
#.gpcoder.com : được đăng ký bởi tất cả những key với pattern kết thúc là gpcoder.com.
Những key sau là hợp lệ: gpcoder.com, java.gpcoder.com, core.java.gpcoder.com.
Những key sau là không hợp lệ: java.gpcoder, gpcoder.com.java.
Flow của một Message trong Topic Exchange như sau:
Một Queue được tạo và binding tới một Topic Exchange với một routing key pattern (P).
Một Producer sẽ tạo một Message với một routing key (K) và publish tới Exchange.
Một Message được Exchange chuyển đến Queue nếu Pattern P match với Key K.
Consumer đăng ký tới Queue để nhận Message.
Ví dụ Topic Exchange trong RabbitMQ
Trong ví dụ này, tôi tạo một Topic Exchange có tên GPCoderTopicExchange, tạo 2 Queue binding tới Topic Exchange này:
QJava : Queue này sẽ nhận tất cả message có Key match với routing key “java.*.gpcoder.com“. Nghĩa là chỉ nhận các message cho một topic Java cụ thể từ gpcoder.com, chẳng hạn: java.core.gpcoder.com, java.collection.gpcoder.com.
QAll : Queue này nhận tất cả message có Key match với routing key “#.gpcoder.com“. Nghĩa là nhận tất cả message từ gpcoder.com, chẳng hạn: design-pattern.gpcoder, java.gpcoder.com, creational.design-pattern.gpcoder.com.
Một số class của chương trình:
ConnectionManager : hỗ trợ tạo Connection đến RabbitMQ.
TopicExchangeChannel : class util hỗ trợ tạo Echange, Queue, binding Queue đến Exchange, publish/ subscribe message, …
Constant : định nghĩa constant chứa các thông tin về tên Exchange, Queue.
Producer: để gửi Message đến Exchange.
Consumer: để nhận Message từ Queue.
App: giả lập việc gửi nhận Message thông qua Topic Exchange của RabbitMQ.
ConnectionManager.java
package com.gpcoder.topicexchange;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ConnectionManager {
private ConnectionManager() {
super();
}
public static Connection createConnection() throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
return factory.newConnection();
}
}
package com.gpcoder.topicexchange;
public final class Constant {
// Exchange
public static final String EXCHANGE_NAME = "GPCoderTopicExchange";
// Queue
public static final String JAVA_QUEUE_NAME = "QJava";
public static final String GENERAL_QUEUE_NAME = "QAll";
private Constant() {
super();
}
// Routing key pattern
public static final String JAVA_ROUTING_KEY = "java.*.gpcoder.com";
public static final String GPCODER_ROUTING_KEY = "#.gpcoder.com";
// Message key
public static final String JAVA_CORE_MSG_KEY = "java.core.gpcoder.com";
public static final String JAVA_MSG_KEY = "java.gpcoder.com";
public static final String DESIGN_PATTERN_MSG_KEY = "design-pattern.gpcoder.com";
public static final String NOT_MATCHING_MSG_KEY = "java.collection.gpcoder.com.vn";
}
package com.gpcoder.topicexchange;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import static com.gpcoder.topicexchange.Constant.*;
public class App {
public static void main(String[] args) throws IOException, TimeoutException {
// Create producers, queues and binding queues to Topic Exchange
Producer producer = new Producer();
producer.start();
// Publish some message
producer.send("[1] A new Java Core topic is published", JAVA_CORE_MSG_KEY);
producer.send("[2] A new Java general topic is published", JAVA_MSG_KEY);
producer.send("[3] A new Design Pattern topic is published", DESIGN_PATTERN_MSG_KEY);
producer.send("[4] Not matching any routing key", NOT_MATCHING_MSG_KEY);
// Create consumers, queues and binding queues to Topic Exchange
Consumer consumer = new Consumer();
consumer.start();
consumer.subscribe();
}
}
Output chương trình:
[Send] [java.core.gpcoder.com]: [1] A new Java Core topic is published
[Send] [java.gpcoder.com]: [2] A new Java general topic is published
[Send] [design-pattern.gpcoder.com]: [3] A new Design Pattern topic is published
[Send] [java.collection.gpcoder.com.vn]: [4] Not matching any routing key
[Received] [QJava]: amq.ctag-LiUyX8m4KIJu0Gb9WlpLdg
[Received] [QJava]: [1] A new Java Core topic is published
[Received] [QAll]: amq.ctag-nmlzYAixFEwB4P0-N6nltw
[Received] [QAll]: [1] A new Java Core topic is published
[Received] [QAll]: amq.ctag-nmlzYAixFEwB4P0-N6nltw
[Received] [QAll]: [2] A new Java general topic is published
[Received] [QAll]: amq.ctag-nmlzYAixFEwB4P0-N6nltw
[Received] [QAll]: [3] A new Design Pattern topic is published
Như bạn thấy:
Consumer binding đến Queue QJava chỉ nhận message được gởi từ message key “java.core.gpcoder.com”.
Consumer binding đến Queue QAll có thể nhận message gửi từ messag key “java.core.gpcoder.com”, “java.gpcoder.com” và “design-pattern.gpcoder.com”. Không nhận message từ “java.collection.gpcoder.com.vn” do message không phải gởi từ “gpcoder.com”.
Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh
Thông thường, chúng ta sẽ không sử dụng trang login mặc định của Spring Security cho các ứng dụng được deploy lên production, nguyên nhân vì sao các bạn cũng có thể đoán được đúng không? Trang login mặc định này để chúng ta learning là chủ yếu. Vậy để thay thế trang login mặc định này, chúng ta sẽ làm như thế nào? Trong bài viết này, mình sẽ hướng dẫn các bạn làm điều này.
Để đơn giản, custom page login của mình có các thành phần cũng giống như trang login mặc định của Spring Security, chỉ khác biệt là mình thay thế chữ “Please sign in” bằng dòng chữ “Welcome to Huong Dan Java, please login in” và nút “Sign in” giờ là “Login”.
Code trang login.html nằm trong thư mục src/main/resources/templates của mình với Bootstrap như sau:
Như các bạn thấy, từ đối tượng HttpSecurity, chúng ta sẽ gọi phương thức formLogin() để lấy về đối tượng FormLoginConfigurer để cấu hình cho trang login của Spring Security. Từ đối tượng FormLoginConfigurer này, chúng ta sẽ sử dụng phương thức loginPage() với tham số là tên của login page để để override lại trang login mặc định của Spring Security.
Mình cũng cấu hình thêm configure(WebSecurity web) để ignore security cho các đường dẫn liên quan đến WebJars, ở đây là đường dẫn đến Bootstrap CSS.
OK, đến đây thì các bạn có thể chạy lại ứng dụng để kiểm tra kết quả.
Trang login của mình giờ sẽ như sau:
Đăng nhập với username là user và password được generated, các bạn sẽ thấy kết quả như sau:
Bài viết được sự cho phép của blogchiasekienthuc.com
Chắc hẳn nhiều bạn ở đây đã biết về xác thực 2 yếu tố (2FA, xác thực 2 lớp hay còn gọi là Two-Factor Authentication) rồi đúng không !
Chắc chắn là như vậy rồi, nếu bạn đã sử dụng Internet, đã tham gia vào các MXH như Facebook, Instagram, hay các dịch vụ mạng và các tài khoản ngân hàng trực tuyến thì việc xác thực 2 yếu là điều bắt buộc trong việc bảo mật thông tin tài khoản.
Hiểu nôm na là khi bạn đăng nhập vào một website, hay đăng nhập một tài khoản/ ứng dụng trực tuyến thì ngoài việc nhập mật khẩu theo cách truyền thống ra thì sẽ có một mã xác thực OTP được gửi về Số điện thoại, Email hoặc là ứng dụng mà bạn đã thiết lập trước đó.
Như vậy, nếu chẳng may bạn có bị lộ mật khẩu thì người khác vẫn không thể đăng nhập vào được tài khoản của bạn được. Vì vãn còn một lớp bảo mật nữa mà 😀
Hiện nay, việc nhận mã OTP bằng điện thoại là phổ biến nhất, nhưng bạn thử nghĩ mà xem, lỡ điện thoại của bạn bị mất hoặc SIM của bạn bị lấy trộm/ bị hack thì kẻ gian sẽ có cơ hội để chiếm đoạt tài khoản của bạn. Có đúng không ạ !
Nhưng các bạn đừng quá lo lắng, bởi đa phần các trang web lớn, các trang TMĐT e-Commerce, các trang cần mức độ bảo mật cao hiện nay đa số đều đã hỗ trợ “Security key” làm phương thức xác thực 2 yếu tố (có thể làm yếu tố chính hoặc dự phòng cho phương pháp SMS hoặc ứng dụng xác thực).
Vâng, và ở trong bài viết này mời các bạn hãy cùng mình tìm hiểu về “Security key” – “Chiếc khóa thần kỳ” này nhé, để xem cách thức hoạt động và mức độ bảo mật của nó ra sao ha.
#1. Security key là gì?
Nói một cách dân dã, đơn giản và dễ hiểu thì Security key là một chiếc chìa khóa. Hình dạng của chiếc chìa khóa này nó cũng không khác gì một chiếc USB Drive để cắm vào cổng USB của máy tính là mấy.
Security key chứa thông tin mã hóa nhằm chứng minh bạn là chủ của tài khoản, cơ chế hoạt động như ổ khóa và chìa khóa vậy.
Sau khi bạn cấu hình Security key vào tài khoản thì trang web là “ổ khoá” còn Security key này sẽ là chiếc “chìa khóa” để mở quyền truy cập vào tài khoản.
Có nghĩa là, sau khi bạn đăng nhập bằng mật khẩu như bình thường thì bước tiếp theo, trang web sẽ yêu cầu bạn cắm Security key vào máy để tiến hành xác thực bạn có quyền truy cập vào tài khoản này.
Các thương hiệu Security key phổ biến hiện nay như: YubiKey, HyperFIDO, Thetis, Google Titan… với các chuẩn cắm USB Type-A, USB Type-C hay kết nối qua Bluetooth, xác thực dùng chuẩn FIDO U2F (FIDO Universal 2nd Factor authentication) là chính, ngoài ra còn có chuẩn Fido2 cải tiến nữa.
Khóa bảo mật này có thể coi như là chiếc chìa khóa xe, chiếc chìa khóa duy nhất để vào nhà bạn => vậy nên bạn cần phải giữ thật kỹ, tốt nhất là móc vào chìa khóa xe.
Lưu ý quan trọng là khóa này không thể mang ra thợ đúc sơ-cua nha các bạn, mình xác nhận là mấy ông thợ sửa khóa đầu ngõ bất lực với trường hợp này nhé ᵔᴥᵔ
Cơ chế hoạt động của Security key thì khá là đơn giản:
Sau khi mở trang web cần truy cập => bạn cắm Security key vào thiết bị mà bạn sử dụng (smartphone hoặc máy tính) hoặc sử dụng kết nối không dây => rồi bấm nút trên Security key => lúc này trình duyệt web (Browser)/ ứng dụng sẽ gửi lệnh truy cập (bao gồm tên miền/domain name) đến Security key.
=> Sau đó key này sẽ ký số(Cryptographically sign) và cho phép bạn truy cập vào, như vậy là bạn đã hoàn thành bước xức thực 2 yếu tố rồi đó.
Các “ông lớn” đang hỗ trợ đăng nhập bằng Security Key như: Twitter, Facebook, Google, Instagram, GitHub, Dropbox, Electronic Arts, Epic Games, Microsoft, Nintendo, Okta, Reddit…
Thậm chí là bạn có thể đăng nhập vào máy Mac OS bằng Security key. Microsoft cũng đang phát triển/ nâng cấp Windows Hello để đăng nhập bằng key dùng chuẩn Fido2 được hiệu quả hơn, chính xác hơn.
#2. Thiết lập Security key với Facebook
Trong bài viết này mình sẽ demo với các bạn các bước thiết lập xác thực 2 yếu tố bằng Security key. Các dịch vụ khác hoàn toàn tương tự vậy thôi nhé các bạn !
+ Bước 1: Đăng nhập vào tài khoản Facebook của bạn => rồi click vào menu bên góc phải => và chọn Settings (Cài đặt).
+ Bước 2: Trong phần Bảo mật => bạn chọn Security and Login (Bảo mật và đăng nhập) như hình bên dưới..
+ Bước 3: Bạn cuộn xuống phần Two-Factor Authentication (Xác thực 2 yếu tố).
+ Bước 4: Sau đó, tại phần Two-Factor Authentication (Xác thực 2 yếu tố) => bạn cuộn xuống và tìm đến phần Add a Backup (Thêm phương thức dự phòng) => và chọnSecurity Key (Khóa bảo mật) như hình bên dưới.
+ Bước 5: Bây giờ bạn hãy nhập mật khẩu tài khoản Facebook của bạn vào => và chọn Submit.
Tiếp theo, cắm Security key vào cổng kết nối trên thiết bị => rồi ấn nút trên thân của khóa => lúc này trang Facebook sẽ hiện lên hộp thoại xác nhận và sau này bạn có thể đăng nhập bằng key này..
_______________________
Đối với Facebook giao diện trên máy tính thì bạn chỉ cần truy cập vào địa chỉ này:
https://www.facebook.com/security/2fac/settings
=> Sau đó trong phần Thêm phương pháp dự phòng => bạn chọn phương pháp Khóa bảo mật như hình bên dưới.
Sau đó bạn nhập mật khẩu và để xác nhận.
Bây giờ bạn hãy kết nối khóa bảo mật USB vào và thực hiện theo hướng dẫn của Facebook..
Quy trình cài đặt, thiết lập Security key để xác thực 2 bước cho tài khoản Google hay Twitter,… đều tương tự như trên, các bạn không rành tiếng Anh thì nên tận dụng tính năng dịch trên trình duyệt web nhé bạn.
#3. Tổng kết
Okay, qua bài viết này thì mình tin bạn đã hiểu hơn về hình thức Security key rồi đúng không. Và qua bài viết này thì mình cũng tin rằng bạn có thể tự thiết lập khóa bảo mật (Security key) làm bước xác thực 2 yếu tố dự phòng rồi.
Nói chung, nguyên tắc của khóa Security key tương tự như một chiếc chìa khóa ngoài đời thực của bạn vậy. Bạn hãy giữ khóa này cẩn thận, nếu làm mất thì phải truy cập ngay vào các tài khoản để xóa Security key đi nhé, đồng thời mua “khóa” mới và thêm vào ngay.
Security key hỗ trợ bảo mật rất cao nhưng có vẻ hơi phức tạp đối với người dùng phổ thông, các bạn có thể tìm hiểu giải pháp dự phòng khác đối với tài khoản của mình như: Mã dự phòng hay là SMS OTP, ứng dụng xác thực 2 yếu tố..
Okay, hi vọng là bài viết này sẽ hữu ích với bạn, chúc các bạn thành công và đừng quên chia sẻ bài viết này nếu bạn thấy thích nhé 🙂
TOP 7 Dấu Hiệu Giúp Nhận Biết Một Cuộc Phỏng Vấn Thành Công
Sau khi đã tham gia một buổi phỏng vấn và nhận thấy định hướng của bản thân hoàn toàn phù hợp với công ty, chắc chắn bạn sẽ càng nôn nóng mong chờ được biết kết quả phỏng vấn hơn bao giờ hết. Tuy nhiên, nhà tuyển dụng sẽ phải mất ít nhất vài ngày đến một tuần để tìm ra ứng viên phù hợp. Vậy có cách nào để dễ nhận biết dấu hiệu bạn đã trúng tuyển vào vị trí mà mình đã tham gia phỏng vấn hơn không?
Bài viết này sẽ chia sẻ thêm với người đọc 7 dấu hiệu giúp nhận biết bạn đã có một buổi phỏng vấn thành công và khả năng trúng tuyển rất cao. Với những ứng viên đang chuẩn bị cho một cuộc phỏng vấn mới, đây cũng là những mẹo hay ho để bạn nhận biết sự hứng thú của nhà tuyển dụng với những thông tin mình chia sẻ.
Những dấu hiệu giúp bạn nhận biết mình đã phỏng vấn thành công
1. Sự hứng thú của người phỏng vấn trong khi trao đổi
Nếu là người tinh tế hoặc để tâm đến thái độ của người đối diện, bạn sẽ dễ dàng nhận thấy sự khác biệt trong cách nói chuyện và đặt câu hỏi của họ. Khi mới bắt đầu, có thể họ chỉ khai thác các vấn đề cơ bản của bạn nên chưa thể hiện thái độ rõ ràng. Tuy nhiên khi đi vào các vấn đề chuyên môn hoặc xử lý các tình huống chẳng hạn, bạn sẽ dễ dàng nhận ra sự khác biệt của người phỏng vấn.
Nếu họ lắng nghe bạn chia sẻ một cách chăm chú và vui vẻ, họ tiếp tục khai thác những vấn đề mà bạn đã nhắc đến – đó chính là một dấu hiệu đáng mừng cho thấy nhà tuyển dụng đang có thiện cảm tốt với bạn. Đương nhiên đây vẫn chưa phải là yếu tố chủ chốt quyết định bạn đã trúng tuyển nhưng chắc chắn sự vui vẻ và chia sẻ chân thành từ người phỏng vấn cũng sẽ khiến bạn thoải mái hơn khi trả lời.
2. Nhà tuyển dụng muốn khai thác thêm thông tin ở bạn
Đa phần các nhà tuyển dụng đều đã có sẵn một checklist những câu hỏi sẽ hỏi bạn cả về thông tin chung lẫn kiến thức chuyên môn. Lý do chủ yếu là vì người phỏng vấn phải gặp rất nhiều ứng viên khác nhau và với mỗi ứng viên họ sẽ có một khoảng thời gian nhất định để trao đổi và lựa chọn. Vậy nên khi tìm được những ứng viên mà họ cảm thấy ưng ý thì họ mới dành thêm thời gian để khai thác thông tin của bạn.
Các thông tin nhà tuyển dụng khai thác thêm từ ứng viên thường là từ vấn đề mà bạn đưa ra trong câu trả lời hoặc các thông tin về những sản phẩm bạn đã làm việc trước đây, các bằng cấp mà bạn đã có, có laptop để sử dụng không (trong trường hợp công ty không cung cấp máy tính để làm việc),…
3. Người phỏng vấn chia sẻ nhiều thông tin về văn hóa công ty
Bên cạnh việc người phỏng vấn khai thác thêm nhiều thông tin từ bạn thì việc họ cố gắng chia sẻ cụ thể hơn về văn hóa doanh nghiệp với bạn cũng là tín hiệu đáng mừng. Các thông tin đó thường là văn hóa ứng xử và làm việc tại công ty, quy trình trao đổi và bàn giao công việc sẽ diễn ra như thế nào, chế độ phúc lợi và các hoạt động bổ trợ tinh thần của công ty ra sao,… Những thông tin cho thấy nhà tuyển dụng đang cố gắng chia sẻ những mặt tốt của công ty với bạn đều là những tín hiệu đáng mừng. Bạn có thể xem đây là dấu hiệu tích cực của buổi phỏng vấn.
4. Nhà tuyển dụng trao đổi về mức lương
Rất nhiều các nhân sự đã xác nhận rằng họ sẽ chia sẻ và trao đổi với ứng viên về mức lương khi cảm thấy người đó phù hợp. Điều đó đồng nghĩa với việc khi nhà tuyển dụng đang trao đổi về mức lương với bạn cũng cho thấy họ đang nghiêm túc xem xét hồ sơ ứng tuyển cũng như những thông tin mà bạn trao đổi. Hãy tận dụng điều này và chia sẻ một cách thành thật, trên tinh thần tiếp thu và trao đổi để hai bên có thể thương lượng được mức thu nhập tốt nhất cho bạn.
Nhà tuyển dụng sẵn sàng trao đổi nhiều thông tin hơn với bạn
5. Người phỏng vấn giữ liên lạc và trao đổi thông tin kịp thời
Trước khi kết thúc buổi phỏng vấn, nhà tuyển dụng sẽ đề cập đến thời gian trả lời kết quả với bạn một cách rõ ràng cũng cho thấy sự chuyên nghiệp và mong muốn nhận bạn vào làm cao hơn. Khi nhận được thư cảm ơn từ ứng viên, nhân sự cũng nhanh chóng phản hồi và nhắc lại về thời gian có kết quả. Đây đều là các dấu hiệu bạn hoàn toàn có thể trúng tuyển.
6. Định hướng công việc cho ứng viên
Đây là phần thông tin đa phần nhà tuyển dụng sẽ dành cho ứng viên được đánh giá là tiềm năng. Trong đó, người phỏng vấn và có thể là quản lý của bạn trong tương lai, sẽ chia sẻ về quá trình làm việc với lĩnh vực này như thế nào, bạn nên và không nên làm những gì nếu muốn đạt được mục tiêu, lộ trình nghề nghiệp của vị trí này ra sao,…
Người phỏng vấn ở vai trò là quản lý phải xử lý rất nhiều công việc khác nhau, thời gian của họ được cân nhắc rất kỹ càng. Chính vì thế, khi sẵn sàng bỏ thời gian để tư vấn và chia sẻ cho bạn những vấn đề này thì khả năng bạn được nhận vào làm cũng được gia tăng đáng kể.
7. Bạn được giới thiệu với phòng ban mình ứng tuyển
Nếu người phỏng vấn dắt bạn đi tham quan công ty hoặc giới thiệu bạn với mọi người ở phòng bạn mà mình đang ứng tuyển, thì khả năng bạn được nhận vào làm là gần như chắc chắn. Có thể nhà tuyển dụng đã xác định muốn tuyển bạn và đang giúp ứng viên làm quen với đồng nghiệp để quá trình làm việc sau này diễn ra dễ dàng hơn.
Tuy nhiên, cũng cần nghiêm túc nhìn nhận rằng đây cũng là cách được nhiều công ty lớn sử dụng để đánh giá thái độ ứng xử trong thực tế của ứng viên. Những gì ứng viên chia sẻ đều là lời nói, nếu thật sự muốn tuyển bạn, nhà tuyển dụng còn cần trực tiếp đánh giá qua những tình huống cụ thể như thế này. Do đó, hãy thận trọng trong cách cư xử và chú ý lời nói để tránh bị mất điểm trong mắt nhà tuyển dụng cũng như các đồng nghiệp trong phòng ban.
Chờ đợi kết quả phỏng vấn chắc chắn là quãng thời gian đầy hồi hộp với ứng viên. Do đó, biết thêm một số dấu hiệu bạn đã trúng tuyển sẽ giúp tiết kiệm thời gian chờ đợi hơn cho những cuộc phỏng vấn có thể chưa thật sự như mong muốn. Bạn sẽ không phải bỏ lỡ những cơ hội khác vì không dám chắc vào kết quả phỏng vấn.
Khi một Message đến Exchange, nếu không tìm thấy Queue nào phù hợp cho Message, Message sẽ tự động bị hủy. RabbitMQ cung cấp một tiện ích mở rộng AMQP được gọi là Alternate Exchange, để collect các Message không thể gửi được trước khi chúng bị huỷ. Chúng ta sẽ biết được cách làm việc và cài đặt của Alternate Exchange trong bài viết này.
Alternate Exchange được định nghĩa để collect các Message không thể gửi được (rejected/ discarded/ unrouted) trước khi chúng bị huỷ.
Bất kỳ 4 loại Exchange: Direct, Fanout, Topic, Headers có thể được chỉ định như một Alternate Exchange cho một Exchange khác thuộc bất kỳ loại nào. Tuy nhiên, ta nên sử dụng Fanout Exchange như một Alternate Exchange vì nó chuyển tiếp tin nhắn vô điều kiện.
Để chỉ định một Alternate Exchange cho một Exchange GPCoder.AltTopicExchange, chúng ta chỉ cần thêm arguments: alternate-exchange=”GPCoder.AltFanoutExchange” cho GPCoder.AltTopicExchange. Khi đó GPCoder.AltFanoutExchange trở thành một Alternate Exchange cho GPCoder.AltTopicExchange.
Flow của một Message trong Alternate Exchange:
Một Producer publish một Message đến source Exchange với một routing key dựa trên loại của Exchange. Trong trường hợp này là GPCoder.AltTopicExchange.
Một Fanout Exchange (GPCoder.AltFanoutExchange), được chỉ định là một AlternateExchange cho GPCoder.AltTopicExchange.
Nếu một Message có routing key match với bất kỳ routing key pattern nào mà Queue đã binding với GPCoder.AltTopicExchange, thì Message sẽ được chuyển đến Queue match đó.
Nếu không match với bất kỳ routing key pattern nào, khi đó Message sẽ bị reject.
Theo mặc định của RabbitMQ, một Message bị reject sẽ bị huỷ. Trong trường hợp chúng ta có Alternate Exchange, nó sẽ nhận các Message bị reject và chuyển đến Queue.
Cuối cùng, Consumer có thể binding đến Queue của Alternate Exchange để xử lý.
Ví dụ binding Exchange to Exchange trong RabbitMQ
Một số class của chương trình:
ConnectionManager : hỗ trợ tạo Connection đến RabbitMQ.
ExchangeChannel : class util hỗ trợ tạo Echange, Queue, binding Queue đến Exchange, binding Exchange đến Exchange, publish/ subscribe message, …
Constant : định nghĩa constant chứa các thông tin về tên Exchange, Queue.
AlternateExchangeProducer : để gửi Message đến GPCoder.AltFanoutExchange.
TopicExchangeProducer : để gửi Message đến GPCoderTopicExchange.
AlternateExchangeConsumer : để nhận Message từ Queue được binding đến GPCoder.AltFanoutExchange.
TopicExchangeConsumer : để nhận Message từ Queue được binding đến GPCoder.AltTopicExchange.
App: giả lập việc gửi nhận Message thông qua Topic Exchange của RabbitMQ.
package com.gpcoder.alternateexchange;
public final class Constant {
// Exchange
public static final String TOPIC_EXCHANGE_NAME = "GPCoder.AltTopicExchange";
public static final String ALTERNATE_EXCHANGE_NAME = "GPCoder.AltFanoutExchange";
// Queue
public static final String JAVA_QUEUE_NAME = "QJava";
public static final String ALL_QUEUE_NAME = "QAll";
public static final String UNKNOWN_QUEUE_NAME = "QUnknown";
// Routing key pattern
public static final String JAVA_ROUTING_KEY = "java.*.gpcoder.com";
public static final String GPCODER_ROUTING_KEY = "#.gpcoder.com";
// Message key
public static final String JAVA_MSG_KEY = "java.gpcoder.com";
private Constant() {
super();
}
}
package com.gpcoder.alternateexchange;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import static com.gpcoder.alternateexchange.Constant.JAVA_MSG_KEY;
public class App {
public static void main(String[] args) throws IOException, TimeoutException {
AlternateExchangeProducer producer1 = new AlternateExchangeProducer();
producer1.start();
TopicExchangeProducer producer2 = new TopicExchangeProducer();
producer2.start();
// Publish some messages
producer2.send("[1] Head First Design Pattern", JAVA_MSG_KEY);
producer2.send("[2] Unknown Message", "random-gpcoder");
AlternateExchangeConsumer consumer1 = new AlternateExchangeConsumer();
consumer1.start();
consumer1.subscribe();
TopicExchangeConsumer consumer2 = new TopicExchangeConsumer();
consumer2.start();
consumer2.subscribe();
}
}
Output chương trình:
[Send] [java.gpcoder.com]: [1] Head First Design Pattern
[Send] [random-gpcoder]: [2] Unknown Message
[Received] [QUnknown]: amq.ctag-qe5VOGnCFLq8_Qu_ajUo_g
[Received] [QUnknown]: [2] Unknown Message
[Received] [QAll]: amq.ctag-Nt0tVcCjOXEr-BTF20roCw
[Received] [QAll]: [1] Head First Design Pattern
Như bạn thấy, Message thứ 2 không match với bất kỳ routing key nào, nên được chuyển xuống Queue QUnknown của Alternate Exchange (GPCoder.AltFanoutExchange).
Bài viết được sự cho phép của tác giả Trần Khôi Nguyên Hoàng
CV là bộ mặt của bạn khi đi xin việc, cho nên CV chỉ cần tập trung vào giới thiệu hai vấn đề chính về bạn: Bạn giỏi và bạn code được. Ngoài ra mấy cái nào không liên quan đến việc thể hiện hai cái ở trên thì không cần thêm vào. Ví dụ sở thích xem film hay đọc truyện thì không cần thêm vào. Tất nhiên nếu sở thích liên quan đến công nghệ thì có thể ghi vào cũng được, ví dụ như thích đọc sách về CNTT, thích tìm hiểu công nghệ mới, vân vân.
Độ dài của CV
Theo như mình biết được thì CV không nên quá 1 trang. Nhất là các bạn mới ra trường như mình (Có gì đâu mà viết)
Lý do của việc viết CV ngắn là vì HR chỉ có thể bỏ ra tầm 10s để đọc CV của mình thôi. Vì thế, việc chú ý vào độ dài là để đảm bảo HR không bỏ sót bất thông tin chính. Thêm nhiều những thứ linh tinh vào cũng không làm được gì và có khi còn làm HR khó chịu. Thậm chí nhiều HR còn loại luôn mấy cái CV dài dài đó.
Bạn nghĩ là bạn có quá nhiều kinh nghiệm cần phải viết thì … cố gắng mà bỏ bớt những cái không quan trọng đi, và chỉ liệt kê những cái xịn nhất thôi
Kinh nghiệm làm việc
Ở mục Kinh nghiệm làm việc thì không nên bỏ toàn bộ lịch sử việc làm vào CV. Chỉ những cái nào liên quan đến vị trí mình xin vào thôi. Ví dụ như xin vào làm Developer thì mấy cái kinh nghiệm như làm tình … nguyện viên thì bỏ đi cũng được.
Ở mỗi vị trí thì nên ghi rõ ra là mình làm được cái gì, bằng cách nào và kết quả ra sao. Cụ thể thì viết làm sao để cho cái kết quả đó có thể “đếm” được.
Ví dụ: Làm hệ thống chat real-time, bằng NodeJS và chịu được tải với 3000 user.
Dự án
Dự án là cách tốt nhất để chứng mình là mình có thể code. Nhất là với sinh viên mới ra trường như mình.
Mình nghĩ là các bạn sinh viên nên làm một số Pet Project lên để có cái bỏ lên Github. (Cố gắng hoàn thành cho chỉn chu, đừng làm xong cái core rồi vất đó). Sau đó thêm dự án đó vào CV. Đồ án tốt nghiệp thì thêm vào CV cũng được.
Chỉ cần đưa ra 2 đến 4 cái dự án của mình. Nói rõ dự án đó là dự án gì, sử dụng ngôn ngữ nào, công nghệ gì, làm cá nhân hay làm team, là pet project hay đồ án tốt nghiệp. Chi tiết về dự án thì không cần thiết lắm, có thể thêm vào nếu dự án hay ho.
Những phần mềm ví dụ như Microsoft Office thì không cần thêm vào, vì nó không có ích nhiều cho dân Developer. Còn những IDE/Text Editor như VsCode hay Netbeans hay Eclipse thì có thể thêm vào cũng được. Nhưng mà thật sự thì mấy cái IDE có khó học đến thế đâu mà thêm vào làm gì? Tất nhiên là khi CV ngắn quá thì mình nghĩ thêm vào cho đủ dày đày CV một tí cũng OK.
Ví dụ như mấy phần mềm quản lý source như Git là một thứ có thể thêm vào, hay Linux là thứ có thể thêm vào.
Theo như mình biết thì kể hết mấy cái ngôn ngữ lập trình mình biết ra thì không phải là ý hay. Vì kể càng nhiều mà khi phỏng vấn đúng mấy cái mình chỉ “biết” sơ sơ thì dễ bị đánh giá là xiaolin, không trung thực. Tốt nhất là kể ra 3 hoặc 4 ngôn ngữ và framwork hay xài nhất, cộng với cái kinh độ của mình.
Ví dụ như: Javascript (Expert), PHP (Intermediate), C++ (Prior Experience).
Một chú ý nữa là không nên ghi số năm kinh nghiệm của mình với một ngôn ngữ nào đó vì nó khó để các bạn HR hình dung lắm. Ví dụ như trong CV bạn viết bạn có 10 năm Javascript không có nghĩa là bạn làm Javascript liên tục trong 10 năm.
Tương tự như ở trên, không nên viết về trình độ của mình trong CV theo kiểu thang đo. Ví dụ như: PHP ( 90%) hay Javascript (100%). Mình thấy nhiều mẫu CV tham khảo hay có cái thước đo 70%, 80%, 90% này. Nhưng tương tự cái trên thì 90% so với cái gì? Đối với bạn, trình độ bạn nghĩ là 90% nhưng với các anh/chị phỏng vấn thì trình độ bạn chỉ là 50% thôi thì sao?
Chính tả/Ngữ pháp
Hiện tại thì mình nghĩ tất cả các CV liên quan đến CNTT đều nên viết bằng tiếng Anh. Và một khi đã viết tiếng Anh thì chú ý sửa lỗi ngữ pháp và chính tả. Điều này làm cho các bạn HR thấy mình có đầu tư vào cái CV của mình. Tốt nhất là nên nhờ một ai đó giỏi tiếng Anh để đọc và sửa lỗi chính tả cũng như ngữ pháp cho mình.
Một số ngôn ngữ lập trình quá cũ và cổ rồi vì không cần thiết phải thêm vào CV. Ví dụ Cobol hay Visual Basic 6. Hai ngôn ngữ này hơi cổ điển rồi thì không thêm vào cũng được. Tất nhiên là nếu bạn làm lập trình VB.Net thì thêm Visual Basic 6 vào là hợp lý. Còn nếu không thì mình nghĩ là không nên ghi vào làm gì.
Đừng có tập trung quá nhiều vào một ngôn ngữ. Tất nhiên bạn sẽ có một ngôn ngữ chính nào đó mà mình giỏi nhất, tuy nhiên đừng chỉ nói quá tập trung vào cái ngôn ngữ đó. Điều dễ bị đánh giá là “Chỉ biết mỗi một ngôn ngữ thì làm ăn gì”. Tương tự với mấy cái Certificate, cái nào giống nhau thì chọn một cái ngon nhất bỏ vào thôi là đươc. Biết tầm 3 – 4 cái ngôn ngữ, trong đó main 1 đến 2 cái là mình nghĩ hợp lý.
Một số thông tin cá nhân khác như tình trạng hôn nhân các thứ thì không cần ghi vào cũng được.
Kết luận
Mình chỉ vẫn là một thằng sinh viên quèn, mới ra trường và chưa có nhiều kinh nghiệm trong việc đi xin việc làm (Có đi xin thực tập ở một Start Up trong 03 tháng thôi). Những kiến thức mình viết ở trên đây không phải là trải nghiệm cá nhân của mình là được lấy ra tư trong cuốn sách khá hay là Cracking the Coding Interview.
Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh
Ngoài hỗ trợ generate public key và private key, Java còn hỗ trợ chúng ta convert các tập tin public key và private key này qua các đối tượng Java. Chúng ta chỉ cần biết định dạng dữ liệu được sử dụng trong những tập tin này mà thôi!
Đầu tiên, các bạn cần đọc các tập tin này và chuyển nội dung của nó sang mảng byte. Ví dụ của mình với private key như sau:
Sau đó thì tuỳ theo định dạng dữ liệu của tập tin public key và private key mà chúng ta sử dụng các đối tượng KeySpec phù hợp để đọc nội dung của các tập tin này.
Bài viết được sự cho phép của blogchiasekienthuc.com
Đối với rất nhiều anh em game thủ, hay những người sử dụng máy tính cho các công việc nặng (như dựng phim, làm đồ họa, render video….) thì mong muốn của họ là được sở hữu một chiếc máy tính có cấu hình khủng.
Nếu như trước đây bạn đã từng biết đến CPU và GPU có thể OverClock (ép xung) để tăng thêm sức mạnh, thì RAM cũng có thể làm được điều đó. Vâng, và XMP trên RAM là một chức năng cho phép hệ thống sử dụng RAM ở mức tối đa, giúp tăng sức mạnh hiện có của thanh Ram đó lên mức cao nhất.
Và XMP cũng là câu trả lời cho những bạn đang thắc mắc là tại sao RAM không hoạt động đúng với số BUS (tốc độ xử lý) như nhà phát hành công bố.
XMP (Extreme Memory Profile) là một công nghệ được Intel phát triển với mục đích là để điều chỉnh tốc độ của thanh Ram lên mức cao nhất có thể. Có nghĩa nó khi bật tính năng XMP trên RAM thì hiệu suất của RAM sẽ cao hơn mức mặc định mà CPU hỗ trợ.
Thông thường, các hệ thống máy tính đều có một mức sử dụng Ram nhất định, kể cả là khi bạn có lắp một thanh Ram với tốc độ cao hơn thì hệ thống cũng chỉ nhận ở mức được thiết lập sẵn mà thôi.
Vậy nên, XMP sinh ra là để loại bỏ rào cản này bằng cách can thiệp vào hệ thống và tăng xung nhịp của Ram lên mức cao nhất có thể, đồng thời nó sẽ hạn chế các vấn đề lỗi phát sinh khi tăng xung nhịp.
#2. XMP hoạt động như thế nào?
Khi bạn khởi động máy tính thì BIOS/ UEFI sẽ thực hiện kích hoạt một con chip (SPD) được gắn trên RAM để điều chỉnh lại mức xung nhịp cũng như là độ trễ (delay) của RAM.
XMP là một bộ phận mở rộng trên con chip SPD, nó có nhiệm vụ là tự động điều chỉnh RAM sao cho thanh RAM đó luôn hoạt động ở mức xung nhịp cao nhất.
Trong trường hợp bạn không kích hoạt XMP thì cũng chả sao cả, máy tính vẫn hoạt động tốt. Tuy nhiên thì RAM sẽ chỉ chạy được với thông số mặc định mà CPU hỗ trợ mà thôi.
Mình lấy một ví dụ cho các bạn dễ hình dung hơn đó là: Bạn có thanh RAM DDR4 có tốc độ 3000MHz, nhưng con chip hiện tại trên máy là Intel Core i5-10400 => thì lúc này, mức RAM mà máy tính sử dụng thực tế chỉ là 2400MHz hoặc 2666MHz mà thôi. Không thể đạt được 3000MHz nếu như bạn không kích hoạt XMP.
#3. XMP không phải là OverClock (OC) RAM
Nhiều bạn đọc đến đây sẽ lầm tưởng rằng XMP là ép xung Ram, nhưng thực chất là không phải như vậy. Về cơ bản thì cả hai đều tăng xung nhịp Ram, nhưng XMP là tăng xung nhịp tới mức mà nhà sản xuất cho phép, còn ép xung [OC] là phá đi cái giới hạn mà nhà sản xuất đưa ra.
Điều này cũng ảnh hưởng trực tiếp tới độ bền linh kiện của 2 phương pháp này. Như các bạn đã biết, để ép xung thì cần phải tăng nguồn điện vào mới có thể tăng xung lên cao, việc này nếu không cẩn thận có thể gây ra tình trạng hỏng Ram vì quá áp.
Còn với XMP thì an toàn hơn, mọi thông số để tăng xung nhịp đều đã được thiết kế dành cho thanh Ram nên gần như là không có ảnh hưởng gì tới độ bền của Ram sất.
#4. Cần lưu ý gì khi kích hoạt XMP trên RAM?
Hệ thống mainboard (bo mạch chủ) của máy tính phải hỗ trợ XMP và tốt nhất là bạn nên sử dụng mainboard của những hãng danh tiếng (có mức độ uy tín cao), có tuổi đời lâu năm trên thị trường mainboard.
Bạn cũng nên cập nhật BIOS lên phiên bản mới cho mainboard của bạn, để đảm bảo không có lỗi nào xảy ra khi kích hoạt XMP. Vì thông thường, qua từng phiên bản nâng cấp BIOS thì các lỗi sẽ được khắc phục, cũng như là nó hoạt động một cách ổn định hơn.
Hạn chế tối đa việc tự vọc vạch ! Vâng, thông thường thì XMP là hoàn toàn tự động, bạn chỉ cần kích hoạt (chuyển sang ON) là xong, nhưng trong một số trường hợp có các tùy chỉnh khác nhau, nếu không đủ kiến thức thì các bạn nên để nguyên mọi thông số như mặc định nhé.
Không phải cứ XMP là máy sẽ nhanh hơn, thông thường khi XMP thì tốc độ nói chung của máy tính sẽ nhanh hơn bởi dữ liệu được nạp vào nhanh hơn, nhưng nó không đúng trong mọi trường hợp đâu các bạn nhé. Tốc độ máy tính phụ thuộc vào cả phần mềm và phần cứng khác nữa, vì vậy hãy xem xét kĩ càng và cẩn thận tối ưu từng chút một.
#5. Bật XMP trong BIOS/ UEFI có nguy hiểm không?
Như mình đã nói ở trên, XMP nó không phải là ép xung nên sẽ không có gì nguy hiểm cả, tất cả đều hoạt động trong sự cho phép của nhà sản xuất.
Tất cả đã được kiểm nghiệm một cách kỹ lưỡng trước khi xuất xưởng, mọi thông số như điện áp, độ trễ, tốc độ xung nhịp… đều đã được đảm bảo an toàn nên các bạn cứ yên tâm mà sử dụng nhé.
#6. Làm thế nào để kích hoạt XMP để RAM hoạt động với hiệu suất cao nhất?
Cũng rất đơn giản thôi, nhưng trước tiên bạn phải biết cách truy cập vào BIOS hoặc UEFI trước đã. Nếu bạn chưa biết thì hãy đọc bài viết này nhé:
Mỗi dòng Mainboard, mỗi hãng sản xuất thì lại có giao diện BIOS/ UEFI khác nhau => vậy nên các bạn phải tự mò thôi.
Nói chung là bạn cứ vào BIOS/ UEFI rồi tìm kiếm đến phần XMP => sau đó kích hoạt (Enable) nó lên là được.
Hoặc
Tuy nhiên mình cũng cần lưu ý với các bạn là không phải máy tính nào bạn cũng có thể kích hoạt được XMP đâu nhé.
Mình lấy ví dụ như máy Mainboard đó không hỗ trợ XMP, hoặc là do phiên bản BIOS đó quá cũ, lâu chưa được update nên chưa có tính năng đó.
Hoặc đơn giản là RAM không có tính năng XMP.
Hoặc cũng có thể là do chip CPU không tương thích với mức xung nhịp khi bật XMP của RAM.
#7. Làm thế nào để kiểm tra máy tính đã kích hoạt XMP chưa?
Rất đơn giản thôi, bạn có thể vào lại BIOS/ UEFI để kiểm tra, hoặc một cách nhanh hơn và dễ dàng hơn khi bạn có thể truy cập vào được Windows, đó là:
Sử dụng phần mềm CPU-Z => sau đó xem trong tab SPD như hình bên dưới chẳng hạn:
#8. XMP có thực sự cần thiết cho RAM không?
Theo như thực tế mình tìm hiểu được thì gần như mọi thiết bị máy tính phổ thông hiện nay, ngay cả khi không bật XMP thì vẫn đủ khả năng để chiến game và làm các công việc nặng như đồ họa hay Render…
Tuy nhiên, nếu bạn là một người khó tính, hoặc bạn có kiến thức về máy tính, hay đơn giản là bạn đang muốn tối ưu tốc độ cho chiếc máy tính của bạn để nó hoàn hảo nhất có thể thì hãy nghĩ đến XMP nhé.
Vâng, trên đây là những khái niệm cơ bản về XMP, cũng như là cách để kích hoạt và sử dụng tính năng XMP này. Nói chung là nếu máy tính có hỗ trợ thì bạn cũng nên bật tính năng này lên để tận dụng hết sức mạnh của RAM.
Bài viết được sự cho phép của BQT Kinh nghiệm lập trình
Mặc dù hiện nay số người hoạt động trong các lĩnh vực công nghệ thông tin ngày càng đông, nhưng không phải ai cũng có thể nắm bắt hết những thuật ngữ chuyên môn. Và API đang là cụm từ được rất nhiều người trong và ngoài giới quan tâm. Vậy API là gì? Các nguyên tắc xây dựng Rest API chuẩn nhất hiện nay là gì? Hãy cùng khám phá mọi thắc mắc trên qua bài viết dưới đây.
Nói một cách đơn giản, API được hiểu là một giao diện lập trình phần mềm trung gian hỗ trợ các ứng dụng giao tiếp với nhau. Tuy nhiên, cần có cái nhìn khái quát hơn về thuật ngữ này.
API là từ viết tắt của cụm Application Programming Interface hay còn gọi là giao diện lập trình ứng dụng. Chúng cho phép các ứng dụng khác giao tiếp hiệu quả với nhau và dùng được cho web-based system, operating system, database system, computer hardware, or software library.
API được ứng dụng phổ biến trong lập trình web hiện đại
Vai trò của API
Cũng giống như bàn phím là phương tiện giao tiếp giữa con người với máy tính, API chính là phương tiện kết nối giữa các chương trình và hệ điều hành. Mỗi hệ điều hành có API khác nhau, phục vụ cho những mục đích khác nhau và không hề có tính tương thích. Có thể thấy Windows có rất nhiều API và Twitter cũng có riêng web API, tuy nhiên các API này hoàn toàn khác biệt.
Giao diện lập trình ứng dụng API cung cấp khả năng truy xuất đến tập hợp các hàm hay dùng. Nói cách khác, API là một công cụ để tạo ra phần mềm. Lập trình viên có thể tự tạo ra các API hoặc mua từ các nhà cung cấp. Hiện nay, để đơn giản hóa lập trình web, hệ điều hành Microsoft đã cho ra đời công nghệ tiên tiến nhất mang tên API Web cho phép xây dựng dịch vụ thành phần mềm phân tán. Web API là mô hình được tạo ra nhằm mục đích hỗ trợ MVC như: routing, controller, action result, filter, filter, loc container, model binder, unit test, injection. Đồng thời còn cung cấp tính năng cho phép restful đầy đủ các phương thức: Get/ Post/ Put/ Delete dữ liệu.
Những ưu điểm nổi bật của API
Giao diện lập trình ứng dụng này là một framework mới cho phép lập trình viên xây dựng các HTTP service một cách đơn giản và tiết kiệm thời gian tối đa. Các ứng dụng sẽ giao tiếp với nhau hiệu quả hơn nhờ vào tính chất tối giản và ưu việt của API. Với mã nguồn mở, người dùng có thể sử dụng bất kỳ Client nào có sử dụng XML hoặc JSON. Không chỉ vậy, API còn có khả năng hỗ trợ các thành phần khác như: HTTP: URI, request/response headers, caching, versioning…
Có thể tổng kết các ưu điểm của API:
Cho phép kết nối mọi lúc mọi nơi nhờ vào Internet
Cần xác nhận trong các giao dịch giao tiếp song phương
Tăng độ tin cậy của thông tin nhờ vào giao tiếp hai chiều
Cấu hình đơn giản so với WCS
Hỗ trợ chức năng Rest full
Mã nguồn mở
API hiện đại
API từ lâu đã được biết đến như phương thức giao tiếp hiệu quả giữa các ứng dụng. Tuy nhiên, API hiện đại đã được nâng cấp, bổ sung thêm một vài tính năng mới cho phép chúng hoạt động tối ưu hơn:
API hiện đại tuân thủ theo các chuẩn (thông thường là HTTP và REST), dễ hiểu, giúp người dùng đơn giản hóa việc sử dụng và truy cập.
API hiện đại được sử lý tương tự như các sản phẩm thay vì việc mã hóa code. Giao diện lập trình này thường được thiết kế cho một đối tượng tiêu dùng cụ thể. Chúng có nhiều phiên bản khác nhau cho người dùng
Các vấn đề theo dõi, quản lý hiệu suất, quy mô nghiêm ngặt hơn vì API hiện đại được chuẩn hóa tối ưu
API hiện đại cũng có lịch sử phát triển vòng đời tương tự như các phần mềm khác từ thiết kế, thử nghiệm, xây dựng, quản lý và các phiên bản…
Một số thông tin về Rest API
Rest API là những API đi theo cấu trúc của Rest và chúng tồn tại theo những quy luật chung nhất sau đây:
Tính nhất quán
Tồn tại ở trạng thái mang tên “không trạng thái” (không có server-side session)
Trong trường hợp cần thiết có thể sử dụng HTTP status code
Có thể sử dụng cả URL endpoint với Logical hierarchy
Versioning không phải trong HTTP header mà trong URL
Rest rất được ưa chuộng cho dữ liệu HTTP vì chúng có kích thước nhỏ gọn. Hiện nay, Rest rất phổ biến trên các website và được đánh giá là lựa chọn lý tưởng số 1 đối với việc phát triển API.
Người dùng API có thể xây dựng một script kết nối với external API server. Sau đó các dữ liệu quan trọng và cần thiết sẽ chuyển sang HTTP. Khi đó, lập trình viên có thể hiển thị dữ liệu trực tiếp lên website mà không phải truy cập cá nhân vào external server.
Dưới đây là 4 lệnh phổ biến nhất cho phép người dùng truy cập vào RESTful API: GET: cho phép truy vấn object POST: giúp tạo object mới PUT: hỗ trợ sửa đổi hoặc thay thế một object DELETE: loại bỏ một object
Tổng kết
API thật sự đang là một trong những vấn đề rất được quan tâm hiện nay. Hy vọng các chia sẻ phía trên của bài viết có thể cung cấp đến quý bạn đọc thông tin liên quan đến thắc mắc API là gì? Các nguyên tắc xây dựng Rest API hiệu quả.
Trước khi bắt đầu tìm hiểu về Java Map và flatMap, bạn nào chưa biết về Java Stream có thể đọc bài Stream – KieBlog để tìm hiểu và nhớ lại kiến thức về Stream trước khi bắt đầu vào nội dung chính của bài đọc.
Trong Stream API, phương thức Map() hoạt động như là một functions có argument (đối số).
Thứ nhất, phương thức này là phương thức trung gian (terminal operation).Phương thức này applie tất cả các chứng năng có ở funtions cho các elements trong Stream.
Cùng xem qua ví dụ sau đây:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MapExample {
public static void main(String[] args) {
List words= Arrays.asList("KieBlog","In", "God","We trust");
List wordCount = words.stream()
.map(String::length)
.collect(Collectors.toList());
wordCount.forEach(System.out::println);
}
}
Ở ví dụ trên, phương thức Map() sử dụng phương thức String::length nhằm tính đoán độ dài các chuỗi String trong List. Kết quả trả về sẽ là một list độ dài các chuỗi.
Output:
16
4
11
6
Phương thức Map() đã hoạt động hoàn hảo. Câu hỏi đặt ra là tại sao ta lại cần thêm phương thức flatMap()?. Tại sao lại cần thêm phương thức này?.
Đầu tiên, không cần phải nạp vào đầu những khái niệm lằng nhằng, rắc rối. Để đơn giản và dễ hiểu, ta cứ nhớ rằng:
flatMap = flat + Map (2 nhân tố cấu thành nên flatmap).
flat ở đây là Flattening (làm phẳng, làm cho bẹp lép :)).
Ủa, vậy làm bẹp lép là làm gì?.
Flattening sẽ chuyển đổi list trong list. Merge lại tất cả lại với nhau thành một list duy nhất. Tất nhiên, nó bao gồm tất cả các phần tử có trong list con.
Từ từ, hãy cùng phân tích định nghĩa đã
3. Tại sao lại cần thêm flatmap?
Để hiểu được tại sao lại cần thêm flatmap, hãy cùng xem xét hai ví dụ sau đây:
Ta có một Array String các chuỗi mã hóa bị trùng lặp. Bài toán đặt ra: loại bỏ trùng lặp, lấy giá trị chuỗi.
Ù uôi, dễ thế!. Chẳng phải sử dụng Java distince() kết hợp với Map là sẽ xong sao?. Vấn đề không đơn giản như thế!, yêu cầu ngặt ngèo hơn. Kiểu trả về bắt buộc phải là Stream<String[]>, không được sử dụng thêm phương thức nào ngoài khác Stream API.
// Chuỗi mã hóa
List enscriptLst = Arrays.asList("AABYAJWLZ","DDA");
// Với yêu cầu ban đầu, ta có thể sử dụng map() và distince(), phân tách các kí tự
// loại bỏ trùng lặp
enscriptLst.stream().map( s-> s.split("")).distinct().collect(Collectors.toList());
Đoạn source này cho ta kết quả chính xác. Đoạn mã được loại bỏ trùng lắp. Nhưng kiểu trả về thì lại là List<String[]>.
Tại sao lại vậy?. Nguyên nhân là do Stream sẽ thực hiện chia tách và loại bỏ các từ trên từng Object String trong Arrays.
Đây chính là lúc mà ta cần tới flatMap:
// Chuỗi mã hóa
List enscriptLst = Arrays.asList("AABYAJWLZ","DDA");
// Với yêu cầu ban đầu, ta có thể sử dụng map() và distince(), phân tách các kí tự
// loại bỏ trùng lặp
enscriptLst.stream().map( s-> s.split("")).distinct().collect(Collectors.toList());
Để ý tới hình ảnh phía dưới để biết phương thức flatMap() đã loại bỏ trùng lặp và trả về cho ta một List<String> duy nhất như thế nào.
Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh
Thuật toán mã hoá RSA sử dụng một cặp public key và private key để hiện thực cơ chế bảo mật. Public key dùng để mã hoá thông tin và private key được sử dụng để giải mã thông tin và ngược lại. Java cung cấp cho chúng ta một số class để làm việc với thuật toán RSA nằm trong package java.security. Trong bài viết này, mình hướng dẫn các bạn cách sử dụng class KeyPairGenerator của Java Security để generate một cặp public key và private key để sử dụng các bạn nhé!
package com.huongdanjava.javaexample;
public class Example {
public static void main(String[] args) {
}
}
Đầu tiên, các bạn cần khởi tạo đối tượng KeyPairGenerator sử dụng phương thức static getInstance() với thuật toán RSA và kích thước 1024 hoặc 2048 như sau:
Sử dụng phương thức generateKeyPair() của đối tượng KeyPairGenerator, các bạn sẽ generate được một cặp public key và private key, thông tin được chứa trong đối tượng KeyPair:
KeyPair keyPair = kpg.generateKeyPair();
Từ đối tượng KeyPair này, các bạn có thể lấy đối tượng chứa public key và private key như sau:
Hẳn các bạn cũng biết, trong thời gian sắp tới, cloud sẽ là từ khóa phổ biến được tìm kiếm. Các nội dung về cloud sẽ được tìm hiểu và chia sẻ, chính vì vậy, mình viết bài viết này nhằm chia sẻ chút kiến thức nhỏ nhoi về Google Associate Cloud Engineer.
Sắp tới sẽ có những series bài viết về các phần của Google Cloud Platform. Hãy cùng theo dõi nhé.
Manage Access Control – nội dung không thể thiếu để bắt đầu làm quen với Google cờ lao.
1. Thiết lập môi trường cho cloud solution.
Bài học bắt đầu cho kì thi chứng chỉ Google Associate Cloud không gì tốt hơn là thực hành tạo một project trên Google Cloud Platform. Mình thành thật khuyên rằng đừng nên bỏ qua những bước đi ban đầu làm quen với Cloud của Google.
Đi chậm mà chắc theo từng bước sẽ có ích cho bạn về lâu về dài sau này.
1.1 Creating project
Tất nhiên, bước đầu tiên luôn là khởi tạo project. Ngoài ra, một nội dung quan trọng không kém cần các bạn nắm bắt là phần quản lý account. Các nội dung chính bao gồm.
Creating projects – tạo project
Assigning users to predefined IAM roles within a project – gán quyền user cho các IAM (Identity access management) được thiết lập sẵn. Để bắt đầu làm việc.
Managing users in Cloud Identity (manually and automated) – quản lý users ở phần định danh của Cloud (bằng tay hoặc tự động)
2. Lên kế hoạch, cấu hình cho cloud solution.
Lên kế hoạch là một phần quan trọng phải biết khi học thi chứng chỉ Google Associate Cloud. Trước khi bắt đầu, ta cần hiểu rõ project của mình sử dụng như thế nào, chi phí khoảng bao nhiêu. Công cụ thường được sử dụng là GCP – Google Cloud Pricing.
Planning and estimating GCP product use using the Pricing Calculator – tính toán và đưa ra dự đoán với GCP, công cụ hỗ trợ là Pricing Calculator
Planning and configuring compute resources: Cấu hình computer resource. Phần này nhằm đưa ra tính toán chính xác cho một khối lượng công việc cụ thể (Compute Engine, Google Kubernetes Engine, App Engine, Cloud Run, Cloud Functions)
Tất nhiên, một ứng dụng không thể thiếu đi trái tim của nó, hệ cơ sở dữ liệu. Trong qúa trình lên kế hoach, ta sẽ phải lựa chọn database để sử dụng. Về database, có thể lựa chọn Cloud SQL, BigQuery, Cloud Spanner, Cloud Bigtable. Về nơi lưu trữ, có thể lựa chọn giữa regional, multi-regional, nearline và coldline.
Google Cloud Pricing – Staying in touch
Đối với chứng chỉ Google Associate Cloud, cần thiết phải biết thêm về configuring network. Để nắm rõ, cần trình bày được sự khác biệt giữa các options load balancing cho ứng dụng. Ngoài ra:
Identifying resource locations in a network for availability – tất nhiên, khi sử dụng, ta phải biết vị trí các resource ở trong network.
Configuring Cloud DNS – tự cấu hình được Cloud DNS
3. Deploy và implement cho cloud solution.
Sau khi đã cấu hình, tính toán và lựa chọn thì công việc tiếp theo sẽ là Deploy và Impelement project. Phần nội dung này khá quan trọng, cũng chiếm nhiều câu hỏi trong đề thi lấy chứng chỉ Google Associate Cloud.
Các nội dung cần chú ý bao gồm:
Launching a compute instance using Cloud Console and Cloud SDK – khởi tạo compute instance sử dụng Cloud Console và Cloud SDK
Generating/uploading a custom SSH key for instances – tạo và upload custom SSH key cho instance.
Creating an autoscaled managed instance group using an instance template – tạo và quản lý autoscaled thông qua các template có sẵn.
Về phần deploy, nội dung này bao gồm các kĩ thuật deploy Google Kubernetes, Cloud Query và các loại database khác. Số lượng database được hỏi khi thi Google Associate Cloud cũng tương đối nhiều.
Vì vậy, nếu có điều kiện, các bạn nên dành thời gian làm quen hết với tất cả các loại. Vừa làm nhiều quen tay, vừa không bỡ ngỡ khi gặp các câu hỏi liên quan tới hệ cơ sở dữ liệu
4. Cấu hình truy cập và bảo mật.
4.1 IAM – định danh và quản lý truy cập.
Phần này chủ yếu chú trọng vào IAM, viết tắt của Identity and Access management (định danh và quản lý truy cập). Đây là nội dung bắt buộc cần nắm bắt đã/đang và trong khi sử dụng Google Cloud Platform.
Về IAM, có 3 nội dung cần tham khảo qua:
Viewing IAM role assignments – xem các nội dung quyền được gán.
Assigning IAM roles to accounts or Google Groups – xác định role cho các account hoặc Google Groups.
Defining custom IAM roles – định nghĩa riêng một IAM roles.
Introduction Google Cloud IAM
4.2 Managing service accounts
Phần nội dung này tập trung chủ yếu vào phân quyền, gán quyền cho các user. Phần account này chạy như là một service độc lập. Vì vậy có thể tích hợp service account này cho một project khác.
Managing service accounts with limited privileges – quản lý service account với một số quyền giới hạn.
Assigning a service account to VM instances – gán quyền service account cho instance của VM.
Granting access to a service account in another project – Gán quyền truy cập cho service account trong một project khác.
5. Tham khảo.
Các bạn có thể tham khảo thêm các bài viết dưới đây để hiểu rõ thêm các phần sẽ thi khi muốn lấy chứng chỉ Google Associate Cloud Certification.
Bài viết được sự cho phép của tác giả Edward Thien Hoang
Khách nhau giữa kiến trúc phân lớp (Layered) và kiến trúc N-tier
HM thấy trên mạng có một số người hỏi về kiến trúc phân lớp (layered) và kiến trúc n-tier. Cũng như nhiều khái niệm khác, nếu tỉ mỉ đi vào chi tiết thì chắc không có định nghĩa thống nhất, tuy nhiên nếu để nắm ý tưởng chung, thì có lẽ tốt nhất là xem “Microsoft application architecture guide” (Microsoft viết mà sai thì chịu thôi :D). Tải miễn phí tại Microsoft, hoặc đọc trong mục patterns & practices của MSDN. Dưới đây tóm lược các ý chính:
Kiến trúc phân lớp nhóm các chức năng liên quan đến nhau trong từng lớp (layer). Các chức năng ở một lớp khi làm nhiệm vụ của mình có thể sử dụng các chức năng mà lớp dưới nó cung cấp. Có hai dạng
Strict layering: Lớp trên chỉ liên hệ với lớp ở ngay dưới nó.
Relaxed layering: Lớp trên có thể liên hệ với nhiều lớp dưới nó.
Khi nói về kiến trúc phân lớp, chúng ta nói về cách tổ chức luận lí của code, ví dụ thông qua các package, namespace, module, v.v. Không hề có gì gợi ý rằng các lớp phải chạy trên các máy khác nhau, hay thậm chí trong các process khác nhau.
Tier là nơi các lớp chạy. Các tier phải tách biệt với nhau về memory space. Nhưng cũng không có nghĩa là các tier phải chạy trên các máy khác nhau. Trong hệ điều hành, các process tách biệt với nhau về memory space, cho nên nếu bạn có một chương trình với hai lớp Presentation và Business Services chạy trên hai process giao tiếp với nhau dùng Web services thì bạn cũng đã có một hệ thống 2-tier rồi.
Các lớp có thể ở cùng một một tier, hoặc được phân tán trên nhiều tier (n-tier). Vậy một tier có thể có nhiều lớp. Tuy nhiên, thông thường với các hệ thống lớn, các tier sẽ không ở trên cùng một máy vật lí, vì các lí do về performance, scalability, fault tolerance, security v.v.
Mô hình 3 tầng (3-TIERS) là gì?
Theo wikipedia thì:
“3-tiers là một kiến trúc kiểu client/server mà trong đó giao diện người dùng (UI-user interface), các quy tắc xử lý(BR-business rule hay BL-business logic), và việc lưu trữ dữ liệu được phát triển như những module độc lập, và hầu hết là được duy trì trên các nền tảng độc lập, và mô hình 3 tầng (3-tiers) được coi là một kiến trúc phần mềm và là một mẫu thiết kế.” (dịch lại từ wikipedia tiếng Anh).
Như vậy, ta có thể mô hình này phân tách ứng dụng ra làm 3 module riêng biệt, bao gồm:
– Tầng Presentation: được dùng để giao tiếp với người dùng, nhiệm vụ chính là hiển thị dữ liệu và nhận dữ liệu từ người dùng.
– Tầng Business Logic: nhiệm vụ chính là cung cấp các chức năng của phần mềm.
– Tầng Data: lưu trữ dữ liệu, cho phép lớp Business Logic có thể tìm kiếm, trích xuất, cập nhật… dữ liệu.
Tại sao là 3-TIERS mà không phải là 3-LAYERS?
Khi dùng từ layer, chúng ta nói tới việc phân chia ứng dụng thành các thành phần một cách logic theo chức năng hoặc theo vai trò, điều này giúp phần mềm của bạn có cấu trúc sáng sủa, dễ dùng lại, từ đó giúp việc phát triển và bảo trì dễ dàng hơn. Các layer khác nhau khi được thực thi vẫn có thể nằm trong cùng một vùng bộ nhớ của một process, và hiển nhiên việc giao tiếp giữa 2 layer có thể không phải là giao tiếp giữa 2 process, đồng nghĩa với việc chúng không liên quan tới mô hình client/server.
Trái lại, tier liên quan đến cách phân chia một cách vật lý các thành phần trên các máy tính khác nhau.
Điều làm nhiều người nhầm lẫn giữa layer và tier là chúng có cùng cách phân chia (presentation, business, data), tuy nhiên trên thực tế chúng khác nhau. Vì cách phân chia như trên nên 1 tier có thể chứa nhiều hơn 1 layer.
3-TIERS có những ưu và nhược điểm gì?
3-tiers là một kiến trúc phần mềm, có nghĩa là bạn có thể dùng nó để xây dựng nên bộ khung tổng thể của ứng dụng. Tuy nhiên bạn cần chú ý những ưu và nhược điểm sau đây để áp dụng nó một cách đúng đắn.
Ưu điểm:
– Dễ dàng mở rộng, thay đổi quy mô của hệ thống: Khi cần tải lớn, người quản trị có thể dễ dàng thêm các máy chủ vào nhóm, hoặc lấy bớt ra trong trường hợp ngược lại.
Nhược điểm:
– Việc truyền dữ liệu giữa các tầng sẽ chậm hơn vì phải truyền giữa các tiến trình khác nhau (IPC), dữ liệu cần phải được đóng gói -> truyền đi -> mở gói trước khi có thể dùng được.
– Việc phát triển ứng dụng phức tạp hơn.
Những công nghệ nào hỗ trợ xây dựng các ứng dụng 3-TIERS?
Tùy thuộc vào nền tảng, bạn có thể chọn một trong các công nghệ như EJB (J2EE), COM+ (Windows), hay cũng có thể dùng các máy chủ web như nền tảng xây dựng lớp giữa (dùng webservice). Tuy nhiên, EJB và COM+ là hai tùy chọn tốt nhất vì nó có nhiều công nghệ hỗ trợ như Object Pooling, Authentication và Authority, Resource management, Remote Object Access, Transaction…
Các công nghệ truyền thông điệp như JMS hay MSMQ cũng hỗ trợ nhiều trong việc tạo các lời gọi không đồng bộ.
Các ứng dựng máy chủ cơ sở dữ liệu có liên quan gì đến mô hình này không?
Có, nó đóng vai trò tầng Data.
Bản thân khi hoạt động, máy chủ CSDL trở thành 1 phần không thể thiếu trong hệ thống, nó chính là nơi chứa dữ liệu của bạn. Việc dùng một hệ CSDL sẵn có là việc nên làm vì nó giúp chúng ta rất nhiều công sức, nhưng điều đó không có nghĩa là nó không thuộc vào hệ thống của chúng ta, chỉ khác ở chỗ đây là một tầng Data được xây dựng sẵn.
Lớ Data Access Layer (DAL) thuộc tầng nào?
Lớp Business Logic.
Trái với nhiều người nghĩ, cứ cái gì có chữ Data thì nó phải thuộc lớp 3, tuy nhiên vì DAL chỉ đóng vai trò truy vấn, chứ bản thân nó không cung cấp dữ liệu, và nó vẫn phải được thực thi bởi các Business Object, vậy nên trong đa số trường hợp nó sẽ nằm trong lớp 2 (một số thiết kế tách nó riêng thành 1 tier).
Nên nhớ rằng việc tách riêng ra một DAL giúp bạn có một thiết kế tốt hơn, nhưng không phải là bắt buộc. Và việc tự tạo một DAL với việc dùng chung một tập các lớp truy xuất dữ liệu được cung cấp bởi một công nghệ/công cụ có sẵn như LINQ to SQL, NHibernate hay Entity Framework không có gì khác nhau về kiến trúc hệ thống.
Có lẽ vì sự tồn tại của DAL mà rất nhiều người hiểu nhầm giữa 3-tiers và 3-layers.
Tôi nên kiểm tra dữ liệu nhập bởi người dùng ở lớp nào?
Kiểm tra dữ liệu ở lớp giao diện giúp giảm tải cho lớp giữa, phản hồi cũng nhanh hơn. Tuy nhiên sẽ khó có thể đảm bảo sẽ không có kẽ hở để những dữ liệu không hợp lệ được chuyển đến lớp Business Logic và thậm chí lớp Data, vậy nên thông thường việc kiểm tra nên được đặt trong tất cả các lớp tùy thuộc vào từng loại dữ liệu và phép kiểm tra.
Tôi có một ứng dụng, nó không có giao diện người dùng vì nó chỉ nhận dữ liệu từ các ứng dụng khác, Tôi có thể viết theo mô hình 3-TIERS được không?
Có, từ Presentation ở đây không mang ý nghĩa giao diện tương tác với người dùng, mà nó có nghĩa rộng hơn là phần tương tác với các hệ thống bên ngoài, ví dụ Presentation có thể là phầnkết nối để truy xuất dữ liệu đến một hệ thống khác, hay một cổng để tiếp nhận các lệnh do một hệ thống khác chuyển đến.
TÔI NÊN ĐỌC THÊM TÀI LIỆU NÀO ĐỂ HIỂU KỸ HƠN VỀ MÔ HÌNH 3 LỚP VÀ CÁCH DÙNG HIỆU QUẢ CÁC CÔNG NGHỆ NHƯ EJB và COM+?
Ngay cả khi chưa viết ứng dụng mới mô hình 3-tiers thì bạn cũng RẤT RẤT RẤT nên đọc 2 quyển trên.
3-TIERS CÓ GIỐNG MVC KHÔNG?
Không, trong 3-tiers, quá trình đi theo chiều dọc, bắt đầu từ Presentation, sang BL, rồi tới Data, và từ Data, chạy ngược lại BL rồi quay ra lại Presentation.
Còn trong MVC, dữ liệu được nhận bởi View, View sẽ chuyển cho Controller cập nhật vào Model, rồi sau đó dữ liệu trong Model sẽ được đưa lại cho View mà không thông qua Controller, do vậy luồng xử lý này có hình tam giác.
Đây là bài viết trong loạt bài viết về “Tổng quan về sự phát triển của kiến trúc phần mềm“. Đây là loạt bài viết chủ yếu giới thiệu về một số mô hình kiến trúc phần mềm hay nói đúng hơn là sự phát triển của chúng qua từng giai đoạn, qua đó giúp chúng ta có cái nhìn tổng quát, up-to-date và là roadmap để bắt đầu hành trình chinh phục (đào sâu) thế giới của những bản thiết kế với vai trò là những kỹ sư và kiến trúc sư phần mềm đam mê với nghề.
Bài viết được sự cho phép của blogchiasekienthuc.com
Nói đến công cụ cứu hộ máy tính thì chắc chắn chúng ta, những kỹ thuật viên, hay là những người có niềm đam mê và thích “vọc vạch” máy tính sẽ nghĩ ngay đến Hiren’s Boot, DLC Boot hay AnhDv Boot đúng không?
Vâng, tất nhiên rồi ! Đây đều là những bộ công cụ cứu hộ máy tính tuyệt vời, và nó đều đã được Admin giới thiệu trong chuyên mục USB BOOT có trên Blog.
Vậy nên hôm nay mình sẽ không giới thiệu với bạn một bộ công cụ nào nữa, mà thay vào đó mình sẽ chia sẻ với các bạn cách để tự tạo một bộ công cụ cứu hộ máy tính cho riêng mình, bạn có thể thỏa mái thêm bớt bất kì phần mềm nào mà mình muốn.
Nghe có vẻ khá thú vị rồi đúng không nào ◉◡◉ Okay, vậy thì bắt đầu nhé !
Đầu tiên, phần mềm này sẽ giúp bạn tạo ra bộ công cụ Windows 10 PE cùng với các phần mềm khác mà bạn thêm bớt vào.
Có thể là bộ công cụ cứu hộ mà bạn tự làm ra sẽ không được đầy đủ và đa dạng như các bộ cứu hộ chuyên nghiệp có trên Blog, tuy nhiên nó là sản phẩm của bạn, và nó phù hợp với nhu cầu sử dụng của bạn. Bởi tất nhiên rồi, đó đều là những phần mềm do chính bạn thêm vào mà 🙂
Ưu điểm lớn nhất của bộ cứu hộ này là độ tùy biến rất cao, nó cho phép bạn thêm bớt các phần mềm vào trong Win PE. Đồng thời giúp bạn có thể kiểm soát được dung lượng của bộ cứu hộ một cách chủ động hơn.
#2. Hướng dẫn sử dụng AOMEI PE Builder
+ Bước 1: Bạn truy cập vào link sau đây để tải nhé (link từ trang chủ – luôn là bản mới nhất) / Link dự phòng !
=> Sau khi tải phần mềm về thì bạn nhấp chuột vào file PEBuilder.exe để khởi chạy phần mềm.
Khi cài đặt đến phần License Agreement bạn chọn ô I accept the agreement => sau đó nhấp vào Next => Next … => và chọn Install để cài đặt phần mềm.
Sau khi cài đặt xong bấm Finish để kết thúc.
+ Bước 2: Bạn ra ngoài màn hình Desktop và nhấp chuột vào AOMEI PE Builder 2.0 (Lưu ý: Có thể khác biệt giữa các phiên bản trong tương lai, ví dụ như AOMEI PE Builder 3.0,…)
+ Bước 3: Khi màn hình cài đặt xuất hiện, bạn bấm Next để tiếp tục.
+ Bước 4: Sau đó phần mềm sẽ cho bạn 2 chọn lựa, đó là: Win PE 32bit hay Win PE 64bit. Hầu hết các đời máy hiện tại đều sử dụng 64bit, bạn có thể căn cứ vào lượng Ram của máy tính để xác định.
Mình thì khuyến khích bạn bạn dùng bản 64bit nhé.
+ Bước 5: Tiếp theo thì phần mềm sẽ hỏi bạn về các phần mềm mà bạn muốn thêm vào bộ cứu hộ. Bạn có thể bấm vào dấu [+] File, Network và System để thêm bớt các công cụ có sẵn trên Win PE.
Trong trường hợp này mình sẽ bỏ tick Recuva do đây là phần mềm khôi phục dữ liệu nhưng mình không cần dùng tới. Còn lại thì mình sẽ giữ nguyên. Các bạn có thể tự tùy chỉnh cho riêng mình nhé.
+ Bước 6: Tiếp theo chính là phần quan trọng nhất, đó là thêm các phần mềm của các bạn vào trong bộ cứu hộ.
Mình khuyên các bạn chỉ nên thêm các phần mềm Portable cho tiện lợi. Thực ra nếu thêm các phần mềm Setup cũng được, nhưng mình không khuyến khích sử dụng đâu nhé.
Bạn bấm vào Add File nếu thêm phần mềm riêng lẻ, hoặc bạn cũng có thể bấm vào Add Folder để thêm Folder vào. Trong trường hợp này mình sẽ thêm file Ghost32 => Chọn xong thì bạn bấm Open.
NOTE: Các công cụ mà bạn muốn tích hợp thì bạn có thể tham khảo trong bài viết này hoặc bài viết này nhé !
Đối với Folder thì mình sẽ thêm Adobe Audition CC 2019 => Sau khi bạn chọn xong thì bấm OK.
Tiếp theo bạn để ý đến phần Size. Nó quyết định 95% dung lượng của file mà bạn tạo ra (chưa tính đến các phần mềm có sẵn).
Chính vì vậy bạn nên thêm bớt từ 20 – 450 MB tùy theo phần mềm mà bạn chọn ở Bước 5. Nếu muốn bỏ đi file hoặc Folder nào đó đi thì bạn chỉ cần bấm vào file hoặc folder đó => rồi bấm Remove => sau khi đã thiết lập xong bạn chọn OK là xong.
+ Bước 7: Bây giờ bạn nhấn Next thì sẽ ra 3 tùy chọn đó là Burn to CD/DVD, USB Boot Device và Export ISO Device.
Burn to CD/DVD: Dành cho các máy tính có ổ đĩa quang có thể ghi được, bạn cho đĩa CD vào và phần mềm sẽ ghi ra đĩa CD đó.
USB Boot Device: Dành cho các bạn muốn ghi thẳng ra USB của mình luôn, nhưng mình không khuyến khích sử dụng do sau này nếu muốn tiếp tục sử dụng thì sẽ không có file lưu trữ.
Export ISO Device: Phần mềm sẽ đóng gói thành file ISO và bạn có thể sử dụng để ghi ra đĩa CD hoặc USB. Mình rất khuyến khích các bạn sử dụng tính năng này, do nó sẽ tạo ra một file ISO để các bạn sử dụng lâu dài.
Nếu bạn lưu file ISO thì bạn chọn Browse => và chọn vị trí để lưu file. Tên gốc của file là ampe.iso, bạn có thể thay đổi tên khác => xong thì bám Save. Ở đây mình sẽ chọn vị trí lưu ở Desktop
Còn đối với USB Boot Device hay Burn to CD/DVD thì sẽ có thanh menu để bạn chọn thiết bị bạn muốn ghi. Sau khi xong thì bạn nhấn Next và đi uống cà phê để phần mềm chạy thôi 😀
Lưu ý: Cần có mạng ổn định để tải Win PE. Nếu mạng kém hoặc không tải được thì bạn xem ở phần #5 nha. Sau khi hoàn thành thì bạn bấm Finish để kết thúc.
#3. Cách Burn File ISO Ra USB
OK, sau khi bạn đã đóng gói xong file ISO rồi thì mình sẽ hướng dẫn các bạn cách burn ra USB nha.
+ Bước 1: Đầu tiên các bạn vào trang chủ của phần mềm Rufus để tải phần mềm Rufus về trước, bạn hãy tải bản Portable (phiên bản di động) cho tiện chứ không phải bản Setup nha.
Sau đó thì bạn có thể xem phần hướng dẫn bên dưới, hoặc xem bài hướng dẫn của Admin tại đây !
+ Bước 2: Sau đó bạn chạy file *.exe vừa tải về thì phần mềm sẽ có giao diện sau đây:
Ở phần Device: Bạn chọn USB mà bạn muốn tạo.
Tiếp theo ở phần Boot Selection: Bạn chọn Select để chọn file ISO mà bạn đã lưu trước đó. File của mình là ampe.iso => xong bạn bấm Open.
Tiếp theo ở phần Partition Scheme: Bạn chọn GPT hoặc MBR, các máy đời mới hiện nay đều sử dụng chuẩn GPT, còn nếu bạn dùng máy đời cũ thì nên dùng MBR.
Tiếp theo đến phần Volume label thì bạn có thể đặt tên cho USB sau khi tạo USB BOOT xong. Sau khi hoàn thành thì bạn bấm Start.
Trong nhiều trường hợp thì sẽ có thông báo như thế này, bạn cứ bấm OK nha.
+ Bước 3: Một bảng thông báo rằng USB sẽ bị xóa sạch dữ liệu. Nếu có dữ liệu trong USB thì bạn nên Backup ra một nơi an toàn => sau đó bạn bấm OK để phần mềm tạo USB BOOT.
Nếu mọi việc đều OK thì ở trên thanh trạng thái Status sẽ có chữ Ready màu xanh. Còn nếu có màu đỏ hoặc vàng thì USB hoặc file của bạn đã có vấn đề hoặc lỗi. Vậy là USB BOOT của bạn đã hoàn thành rồi.
#4. Test WinPE và Bộ Cứu Hộ
Ok, bây giờ đến phần test bộ cứu hộ rồi. Bạn có thể test trực tiếp trên máy tính thật (vào Mini Windows đấy các bạn), hoặc sử dụng các phần mềm test USB BOOT để test nhé.
Đây chính là giao diện sau khi mình truy cập vào Win PE, ngoài các công cụ có sẵn thì bạn có thể thấy được 1 Folder tên là User Tool. Đó chính là Folder chứa các phần mềm mà bạn đã thêm vào đấy.
Do trước đó mình có thêm 1 File là Ghost32.exe và 1 Folder là Adobe Audition CC nên khi vào bộ cứu hộ thì sẽ có các phần mềm này.
#5. Dành cho các bạn mạng kém hoặc không tải được Win PE (làm thủ công)
Như mình đã nói ở trên, nếu các bạn không tải được Win PE qua phần mềm thì mình sẽ hướng dẫn các bạn cách tải từ bên ngoài.
+ Bước 1: Bạn vào link này / hoặc link Mediafire dự phòng tại đây !
Lưu ý là đối với link dự phòng thì sau khi bạn tải file về, bạn hãy thêm đuôi .zip vào sau tên file nhé. Như hình bên dưới các bạn nha.
+ Bước 2: Trên màn hình sẽ có 2 chữ Download. Màu xanh lam là link tải từ Google Drive, còn màu xanh lá là link từ OneDrive. Bạn tải link nào cũng được nhưng mình khuyên các bạn nên dùng link Google Drive cho nhanh.
Sau đó chọn phiên bản 32bit hoặc 64bit, tùy theo nhu cầu sử dụng của bạn nha.
+ Bước 3: Bạn sẽ được chuyển đến Google Drive. Ở đây có 2 file nhưng bạn chỉ cần để ý đến phần x86 hoặc x64 (x86 là bản Win PE 32bit, x64 là bản Win PE 64bit). Bạn cần bản nào thì chọn bản đó để tải xuống.
Ở đây mình sẽ chọn bản 64bit. Sau đó bạn bấm vào biểu tượng tải xuống bên trên góc phải.
+ Bước 4: Tiếp theo bạn chỉ việc bấm Download Anyway để tải xuống
+ Bước 5: Sau đó bạn giải nén file bằng cách bấm chuột phải vào file vừa tải và chọn Extract All, hoặc Extract here...
Ở phần File will be extracted to this folder bạn paste đường dẫn sau: C:\Program Files (x86)\AOMEI PE Builder 2.0
NOTE: Đường dẫn trên là đối với Windows 64bit, còn Windows 32bit thì đường dẫn sẽ như này nha các bạn:
C:\Program Files\AOMEI PE Builder 2.0
=> Sau đó sẽ có một bảng thông báo xuất hiện, bạn bấm Continue để tiếp tục.
+ Bước 6: Tiếp theo bạn vào đường dẫn:
Và copy phần sau vào trong file (Nhớ xóa toàn bộ nội dung ở trong file đó trước khi paste nha các bạn):
Trường hợp 1: Nếu bạn đang sử dụng Windows 64bit thì đường dẫn sẽ như thếnày:
Đường dẫn: C:\Program Files (x86)\AOMEI PE Builder 2.0\ DownloadPath.ini
[ISO_X86]
WorkPath=C:\Program Files (x86)\AOMEI PE Builder 2.0\WinPE Env x86\WinPE Env\x86
[ISO_X64]
WorkPath=C:\Program Files (x86)\AOMEI PE Builder 2.0\WinPE Env x86\WinPE Env\x64
=> Sau đó bạn lưu file ra ngoài màn hình Desktop (lưu tên giống với tên file mặc định nha các bạn).
Trường hợp 2: Nếu bạn đang sử dụng Windows 32bit thì đường dẫn sẽ như thếnày:
Đường dẫn: C:\Program Files\AOMEI PE Builder 2.0\ DownloadPath.ini
[ISO_X86]
WorkPath=C:\Program Files\AOMEI PE Builder 2.0\WinPE Env x86\WinPE Env\x86
[ISO_X64]
WorkPath=C:\Program Files\AOMEI PE Builder 2.0\WinPE Env x86\WinPE Env\x64
Lưu ý: Không được lưu trực tiếp lên file cũ do Notepad sẽ báo lỗi không có quyền để ghi đè nha các bạn. Bạn có thể nhấn vào File => chọn Save as... để lưu ra một vị trí khác…
+ Bước 7: Tiếp theo, bạn copy file này này vào lại đường dẫn: C:\Program Files (x86)\AOMEI PE Builder 2.0
Đường dẫn trên là đối với Windows 64bit, còn Windows 32bit thì đường dẫn sẽ như này nha các bạn:
C:\Program Files\AOMEI PE Builder 2.0
Bạn chọn Replace the file in the destination để thực hiện ghi đè lên file cũ. Phải có phần ghi đè này mới đúng nhé các bạn, không có tức là bạn đã lưu sai tên. Rồi bấm Continue là xong rồi đó các bạn.
#6. Lời Kết
Như vậy là mình đã hướng dẫn xong cho các bạn cách tự tạo WinPE, tự tạo bộ công cụ cứu hộ máy tính bằng phần mềm AOMEI PE Builder rồi đó.
Nói chung là cũng khá đơn giản đúng không nào. Chúc các bạn sẽ thành công, và mong mọi người hãy comment để ủng hộ bài viết đầu tiên của mình, có như vậy mình mới hoàn thiện hơn ở các bài viết sau.
Bài viết được sự cho phép của tác giả Trần Anh Tuấn
Ở phần trước mình đã hướng dẫn cho các bạn các kiến thức về các thẻ trong Form một cách khá chi tiết kèm các hình ảnh minh hoạ cũng như các lưu ý khi làm việc với Form. Tiếp tục ở phần 3 này mình sẽ nói về các thẻ thường được dùng trong cặp thẻ head nhé.
Đây là thẻ định nghĩa tiêu đề của trang web, nó sẽ hiển thị trên tab của trình duyệt ví dụ các bạn đang đọc bài này của mình thì trong HTML của nó sẽ là như đoạn code này, các bạn có thể rê chuột vào tab để Chrome hiển thị tooltip lên là thấy nha.
<title>HTML cơ bản toàn tập cho người mới phần 3 - Evondev Blog</title>
Đoạn code này chắc các bạn sẽ hay thấy ở các trang web, đoạn này giúp web chúng ta hiển thị responsive để các bạn có thể code responsive đấy.
<metacharset="UTF-8">
Đoạn này thì định nghĩa hệ thống kí tự mà sẽ hiển thị trên web thường hay dùng là UTF-8
<metaname="description"content="Đoạn mô tả ngắn cho trang web"/>
Thẻ này dùng để khai báo mô tả ngắn cho trang web khi các bạn tìm kiếm trên Google sẽ thấy ngoài tiêu đề thì có 1 đoạn chữ ở dưới tiêu đề mô tả cho trang web, nếu không có thẻ này thì Google sẽ lấy nội dung trong bài viết
<metaname="robots"content="noindex"/>
Thẻ này là để báo cho Google biết là trang web này có cần được index không(lập chỉ mục khi tìm kiếm), nếu không khai báo thẻ này thì Google sẽ hiểu là có index nha.
<htmllang="en">
Thuộc tính lang=”en” dùng để khai báo ngôn ngữ chính mà trang web đang sử dụng để từ đó công cụ tìm kiếm như Google hướng tới người dùng tốt hơn. Nếu website là tiếng Anh thì lang=”en” còn tiếng Việt thì lang=”vi”.
## Thẻ style
Thẻ này dùng để chứa code CSS bên trong trang web luôn, các bạn sẽ hay nghe từ internal CSS đấy
Hiểu đơn giản nhất là thẻ này dùng để chèn các link như CSS hay là Google fonts từ local hay là từ bên ngoài vào ví dụ dưới đây là sử dụng file styles.css ở dưới máy tính, và dùng font Roboto từ Google fonts
DÀNH CHO BẠN:
Mình có khoá học HTML CSS từ cơ bản tới nâng cao cho người mới, nếu bạn quan tâm thì bạn có thể học thử miễn phí bằng việc nhấn vào đây nha.
Trong đó href sẽ truyền vào đường dẫn, còn rel thì có các loại như là stylesheet tức là liên quan tới CSS, icon liên quan tới favicon, author dẫn đến trang tác giả của trang web, preconnect để khởi tạo kết nối mạng sớm hơn và tăng hiệu suất tải trang. Google font hay dùng để tăng tốc độ load font của họ.
Đúng như tên gọi của nó thẻ dùng để các bạn chèn nhạc vào trang web của chúng ta, trong đó src là truyền vào đường dẫn tập tin còn controls nghĩa là cho phép hiển thị các công cụ như nút play, nút pause…
Các thẻ inline như sup để làm chữ nằm lên trên khi chúng ta nói về độ C có biểu tượng độ ở phía trên, còn thẻ sub thì ngược lại nó sẽ nằm dưới như khi chúng ta nói về H2O thì số 2 sẽ nằm ở dưới.
Nước là H<sub>2</sub>O.
30<sup>o</sup>C
Nếu các bạn muốn chữ có đường line gạch ngang, giống thuộc tính trong CSS text-decoration: line-through thì dùng thẻ del
<p>This text is normal but <del>this text is strike</del></p>
Thẻ blockquote khi bạn muốn trình bày nội dung dạng quote, kiểu như câu nói trích dẫn
<blockquotecite="https://en.wikiquote.org/wiki/Marie_Curie">
Be less curious about people and more curious about ideas.
</blockquote>
Thẻ code giúp hiển thị dưới dạng code trong văn bản, các bạn có thể thấy rõ những chữ màu đỏ trong bài của mình mà các bạn đang đọc là mình dùng thẻ code để nó hiển thị như thế.
Type <code>npm install</code> in your terminal to install a project’s dependencies.
Thẻ figure và span thường được sử dụng chung với nhau khi muốn hiển thị hình ảnh kèm theo caption ở dưới
<figure>
<imgsrc="/images/html-reference-logo.png"alt="HTML Reference logo">
<span>The HTML Reference logo</span>
</figure>
Tạm kết
Series HTML cơ bản tạm thời kết thúc ở đây, sắp tới mình sẽ chia sẻ một series khác là CSS cơ bản toàn tập để các bạn được củng cố kiến thức tốt và vững chắc hơn. Hi vọng qua series HTML cơ bản sẽ có ích phần nào cho các bạn trong quá trình học tập và cải thiện kiến thức nhe..
Bài viết được sự cho phép của blogchiasekienthuc.com
Để tiếp nối series cài đặt Hackintosh, hôm nay mình sẽ tiếp tục hướng dẫn cho các bạn cách để cài đặt macOS lên Laptop và PC với OpenCore Bootloader một cách dễ dàng nhất nhé.
Lưu ý rằng trong bài viết này mình sẽ không đề cập đến Clover Bootloader, vậy nên nếu bạn muốn cài đặt macOS lên PC/ Laptop với bootloader này thì hãy xem lại những bài viết trước về chủ đề Hackintosh trên Blog chia sẻ kiến thức nhé !
#1. Cần chuẩn bị những gì?
Cách lựa chọn phần cứng (Laptop hoặc PC) để tương thích và chạy tốt Hackintosh. Bạn có thể xem lại bài viết hướng dẫn lựa chọn phần cứng của mình tại đây !
Bộ cài macOS và tạo USB cài đặt macOS với OpenCore Bootloader. Các bạn có thể xem lại bài hướng dẫn tạo USB cài macOS với bootloader này của mình tại đây (rất đầy đủ link nha các bạn) !
Xem cách thiết lập BIOS chuẩn nhất để cài đặt macOS. Xem hướng dẫn tại đây nha các bạn !
Bạn hãy chuẩn bị một ổ cứng mới (hoặc một ổ cứng chưa có dữ liệu). Vì trong quá trình cài đặt thì bạn sẽ phải xóa hết dữ liệu của mình (vì thế nên bạn hãy sao chép tất cả những dữ liệu quan trọng trước khi cài đặt nhé). Còn nếu bạn nào muốn chạy song song cả Windows lẫn macOS thì hãy chờ những bài viết tiếp theo của mình nhé.
#2. Các bước cài đặt Hackintosh trên Laptop/ PC với OpenCore Bootloader
+ Bước 1: Sau khi cắm USB Hackintosh vào máy, bạn tiến hành truy cập vào Boot Menu => chọn thiết bị USB có chứa bộ cài macOS (ở đây của mình là UEFI:USB Flash Disk 1100) => và nhấn Enter để thực hiện quá trình boot vào bộ cài macOS.
Dưới đây là một list các phím Boot menu mà các nhà sản xuất mainboard sử dụng, bạn tham khảo qua nhé.
+ Bước 2: Sau khi vào được bộ cài macOS thì ta sẽ được như hình bên dưới.
Bạn dùng phím mũi tên trỏ vào dòng chữ có tên: Install macOS + [tên phiên bản] (external)
Ở đây mình sử dụng bộ cài macOS Mojave để cài đặt Hackintosh nên mình sẽ chọn dòng chữ: Install macOS Mojave (external). Bạn nhấn phím Enter trên bàn phím để tiến hành boot vào bộ cài Hackintosh.
+ Bước 3: Sau khi boot vào bộ cài thành công ta sẽ vào được màn hình như hình bên dưới.
+ Bước 4: Ở bước này, bạn CHƯA bấm Continue ngay, mà thay vào đó, bạn phải định dạng lại ổ cứng để có thể cài đặt được macOS.
Để làm được việc này thì bạn hãy làm như sau: Bạn di chuyển chuột vào mục Utilities => và chọn Disk Utility.
Sau khi vào Disk Utility ta sẽ được như hình sau:
+ Bước 5: Bạn nhấn View => chọn Show all devices để hiển thị tất cả các ổ cứng/ USB/ thiết bị lưu trữ ngoài có trong máy tính.
+ Bước 6: Bạn hãy chọn ổ cứng cần cài đặt macOS (ở đây của mình là SSD 128GB Media) => rồi nhấn Erase => sau đó nhập các thông tin như sau:
Phần Name: Bạn có thể để bất cứ tên gì bạn thích (ở đây mình sẽ đặt là New Volume)
Phần Format: Bạn để là Mac OS Extended (Journaled). Nếu bạn muốn cài đặt macOS Catalina trở lên thì bắt buộc phải để là APFS nha các bạn.
Phần Scheme: Bạn để là GUID Partition Map (GPT).
=> Sau khi thiết lập xong các thông tin cần thiết => bạn nhấn Erase để tiến hành định dạng lại ổ cứng.
+ Bước 7: Sau khi định dạng xong ổ cứng, ta tiến hành thoát khỏi Disk Utility. Quay trở lại với màn hình cài đặt macOS => bạn nhấn Continue để tiếp tục.
+ Bước 8: Bạn nhấn Continue => chọn Agree để chấp nhận điều khoản sử dụng của macOS.
+ Bước 9: Bạn chọn ổ cứng mà mình cần cài macOS lên (ở đây của mình là New Volume) => rồi nhấn Continue để tiến hành cài đặt.
+ Bước 10: Quá trình cài đặt macOS sẽ mất khoảng từ 15-20 phút tuỳ thuộc vào tốc độ của máy tính của bạn. Trong thời gian này, bạn không nên động chạm tới máy tính để tránh rủi ro.
+ Bước 11: Sau khi cài đặt macOS xong, máy tính của bạn sẽ tự khởi động lại. Lúc này bạn chớ vội rút USB ra, vì lúc này máy tính sẽ chưa có bootloader để boot vào macOS cho bạn đâu.
Bạn cứ giữ nguyên USB ở đó, tiếp tục vào lại boot menu rồi chọn lại ổ cứng để cài macOS lúc nãy (của mình là New Volume) => rồi nhấn Enter để tiến hành boot vào macOS nhé.
+ Bước 12: Sau khi boot được vào bộ cài macOS thì ta sẽ được như hình dưới. Bạn tiến hành chọn quốc gia/vùng (ở đây chúng ta sẽ chọn là Việt Nam) => và nhấn Continue để tiếp tục.
+ Bước 13: Đây là bước chọn ngôn ngữ cho bàn phím. Bạn nên để là ABC (vì bàn phím tiếng Việt không được thực sự tốt lắm) => sau đó nhấn Continue
+ Bước 14: Bước này bạn chọn là My computer does not connect to the Internet => và nhấn Continue để tiếp tục.
Tiếp tục, bạn nhấn Continue để chuyển sang bước tiếp theo.
+ Bước 15: Ở bước này macOS sẽ hỏi chúng ta có muốn chuyển dữ liệu sang macOS hay không. Ở đây thì chúng ta sẽ chọn Don’t transfer any information now => rồi nhấn Continue để tiếp tục.
Bạn nhấn Agree => Agree để đồng ý với các điều khoản sử dụng của macOS.
+ Bước 16: Ở bước này bạn sẽ tạo cho mình một tài khoản (User bên Windows) để có thể sử dụng macOS. Bạn sẽ nhập các thông tin sau:
Tên tài khoản
Mật khẩu (bắt buộc)
=> Sau khi điền xong các thông tin này bạn nhấn Continue để tiếp tục.
+ Bước 17: Đây là bước tinh chỉnh một số cài đặt cho macOS. Nhưng theo mình là bạn cứ để nguyên, không đụng chạm gì đến chúng cả.
Đây là bước chọn theme cho macOS. Ở đây mình sẽ chọn giao diện tối (Dark Mode) để nhìn cho ngầu ha ᵔᴥᵔ
Máy sẽ mất một khoảng thời gian ngắn để có thể vào được macOS.
Như vậy là chúng ta đã vào được macOS rồi. Cũng rất đơn giản vậy thôi các bạn ạ ^^!
III. Lời kết
Như vậy là mình đã vừa hướng dẫn xong các bạn cách để cài đặt macOS lên Laptop hoặc PC với OpenCore Bootloader rồi ha.
Nếu bạn thấy bài viết này hay thì đừng quên chia sẻ bài viết này cho mọi người cùng biết nhé. Và cũng đừng quên đón đọc những bài viết tiếp theo trong series Hackintosh của mình nhé.
Chắc hẳn bạn nào mới bắt đầu tìm hiểu Google Adsense đều thắc mắc về Page RPM và Impression RPM. Vậy chính xác thì hai thông số hiển thị trong report của Adsense này là gì?.
Với người mới bắt đầu, hiểu các từ viết tắt của Google Adsense chưa bao giờ dễ dàng
Hãy cùng tìm hiểu qua bài viết dưới đây của Kieblog, giúp hiểu rõ hơn cách tính của hai thông số này.
BẮT ĐẦU NHÉ!
2. Page RPM là gì?
Page RPM trong báo cáo của google Adsense
Theo lời giải thích hơi khó hiểu của anh Google thì:
Page RPM = (Estimated earnings / Number of page views) * 1000
Page RPM = (Con số dự tính nhận được / Số lần trang được ghé thăm) * 1000
Định nghĩa thì khó hiểu lắm, hãy cùng xem xét ví dụ dưới đây:
Ví dụ: Xem ở phần thống kê (trên web Adsense hoặc ứng dụng trên Mobile). Nếu trang chúng ta nhận được $0.15 cho 25 lượt ghé thăm thì con số Page RPM là con số ước tính khi 1000 lần ghé thăm trang.
Cụ thể, 1000 lần trang được ghé thăm ta nhận được:
($0.15 / 25) * 1000 = 6.00 Trump.
Ra thế, nhưng nếu con số này chỉ là ước tính. Vậy cụ thể sao biết được cuối tháng nhận được bao nhiêu?
Câu trả lời là con số cuối tháng nhận được còn tùy vào lượt xem biến động trong từng ngày, site của bạn có thể bị bất chợt down vào một ngày đẹp trời.
Page RPM thường thay đổi theo mùa, khoảng dịp lễ hay black friday có thể tăng cao hơn bình thường. Cũng có thể thay đồi tùy theo nội dung quảng cáo, nội dung quảng cáo tới từ hãng nào, …
Một điều lưu ý nữa là Google Adsense chỉ hiển thị thu nhập ước tính cho tới cuối tháng, hết tháng con số này lại sẽ thay đổi.
RPM is a commonly used number in advertising programs, and you may find it helpful for comparing revenue across different channels.
RPM là con số thường được sử dụng để so sánh giữa các trang khác nhau (có thể hữu ích)
3. Impression RPM là gì?
Page impresison is the exact number of times a specific Web site has been accessed or viewed by a user.
Page impression là con số chính xác số lần website của bạn được xem hay ghé thămbởi người dùng
Con số này thường được sử dụng để báo cáo cho khách hàng của Google Adsense rằng quảng cáo họ đã trả tiền để chạy được hiển thị bao nhiêu lần.
Impression RPM khác so với Page RPM do Impression ở đây được hiểu là ấn tượng của người dùng với website (số lần quảng cáo được tải lên trang, số lần quảng cáo họ nhìn thấy).
Each time a person loads a page then their ad appears and it is counted as one impression for the individual.
Mỗi lần người dùng tải trang và quảng cáo xuất hiện, nó được đếm như là một ấn tượng cá nhân người dùng với website
Page Impression cũng thường được biết tới như là CPM -> “Cost Per 1000 Impressions “. Con số này cũng được dự tính theo 1000 lần, từ đầu tháng cho tới cuối tháng
4. Sự khác biệt giữa Page RPM và Impression RPM
Điểm khác biệt rõ ràng nhất chính là Page RPM tính trên số lượt ghé thăm, còn Impression RPM tính theo số lượng nhìn thấy (tương đối) của quảng cáo trên site người dùng.
Nếu số lượng quảng cáo trên site lớn, thường giá trị của Impression RPM sẽ cao hơn Page.
Các site bình thường trung bình sẽ nhận được RPM giao động trong khoảng $2 -$5. Khá hơn sẽ giao động từ $5 cho tới $10. Các trang có lượt truy cập cao hơn thường ở mức lớn hơn $50
Phù, cuối cùng cũng hết. Tập tành làm quen với Google Adsense thật vất vả quá đi!
Tí quên, Còn Your earnings?. Chốt, không cần nói gì nhiều. CÀNG LỚN CÀNG TỐT.
CHỐT!
Đm, viết nhiều nhưng có khi các ông lại chả nhớ bằng 1 tấm gif!