Home Blog Page 75

Semaphore trong Java

Semaphore trong Java
Semaphore trong Java

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

Semaphore là gì?

Semaphore là một cơ chế giúp quản lý các nguồn chia sẻ và đảm bảo access không bị tắc nghẽn.

Có hai loại semaphore: binary semaphore và counting semaphore.

  • Binary semaphore (Mutex): được dùng làm lock vì nó chỉ có 2 giá trị là 0 và 1. Hai giá trị này đại diện cho trạng thái lock hay unlock.
  • Counting semaphore: thực hiện đếm resource để cho biết mức độ sẵn sàng của resource.

Xem thêm tuyển dụng Java lương hấp dẫn trên TopDev

Cơ chế hoạt động

Một Semaphore lưu trữ một danh sách các permit (hay ticket), mỗi khi gọi acquire() sẽ lấy 1 ticket từ Semaphore, mỗi khi gọi release() sẽ trả ticket về Semaphore. Nếu ticket không có sẵn, acquire() sẽ bị lock cho đến khi có ticket. Để kiểm tra số lượng ticket còn lại, sử dụng phương thức availablePermits().

Ví dụ chúng ta gọi các phương thức tuần tự như sau:

// Tạo một Semaphore có 5 ticket
Semaphore semaphore = new Semaphore(5);

// Yêu cầu lấy 1 ticket để sử dụng
semaphore.acquire(); // 5-1

// Đếm về số lượng ticket có sẵn
int numberOfAvailableTickets = semaphore.availablePermits(); // 4

// Trả 1 ticket về Semaphore
semaphore.release(); // 4+1

// Đếm về số lượng ticket có sẵn
semaphore.availablePermits(); // 5

Ví dụ sử dụng Semaphore

Giả sử một ngân hàng có 4 cây ATM, mỗi cây chỉ có thể phục vụ được một khách hàng tại một thời điểm. Chương trình bên dưới cho thấy Semaphore có thể đảm bảo chỉ tối đa 4 người có thể truy cập tại một thời điểm.

WorkerThread.java

package com.gpcoder.semaphore;

import java.util.concurrent.Semaphore;

public class WorkerThread extends Thread {

private final Semaphore semaphore;
private String name;

public WorkerThread(Semaphore semaphore, String name) {
this.semaphore = semaphore;
this.name = name;
}

public void run() {
try {
System.out.println(name + ": acquiring lock...");
System.out.println(name + ": available Semaphore permits now: " + semaphore.availablePermits());
semaphore.acquire();
System.out.println(name + ": got the permit!");

try {
System.out.println(name + ": is performing operation, available Semaphore permits : "
+ semaphore.availablePermits());
Thread.sleep(100); // simulate time to work
} finally {
// calling release() after a successful acquire()
System.out.println(name + ": releasing lock...");
semaphore.release();
System.out.println(name + ": available Semaphore permits now: " + semaphore.availablePermits());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

SemaphoreExample.java

package com.gpcoder.semaphore;

import java.util.concurrent.Semaphore;

public class SemaphoreExample {

private static Semaphore semaphore = new Semaphore(4);

public static void main(String[] args) {
System.out.println("Total available Semaphore permits: " + semaphore.availablePermits());
for (int i = 1; i <= 6; i++) {
WorkerThread atmWorker = new WorkerThread(semaphore, "AMT " + i);
atmWorker.start();
}
}
}

Chạy chương trình trên, ta có kết quả sau:

Total available Semaphore permits: 4
AMT 1: acquiring lock...
AMT 1: available Semaphore permits now: 4
AMT 1: got the permit!
AMT 1: is performing operation, available Semaphore permits : 3
AMT 2: acquiring lock...
AMT 2: available Semaphore permits now: 3
AMT 2: got the permit!
AMT 2: is performing operation, available Semaphore permits : 2
AMT 3: acquiring lock...
AMT 3: available Semaphore permits now: 2
AMT 3: got the permit!
AMT 3: is performing operation, available Semaphore permits : 1
AMT 6: acquiring lock...
AMT 4: acquiring lock...
AMT 4: available Semaphore permits now: 1
AMT 4: got the permit!
AMT 4: is performing operation, available Semaphore permits : 0
AMT 5: acquiring lock...
AMT 5: available Semaphore permits now: 0
AMT 6: available Semaphore permits now: 1
AMT 3: releasing lock...
AMT 4: releasing lock...
AMT 1: releasing lock...
AMT 4: available Semaphore permits now: 2
AMT 2: releasing lock...
AMT 2: available Semaphore permits now: 2
AMT 6: got the permit!
AMT 6: is performing operation, available Semaphore permits : 2
AMT 5: got the permit!
AMT 5: is performing operation, available Semaphore permits : 2
AMT 1: available Semaphore permits now: 3
AMT 3: available Semaphore permits now: 2
AMT 6: releasing lock...
AMT 6: available Semaphore permits now: 3
AMT 5: releasing lock...
AMT 5: available Semaphore permits now: 4

Ví dụ sử dụng Mutex

Mutex là một Semaphore với bộ đếm là 1. Tình huống có thể sử dụng là lock tài khoản khi rút tiền. Tại một thời điểm chỉ 1 thao tác rút tiền được chấp nhận.

Chúng ta sẽ sử dụng lại worker ở trên. Giả sử có 6 user cùng login vào một tài khoản ở các cây ATM khác nhau để thực hiện rút tiền, nếu chúng ta không sử dụng cơ chế synchronized thì cả 6 người đều có thể rút tiền cùng lúc và có thể rút nhiều hơn số tiền hiện có trong tài khoản.

Đoạn code bên dưới sử dụng Mutex giúp chúng ta kiểm soát được vấn đề này một cách dễ dàng.

package com.gpcoder.semaphore;

import java.util.concurrent.Semaphore;

public class MutexExample {

private static Semaphore semaphore = new Semaphore(1);

public static void main(String[] args) {
System.out.println("Total available Semaphore permits: " + semaphore.availablePermits());
for (int i = 1; i <= 6; i++) {
WorkerThread atmWorker = new WorkerThread(semaphore, "AMT " + i);
atmWorker.start();
}
}
}

Chạy chương trình trên, chúng ta có kết quả sau:

Total available Semaphore permits: 1
AMT 1: acquiring lock...
AMT 2: acquiring lock...
AMT 1: available Semaphore permits now: 1
AMT 2: available Semaphore permits now: 1
AMT 1: got the permit!
AMT 1: is performing operation, available Semaphore permits : 0
AMT 3: acquiring lock...
AMT 3: available Semaphore permits now: 0
AMT 4: acquiring lock...
AMT 4: available Semaphore permits now: 0
AMT 5: acquiring lock...
AMT 6: acquiring lock...
AMT 6: available Semaphore permits now: 0
AMT 5: available Semaphore permits now: 0
AMT 1: releasing lock...
AMT 1: available Semaphore permits now: 1
AMT 2: got the permit!
AMT 2: is performing operation, available Semaphore permits : 0
AMT 2: releasing lock...
AMT 2: available Semaphore permits now: 0
AMT 3: got the permit!
AMT 3: is performing operation, available Semaphore permits : 0
AMT 3: releasing lock...
AMT 3: available Semaphore permits now: 1
AMT 4: got the permit!
AMT 4: is performing operation, available Semaphore permits : 0
AMT 4: releasing lock...
AMT 6: got the permit!
AMT 6: is performing operation, available Semaphore permits : 0
AMT 4: available Semaphore permits now: 0
AMT 6: releasing lock...
AMT 5: got the permit!
AMT 5: is performing operation, available Semaphore permits : 0
AMT 6: available Semaphore permits now: 0
AMT 5: releasing lock...
AMT 5: available Semaphore permits now: 1

Bài viết đến đây là hết. Hy vọng sau bài viết này các bạn hiểu rõ hơn về Semaphore, cũng như biết cách sử dụng nó trong một số tình huống thích hợp.

Tài liệu tham khảo:

Cơ bản về Apache Kafka

apache kafka
Cơ bản về Apache Kafka

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

Apache Kafka là một open-source distributed event streaming platform giúp chúng ta có thể tạo và process data stream real-time. Các bạn có thể hình dung đến những hệ thống mà mỗi giây, data đều được sinh ra và cần được xử lý ví dụ như hệ thống quản lý giao thông, quản lý thời tiết, data sẽ được tạo ra từ nhiều nơi khác nhau và cần được collect để xử lý. Sử dụng Apache Kafka với high scalable có thể giúp chúng ta giải quyết những bài toán như vậy.

  Cài đặt Apache Kafka trên macOS
  Sử dụng Apache POI để đọc, ghi dữ liệu từ file Excel trong Selenium

Xem thêm các việc làm Flutter lương cao trên TopDev

Apache Kafka là một hệ thống enterprise messaging, hoạt động với cơ chế Pub-Sub messaging architecture. Chúng ta có Message Publisher, Message Consumer và Message Broker. Message Publisher là nơi produce các message và emit các message vào Message Broker, Message Consumer là nơi nhận, nó sẽ subscribe Message Broker và nhận các message của Message Publisher thông qua Message Broker.

Apache Kafka Server sẽ đóng vai trò là một Message Broker.

Apache Kafka cung cấp cho chúng ta các library để làm việc với Publisher và Subscriber. Ngoài ra, Apache Kafka còn có thêm các component khác như Kafka Connect, Kafka Streams.

Apache Kafka được xây dựng từ đầu bởi LinkedIn, một mạng xã hội về việc làm, và được open-source từ năm 2011.

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

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

Xem thêm thông tin việc làm CNTT hấp dẫn trên TopDev

Viết Thư Từ Chối Phỏng Vấn Bằng Tiếng Anh Như Thế Nào Cho Chuyên Nghiệp?

viết thư từ chối phỏng vấn bằng tiếng anh
Viết Thư Từ Chối Phỏng Vấn Bằng Tiếng Anh Như Thế Nào Cho Chuyên Nghiệp?

Sau khi đã nhận được kết quả ứng tuyển thành công vào một công ty, có thể bằng nhiều lý do khác nhau mà bạn cảm thấy mình không thể tiếp nhận phỏng vấn vị trí đó như dự định, làm sao để có thể từ chối một cách lịch sự nhất? Đặc biệt, trong một số trường hợp, bạn còn phải viết thư bằng tiếng anh, vậy đâu là cách viết thư từ chối phỏng vấn bằng tiếng Anh chuyên nghiệp và khéo léo? Bài viết dưới đây sẽ giúp bạn có thêm thông tin về vấn đề này.

viết thư từ chối phỏng vấn bằng tiếng Anh
Cách viết thư từ chối phỏng vấn bằng tiếng Anh

Cấu trúc cần có khi viết thư từ chối phỏng vấn bằng tiếng Anh

Cũng như cách bạn nộp đơn ứng tuyển vào công ty, khi viết đơn từ chối bất cứ vị trí nào cũng hãy thể hiện một nội dung chuyên nghiệp với bố cục rõ ràng. Nó sẽ giúp bạn xây dựng được ấn tượng tốt trong mắt nhà tuyển dụng và biết đâu bạn có thể quay lại công ty ở một vị trí khác trong tương lai. Trên hết, nó giúp tiết kiệm thời gian và công sức chờ đợi của cả hai phía. Theo đó, bố cục email sẽ gồm có:

Xem thêm các việc làm PHP lương cao trên TopDev

  • Tiêu đề email: Có 2 cách để bạn viết thư từ chối nhà tuyển dụng. Nếu bạn trả lời trực tiếp trên email mời phỏng vấn nhà tuyển dụng gửi đến thì không cần có tiêu đề. Trong trường hợp bạn viết một email mới, cần viết tiêu đề một cách ngắn gọn, rõ ràng, khái quát được mục đích bạn viết bức thư này. Chẳng hạn “Thank you for the interview letter”, “Reply for the interview invitation”,…
  • Lời chào và cảm ơn: Đây là yếu tố cực kỳ cần thiết và cũng là cách mở đầu bức thư của bạn. Gửi lời chào trực tiếp đến người gửi thư phỏng vấn cho bạn hoặc chức danh vị trí của họ – Dear HR Manager,… để thể hiện sự tôn trọng với người đọc.

Hãy gửi lời cảm ơn đến họ vì đã lựa chọn bạn cho vị trí này. Nhân sự và các phòng ban liên quan đã dành khá nhiều thời gian để chọn lọc hồ sơ và tin tưởng chọn bạn cho vị trí này, do đó trong trường hợp không thể tiếp nhận phỏng vấn, việc gửi lời cảm ơn đến họ sẽ khiến nhà tuyển dụng đỡ cảm thấy hụt hẫng và có ấn tượng tốt về bạn hơn.

  • Thank you very much for the interview invitation at A Company. I appreciate being considered as a candidate with the B position.
  • I’m grateful for the opportunity to interview at A Company with the B position.

Xem thêm Phỏng Vấn Data Analyst: TOP 5 Câu Hỏi Bạn Không Nên Bỏ Qua!

  • Trình bày lý do từ chối phỏng vấn: Đây là nội dung chính của bức thư. Có thể có nhiều lý do khác nhau khiến bạn quyết định từ chối lời mời, nhưng không ai biết được trong tương lai bạn có thể gặp lại công ty ở thời điểm nào đó, nên cách tốt nhất là hãy giữ lại ấn tượng tốt đẹp trong nhau. Hãy cố gắng lựa chọn một lý do từ chối nhẹ nhàng và đủ sức thuyết phục.
  • Gửi lời cảm ơn và lời chúc đến nhà tuyển dụng để kết thúc bức thư.
  • Thank you again and hope you will find a suitable candidate as soon as possible.
  • I wish you all the best in your search for a candidate.
thư từ chối
Một số điều cần có trong thư từ chối

Một số lưu ý khi viết thư từ chối phỏng vấn bằng tiếng Anh

Bạn nên cố gắng xem xét thật kỹ và sớm nhất lời mời phỏng vấn, và nếu không muốn tiếp nhận phỏng vấn hãy phản hồi bằng thư từ chối sớm nhất có thể, tốt nhất là trong khoảng 24 giờ. Việc này sẽ giúp nhà tuyển dụng tìm được ứng viên khác và tiến hành phỏng vấn, tránh lãng phí thời gian chờ đợi của cả 2 bên.

Về lý do từ chối, như đã nói ở trên, đừng đưa ra một lý do quá khách quan và không có gì đảm bảo tính xác thực của nó, như tin đồn về môi trường công ty không tốt hay phúc lợi công ty còn thấp,… Những lý do này sẽ rất dễ khiến bạn rơi vào “blacklist” của công ty và nó thật sự không tốt cho tương lai của bạn sau này.

  Văn hóa giao tiếp qua Email - Thế nào là chuyên nghiệp?
  Bí quyết phát triển hoạt động nhân sự qua email (Email Marketing) hiệu quả - Bạn đã biết?

Văn phong sử dụng trong bức thư cần lịch sự và chuyên nghiệp nhất có thể. Điều này không chỉ giúp bạn xây dựng ấn tượng tốt trong mắt nhà tuyển dụng mà biết đâu, trong tương lai khi bạn quay lại công ty ở một vai trò khác và điều này sẽ không gây cản trở đến công việc của bạn.

Ngoài ra, nếu được, bạn hoàn toàn có thể giới thiệu một ứng viên khác cho công ty. Hãy đưa ra vài thông tin cơ bản về ứng viên này cũng như để lại cách thức liên lạc trong bức thư. Nhà tuyển dụng sẽ quyết định việc lựa chọn và tiến hành các bước tiếp theo.

Một số mẫu email tham khảo

Một số mẫu viết thư từ chối phỏng vấn bằng tiếng Anh bạn có thể tham khảo:

email mẫu

Mẫu 1:

Dear Ms. Han – HR Manager of A Company,

I’m very appreciative of the interview opportunity for Content Marketing and learning more about your organization.

However, I regret that I have to deny this opportunity at this time. Because I was offered and accepted a job at another company, so I’m respectfully declining this offer.

Thank you again and hope you will find a suitable candidate as soon as possible.

Best regards,

Mẫu 2:

Dear Ms. Jennie,

Thank you so much for reaching out and send me the interview invitation,

However, I would like to withdraw my application for this position because of personal reasons.

I will keep this role in mind and refer anyone who may be a good match.

Thank you again for considering me for this position. Please do not hesitate to get in touch if you have any questions.

Thanks and Best regards,

Tùy theo mỗi lý do cá nhân khác nhau, mọi người sẽ có cách riêng để viết thư từ chối phỏng vấn. Tuy nhiên, về cơ bản cần đảm bảo đủ các yếu tố như trên để không làm khó chịu người nhận. Đón đọc thêm nhiều nội dung hấp dẫn khác cùng Topdev.vn/blog nhé!

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

Xem thêm tuyển dụng việc làm IT hấp dẫn trên TopDev

Thu thập và làm sạch dữ liệu khi sử dụng Linear Regression

linear regression
Thu thập và làm sạch dữ liệu khi sử dụng Linear Regression

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

Video trong bài viết

Trước khi bắt đầu bài đầu tiên thu thập và làm sạch dữ liệu chuẩn bị cho thuật toán Linear Regression, chúng ta sẽ tìm hiểu qua quy trình làm việc khoa học dữ liệu.

  Ấn tượng trước những bước đột phá công nghệ mới của Line!
  Cơ hội nào cho "dân buôn" online?

Xem thêm tuyển 3D Artist hấp dẫn trên TopDev

Quy trình làm việc khoa học dữ liệu

Bất kể bạn áp dụng thuật toán Machine Learning nào cho vấn đề bạn gặp phải, chúng ta sẽ đều phải trải qua một quy trình làm việc (data science workflow).

Quy trình làm việc khoa học dữ liệu

Trong tất cả các dự án của Khóa học Machine Learning từ A đến Z, chúng ta sẽ áp dụng quy trình này, tuy rằng không phải lúc nào cũng đủ tất cả các bước trong đấy. Trong quy trình này có 3 giai đoạn và rất nhiều các bước.

  • Giai đoạn 1 – Chuẩn bị dữ liệu: Dữ liệu được thu thập và làm sạch. Phần lớn dữ liệu gốc đều ở dạng hỗn loạn, có thể thiếu thông tin hoặc thông tin sai lệch, do vậy cần được xử lý trước khi đưa vào các mô hình thuật toán.
  • Giai đoạn 2 – Thử nghiệm: tại giai đoạn này các giả định được đặt ra, dữ liệu được trực quan hóa thông qua các biểu đồ và lựa chọn các mô hình.
  • Giai đoạn 3 – Triển khai: Các báo cáo, đánh giá quá trình áp dụng và kết quả được triển khai thông qua các ứng dụng thực tế hoặc đơn thuần là các báo cáo.

Khi thực hiện các giai đoạn, có thể quay lại giai đoạn trước đó để chuẩn bị dữ liệu tốt hơn. Quy trình này được áp dụng cho các công việc thực tế, nhưng với các bài hướng dẫn trong Machine Learning AZ chúng ta sẽ rút gọn các bước lại để tập trung vào việc học tập hơn.

Các bước thực hiện trong Machine Learning A đến Z

Các bước được cô đọng lại như sau:

  • Bước 1: Hình thành câu hỏi
  • Bước 2: Thu thập dữ liệu
  • Bước 3: Làm sạch dữ liệu
  • Bước 4: Khám phá và trực quan hóa dữ liệu với biểu đồ
  • Bước 5: Áp dụng thuật toán hay mô hình huấn luyện
  • Bước 6: Đánh giá kết quả

Bài toán dự đoán doanh thu phim

Trước khi bắt đầu xử lý một vấn đề, chúng ta nên bỏ chút thời gian đặt ra những câu hỏi như Cái gì, Tại sao, Làm như thế nào, Ở đâu… những câu hỏi này giúp chúng ta trìu tượng hóa vấn đề. Cũng chính nhờ những câu hỏi này chúng ta sẽ xác định được loại dữ liệu nào là cần thiết bởi trong thực tế lượng dữ liệu là rất khổng lồ.

Trở lại với vấn đề mà chúng ta đang gặp phải trong bài toán dự đoán doanh thu phim. Những nhà đầu tư phim, họ rót vốn cho các bộ phim và quan tâm đến lợi nhuận các bộ phim. Áp dụng quy trình data science ở trên, bước đầu tiên chúng ta cần hình thành các câu hỏi. Câu hỏi đầu tiên:

“Chúng ta cần bao nhiêu tiền để sản xuất bộ phim này?”

Câu hỏi này thật sự chưa ổn, nó hơi mơ hồ. Với những người đầu tư phim, họ quan tâm đến lợi nhuận hơn là chi phí. Do đó, câu hỏi nên là:

“Chúng ta thu được bao nhiêu khi sản xuất bộ phim này?”

Câu hỏi này tốt hơn nhiều, nó đã nhắm đến lợi nhuận là tiêu chí các nhà đầu tư đo lường và kiểm tra cơ hội đầu tư. Chúng ta cần suy nghĩ thêm chút, vậy doanh thu sẽ phụ thuộc vào những yếu tố nào? Chúng ta có thể kể ra một loạt các yếu tố sau:

  • Diễn viên có phải các ngôi sao hạng A?
  • Kịch bản hay và nổi tiếng không?
  • Phim có sử dụng các công nghệ tạo hiệu ứng hiện đại nhất?
  • Chi phí quảng bá phim
  • Đạo diễn phim có số má trên thị trường không?

Nhưng chung quy, tất cả các yếu tố này đều quay về chi phí sản xuất hay ngân sách dành cho một bộ phim. Như vậy, chúng ta cần tìm hiểu chi phí sản xuất tất cả các bộ phim đã được phát hành. Các phim như Avatar, Titanic, The Advengers… đã tiêu tốn rất nhiều chi phí, nhưng họ cũng thu lại lợi nhuận khổng lồ. Những thông tin này thì có liên quan gì đến bộ phim mà chúng ta sắp đầu tư? Nếu chúng ta biết được các bộ phim đã tiêu tốn như thế nào để đạt được doanh thu này, chúng ta có thể dự đoán được doanh thu bộ phim của chúng ta thông qua thuật toán Linear Regression – Hồi quy tuyến tính.

Biểu đồ doanh thu ngân sách

Trong bài toán của chúng ta có hai yếu tố:

  • Doanh thu của bộ phim chính là mục tiêu cần tìm ra (target).
  • Ngân sách bộ phim là tham số độc lập (feature).

Như vậy chúng ta đã hoàn thành bước đầu tiên trong dự án, chúng ta đã xác định được các yếu tố (loại dữ liệu cần thiết). Chúng ta sẽ chuyển sang bước tiếp theo thu thập và làm sạch dữ liệu.

Thu thập dữ liệu phim

Trong phần trước, dựa vào các câu hỏi và phân tích chúng ta đã biết cần phải có những dữ liệu về doanh thu và ngân sách các bộ phim. Vậy cần tìm dữ liệu này ở đâu, rất may trong trang web www.the-numbers.com có tất cả dữ liệu này. Tất cả dữ liệu doanh thu và ngân sách trong website này ở dạng bảng và tôi đã đưa chúng vào file csv.

Bạn có thể tải về dữ liệu trong file cost_revenue_dirty.csv để tiếp tục bước tiếp theo làm sạch dữ liệu.

Tiền xử lý dữ liệu

Mở file cost_revenue_dirty.csv và xem nội dung file, có rất nhiều các thông tin bất thường, có những bộ phim có doanh thu bằng 0, ví dụ như phim Singularity chẳng hạn. Nguyên nhân là gì, nếu bạn xem cột Ngày phát hành (Cột B) thì hóa ra phim này chưa ra mắt, đến tận 31/12/2020 mới phát hành.

Dữ liệu doanh thu và ngân sách phim từ the-numbers.com

Thực hiện lọc các phim có doanh thu bằng 0, chúng ta thấy có rất nhiều các bộ phim như vậy. Nhưng không phải tất cả trong số chúng là chưa phát hành mà có những bộ phim đã hoàn thành nhưng không được công chiếu do đang trong quá trình kiện tụng và còn nhiều lý do khác nữa… Như vậy, chúng ta cần loại bỏ tất cả những bộ phim này vì nó là những trường hợp ngoại lệ, nó làm cho dữ liệu của chúng ta thiếu sót.

Chúng ta cũng chỉ cần hai loại thông tin là doanh thu và ngân sách phim do vậy chỉ giữ lại các cột Production Budget ()vàWorldwideGross()vàWorldwideGross(). Các cột thứ hạng phim, ngày phát hành, doanh thu trong nước sẽ được loại bỏ.

Sau khi loại bỏ các thông tin bị mất, sai sót, không đúng định dạng hoặc không cần thiết, chúng ta có được file dữ liệu sau khi làm sạch cost_revenue_clean.csv.

Như vậy, chúng ta đã hoàn thành 2 trong 6 bước đầu tiên là tìm ra các thông tin cần thiết và thu thập, làm sạch dữ liệu. Trong bài tiếp theo chúng ta sẽ tiếp tục bước tiếp theo là khai phá dữ liệu và trực quan hóa dữ liệu thông qua vẽ biểu đồ từ dữ liệu có được.

Tài nguyên liên quan bài viết

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

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

Xem thêm tuyển dụng việc làm CNTT hấp dẫn trên TopDev

Cài đặt Apache Kafka sử dụng Docker Compose

cài đặt apache kafka
Cài đặt Apache Kafka sử dụng Docker Compose

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

Trong bài viết trước, mình đã hướng dẫn các bạn cách cài đặt Apache Kafka trên macOS, việc cài đặt sử dụng Docker Compose sẽ giúp chúng ta nhanh chóng start lên một Apache Kafka server mà không tốn nhiều effort, chỉ cần cài đặt Docker và tập tin docker-compose.yml. Cụ thể như thế nào? Trong bài viết này, mình sẽ hướng dẫn các bạn cách cài đặt Apache Kafka sử dụng Docker Compose các bạn nhé!

  Cách thiết lập một dự án Symfony để làm việc với Docker Subdomains

Xem thêm các việc làm OOP lương cao trên TopDev

Mình sẽ tạo mới một tập tin docker-compose.yml với Docker Compose version như sau:

version: '3.8'

Chúng ta sẽ khai báo 2 service, một cho Apache Zookeeper và một cho Apache Kafka:

services:
zookeeper:

kafka:

Vì Apache Kafka muốn chạy được phải có Apache Zookeeper nên mình sẽ khai báo service zookeeper trước.

Chúng ta sẽ sử dụng các Docker Image của Apache Kafka và Apache Zookeeper từ Confluent Platform https://www.confluent.io/ với địa chỉ Docker Hub là https://hub.docker.com/u/confluentinc. Nói nôm na thì Confluent Platform là một mở rộng của Apache Kafka, được xây dựng từ Apache Kafka cùng với các công cụ và service hữu ích khác giúp chúng ta có thể dễ dàng chạy và sử dụng Apache Kafka đó các bạn! Không có Docker Official Image từ Apache Kafka nên chúng ta có thể sử dụng các Docker Images từ Confluent Platform này.

Nội dung của zookeeper service như sau:

zookeeper:
image: confluentinc/cp-zookeeper:latest
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ports:
- 2181:2181

ZOOKEEPER_CLIENT_PORT là biến môi trường bắt buộc để định nghĩa port mà client có thể kết nối tới Apache Zookeeper. Trong trường hợp của chúng ta là Apache Kafka đó các bạn! Ở đây, mình cũng expose port 2181 là port mặc định của Apache Zookeeper ra ngoài.

Còn kafka service thì có nội dung như sau:

kafka:
image: confluentinc/cp-kafka:latest
depends_on: 
- zookeeper
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:29092
ports:
- 29092:29092

KAFKA_ZOOKEEPER_CONNECT được sử dụng để định nghĩa Apache ZooKeeper server mà Apache Kafka sẽ connect tới, còn KAFKA_ADVERTISED_LISTENERS dùng để expose Apache Kafka ra ngoài container để các client có thể connect tới. Đây là những biến môi trường bắt buộc các bạn phải khai báo khi start Docker Container từ cp-kafka Docker Image các bạn nhé!

Chúng ta cũng khai báo networks như sau:

networks:
huongdanjava:
driver: bridge

Toàn bộ nội dung của tập tin docker-compose.yml như sau:

version: '3.8'

services:
zookeeper:
image: confluentinc/cp-zookeeper:latest
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ports:
- 2181:2181

kafka:
image: confluentinc/cp-kafka:latest
depends_on: 
- zookeeper
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:29092
ports:
- 29092:29092

networks:
huongdanjava:
driver: bridge

Nếu bây giờ, các bạn chạy command docker compose up trong thư mục chứa tập tin docker-compose.yml này, các bạn sẽ thấy kết quả như sau:

Bây giờ thì các bạn có thể connect tới Apache Kafka server này để sử dụng rồi.

Bài viết gốc được đăng tải tại huongdanjava.com
Có thể bạn quan tâm:
Xem thêm tuyển dụng việc làm IT hấp dẫn trên TopDev

Producer – Consumer và đồng bộ hóa các luồng trong Java

đồng bộ hóa trong java
Vấn đề Nhà sản xuất (Producer) – Người tiêu dùng (Consumer) và đồng bộ hóa các luồng trong Java

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

Producer/ Consumer là một ví dụ kinh điển về vấn đề đồng hóa các luồng (multi-threading synchronization). Trong bài này tôi sẽ giới thiệu với các bạn vấn đề này và cách giải quyết để giúp các bạn hiểu rõ hơn về Java concurrency và mutli-threading.

Xem thêm các việc làm Java lương cao trên TopDev

Mô tả vấn đề Producer/ Consumer

Vấn đề mô tả hai đối tượng nhà sản xuất (Producer) và người tiêu dùng (Consumer), cả hai cùng chia sẻ một bộ đệm có kích thước cố định được sử dụng như một hàng đợi (queue).

Producer: công việc của nhà sản xuất là tạo dữ liệu, đưa nó vào bộ đệm và bắt đầu lại.

Consumer: công việc người tiêu dùng là tiêu thụ dữ liệu (nghĩa là loại bỏ nó khỏi bộ đệm), từng phần một và xử lý nó. Consumer và Producer hoạt động song song với nhau.

Vấn đề là đảm bảo rằng nhà sản xuất không thể thêm dữ liệu vào bộ đệm nếu nó đầy và người tiêu dùng không thể xóa dữ liệu khỏi bộ đệm trống, đồng thời đảm bảo an toàn cho luồng (thread-safe).

Giải pháp để giải quyết vấn đề Producer/ Consumer

Ý tưởng:

  • Giải pháp cho nhà sản xuất là đi ngủ (wait) nếu bộ đệm đầy. Lần tiếp theo người tiêu dùng xóa một mục khỏi bộ đệm, nó đánh thức (notify) nhà sản xuất, bắt đầu đưa dữ liệu vào bộ đệm.
  • Theo cách tương tự, người tiêu dùng có thể đi ngủ (wait) nếu thấy bộ đệm trống. Lần tiếp theo nhà sản xuất đưa dữ liệu vào bộ đệm, nó đánh thức (notify) người tiêu dùng đang ngủ (wait).
  • Trong khi làm tất cả điều này, phải đảm bảo an toàn cho luồng (thread safe).

Có nhiều giải pháp cho vấn đề Producer Consumer trong Java:

  • Sử dụng synchronized.
  • Sử dụng BlockingQueue.
  • Sử dụng Semaphore.
  • Sử dụng JMS (Java Messaging Service): JMS là một implementation của vấn đề Producer Consumer. Nhiều nhà sản xuất và nhiều người tiêu dùng có thể kết nối với JMS và phân phối công việc. Tôi sẽ giới thiệu với các bạn JMS ở một bài viết khác.

Trong bài này, tôi sẽ giới thiệu với các bạn cách sử dụng BlockingQueue và Semaphore.

Sử dụng BlockingQueue

Để giải quyết vấn đề, chúng ta sẽ cần 3 class:

  • Blocking Queue :
    • Sử dụng synchronized để đảm bảo thread-safe.
    • put() : sử dụng wait() để chờ khi queue đã đầy (full), và notifyAll() để thông báo khi thêm data mới vào queue.
    • take() : sử dụng wait() để chờ khi queue rỗng (empty), và notifyAll() để thông báo khi lấy data ra khỏi queue.
  • Producer Thread(s) : các thread mô phỏng các nhà sản xuất.
  • Consumer Thread(s) : các thread mô phỏng các người tiêu dùng.

Các bạn có thể sử dụng BlockingQueue có sẵn trong package java.util.concurrent . Trong bài này, tôi sẽ tự tạo một BlockingQueue để mô phỏng cơ chế hoạt động của Blocking Queue cho các bạn dễ hiểu.

BlockingQueue.java

package com.gpcoder.producerconsumer;

import java.util.LinkedList;

public class BlockingQueue {
private static final int compacity = 10;
private final LinkedList items = new LinkedList<>();

public synchronized void put(T value) throws InterruptedException {
while (items.size() == compacity) {
System.out.println("Queue is full");
wait();
}
items.addLast(value);
notifyAll();
}

public synchronized T take() throws InterruptedException {
while (items.size() == 0) {
System.out.println("Queue is empty");
wait();
}
notifyAll();
return items.removeFirst();
}

public synchronized int size() {
return items.size();
}
}

Producer.java

package com.gpcoder.producerconsumer;

import java.util.concurrent.ThreadLocalRandom;

public class Producer implements Runnable {

private final BlockingQueue queue;

Producer(BlockingQueue queue) {
this.queue = queue;
}

public void run() {
try {
while (true) {
queue.put(produce());
System.out.println("Produced resource - Queue size() = " + queue.size());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

private Integer produce() throws InterruptedException {
Thread.sleep(50); // simulate time to produce the data
return ThreadLocalRandom.current().nextInt(1, 100);
}
}

Consumer.java

package com.gpcoder.producerconsumer;

import java.util.concurrent.ThreadLocalRandom;

public class Consumer implements Runnable {

private final BlockingQueue queue;

Consumer(BlockingQueue queue) {
this.queue = queue;
}

public void run() {
try {
while (true) {
queue.take();
System.out.println("Consumed resource - Queue size() = " + queue.size());
Thread.sleep(ThreadLocalRandom.current().nextInt(50, 300)); // simulate time passing
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Main.java

package com.gpcoder.producerconsumer;

public class Main {

public static void main(String[] args) throws InterruptedException {
BlockingQueue boundedBuffer = new BlockingQueue<>();

Producer producer = new Producer(boundedBuffer);
Consumer consumer1 = new Consumer(boundedBuffer);
Consumer consumer2 = new Consumer(boundedBuffer);
Consumer consumer3 = new Consumer(boundedBuffer);

new Thread(producer).start();
new Thread(consumer1).start();
new Thread(consumer2).start();
new Thread(consumer3).start();

Thread.sleep(5000); // After 5s have another comsumer
Consumer consumer4 = new Consumer(boundedBuffer);
new Thread(consumer4).start();
}
}

Output chương trình:

Queue is empty
Queue is empty
Queue is empty
Queue is empty
Produced resource - Queue size() = 0
Consumed resource - Queue size() = 0
Queue is empty
Produced resource - Queue size() = 0
Consumed resource - Queue size() = 0
Produced resource - Queue size() = 1
Consumed resource - Queue size() = 0
Queue is empty
Produced resource - Queue size() = 1
Consumed resource - Queue size() = 0
....
Produced resource - Queue size() = 10
Queue is full
Consumed resource - Queue size() = 9
Produced resource - Queue size() = 10
Queue is full
Consumed resource - Queue size() = 9
Produced resource - Queue size() = 10
Consumed resource - Queue size() = 9
....
Produced resource - Queue size() = 10
Consumed resource - Queue size() = 9
Consumed resource - Queue size() = 8
Produced resource - Queue size() = 9
Consumed resource - Queue size() = 8
...
Consumed resource - Queue size() = 1
Produced resource - Queue size() = 2
Consumed resource - Queue size() = 1
Consumed resource - Queue size() = 0
Queue is empty
Produced resource - Queue size() = 1
Consumed resource - Queue size() = 0
...
  10 lý do cho thấy tại sao bạn nên theo học ngôn ngữ lập trình Java
  10 tips để trở thành Java Developer xịn hơn

Sử dụng Semaphore

Tương tự như BlockingQueue ở trên, với Semaphore chúng ta cũng cần 3 class:

  • Queue : sử dụng 2 Semaphore để đồng bộ hóa dữ liệu.
    • Semaphore Producer: được set giá trị là maximum của buffer size. Giá trị này tương ứng với số lượng item có thể được thêm vào buffer.
    • Semaphore Consumer: được set giá trị là 0. Giá trị này tương ứng với số lượng item có thể được lấy ra khỏi buffer.
  • Producer Thread(s) : các thread mô phỏng các nhà sản xuất.
  • Consumer Thread(s) : các thread mô phỏng các người tiêu dùng.

Ví dụ:

Để dễ theo dõi, tôi sẽ thêm một số dòng log để xem cách hoạt động của Semaphore.

package com.gpcoder.producerconsumer.semaphore;

import java.util.Stack;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadLocalRandom;

public class ProducerConsumerSemaphore {

private static final int BUFFER_SIZE = 4;
private final Semaphore writePermits = new Semaphore(BUFFER_SIZE);
private final Semaphore readPermits = new Semaphore(0);
private final Stack buffer = new Stack<>();

class Producer implements Runnable {
private String name;

public Producer(String name) {
this.name = name;
}

@Override
public void run() {
try {
while (true) {
System.out.println(name + ": acquiring lock...");
System.out.println(name + ": Producer available Semaphore permits now: " + writePermits.availablePermits());
writePermits.acquire();
System.out.println(name + ": got the permit!");

Thread.sleep(50); // simulate time to work
int data = ThreadLocalRandom.current().nextInt(100);
System.out.println(name + ": produced data " + buffer.push(data));

System.out.println(name + ": releasing lock...");
readPermits.release();
System.out.println(name + ": Consumer available Semaphore permits now: " + readPermits.availablePermits());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

class Consumer implements Runnable {
private String name;

public Consumer(String name) {
this.name = name;
}

@Override
public void run() {
try {
while (true) {
System.out.println(name + ": acquiring lock...");
System.out.println(name + ": Consumer available Semaphore permits now: " + readPermits.availablePermits());
readPermits.acquire();

Thread.sleep(ThreadLocalRandom.current().nextInt(50, 300)); // simulate time to work
System.out.println(name + ": consumed data " + buffer.pop());

System.out.println(name + ": releasing lock...");
writePermits.release();
System.out.println(name + ": Producer available Semaphore permits now: " + writePermits.availablePermits());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) throws InterruptedException {
ProducerConsumerSemaphore obj = new ProducerConsumerSemaphore();
Producer producer = obj.new Producer("Producer 1");
new Thread(producer).start();

for (int i = 1; i <= 3; i++) {
Consumer consumer = obj.new Consumer("Consumer " + i);
new Thread(consumer).start();
}

Thread.sleep(5000); // After 5s have another comsumer
Consumer consumer = obj.new Consumer("Consumer " + 4);
new Thread(consumer).start();
}
}

Chạy chương trình trên, ta có kết quả sau:

Producer 1: acquiring lock...
Producer 1: Producer available Semaphore permits now: 4
Producer 1: got the permit!
Consumer 1: acquiring lock...
Consumer 1: Consumer available Semaphore permits now: 0
Consumer 2: acquiring lock...
Consumer 2: Consumer available Semaphore permits now: 0
Consumer 3: acquiring lock...
Consumer 3: Consumer available Semaphore permits now: 0
Producer 1: produced data 94
Producer 1: releasing lock...
Producer 1: Consumer available Semaphore permits now: 0
Producer 1: acquiring lock...
Producer 1: Producer available Semaphore permits now: 3
Producer 1: got the permit!
Producer 1: produced data 85
Producer 1: releasing lock...
Producer 1: Consumer available Semaphore permits now: 1
Producer 1: acquiring lock...
Producer 1: Producer available Semaphore permits now: 2
...
Consumer 2: Producer available Semaphore permits now: 1
Consumer 2: acquiring lock...
Consumer 2: Consumer available Semaphore permits now: 0
Producer 1: produced data 9
Producer 1: releasing lock...
Producer 1: Consumer available Semaphore permits now: 1
Producer 1: acquiring lock...
Producer 1: Producer available Semaphore permits now: 1
Producer 1: got the permit!
...

Tại sao vấn đề Producer/ Consumer lại quan trọng?

Có thể được sử dụng để phân phối công việc giữa các worker khác nhau, dễ dàng tăng hoặc giảm theo yêu cầu. Như bạn thấy trong ví dụ trên, ban đầu một Producer có thể sản xuất đủ cho 3 Consumer. Nhưng sau đó, có thêm một Consumer thì hệ thống sản xuất không đáp ứng được, do đó chúng ta cần tăng hiệu suất của Producer lên hoặc tạo thêm một Producer khác để đủ phục vụ cho Consumer, tránh tình trạng thiếu sản phẩm.

Producer và Consumer được kết nối thông qua BlockingQueue, nó không biết sự hiện diện của nhau, tách rời các mối quan tâm (separation of concern), giúp hệ thống thống có thiết kế tốt hơn, nên dễ dàng nâng cấp và mở rộng.

Producer và Consumer không cần phải có sẵn cùng một lúc. Consumer có thể nhận các nhiệm vụ được sản xuất bởi Producer tại một thời điểm khác nhau.

Tài liệu tham khảo:

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

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

Xem thêm tuyển dụng IT hấp dẫn trên TopDev

Cài đặt Apache Kafka trên macOS

cài đặt apache kafka
Cài đặt Apache Kafka trên macOS

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

Trong bài viết này, mình hướng dẫn các bạn cách cài đặt Apache Kafka trong macOS các bạn nhé!

Đầu tiên, các bạn cần đi đến trang Download của Apache Kafka https://kafka.apache.org/downloads để download latest version của nó! Các bạn hãy download phiên bản binary mới nhất.

  Apache Kafka là gì?
  Capacity Planning - Dự toán công suất cho ứng dụng (Tập 1 )

Xem thêm Tuyển dụng Machine Learning Fresher trên TopDev

Sau khi download xong, các bạn hãy giải nén tập tin download này ra.

Để có thể sử dụng Apache Kafka ở bất kỳ đâu trong máy của mình, mình sẽ chỉnh sửa tập tin .bash_profile để thêm đường dẫn tới thư mục bin của Kafka như sau:

export PATH=/Users/khanh/Downloads/kafka_2.13-3.0.0/bin:$PATH

Để những thay đổi của chúng ta có hiệu lực liền, các bạn hãy reload lại tập tin .bash_profile với câu lệnh như sau:

. ./.bash_profile

Để start Apache Kafka, điều đầu tiên, chúng ta cần làm là start Apache ZooKeeper trước.
Các bạn hãy đi đến thư mục của Apache Kafka. Trong thư mục này có thư mục config:

với những tập tin cấu hình mặc định của Apache Kafka và cả Apache ZooKeeper.

Các bạn có thể start Apache ZooKeeper sử dụng command như sau:

zookeeper-server-start.sh config/zookeeper.properties

Kết quả:
Sau khi đã start Apache ZooKeeper rồi thì lúc này, các bạn có thể start Apache Kafka.

Các bạn hãy chạy command sau:

kafka-server-start.sh config/server.properties

Kết quả:
Đến đây thì chúng ta đã hoàn thành việc cài đặt Apache Kafka trên macOS rồi đó các bạn!

Bài viết gốc được đăng tải tại huongdanjava.com
Có thể bạn quan tâm:
Xem thêm tuyển dụng việc làm IT hấp dẫn trên TopDev

Phỏng Vấn Data Analyst: TOP 5 Câu Hỏi Bạn Không Nên Bỏ Qua!

phỏng vấn data analyst
Phỏng Vấn Data Analyst: TOP 5 Câu Hỏi Bạn Không Nên Bỏ Qua!

Để hoàn thành tốt bất cứ quá trình ứng tuyển nào, việc chuẩn bị trước cho các buổi phỏng vấn luôn rất cần thiết nếu muốn có một kết quả thành công. Với vị trí hot như Data Analyst, đâu là những câu hỏi mà ứng viên nên tham khảo trước và có sự chuẩn bị kỹ lưỡng cho mình? Tìm hiểu thêm về top những câu hỏi phỏng vấn Data Analyst mà bạn không nên bỏ qua với bài viết dưới đây nhé!

phỏng vấn business analyst
Top câu hỏi phỏng vấn dành cho Business Analyst

1. Điều gì khiến bạn quyết định trở thành một Data Analyst?

Mục tiêu nghề nghiệp, lý do lựa chọn công việc là một câu hỏi quen thuộc trong hầu hết các buổi phỏng vấn ở mọi ngành nghề. Mục đích của nhà tuyển dụng khi đặt câu hỏi này là để xác định được quyết tâm cũng như mức độ gắn bó của bạn với công việc ra sao, bạn đã có những kinh nghiệm gì và có thể đóng góp được gì cho vị trí mà công ty đang đăng tuyển.

Với một nhà phân tích dữ liệu, câu trả lời của bạn có thể xoay quanh đam mê với việc nghiên cứu những con số, phân tích và đánh giá số liệu chẳng hạn. Đây là một câu hỏi rộng, và không có bất cứ khuôn khổ nào cho nó cả, việc yêu thích số liệu và hứng thú trong việc diễn giải nó hoàn toàn có thể là lý do để bạn lựa chọn gắn bó với công việc. Bạn càng trả lời sáng tạo và hấp dẫn sẽ càng để lại ấn tượng trong mắt người phỏng vấn, cũng như tạo thiện cảm trong suốt buổi phỏng vấn.

2. Trách nhiệm chính của một Data Analyst là gì?

Câu hỏi này sẽ cho nhà tuyển dụng thấy được những hiểu biết căn bản của ứng viên với vị trí Data Analyst. Để có thể kéo dài buổi phỏng vấn, nhà tuyển dụng có hứng thú đào sâu vào kiến thức và kỹ năng của bạn trong buổi phỏng vấn, việc cho thấy kiến thức nền tảng của bạn là rất cần thiết.

  "Làm PM, theo anh không cần biết về code, nhưng phải hiểu về SQL, database, những khái niệm cơ bản của code"
  Big Data có thật sự “toàn năng” và các cơ hội nghề nghiệp trong tương lai?

Với câu hỏi như thế này, bạn có thể tìm hiểu sơ qua về công việc chính mà một Data Analyst sẽ đảm nhận gồm những gì. Đó có thể là việc thu thập, tổng hợp và sắp xếp các dữ liệu. Đánh giá, đưa ra những nhận xét để thúc đẩy hiệu quả và khắc phục các sai sót. Bên cạnh đó thì khả năng phát hiện kịp thời các sai sót cũng là nhiệm vụ mà các Data Analyst cần phải đảm nhận.

câu hỏi phỏng vấn data analyst
Các câu hỏi thường gặp khi phỏng vấn Data Analyst

3. Thách thức lớn nhất bạn đã từng gặp phải trong quá trình làm một Data Analyst là gì?

Câu hỏi này sẽ vừa cho thấy kinh nghiệm làm việc vừa cho thấy khả năng giải quyết vấn đề của các Data Analyst tương lai. Do đó, hãy chắc chắn rằng khi bạn trình bày vấn đề mình đang gặp phải cũng cần đồng thời chia sẻ giải pháp mà bạn đã sử dụng để khắc phục khó khăn đó. Đây là cơ sở rất quan trọng để người phỏng vấn đánh giá năng lực của ứng viên, vậy nên hãy có sự chuẩn bị kỹ càng cho những câu hỏi như thế này.

Tùy theo kinh nghiệm làm việc của mỗi cá nhân mà bạn sẽ nhìn nhận được đâu là vấn đề khó khăn nhất mà mình đang gặp phải. Nên lựa chọn những vấn đề nào có thể đánh giá được khả năng thật sự của bạn cũng như có thể dễ dàng để nhà tuyển dụng hiểu được thay vì các vấn đề có tính chuyên biệt quá cao. Và đừng quên, trình bày cách giải quyết khó khăn thách thức sẽ càng thể hiện rõ năng lực của bạn.

Xem thêm tuyển dụng Data Analytics lương cao trên TopDev

4. Đã có kinh nghiệm làm việc trong lĩnh vực nào và sử dụng công cụ gì để phân tích dữ liệu?

Đây là câu hỏi giúp nhà tuyển dụng đánh giá về kinh nghiệm làm việc trước đây của bạn. Có khá nhiều lĩnh vực hiện nay cần đến sự giúp sức của các Data Analyst như phân tích hoạt động tiếp thị, phân tích tài chính, phân tích hoạt động,… Đừng quên chia sẻ với người phỏng vấn lĩnh vực mà bạn yêu thích và có mong muốn gắn bó để đánh giá sự thích hợp của bạn với công việc mình đang ứng tuyển.

business analyst làm gì

Thông tin về việc bạn đã từng sử dụng qua những công cụ phân tích dữ liệu nào sẽ cho thấy năng lực chuyên môn của bạn. Với câu hỏi này, hãy trình bày với nhà tuyển dụng những phần mềm bạn đã từng sử dụng, đâu là phần mềm bạn cảm thấy yêu thích nhất và thế mạnh của phần mềm đó là gì. Việc sử dụng phần mềm hiệu quả cũng là yếu tố quan trọng để nâng cao hiệu suất làm việc.

5. Khi được giao một dự án phân tích dữ liệu mới, bạn sẽ bắt đầu quy trình làm việc của mình như thế nào?

Câu hỏi này mục đích để đánh giá cách bạn xử lý vấn đề và quy trình làm việc có thật sự đáp ứng được mong đợi của nhà tuyển dụng hay không. Tùy theo cách làm việc của mình mà bạn có thể chia sẻ để nhà tuyển dụng đánh giá. Quan trọng nhất là đảm bảo được tính tối ưu về thời gian, chi phí và đạt được hiệu suất cao nhất cho dự án đó, còn lại sẽ không có bất cứ một quy chuẩn cụ thể nào yêu cầu bạn bắt buộc phải làm như thế cả.

Data Analyst là một công việc đòi hỏi tính kỹ lưỡng và cẩn thận cao. Do đó, trong suốt buổi phỏng vấn Data Analyst hãy cho nhà tuyển dụng thấy được rằng bạn là người đủ cẩn thận và đủ năng lực để đáp ứng những gì họ yêu cầu. Kinh nghiệm và bằng cấp dù quan trọng nhưng sẽ không là yếu tố quyết định tất cả. Đón đọc thêm nhiều bài viết khác về chủ đề Data Analyst tại Topdev.vn/blog nhé!

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

Xem thêm tuyển dụng IT hấp dẫn trên TopDev

Lưu RegisteredClient vào database trong Spring Authorization Server

registeredclient
Lưu RegisteredClient vào database trong Spring Authorization Server

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

Trong bài viết trước, mình đã hướng dẫn các bạn cách hiện thực một Authorization Server sử dụng Spring Authorization Server, nhưng thông tin về RegisteredClient trong bài viết này được lưu trong memory. Để lưu thông tin RegisteredClient vào database thì 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 các bạn nhé!

  Bean, ApplicationContext, Spring Bean Life Cycle và Component scan
  Authentication trong Spring Security

Xem thêm các chương trình tuyển dụng Spring lương cao trên TopDev

Đầu tiên, mình cũng tạo mới một Spring Boot project với Web Starter, Security Starter, Data JPA, PostgreSQL Driver:

và Spring Authorization Server:

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>0.2.0</version>
</dependency>

để làm ví dụ.

Kết quả:

Mình sẽ cấu hình Spring Security như trong bài viết Hiện thực OAuth Authorization Server sử dụng Spring Authorization Server như sau:

package com.huongdanjava.springauthorizationserver;

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@EnableWebSecurity
public class SpringSecurityConfiguration {

@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
// @formatter:on

return http.build();
}

@Bean
public UserDetailsService users() {
// @formatter:off
UserDetails user = User.withDefaultPasswordEncoder()
.username("admin")
.password("password")
.roles("ADMIN").build();
// @formatter:on

return new InMemoryUserDetailsManager(user);
}

}


Còn cấu hình cho Authorization Server, mình cũng làm tương tự như bài viết Hiện thực OAuth Authorization Server sử dụng Spring Authorization Server này nhưng phần khai báo thông tin RegisteredClient mình sẽ làm sau:

package com.huongdanjava.springauthorizationserver;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.time.Duration;
import java.util.UUID;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
import org.springframework.security.web.SecurityFilterChain;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;

@Configuration
public class AuthorizationServerConfiguration {

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);

return http.formLogin(Customizer.withDefaults()).build();
}

@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}

@Bean
public JWKSource<SecurityContext> jwkSource() throws NoSuchAlgorithmException {
RSAKey rsaKey = generateRsa();
JWKSet jwkSet = new JWKSet(rsaKey);

return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}

private static RSAKey generateRsa() throws NoSuchAlgorithmException {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

// @formatter:off
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
// @formatter:on
}

private static KeyPair generateRsaKey() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);

return keyPairGenerator.generateKeyPair();
}

@Bean
public ProviderSettings providerSettings() {
// @formatter:off
return ProviderSettings.builder()
.issuer("http://localhost:8080")
.build();
// @formatter:on
}

@Bean
public TokenSettings tokenSettings() {
//@formatter:off
return TokenSettings.builder()
.accessTokenTimeToLive(Duration.ofMinutes(30L))
.build();
// @formatter:on
}
}

Để lưu thông tin RegisteredClient vào database, đầu tiên, chúng ta cần định nghĩa database structure để làm việc này.

Mặc định thì Spring Authorization Server cung cấp cho chúng ta script database để tạo database structure. Các bạn có thể copy chúng trong tập tin .jar của Spring Authorization Server:

Các bạn có thể vào Github của Spring Authorization Server ở đây để copy những tập tin này.

Mình sẽ sử dụng Flyway để quản lý database migration:

<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>

bằng cách copy những tập tin schema của Spring Authorization Server vào thư mục src/main/resources/db/migration như sau:

Trong script tạo table oauth2_authorization trong tập tin V1__oauth2-authorization-schema.sql có định nghĩa kiểu dữ liệu BLOB, có lẽ cho Oracle database:

CREATE TABLE oauth2_authorization (
id varchar(100) NOT NULL,
registered_client_id varchar(100) NOT NULL,
principal_name varchar(200) NOT NULL,
authorization_grant_type varchar(100) NOT NULL,
attributes varchar(4000) DEFAULT NULL,
state varchar(500) DEFAULT NULL,
authorization_code_value blob DEFAULT NULL,
authorization_code_issued_at timestamp DEFAULT NULL,
authorization_code_expires_at timestamp DEFAULT NULL,
authorization_code_metadata varchar(2000) DEFAULT NULL,
access_token_value blob DEFAULT NULL,
access_token_issued_at timestamp DEFAULT NULL,
access_token_expires_at timestamp DEFAULT NULL,
access_token_metadata varchar(2000) DEFAULT NULL,
access_token_type varchar(100) DEFAULT NULL,
access_token_scopes varchar(1000) DEFAULT NULL,
oidc_id_token_value blob DEFAULT NULL,
oidc_id_token_issued_at timestamp DEFAULT NULL,
oidc_id_token_expires_at timestamp DEFAULT NULL,
oidc_id_token_metadata varchar(2000) DEFAULT NULL,
refresh_token_value blob DEFAULT NULL,
refresh_token_issued_at timestamp DEFAULT NULL,
refresh_token_expires_at timestamp DEFAULT NULL,
refresh_token_metadata varchar(2000) DEFAULT NULL,
PRIMARY KEY (id)
);

Nếu các bạn đang sử dụng PostgreSQL database như mình thì cần phải đổi sang kiểu BYTEA các bạn nhé! Không thì chạy database migration sẽ bị lỗi.

Khai báo Datasource để chạy database migration như sau:

spring.datasource.url=jdbc:postgresql://localhost:5432/authorization_server
spring.datasource.username=khanh
spring.datasource.password=1

Giờ thì các bạn có thể định nghĩa RegisteredClient trong database, ví dụ như sau:

@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
// @formatter:off
RegisteredClient registeredClient = RegisteredClient.withId("e4a295f7-0a5f-4cbc-bcd3-d870243d1b05")
.clientId("huongdanjava1")
.clientSecret("123")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.tokenSettings(tokenSettings())
.build();
// @formatter:on

JdbcRegisteredClientRepository registeredClientRepository =
new JdbcRegisteredClientRepository(jdbcTemplate);
registeredClientRepository.save(registeredClient);

return registeredClientRepository;
}

Ở đây, mình định nghĩa một RegisteredClient với grant type là client_credentials với ID cố định để mỗi khi start app, không có duplicate record trong database. Tuỳ theo nhu cầu thì các bạn hãy viết code tương ứng nhé!

Chúng ta sẽ sử dụng đối tượng JdbcRegisteredClientRepository để lưu thông tin RegisteredClient này. Tham số khi khởi tạo đối tượng JdbcRegisteredClientRepository là JdbcTemplate.

Lúc này, nếu chạy ứng dụng lên các bạn sẽ thấy trong table oauth2_registered_client, một record mới của RegisteredClient mà mình đã khai báo ở trên được insert vào:

Các bạn cũng nên để ý là client secret được mã hoá sử dụng class DelegatingPasswordEncoder với thuật toán bcrypt. Hiện tại chúng ta chưa thể khai báo thuật toán mà mình muốn!

Xong rồi đó các bạn, nếu bây giờ các bạn chạy ứng dụng và lấy token của clientId ở trên, các bạn sẽ thấy kết quả như sau:

Tải Windows 10 Với Giá $8.19 & Miễn Phí Nâng Cấp Phiên Bản Windows 11

Hệ điều hành Windows 11 vượt trội chính thức ra mắt vào 05/10/2021 là phiên bản nâng cấp hoàn toàn miễn phí cho người dùng hiện tại đang sử dụng Windows 10.

Chỉ cần bạn là người mua Windows 10, bạn có cơ hội lựa chọn nâng cấp lên Windows 11 mà không phải trả thêm bất kỳ chi phí nào. Trong trường hợp máy của bạn chưa được cài đặt Windows 10, Keysoff.com cung cấp những ưu đãi tốt nhất về giải pháp an toàn, không rắc rối và tiết kiệm cho phần mềm Windows 10.

Nhận ưu đãi tốt nhất cho Windows 10 chỉ từ 8.19$

Keysoff.com mong muốn mang đến gói ưu đãi tiết kiệm nhất đối với các sản phẩm phần mềm với mức giá phù hợp để bạn có thể tận hưởng dịch vụ khách hàng đặc biệt cho trải nghiệm mua hàng.

Nâng cấp hệ điều hành Windows nhanh nhất với mức giảm giá ưu đãi 40%

Windows 10 là phiên bản hệ điều hành nhanh nhất và an toàn nhất cho đến thời điểm hiện tại. Bạn đang thiếu rất nhiều tính năng và ưu điểm xử lý giúp máy tính hoạt động hiểu quả hơn khi chưa cài Windows 10 cho máy tính của mình. Bạn có thể sở hữu key win bản quyền Windows 10 chính quyền trong chương trình ưu đãi của Keysoff.com và được giảm giá 40% khi áp dụng mã khuyến mãi “ESL40“.

Hoàn thành nhiều hơn với thời gian ngắn với MS Office cùng mức giảm giá 55%

Bộ phần mềm tối ưu MS Office giúp bạn hoàn thành nhiều công việc trong thời gian ngắn. Keysoff.com đưa ra ưu đãi lớn 55% trong chương trình khuyến mãi mùa thu cho 2 phiên bản MS Office 2016 và 2019 khi áp dụng mã giảm giá “ESL55” khi thanh toán.

Chúng tôi cung cấp nhiều công cụ phần mềm giúp bạn làm việc và học tập trên máy tính thoải mái hơn, an toàn hơn với mức giá ưu đãi cho toàn bộ sản phẩm và giảm giá tới 90% trong dịp khuyến mãi mùa hè với phiên bản tải về chính thức và dễ dàng cài đặt. Đừng bỏ lỡ dịp khuyến mãi lớn này.

Refactoring Design Pattern với tính năng mới trong Java 8

refactoring design pattern
Refactoring Design Pattern với tính năng mới trong Java 8

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

Trong bài này, tôi sẽ giới thiệu với các bạn cách sử dụng một số tính năng mới trong Java 8 như Lambda , Function, Supplier, … để refactor code của một số Design Pattern.

Xem thêm nhiều việc làm Java lương cao trên TopDev

Refactoring Strategy Design Pattern

Strategy.java

package com.gpcoder.designpatterns.strategy;

public interface Strategy {
void performTask();
}

Strategy Pattern không sử dụng Lambda

StartegyPatternExample.java

package com.gpcoder.designpatterns.strategy;

import java.util.Arrays;
import java.util.List;

class EagerStrategy implements Strategy {

@Override
public void performTask() {
System.out.println("Eager strategy");
}
}

class LazyStratgey implements Strategy {

@Override
public void performTask() {
System.out.println("Lazy strategy");
}
}

public class StartegyPatternExample {
public static void main(String[] args) {
Strategy eagerStrategy = new EagerStrategy();
Strategy lazyStrategy = new LazyStratgey();
List strategies = Arrays.asList(eagerStrategy, lazyStrategy);
for (Strategy stg : strategies) {
stg.performTask();
}
}
}

Strategy Pattern sử dụng Lambda

package com.gpcoder.designpatterns.strategy;

import java.util.Arrays;
import java.util.List;

public class LambdaStartegyPatternExample {

public static void main(String[] args) {
Strategy eagerStrategy = () -> System.out.println("Eager strategy");
Strategy lazyStrategy = () -> System.out.println("Lazy strategy");
List strategies = Arrays.asList(eagerStrategy, lazyStrategy);
strategies.forEach((elem) -> elem.performTask());
}
}

Như bạn thấy, sử dụng Lambda code chúng ta đơn giản hơn nhiều, không cần tạo thêm các class.

  30 tiện ích Chrome (extensions) cho Designer và Developer
  30 tiện ích Chrome cho designer và dev

Refactoring Observer Design Pattern

Observer.java

package com.gpcoder.designpatterns.observer;

public interface Observer {
void update(String str);
}

Subject.java

package com.gpcoder.designpatterns.observer;

public interface Subject {

void registerObserver(Observer observer);

void notifyObservers(String str);
}

AccountService.java

package com.gpcoder.designpatterns.observer;

import java.util.ArrayList;
import java.util.List;

public class AccountService implements Subject {

private final List observers = new ArrayList<>();

public void login(String username) {
System.out.println("Login: " + username);
notifyObservers(username);
}

@Override
public void registerObserver(Observer observer) {
if (!observers.contains(observer)) {
observers.add(observer);
}
}

@Override
public void notifyObservers(String str) {
for (Observer observer : observers) {
observer.update(str);
}
}
}

Observer Pattern không sử dụng Lambda

package com.gpcoder.designpatterns.observer;

class Logger implements Observer {
@Override
public void update(String str) {
System.out.println("Logger: " + str);
}
}

class Mailer implements Observer {
@Override
public void update(String str) {
System.out.println("Mailer: " + str);
}
}

public class ObserverPatternExample {
public static void main(String[] args) {
AccountService account = new AccountService();
// Register Observers
account.registerObserver(new Logger());
account.registerObserver(new Mailer());
// Call service
account.login("gpcoder");
}
}

Observer Pattern sử dụng Lambda

package com.gpcoder.designpatterns.observer;

public class LambdaObserverPatternExample {
public static void main(String[] args) {
AccountService account = new AccountService();
// Register Observers
account.registerObserver(str -> System.out.println("Logger: " + str));
account.registerObserver(str -> System.out.println("Mailer: " + str));
// Call service
account.login("gpcoder");
}
}

Chạy 2 chương trình trên, ta có cùng kết quả:

Login: gpcoder
Logger: gpcoder
Mailer: gpcoder

Refactoring Chain of Responsibility Pattern

Filter.java

package com.gpcoder.designpatterns.chain;

public abstract class Filter {

private Filter nextFilter;

public String doFilter(String str) {
String result = handleString(str);
if (nextFilter != null) {
return nextFilter.doFilter(result);
}
return result;
}

public void setNextFilter(Filter nextFilter) {
this.nextFilter = nextFilter;
}

protected abstract String handleString(String str);
}

Chain of Responsibility Pattern không sử dụng Lambda

package com.gpcoder.designpatterns.chain;

class Filter1 extends Filter {
@Override
protected String handleString(String str) {
System.out.println("Filter1: " + str);
return str + "->Filter1";
}
}

class Filter2 extends Filter {
@Override
protected String handleString(String str) {
System.out.println("Filter2: " + str);
return str + "->Filter2";
}
}

class Filter3 extends Filter {
@Override
protected String handleString(String str) {
System.out.println("Filter3: " + str);
return str + "->Filter3";
}
}

class AppFilter {
public static Filter getFilter() {
Filter1 filter1 = new Filter1();
Filter2 filter2 = new Filter2();
Filter3 filter3 = new Filter3();
filter1.setNextFilter(filter2);
filter2.setNextFilter(filter3);
return filter1;
}
}

public class ChainOfResponsibilityExample {

public static void main(String[] args) {
// Build the chain of responsibility
Filter filter = AppFilter.getFilter();
// Execute filter
String result = filter.doFilter("gpcoder");
System.out.println("Final data: " + result);
}
}

Chain of Responsibility Pattern sử dụng Lambda và Function

package com.gpcoder.designpatterns.chain;

import java.util.function.Function;
import java.util.function.UnaryOperator;

public class LamdaChainOfResponsibilityExample {

public static void main(String[] args) {

UnaryOperator filter1 = (str) -> {
System.out.println("Filter1: " + str);
return str + "->Filter1";
};

UnaryOperator filter2 = (str) -> {
System.out.println("Filter2: " + str);
return str + "->Filter2";
};

UnaryOperator filter3 = (str) -> {
System.out.println("Filter3: " + str);
return str + "->Filter3";
};

// Compose all functions resulting in a chain of operations.
Function<String, String> appFilter = filter1.andThen(filter2).andThen(filter3);
String result = appFilter.apply("gpcoder");
System.out.println("Final data: " + result);
}
}

Lưu ý: UnaryOperator là một Function, có cùng kiểu dữ liệu đầu vào và đầu ra. UnaryOperator<String> tương đương với cách viết Function<String, String>.

Chạy 2 chương trình trên, chúng ta có cùng kết quả:

Filter1: gpcoder
Filter2: gpcoder->Filter1
Filter3: gpcoder->Filter1->Filter2
Final data: gpcoder->Filter1->Filter2->Filter3

Refactoring Factory Method Design Pattern

Bank.java

package com.gpcoder.designpatterns.factory;

public interface Bank {
String getBankName();
}

TPBank.java

package com.gpcoder.designpatterns.factory;

public class TPBank implements Bank {
@Override
public String getBankName() {
return "TPBank";
}
}

VietcomBank.java

package com.gpcoder.designpatterns.factory;

public class VietcomBank implements Bank {
@Override
public String getBankName() {
return "VietcomBank";
}
}

BankType.java

package com.gpcoder.designpatterns.factory;

public enum BankType {
VIETCOMBANK, TPBANK;
}

Factory Method Pattern không sử dụng Java 8

package com.gpcoder.designpatterns.factory;

class BankFactory {
public static final Bank getBank(BankType bankType) {
switch (bankType) {
case TPBANK:
return new TPBank();
case VIETCOMBANK:
return new VietcomBank();
default:
throw new IllegalArgumentException("This bank type is unsupported");
}
}
}

public class FactoryMethodExample {
public static void main(String[] args) {
Bank bank = BankFactory.getBank(BankType.TPBANK);
System.out.println(bank.getBankName()); // TPBank
}
}

Factory Method Pattern sử dụng Supplier và Method Reference

package com.gpcoder.designpatterns.factory;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

class Java8BankFactory {

private final static Map<BankType, Supplier> map = new HashMap<>();

static {
map.put(BankType.TPBANK, TPBank::new);
map.put(BankType.VIETCOMBANK, VietcomBank::new);
}

public static final Bank getBank(BankType bankType) {
Supplier bank = map.get(bankType);
if (bank == null) {
throw new IllegalArgumentException("This bank type is unsupported");
}
return bank.get();
}
}

public class Java8FactoryMethodExample {
public static void main(String[] args) {
Bank bank = Java8BankFactory.getBank(BankType.TPBANK);
System.out.println(bank.getBankName()); // TPBank
}
}

Java 8 mang đến cho chúng ta rất nhiều tiện ích, các bạn hãy thử refactor code của mình sang Java 8 để code được gọn ràng hơn.

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

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

Xem thêm tuyển dụng công nghệ thông tin hấp dẫn trên TopDev

Sử dụng Flyway trong Spring Boot

flyway
Sử dụng Flyway trong Spring Boot

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

Mình đã giới thiệu với các bạn về Flyway để hiện thực database migration. Đối với các ứng dụng Spring Boot muốn sử dụng Flyway thì việc integrate này rất dễ dàng vì Spring Boot đã hỗ trợ. Cụ thể như thế nào? Chúng ta hãy cùng nhau tìm hiểu trong bài viết này các bạn nhé!

  Cách làm một ứng dụng Chat cho Android & iOS bằng Contus Fly như thế nào?

Xem thêm việc làm Spring Boot lương cao trên TopDev

Đầu tiên, mình sẽ tạo mới một Spring Boot project với Flyway, Spring Data JDBC và PostgreSQL dependencies như sau:

Kết quả:

Nếu kiểm tra thư mục src/main/resources trong project vừa tạo, các bạn sẽ thấy có một thư mục là db/migration. Đây chính là thư mục mặc định mà chúng ta sẽ chứa các tập tin SQL để Spring Boot với Flyway làm database migration đó các bạn!

Bây giờ, để làm ví dụ, mình sẽ tạo mới một tập tin SQL V2021.10.20.00000__Init.sql nằm trong thư mục src/main/resources/db/migration:

để tạo mới table student như sau:

CREATE TABLE student (
ID INT PRIMARY KEY,
name VARCHAR(250) NOT NULL
);

Việc tiếp theo chúng ta cần làm là cấu hình Datasource trong tập tin application.properties, ví dụ của mình như sau:

spring.datasource.url=jdbc:postgresql://localhost:5432/flyway_example
spring.datasource.username=khanh
spring.datasource.password=1

với flyway_example là tên database ví dụ của mình.

Bây giờ, nếu các bạn chạy ứng dụng và truy cập đến database trên, các bạn sẽ thấy kết quả như sau:

Table flyway_schema_history và cả table student mà chúng ta định nghĩa trong tập tin SQL đã được tạo.

Các bạn có thể thấy, việc sử dụng Flyway trong ứng dụng Spring Boot rất đơn giản, không tốn nhiều effort phải không các bạn?

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

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

Xem thêm tuyển dụng công nghệ hấp dẫn trên TopDev

ServletContextEvent và ServletContextListener trong Jakarta EE Servlet

jakarta ee servlet
ServletContextEvent và ServletContextListener trong Jakarta EE Servlet

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

ServletContextEvent và ServletContextListener là 2 đối tượng của Jakarta EE Servlet có nhiệm vụ ghi nhận và xử lý những thay đổi trong ServletContext của một ứng dụng web khi ứng dụng web này được deploy lên Server Runtime. Bất kỳ những thay đổi nào của ServletContext, ServletContextEvent cũng sẽ ghi nhận và ServletContextListener cũng sẽ thực hiện những thay đổi đó.

  Nói về ServletContext và ServletConfig trong Jakarta EE Servlet
  Định nghĩa request URL trong ứng dụng Jakarta EE RESTful Web Services

Xem thêm tuyển dụng Scala hấp dẫn trên TopDev

Những thay đổi này sẽ xảy ra lúc ứng dụng được deploy và lúc chúng ta stop ứng dụng. Các bạn có thể thêm code để khi ứng dụng chạy lên hoặc stop đi, một số thao tác sẽ được thực hiện tuỳ theo nhu cầu của mình. Để làm được điều này, các bạn hãy tạo mới một custom class implement ServletContextListener interface như sau:

package com.huongdanjava.jakartaee;

import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;

public class HuongDanJavaServletContextListener implements ServletContextListener {

@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed");
}

@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized");
}
}

Sau đó thì khai báo class custom này vào tập tin web.xml như sau:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">

<listener>
<listener-class>com.huongdanjava.jakartaee.HuongDanJavaServletContextListener</listener-class>
</listener>
</web-app>

Hoặc các bạn cũng có thể sử dụng annotation @WebListener với custom class trên:

package com.huongdanjava.jakartaee;

import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;

@WebListener
public class HuongDanJavaServletContextListener implements ServletContextListener {

@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed");
}

@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized");
}
}

Một trong 2 cách thôi các bạn nhé!

Lúc này, khi chạy ứng dụng, các bạn sẽ thấy code bên trong phương thức contextInitialized() được gọi như sau:

Nếu bạn stop Server Runtime, các bạn sẽ thấy code trong phương thức contextDestroyed() được gọi:

Những Thông Tin Cần Biết Về Vị Trí ICT Business Analyst

ict business analyst
Những Thông Tin Cần Biết Về Vị Trí ICT Business Analyst

Đi cùng với sự phát triển của thời đại công nghệ, vị trí Business Analyst cũng nổi lên như một yếu tố then chốt trong việc nghiên cứu và tìm hiểu giá trị thực sự của một doanh nghiệp. Trong nhóm ngành BA, ICT Business Analyst dường như vẫn là cái tên xa lạ với nhiều người. Vậy cụ thể thì công việc này là gì, cần có những kỹ năng và chuyên môn ra sao để đảm nhận chúng? Cùng TopDev tìm hiểu thêm về vị trí này với bài viết dưới đây.

ict business analyst
ICT Business Analyst và những điều cần biết

ICT Business Analyst là gì?

Với vị trí chuyên môn tương đồng với các Business Analyst khác, công việc của ICT Business Analyst cũng là phân tích các nghiệp vụ kinh doanh nhưng tập trung nhiều hơn vào các yếu tố liên quan đến phần mềm và truyền thông – Information and Communication Technology. Theo đó, các ICT BA sẽ tận dụng kiến thức của mình để xây dựng hệ thống thông qua việc phân tích và lên kế hoạch phát triển công nghệ cũng như đẩy mạnh các yếu tố truyền thông. Ngách làm việc chính của ICT BA là với các System Analysts.

Theo tài liệu của một số bên liên quan thì ICT BA sẽ sử dụng kỹ thuật mô hình hóa dữ liệu và quy trình để thiết lập nên một hệ thống cụ thể cho việc thiết kế cũng như phát triển phần mềm được trơn tru và hiệu quả hơn. Do đặc thù của vị trí này không chỉ liên quan đến phân tích nghiệp vụ mà còn cả công nghệ thông tin và truyền thông nên các ứng viên tốt nghiệp từ những chuyên ngành này cũng là ứng viên sáng giá cho vị trí ICT Business Analyst.

Công việc của một ICT Business Analyst

Công việc chính của một ICT BA sẽ là phụ trách trao đổi thông tin với người dùng cuối để nắm được các vấn đề mà họ đang gặp phải, tổng hợp thành các tài liệu nghiệp vụ cần thiết cho công việc.

  • Bằng các phương thức nghiệp vụ, các ICT BA phải xác định và đánh giá đúng tình trạng vấn đề mà công ty đang gặp phải để đưa ra hướng giải quyết thích hợp, giảm thiểu tối đa các sai sót có thể xảy ra cũng như nâng cao hiệu suất công việc.
  • Sử dụng các phương pháp nghiệp vụ để quản lý dự án đảm bảo đạt được kết quả tối đa, phát triển kế hoạch dự án với chi phí và nguồn lực hợp lý nhất.
  Business Analyst Cần Học Gì Để Trở Thành Chuyên Gia Trong Ngành?
  Công Việc Của Business Analyst Và Tất Tần Tật Các Thông Tin Cần Biết Về Nghề BA
  • Chịu trách nhiệm triển khai các dự án trên nhiều nền tảng với nhiều giải pháp khác nhau để kiểm thử chất lượng hệ thống và đảm bảo tính toàn vẹn ở mức cao nhất.
  • Triển khai chạy thử các giải pháp mới để tối ưu hiệu quả cho việc phát triển sản phẩm cũng như nâng cao trải nghiệm của người dùng.
  • Nghiên cứu và lên phương án cho các tài liệu để giới thiệu đến người dùng cũng như tiến hành các khóa đào tạo chính thức nếu cần thiết.
  • Chịu trách nhiệm chính trong việc đưa ra các tư vấn chính xác liên quan đến kỹ thuật và hỗ trợ truyền thông cho các giải pháp mới hoặc tối ưu các phương án ở thời điểm hiện tại.

Với những nhiệm vụ và các vai trò như thế, có thể khái quát sản phẩm cụ thể của một ICT Business Analyst là cho ra các quy trình nghiệp vụ đã được mô hình hóa và cung cấp các giải pháp cho hệ thống cũ, mới.

Học gì để trở thành ICT Business Analyst?

Để trở thành một ICT BA, việc lựa chọn các ngành học liên quan đến khoa học máy tính, công nghệ, phần mềm và hệ thống thông tin kinh tế sẽ giúp ích rất nhiều cho công việc của bạn sau này.

công việc ict business analyst
ICT Business Analyst học gì?

Cụ thể, trong quá trình học bạn sẽ được tiếp xúc với các kiến thức về kỹ thuật có thể áp dụng trong việc phân tích hệ thống. Các kiến thức về ứng dụng phần mềm, phần cứng, nghiệp vụ ICT và các giải pháp.

Bên cạnh đó, các kỹ năng mềm như kỹ năng quản lý dự án, kỹ năng làm việc nhóm, giải quyết vấn đề và kỹ năng đàm phán, thuyết phục là những điều bạn nên tìm hiểu qua nếu muốn công việc của mình diễn ra thuận lợi nhất sau này.

Xem thêm các việc làm Business Analyst lương cao trên TopDev

ICT Business Analyst là một ngành nghề mới, do đó để có thể theo đuổi ngành này một cách lâu dài và chuyên nghiệp nhất, bạn cần có sự nghiên cứu kỹ lưỡng và đầu tư thật sự cho nó. Hi vọng bài viết này sẽ giúp bạn tìm kiếm được một số thông tin cần thiết. Đón đọc thêm nhiều chủ đề hấp dẫn khác tại Topdev.vn/blog nhé!

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

Xem thêm công việc CNTT hấp dẫn trên TopDev

Appota tổ chức Webinar: “Giải Mã Nhân Lực Ngành IT”

Song hành với sự phát triển của công nghệ thì lĩnh vực IT cũng đang nhận được sự quan tâm rất lớn từ thị trường tuyển dụng. Chương trình: “Giải mã nhân lực ngành IT” diễn ra vào 15h ngày 22/10 sẽ đem đến cho các bạn trẻ, đặc biệt là các bạn sinh viên những góc nhìn thực tế về lĩnh vực này.

Dựa trên Báo cáo về thị trường IT Việt Nam 2021 của TopDev, đến năm 2022 Việt Nam sẽ còn cần đến 530.000 nhân lực trong ngành công nghệ thông tin. Tuy nhiên nguồn cung nhân lực sẽ chỉ đáp ứng được 72%. Thực tế là, số lượng ngành học về công nghệ thông tin ở các trường đại học đang mở rộng ngày càng nhiều cũng như số lượng cử nhân tốt nghiệp chuyên ngành này vẫn tăng cao qua mỗi năm, tại sao vẫn có sự chênh lệch này? Sự thiếu hụt này chủ yếu là do trình độ của lập trình viên và yêu cầu doanh nghiệp đặt ra vẫn chưa thực sự cân bằng với nhau. Trong số hơn 55.000 sinh viên công nghệ thông tin tốt nghiệp mỗi năm chỉ có khoảng 16.500 sinh viên (38%) đáp ứng được những kỹ năng và chuyên môn mà doanh nghiệp cần.

Vậy đâu là nguyên nhân dẫn đến tình trạng này, Webinar sẽ diễn ra dưới hình thức trực tuyến, phần nào đem đến câu trả lời và những nhận định từ các chuyên gia hàng đầu trong ngành với sự tham gia của: Anh Nguyễn Văn Vũ – CTO Appota, Anh Đặng Minh Tuấn – Head of PTIT Blockchain Lab, Tác giả phần mềm Vietkey, Anh Lê Ngọc Tuấn –  Founder Maker HaNoi, Anh Đặng Thái Sơn – CMO Appota (host).

Đầu tiên là khách mời – TS Đặng Minh Tuấn là tác giả của Vietkey, phần mềm gõ tiếng Việt đầu tiên trước khi UNICODE chính thức áp dụng. Nguyên là Đại tá, Phó Giám đốc Phòng thí nghiệm trọng điểm Quốc gia về An toàn thông tin, anh hiện đang giữ chức vụ Viện trưởng Viện Nghiên cứu Ứng dụng CMC; Trưởng phòng Lab Blockchain – Học viện Công nghệ Bưu chính Viễn thông; Phó Chủ tịch Câu lạc bộ FinTech của Hiệp hội Ngân hàng Việt Nam. Là Tiến sĩ chuyên ngành Toán và Mật mã, và nghiên cứu Blockchain từ 2014, anh Tuấn sở hữu nhiều kinh nghiệm về nhiều lĩnh vực Blockchain như Cryptography, Smart Contract, Consensus algorithms…

Anh Đặng Minh Tuấn – Head of PTIT Blockchain Lab, Tác giả phần mềm Vietkey

Tiếp đến, khách mời –  Anh Lê ngọc Tuấn là Founder Maker Hà Nội, là một chuyên gia về Robotic với 12 năm kinh nghiệm về lập trình nhúng, robot và các thiết bị điều khiển tự động. Ngoài ra, anh còn là thành viên Ban điều hành Mạng lưới khởi nghiệp IoT Việt Nam. Hiện tại anh đang công tác tại VinAI. Maker Hà Nội là cộng đồng những nhà sáng chế trong lĩnh vực công nghệ trên địa bàn Hà Nội nổi tiếng với các sản phẩm IoT và ứng dụng thực tiễn. Tổ chức này không chỉ phát triển thành công nhiều dự án IoT mà còn góp phần thúc đẩy tinh thần sáng tạo cho thế hệ trẻ thông qua hàng chục khóa đào tạo về phát triển robotic, sản phẩm nhà thông minh cho hàng trăm học sinh các cấp. Đặc biệt Maker Hanoi còn xây dựng các mã nguồn mở về IoT và trí tuệ nhân tạo cho cộng đồng các bạn sinh viên Việt Nam.

Anh Lê Ngọc Tuấn – Founder Maker HaNoi

Khách mời thứ 3, anh Nguyễn Văn Vũ – CTO của Appota. Với gần 15 năm kinh nghiệm trong ngành CNTT,  anh Vũ trực tiếp tham gia xây dựng và phát triển các sản phẩm, dự án ở nhiều lĩnh vực công nghệ như: Fintech; Saas; Blockchain, IOT… Các dự án tiêu biểu như: Giải pháp quản trị nhân sự ACheckin (dự án được vinh danh tại giải Sao Khuê 2021), Ví điện tử Appota, Dự án smarthome AppotaHome… Anh có vai trò quan trọng trong việc đưa Appota trở thành một trong những công ty tiên phong xây dựng hệ sinh thái sản phẩm, dịch vụ cho người dùng di động, đồng thời là một trong số đơn vị phát hành game lớn nhất tại Việt Nam.

Anh Nguyễn Văn Vũ – CTO Appota

Dẫn dắt chương trình – Anh Đặng Thái Sơn – Hiện nay đang là CMO của Appota đồng thời là Giám đốc dự án Appota Esport.  Trong 8 năm gắn bó cùng Appota, anh đã tham gia vào nhiều dự án khởi nghiệp, nghiên cứu thị trường, tổ chức sự kiện, chương trình hội thảo và hoạt động cộng đồng nhằm kết nối các startup Việt Nam và các đối tác nước ngoài. Anh cũng là người truyền cảm hứng, tạo dựng thương hiệu cho các sản phẩm công nghệ của Appota, đưa các sản phẩm đến gần hơn với người dùng. Trong chương trình, anh Sơn đóng vai trò (host), người dẫn dắt và kết nối các khách mời.

Anh Đặng Thái Sơn – CMO Appota

Với kinh nghiệm 10 năm hoạt động trong lĩnh vực Công nghệ, Appota luôn khao khát được truyền cảm hứng cho cộng đồng và mang đến nhiều kiến thức cho các bạn trẻ thông qua sự kiện lần này. Webinar: “Giải mã nhân lực ngành IT” chắc chắn sẽ cung cấp những thông tin, kiến thức thực tế, giúp các bạn trẻ hiểu rõ hơn về xu hướng cũng như kỹ năng cần thiết để phát triển trong ngành công nghệ. Bên cạnh đó, các bạn cũng sẽ được lắng nghe những chia sẻ về ngành CNTT từ các chuyên gia hàng đầu, giúp các bạn có những định hướng đúng đắn trong việc chọn môi trường phát triển phù hợp. Ngoài ra, chương trình cũng mang đến cơ hội tiếp xúc giữa doanh nghiệp và  ứng viên từ đó mở rộng cơ hội nghề nghiệp cho các bạn trẻ, cũng như đem đến nguồn nhân lực tiềm năng và ưu tú cho các đơn vị.

Đăng ký chương trình và gửi câu hỏi bạn cho chương trình và các diễn giả Tại đây: https://webinar.appota.com/

Những bước cơ bản cần làm ngay sau khi cài đặt WordPress

cài đặt wordpress
Những bước cơ bản cần làm ngay sau khi cài đặt WordPress

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

Chào các bạn, trong bài các bài viết hôm trước thì mình đã cùng các bạn cài đặt thành công WordPress và chạy một trang web WordPress thông qua server Xampp một cách khá là đơn giản rồi.

Vậy nên trong bài viết tiếp theo này, mình sẽ cùng các bạn thực hiện nốt một số thao tác cơ bản sau khi cài đặt.

Các thao tác này liên quan đến việc sử dụng theme, đăng bài viết và chỉnh sửa thông tin tài khoản quản trị. Okay, giờ thì bắt đầu thôi nhỉ !

  50 keywords mà mọi JAVA developer nên biết
  11 cách tăng tốc nhanh cho WordPress bằng file wp-conig.php

Xem thêm việc làm WordPress lương cao trên TopDev

#1. Khởi chạy server và truy cập vào web quản trị

Ban đầu, để có thể truy cập vào trang web người dùng, cũng như trang web quản trị thì các bạn lưu ý là phải mở Xampp Server và chạy cho mình hai module sau:

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (1)

Sau đó, để truy cập vào trang quản trị như hình bên dưới thì các bạn sẽ phải nhập thông tin đăng nhập bao gồm:

  1. Username hoặc Email mà các bạn đã đăng ký tài khoản.
  2. Mật khẩu của tài khoản đó.

Những thông tin này thì các bạn phải nhớ nhé, vì chúng ta sẽ phải sử dụng rất thường xuyên. Bạn nên lưu lại từ bước cài đặt trong quá trình cài đặt mà mình đã hướng dẫn ở bài viết trước.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (2)

#2. Thay đổi và tùy chỉnh theme (giao diện) cho trang web

Sau khi truy cập được vào trang web quản trị như bên dưới thì ở tab Appearance => các bạn chọn mục Theme.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (3)

Bên dưới là giao diện chính để các bạn thực hiện các thao tác quản lý theme cho trang web. Tại đây các bạn có thể:

  1. Tìm kiếm theme
  2. Biết được các theme đã được cài đặt
  3. Biết được theme nào đang được sử dụng cho trang web
  4. Cài đặt theme bằng cách bấm vào Upload Theme

Cụ thể các bạn xem hình bên dưới để dễ hình dung hơn. Thông thường các theme có sẵn trên WordPress là các theme miễn phí, tính năng hạn chế, nhưng bạn có thể sử dụng tạm để làm quen với WordPress cũng được.

Còn khi đã quen rồi và bạn quyết định đưa website của bạn lên Internet thì bạn có thể lên các chợ theme lớn như Themeforest, MOJO Marketplace, Mythemeshop, Elegant Themes, StudioPress…. để mua và sử dụng.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (4)

WordPress hỗ trợ hai hình thức cài đặt theme, đó là:

  • Bạn sẽ tìm kiếm trong danh sách các theme miễn phí có trên WordPress và tiến hành cài đặt như một Plugin.
  • Bạn có thể mua/ tải theme về máy tính => sau đó upload lên để cài đặt (đối với các theme trả phí bạn sẽ phải sử dụng phương pháp này).

——–// phương pháp cài theme trực tiếp //———

Với cách đầu tiên thì như hình bên dưới, các bạn có thể tìm một theme nào đó => rồi bấm Preview để xem trước theme đó như thế nào, hoặc bấm Install để cài đặt ngay lập tức.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (5)

Ví dụ như ở đây mình bấm Preview để xem trước thì lúc này trang web sẽ được chuyển hướng sang một trang khác để mình có thể xem trước giao diện theme đó sẽ như thế nào.

=> Nếu các bạn thấy theme đó phù hợp thì có thể bấm Install để tiến hành cài đặt.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (6)

Sau khi cài đặt xong, các bạn quay lại trang ban đầu. Tại đây, các bạn bấm Active để theme đó chính thức được sử dụng cho trang web của mình.

Lưu ý: Theme nào đang được sử dụng để hiển thị cho trang web sẽ có chữ Actived nha các bạn.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (7)

——–// phương pháp cài theme bằng phương pháp upload //———

Nhưng như mình đã nói bên trên rồi đó, thông thường các theme miễn phí của WordPress không đáp ứng được hết các nhu cầu sử dụng của người dùng.

Chính vì vậy chúng ta thường lựa chọn mua theme => rồi sau đó sử dụng tính năng Upload Theme để cài đặt theme đó cho trang web.

Để có thể tải và cài đặt theme thì các bạn có xem hình bên dưới. Theme cho WordPress thường sẽ được nén dưới dạng file *.zip. Các bạn chỉ cần tải theme lên rồi cài đặt là được.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (8)

Sau khi tải lên thì các bạn bấm Install Now để WordPress bắt đầu cài đặt.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (9)

Okay, quá trình cài đặt theme diễn ra nhanh hay chậm sẽ tùy thuộc vào dung lượng của theme các bạn tải lên. Theme càng nặng thì quá trình cài đặt có thể sẽ lâu hơn.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (10)

Sau khi cài đặt nếu các bạn muốn sử dụng theme đó cho trang web thì nhớ là phải bấm Active như hình bên dưới nhé.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (11)

#3. Tạo bài viết mới trên WordPress

Tạo bài viết mới là một trong những thao tác mà chúng ta thường xuyên phải làm. Để có thể tạo được bài viết mới thì các bạn chọn vào tab Posts như hình bên dưới.

Ở đây các bạn có thể:

  • Thêm mới bài viết bằng cách bấm vào Add New
  • Tìm kiếm bài viết
  • Xem danh sách các bài viết cũng như một số công cụ filter.

Cụ thể các bạn có thể xem ảnh bên dưới để dễ hình dung hơn.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (12)

Tiếp theo, để tạo bài viết mới thì các bạn bấm vào Add New. Phiên bản WordPress mình cài có thể sẽ hơi khác một chút so với các phiên bản trước ở phần này.

Cụ thể là ở các phiên bản trước, khi chúng ta tạo mới một bài viết thì sẽ không chuyển qua trang mới, nhưng ở phiên bản mới (phiên bản 5.8.1) thì nó sẽ chuyển qua một tab mới như bên dưới.

NOTE: Đối với những người dùng WordPress lâu năm, đã quá quen thuộc với giao diện WordPress cũ thì mọi người sẽ thường sẽ chuyển về giao diện soạn thảo cũ cho dễ làm.

Nhưng với những bạn mới làm quen với WordPress thì mình khuyên các bạn nên sử dụng giao diện soạn thảo mới này của WordPress. Bởi giao diện mới này sẽ có nhiều ưu điểm nổi bật hơn.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (13)

Sau khi hoàn thành bài viết, các bạn có thể bấm vào phần Preview để xem trước nội dung bài viết khi hiển thị lên web sẽ như thế nào.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (14)

Hình bên dưới chính là bài viết mình đang xem ở chế độ Preview (xem trước)..

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (15)

Nếu các bạn thấy bài viết ổn rồi thì hãy quay lại trang trước => và bấm Publish để xuất bản bài viết.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (16)

Vậy là bài viết được đã được xuất bản ở chế độ Public (công khai), các bạn có thể lên web để kiểm tra và đọc bài viết mà bạn vừa xuất bản.

Bạn có thể bấm vào nút View Post hoặc cũng có thể bấm Copy Link để sao chép lấy link trực tiếp của bài viết.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (17)

Quay lại màn hình chính, các bạn sẽ thấy bài viết vừa rồi đã được thêm vào danh sách. Ở đây các bạn hoàn toàn có thể bấm Edit để chỉnh sửa bài viết, hoặc Trash để xóa bài viết đó.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (18)

Nhưng tất nhiên, bài viết này mình chỉ giới thiệu cho các bạn biết cách để viết một bài viết mới và xuất bản bài viết. Còn bạn chưa nên bắt tay vào viết ngay nhé, vì chúng ta còn một mớ việc phải làm tiếp theo.

Như thiết lập đường link cho bài viết, tạo các chuyên mục, tạo Menu, Logo, và các Plugin hỗ trợ SEO….

#4. Chỉnh sửa thông tin tài khoản User trên WordPress

WordPress cho phép quản trị viên có quyền thêm tài khoản truy cập vào trang admin. Vì vậy, ở tab Users các bạn có thể tùy chỉnh các thông tin liên quan đến người được truy cập vào trang này.

Ở đây mình có một tài khoản là blogchiasekienthuc – tài khoản này có quyền admin. Các bạn có thể bấm vào để chỉnh sửa thông tin.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (19)

Đầu tiên là một số cài đặt liên quan đến giao diện và trải nghiệm người dùng. Các bạn có thể lựa chọn sao cho phù hợp là được.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (20)

Tiếp theo là phần cài đặt liên quan đến tên đăng nhập, phần này các bạn có thể hoặc không cần bổ sung cũng được.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (21)

Cuối cùng là phần các bạn có thể thay đổi ảnh đại diện (hiển thị kèm theo bài đăng) nếu bạn là tác giả của bài đăng đó hoặc đổi mật khẩu khi cần thiết.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (22)

#5. Lời kết

Vâng, như vậy là trong bài viết này mình đã hướng dẫn xong cho các bạn thực hiện một số thao tác cơ bản ngay sau khi các bạn cài đặt thành công WordPress rồi nhé.

Còn rất nhiều những thao tác quan trọng khác để chúng ta có thể hoàn thiện một trang web và mình sẽ cùng các bạn tìm hiểu trong các bài viết sau. Hẹn gặp lại các bạn trong những bài viết tiếp theo nhé !

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

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

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

Nói về ServletContext và ServletConfig trong Jakarta EE Servlet

jakarta ee servlet
Nói về ServletContext và ServletConfig trong Jakarta EE Servlet

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

ServletContext là một đối tượng được khởi tạo bởi Web Container khi chúng ta deploy ứng dụng Jakarta EE web application. Nó được sử dụng lưu trữ và lấy thông tin mà chúng ta cấu hình cho tất cả các Servlet có trong ứng dụng. Còn ServletConfig thì giúp chúng ta lấy thông tin được định nghĩa cho một Servlet cụ thể nào đó. Cụ thể như thế nào? Trong bài viết này, mình sẽ thảo luận với các bạn về ServletContext và ServletConfig trong Jakarta EE Servlet các bạn nhé!

  Tạo ứng dụng Jakarta EE MVC sử dụng Maven trong Eclipse
  Định nghĩa request URL trong ứng dụng Jakarta EE RESTful Web Services

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

Đầu tiên, mình sẽ mới một Jakarta EE Servlet Maven project để làm ví dụ:

Bây giờ, mình sẽ tạo mới 2 Servlet:

HelloWorldServlet

package com.huongdanjava.jakartaee.servlet;

import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloWorldServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// Writing the message on the web page
PrintWriter out = resp.getWriter();
out.println("Hello Servlet");
}
}

và HuongDanJavaServlet:

package com.huongdanjava.jakartaee.servlet;

import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HuongDanJavaServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// Writing the message on the web page
PrintWriter out = resp.getWriter();
out.println("Huong Dan Java Servlet");
}
}

rồi khai báo chúng trong tập tin web.xml như sau:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">

<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.huongdanjava.jakartaee.servlet.HelloWorldServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

<servlet>
<servlet-name>huongdanjavaServlet</servlet-name>
<servlet-class>com.huongdanjava.jakartaee.servlet.HuongDanJavaServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>huongdanjavaServlet</servlet-name>
<url-pattern>/huongdanjava</url-pattern>
</servlet-mapping>
</web-app>

Kết quả khi chạy ứng dụng, request tới http://localhost:8080/hello như sau:

Và request tới http://localhost:8080/huongdanjava như sau:

Nếu các bạn định nghĩa thông tin cho tất cả các Servlet trong ứng dụng sử dụng một hoặc nhiều tag <context-param> trong tập tin web.xml như sau:

<context-param>
<param-name>name</param-name>
<param-value>Khanh</param-value>
</context-param>

<context-param>
<param-name>website</param-name>
<param-value>Huong Dan Java</param-value>
</context-param>

Khi đó, trong cả 2 Servlet mà mình định nghĩa ở trên đều có thể lấy được thông tin cấu hình này bằng cách sử dụng đối tượng ServletContext, ví dụ như sau:

package com.huongdanjava.jakartaee.servlet;

import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloWorldServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// Writing the message on the web page
PrintWriter out = resp.getWriter();
out.println("Hello Servlet");

ServletContext servletContext = getServletContext();
out.println(servletContext.getInitParameter("name"));
out.println(servletContext.getInitParameter("website"));
}
}

Hay:

package com.huongdanjava.jakartaee.servlet;

import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HuongDanJavaServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// Writing the message on the web page
PrintWriter out = resp.getWriter();
out.println("Huong Dan Java Servlet");

ServletContext servletContext = getServletContext();
out.println(servletContext.getInitParameter("name"));
out.println(servletContext.getInitParameter("website"));
}
}

Phương thức getServletContext() sẽ trả về đối tượng ServletContext được manage bởi Web Container mà ứng dụng đang được deploy, được khởi tạo lúc chúng ta deploy ứng dụng.

Kết quả:

Hay:

Còn nếu các bạn định nghĩa thông tin cho một Servlet cụ thể nào đó bằng cách khai báo tag <init-param> bên trong tag <servlet>, thì thông tin này sẽ không available với các Servlet khác của ứng dụng và chúng ta lấy những thông tin này sử dụng ServletConfig.

Ví dụ bây giờ, mình khai báo tập tin web.xml như sau:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">

<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.huongdanjava.jakartaee.servlet.HelloWorldServlet</servlet-class>
<load-on-startup>1</load-on-startup>

<init-param>
<param-name>name</param-name>
<param-value>Khanh</param-value>
</init-param>

<init-param>
<param-name>website</param-name>
<param-value>Huong Dan Java</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

<servlet>
<servlet-name>huongdanjavaServlet</servlet-name>
<servlet-class>com.huongdanjava.jakartaee.servlet.HuongDanJavaServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>huongdanjavaServlet</servlet-name>
<url-pattern>/huongdanjava</url-pattern>
</servlet-mapping>
</web-app>

thì với đoạn code ở trên, chúng ta sẽ không lấy được thông tin context-param nữa:

Và:

Các bạn có thể lấy thông tin được khai báo trong HelloServlet bằng cách sử dụng ServletConfig như sau:

package com.huongdanjava.jakartaee.servlet;

import jakarta.servlet.ServletConfig;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloWorldServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// Writing the message on the web page
PrintWriter out = resp.getWriter();
out.println("Hello Servlet");

ServletConfig servletConfig = getServletConfig();
out.println(servletConfig.getInitParameter("name"));
out.println(servletConfig.getInitParameter("website"));
}
}

Kết quả:
Request tới http://localhost:8080/huongdanjava, các bạn sẽ thấy kết quả như sau:

Còn nhiều thông tin khác mà các bạn có thể cấu hình với Jakarta EE Servlet và cũng có thể sử dụng ServletContext và ServletConfig để lấy những thông tin này các bạn nhé!

Điều gì đã thu hút hơn 700 nhân sự tham gia vào “Đại Dự Án Lịch Sử” của CMC Global chỉ sau 3 tháng?

ĐẠI DỰ ÁN S1K được xem là một dự án mang tính lịch sử và được chú trọng nhất tại CMC Global với quy mô tương đương một công ty lớn của Việt Nam! Điều gì đã khiến Đại dự án này được các tài năng công nghệ đặc biệt quan tâm đồng thời được CMC Global đầu tư một khoản tiền khổng lồ đến như vậy. Hãy cùng tìm hiểu những sự thật về “đại dự án” mang tính lịch sử này của CMC Global trong nội dung tiếp theo của bài viết.

S1K – Đại dự án công nghệ với quy mô tầm cỡ một công ty lớn tại Việt Nam?

Xét về quy mô dự án, đại dự án S1K của CMC Global với tổng quy mô lên đến 1000 nhân sự. Nếu chỉ đánh giá tiêu chí về số lượng nhân sự thì S1K quy tụ các tài năng công nghệ với số lượng gấp 3 lần số lượng nhân sự của một doanh nghiệp cỡ lớn. Bên cạnh đó, đại dự án S1K được CMC Global tiết lộ rằng dự án với tổng giá trị thậm chí lên đến hàng triệu đô la. Đây là con số ấn tượng đối với một dự án công nghệ tại Việt Nam.

Đại dự án S1K gồm 6 domain đa dạng lĩnh vực: Finance, Healthcare, Security, Logistic, Intelligent Factory, Cloud. Đây cũng chính là một điểm thú vị của S1K. Các nhân sự tham gia vào dự án sẽ được đối mặt với các bài toán đa dạng lĩnh vực. Đây là một thách thức nhưng đồng thời cũng là một cơ hội để các tài năng thực sự có được cơ hội khẳng định năng lực bản thân và tỏa sáng thay vì chỉ thực hiện những công việc đơn điệu.

Các dự án của CMC Global được trang bị hệ thống, công cụ làm việc tối tân, ứng dụng công nghệ hiện đại để hỗ trợ tối đa cho công việc của nhân viên. Chính sự đầu tư với quy mô tầm cỡ, tập trung cùng với nguồn nhân lực tài năng và mạnh mã đã giúp đại dự án này của CMC Global thu về một kết quả tăng trưởng lên tới 200% cho G1 Division.

Điều gì ở dự án S1K thu hút đông đảo nhân sự trong ngành IT chỉ sau 3 tháng?

Bên cạnh cơ hội được thể hiện năng lực bản thân, chấp nhận các thử thách và nâng cao kỹ năng, đại dự án S1K của CMC Global còn đưa ra những chế độ đãi ngộ cực kì hấp dẫn dành cho nhân viên của dự án.

Đầu tiên là mức thu nhập hấp dẫn được đưa ra dựa trên năng lực của ứng viên với mức lương tối đa lên đến 50,000,000 đồng mỗi tháng và kèm theo đó là thưởng hiệu suất năm với mức từ 2 đến 3 tháng lương. Với tinh thần coi trọng nhân tài thì đây là một cơ hội để các ứng viên có thể đưa ra những mức lương phù hợp với năng lực thực sự của mình dù bạn làm việc trong bất cứ mảng công việc nào.

Một điểm đặc biệt khi ứng tuyển cho đại dự án S1K của CMC Global đó chính là quyền lợi đồng hành với mức Singing Bonus lên tới 1 THÁNG LƯƠNG. Các ứng viên sẽ được ký hợp đồng ngay mà không cần thông qua quá trình thử việc.

Cùng với các mức lương thưởng hấp dẫn thì ở S1K các nhân viên của dự án sẽ nhận được chế độ đãi ngộ toàn diện bao gồm:

  • Gói phúc lợi Lễ, Tế lên tới 18,000,000 đồng mỗi năm.
  • Gói bảo hiểm CMC-Care, tiêm vaccine Covid, hỗ trợ mùa dịch, đào tạo,…
  • Cơ hội làm việc và phát triển cùng đối tác tập đoàn công nghệ hàng đầu thế giới, được học hỏi từ PM, chuyên gia giàu kinh nghiệm, ứng dụng công nghệ mới nhất.

Những “mảnh ghép” còn lại trong dự án S1K gồm những vị trí nào?

JAVA DEVELOPER

Các công việc của vị trí này gồm:

  • Phân tích yêu cầu, điều tra, đề xuất giải pháp
  • Tạo thiết kế chi tiết, lập trình và thử nghiệm.
  • Báo cáo tình trạng, vấn đề cho khách hàng và người quản lý trực tiếp

Để ứng tuyển cho vị trí này CMC Global yêu cầu ứng viên:

  • Có tối thiểu 2 năm kinh nghiệm phát triển phần mềm với ngôn ngữ lập trình: Java / Springboot
  • Có kinh nghiệm với Oracle, PL / SQL là một điểm cộng
  • Có kinh nghiệm với Javascript Framework là một lợi thế
  • 3-7 năm kinh nghiệm liên quan trong phát triển web / java
  • Có kinh nghiệm về Tuning
  • Thành thạo với Git / Jenkin

VUEJS DEVELOPER

Các công việc của vị trí này gồm:

  • Thiết kế, phát triển, coding, kiểm tra, sửa đổi, cập nhật, sửa lỗi các ứng dụng Vue.js (fix bug)
  • Lập kế hoạch, tổ chức và phát triển user-facing features cho các component trong dynamic platform của tổ chức
  • Viết và tối ưu hóa code của các web application của phía client, tạo một ứng dụng nhanh với UI/UX tốt
  • Xây dựng modular, các component và các thư viện có thể tái sử dụng
  • Phân tích và tối ưu hóa code của ứng dụng hướng đến hiệu quả và hiệu suất
  • Thường xuyên cập nhật tất cả các sự phát triển mới nhất của Javascript và Vue.js
  • Theo dõi các bản cập nhật bảo mật và các vấn đề được tìm thấy với Vue.js và tất cả các phần phụ thuộc của dự án
  • Đề xuất bất kỳ nâng cấp và cập nhật nào cần thiết để theo kịp các best practice về bảo mật và phát triển hiện đại
  • Có kỹ năng làm việc nhóm tốt và tư duy cởi mở

Để ứng tuyển cho vị trí này CMC Global yêu cầu ứng viên:

  • Có hơn 3 năm kinh nghiệm phát triển ứng dụng Vue.js
  • Rất thành thạo với ngôn ngữ Javascript, ES6+ syntax và features hiện đại của JS
  • Rất thành thạo với Vue.js framework và các phần tử cốt lõi của nó như component, reactivity và virtual DOM
  • Quen thuộc với Vue.js ecosystem, bao gồm Veu CLI, Vuex, Vue Router và Nuxt.js
  • Hiểu rõ về HTML5, CSS3, bao gồm cả SASS hoặc LESS
  • Hiểu về server-side rendering, các lợi ích và use case của nó
  • Có kiến thức về functional programming và các object-oriented programming paradigm
  • Khả năng viết code javascript hiệu quả, an toàn, được tài liệu tốt và sạch sẽ
  • Có kinh nghiệm với Phát triển Agile, ITIL là một điểm cộng

SYSTEM DEVELOPER

Công việc chính của vị trí này là quản lý hạ tầng cho Hệ thống Cloud của khách hàng:

  • Incident/Problem/Change/Security/SR Management theo tiêu chuẩn ITSM process.
  • Giám sát hệ thống server (Zabbix, Grafana …) gồm:
    • Windows, Linux (Redhat, CentOS).
    • DBMS (Oracle, PostgreSQL).
    • Giám sát hệ thống thời gian thực, phân tích nguyên nhân và xử lý sự cố trong trường hợp hệ thống bị lỗi
    • Hỗ trợ sao lưu và phục hồi DATA hệ thống
    • Quản lý thông tin cấu hình hệ thống và quản lý sổ tay vận hành hệ thống
    • Hoạt động thay đổi hệ thống
    • Quản lý bảo mật hệ thống
    • Cài đặt và cấu hình hệ thống mới
  • Quản lý VMWare VSphere bao gồm cấu hình việc tạo / thay đổi / xóa VM và môi trường theo các quy trình tiêu chuẩn
  • Bảo trì cấu hình

Để ứng tuyển cho vị trí này CMC Global yêu cầu ứng viên:

  • Có ít nhất 3 năm ở vị trí System Engineer, quản lý hạ tầng CNTT
  • Có kinh nghiệm quản trị Window Server, CentOS, Ubuntu, RedHat OS
  • Kinh nghiệm quản trị VMware VSphere, VCenter
  • Cài đặt và vận hành Công cụ Sao lưu / Khôi phục (Veritas Netbackup)
  • Ứng viên phải có kinh nghiệm ít nhất 3 trong số dưới đây:
    • Cấu hình tự động hóa Ansible là một điểm cộng lớn
    • Quản lý và thiết lập công cụ giám sát (Zabbix, Grafana, ELK…)
    • Áp dụng các bản vá hệ thống và nâng cấp phần mềm
    • Cấu hình và hỗ trợ storage-based data replication
    • Cung cấp và quản lý các volume được trình bày cho Linux, Unix
    • Hỗ trợ các chiến lược HA và DR với các công nghệ lưu trữ.
  • Khả năng làm việc độc lập và xác định các cơ hội để cải tiến quy trình.
  • Mong muốn học các kỹ năng mới, đảm nhận các nhiệm vụ mới khi cần thiết và làm việc như một phần của nhóm.

DEVOPS ENGINEER

Các công việc của vị trí này gồm:

  • Phát triển Ansible installation playbook
  • Cấu hình, quản trị CI/CD utilities
  • Chủ động giám sát hiệu suất hệ thống và lập kế hoạch năng lực (capacity planning)
  • Triển khai ứng dụng thông qua CI/CD
  • Phát triển một bộ tài liệu hoàn chỉnh mô tả cấu hình hiện tại của hệ thống
  • Cài đặt, cấu hình và điều chỉnh hệ thống web, ứng dụng và cơ sở dữ liệu
  • Tổng hợp và thống kê các báo cáo hàng ngày từ Pinpoint và Grafana
  • Hỗ trợ nhóm phát triển khắc phục sự cố và giải quyết các sự cố hệ thống (Web, WAS, Middleware, DB)
  • Làm việc được dưới điều kiện môi trường hạn chế, không có camera và làm việc trong môi trường Virtual Desktop Interface (VDI)

Để ứng tuyển cho vị trí này CMC Global yêu cầu ứng viên:

  • Có tối thiểu 2 năm kinh nghiệm quản lý hệ thống Linux
  • Có kinh nghiệm với CI/CD Tool: Jenkins / Docker / Ansible / K8S / Sonarqube….
  • Quản lý kiểm soát nguồn bao gồm GIT và SVN
  • Viết script và tự động hóa bằng Python / Groovy / Java / Bash.
  • Có 1-2 năm kinh nghiệm với Tomcat hoặc Apache hoặc Jboss hoặc Nginx
  • Có tối thiểu 1 năm với MySQL hoặc MariaDB
  • Kiến thức mạng cơ bản.
  • Sẵn sàng học hỏi công nghệ mới.

CMC Global yêu cầu ứng viên có các kỹ năng bắt buộc về:

  • OS: CentOS, RHEL, Ubuntu
  • Kiến thức về Java/JVM based languages.
  • WEB/WAS platform : Apache, Tomcat
  • Monitor: Zabbix, Pinpoint, Grafana.
  • CI/CD: Jenkins, Ansible.
  • Database: MySQL, MariaDB, NoSQL.
  • Cloud: AWS, Azure

Chi tiết về Chương trình tuyển dụng Dự án S1K tại địa chỉ https://s1k.cmcglobal.com.vn

#2 Lập trình Golang ăn xổi: Clean architecture

Clean architecture
Clean architecture

Bài viết được sự cho phép của tác giả Lê Xuân Quỳnh

Trong bài #1, tôi đã nói qua loa về mô hình này. Tuy nhiên, sau một khoảng thời gian đọc hiểu các bài viết từ các site nước ngoài, tôi nhận ra là Clean architecture trong Golang nó khác nhiều so với lập trình iOS mà tôi đã triển khai. Lý do viết bài này hơi chậm là do tôi cần thời gian đọc hiểu, ngâm cứu. Nói là ăn xổi nhưng mà cũng phải từ từ mới chén được các bạn à. Okey, vậy hôm nay chúng ta có gì?

  Channel trong Golang là gì? So sánh Callback function và mutex lock với channel
  Clean Architecture: Đứng trên vai những gã khổng lồ

Xem thêm tuyển dụng Golang hấp dẫn trên TopDev

Clean architecture trong Golang.

Trong bài 1 tôi có đưa các bạn 1 đường dẫn Github. Tuy nhiên sau 1 tuần tôi mới biết là những thứ tôi viết đó còn thô sơ lắm, nếu đưa triển khai cho các bạn thì ắt hẳn chúng ta sẽ hơi hổng 1 số lượng kiến thức. Do vậy mà tôi sẽ dùng Repository mới ở đây của 1 developer nước ngoài:

https://github.com/eminetto/clean-architecture-go-v2

Và tôi cũng mượn chính bài viết của các giả tại đây để viết cho bài viết của tôi, nói đúng ra là tôi dịch.

Các bạn cứ tải source về và nghiên cứu vì nó xịn hơn source mà tôi viết. Một số bài viết khác mà bạn có thể đọc thêm ở đây, do các pro dev viết:

https://dev.to/aleksk1ng/my-first-go-rest-api-3bl3

Tuy nhiên, với bài viết trên thì khó cho người mới lắm. Họ cho code nhưng không giải thích thì cũng khó mà nắm được đúng không nào?

Bây giờ chúng ta sẽ đi vào thành phần đầu tiên của Clean architecture:

Entity Layer

Hay nói cách khác là lớp model. Đây là lớp trong cùng của kiến trúc Clean architecture.

Theo bài đăng của tác giả mô hình này – Uncle Bob:

  • Các Entities chứa đựng quy tắc của business ứng dụng. Nó có thể là các objects của phương thức sử dụng, tập hợp các cấu trúc data hay các functions. Nó được sử dụng bởi nhiều ứng dụng trong 1 công ty.

Cấu trúc entity như sau:

entity

Ở package trên, các entities gồm book, book_test, entity.. Ví dụ với user như sau:

package entity

import (
	"time"

	"golang.org/x/crypto/bcrypt"
)

//User data
type User struct {
	ID        ID
	Email     string
	Password  string
	FirstName string
	LastName  string
	CreatedAt time.Time
	UpdatedAt time.Time
	Books     []ID
}

//NewUser create a new user
func NewUser(email, password, firstName, lastName string) (*User, error) {
	u := &User{
		ID:        NewID(),
		Email:     email,
		FirstName: firstName,
		LastName:  lastName,
		CreatedAt: time.Now(),
	}
	pwd, err := generatePassword(password)
	if err != nil {
		return nil, err
	}
	u.Password = pwd
	err = u.Validate()
	if err != nil {
		return nil, ErrInvalidEntity
	}
	return u, nil
}

//AddBook add a book
func (u *User) AddBook(id ID) error {
	_, err := u.GetBook(id)
	if err == nil {
		return ErrBookAlreadyBorrowed
	}
	u.Books = append(u.Books, id)
	return nil
}

//RemoveBook remove a book
func (u *User) RemoveBook(id ID) error {
	for i, j := range u.Books {
		if j == id {
			u.Books = append(u.Books[:i], u.Books[i+1:]...)
			return nil
		}
	}
	return ErrNotFound
}

//GetBook get a book
func (u *User) GetBook(id ID) (ID, error) {
	for _, v := range u.Books {
		if v == id {
			return id, nil
		}
	}
	return id, ErrNotFound
}

//Validate validate data
func (u *User) Validate() error {
	if u.Email == "" || u.FirstName == "" || u.LastName == "" || u.Password == "" {
		return ErrInvalidEntity
	}

	return nil
}

//ValidatePassword validate user password
func (u *User) ValidatePassword(p string) error {
	err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(p))
	if err != nil {
		return err
	}
	return nil
}

func generatePassword(raw string) (string, error) {
	hash, err := bcrypt.GenerateFromPassword([]byte(raw), 10)
	if err != nil {
		return "", err
	}
	return string(hash), nil
}

Với entity này, chúng ta có các business như:

  • Tạo 1 user mới với email, password, firstName, lastName.
  • Thêm/xóa/lấy ra 1 cuốn sách Book bởi 1 user theo id của sách.
  • Kiểm tra 1 user có hợp lệ không thông qua các parameters của nó.
  • Tạo 1 mật khẩu hash từ mật khẩu dạng plan text chưa qua mã hóa.

Phần tiếp theo là:

Use Case Layer

Theo tác giả Uncle Bob:

Phần mềm trong lớp này chứa các quy tắc nghiệp vụ cụ thể của ứng dụng. Nó đóng gói và triển khai tất cả các trường hợp sử dụng của hệ thống.

Nó có thể trông như sau:

domain

Các package trong usercase này sẽ triển khai các quy tắc của sản phẩm, ví dụ với quy tắc loan – cho mượn, file service.go sẽ như sau:

package loan

import (
	"github.com/eminetto/clean-architecture-go-v2/entity"
	"github.com/eminetto/clean-architecture-go-v2/usecase/book"
	"github.com/eminetto/clean-architecture-go-v2/usecase/user"
)

//Service loan usecase
type Service struct {
	userService user.UseCase
	bookService book.UseCase
}

//NewService create new use case
func NewService(u user.UseCase, b book.UseCase) *Service {
	return &Service{
		userService: u,
		bookService: b,
	}
}

//Borrow borrow a book to an user
func (s *Service) Borrow(u *entity.User, b *entity.Book) error {
	u, err := s.userService.GetUser(u.ID)
	if err != nil {
		return err
	}
	b, err = s.bookService.GetBook(b.ID)
	if err != nil {
		return err
	}
	if b.Quantity <= 0 {
		return entity.ErrNotEnoughBooks
	}

	err = u.AddBook(b.ID)
	if err != nil {
		return err
	}
	err = s.userService.UpdateUser(u)
	if err != nil {
		return err
	}
	b.Quantity--
	err = s.bookService.UpdateBook(b)
	if err != nil {
		return err
	}
	return nil
}

//Return return a book
func (s *Service) Return(b *entity.Book) error {
	b, err := s.bookService.GetBook(b.ID)
	if err != nil {
		return err
	}

	all, err := s.userService.ListUsers()
	if err != nil {
		return err
	}
	borrowed := false
	var borrowedBy entity.ID
	for _, u := range all {
		_, err := u.GetBook(b.ID)
		if err != nil {
			continue
		}
		borrowed = true
		borrowedBy = u.ID
		break
	}
	if !borrowed {
		return entity.ErrBookNotBorrowed
	}
	u, err := s.userService.GetUser(borrowedBy)
	if err != nil {
		return err
	}
	err = u.RemoveBook(b.ID)
	if err != nil {
		return err
	}
	err = s.userService.UpdateUser(u)
	if err != nil {
		return err
	}
	b.Quantity++
	err = s.bookService.UpdateBook(b)
	if err != nil {
		return err
	}

	return nil
}

Ở file trên các rule sau được triển khai:

  • Mượn sách bởi 1 người dùng.
  • Trả 1 cuốn sách bởi người dùng.

Tiếp theo ta cùng nghiên cứu lớp:

Frameworks and Drivers layer

Theo tác giả Uncle Bob:

Lớp ngoài cùng thường chứa các frameworks và tools như Database, Web Framework… Lớp này chứa tất cả chi tiết của go:

driver

Cho ví dụ, với file infrastructure/repository/user_mysql.go, chúng ta triển khai các interface Repository của MySQL. Nếu chúng ta muốn chuyển đổi sang cơ sở dữ liệu mới, đây là nơi chuyển đổi.

Tiếp theo là lớp:

Interface Adapters layer

Codes ở lớp này được thích ứng và chuyển đổi dữ liệu tới format được sử dụng bởi các entities và các use cases cho việc mở rộng bởi các tác nhân bên ngoài như như databases, web… Ở lớp Application, có 2 cách để truy cập tới các UseCases. Đầu tiên là các API và thứ 2 là các command line của ứng dụng CLI.

Cấu trúc của CLI đơn giản như sau:

cliVí dụ nó được sử dụng bởi domain packages để thực hiện việc tìm kiếm sách:
dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:3306)/%s?parseTime=true", config.DB_USER, config.DB_PASSWORD, config.DB_HOST, config.DB_DATABASE)
db, err := sql.Open("mysql", dataSourceName)
if err != nil {
	log.Fatal(err.Error())
}
defer db.Close()
repo := repository.NewBookMySQL(db)
service := book.NewService(repo)
all, err := service.SearchBooks(query)
if err != nil {
	log.Fatal(err)
}
for _, j := range all {
	fmt.Printf("%s %s \n", j.Title, j.Author)
}

Trong ví dụ trên, bạn có thể thấy được cách sử dụng bởi package config.

configCấu trúc API thường phức tạp hơn với 3 packages: handler, presenter và middleware.

Package handler bao gồm các requests và responses, cũng như các quy tắc có sẵn của business ở usecases:

handlerLớp presenters chịu trách nhiệm định dạng dữ liệu sinh ra giống như response bởi các handlers.
presenterTheo cách này, với entity User:
type User struct {
	ID        ID
	Email     string
	Password  string
	FirstName string
	LastName  string
	CreatedAt time.Time
	UpdatedAt time.Time
	Books     []ID
}

Nó sẽ được chuyển đổi thành:

type User struct {
	ID        entity.ID `json:"id"`
	Email     string    `json:"email"`
	FirstName string    `json:"first_name"`
	LastName  string    `json:"last_name"`
}

Điều này cho phép chúng ta kiểm soát 1 entity sẽ được cung cấp thông qua API.

Trong packages cuối cùng của API là các middlewares, được sử dụng bởi các endpoints:

middlwareLớp tiếp theo là:

Support packages

Chúng là các packages cung cấp các hàm dùng chung như mã hóa, logging, xử lý files,… Chúng là các tính năng không thuộc domain của ứng dụng, và tất cả các lớp đều có thể sử dụng chúng. Ngay cả các ứng dụng khác cũng có thể import và sử dụng các packages này.

pkgHãy xem thêm ở README.md để hiểu chi tiết hơn, chẳng hạn hướng dẫn cách xây dựng và sử dụng ví dụ này.

Nếu bạn yêu thích Golang thì bài viết này là 1 ví dụ tuyệt vời để học nó.

Xem thêm nhiều bài viết tại www.facebook.com/codetoanbug

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

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

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

Định nghĩa request URL trong ứng dụng Jakarta EE RESTful Web Services

request url
Định nghĩa request URL trong ứng dụng Jakarta EE RESTful Web Services

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

Trong ứng dụng Jakarta EE RESTful Web Services, để định nghĩa request URL, chúng ta sẽ sử dụng annotation @Path. Annotation này chỉ có duy nhất một attribute là value() với giá trị là request URL mà chúng ta muốn định nghĩa.

Các bạn có thể định nghĩa annotation @Path ở class level hoặc method level.

Định nghĩa ở class level là bắt buộc các bạn nhé!

  Tạo ứng dụng Jakarta EE MVC sử dụng Maven trong Eclipse
  API là gì? Các nguyên tắc xây dựng Rest API

Xem thêm nhiều việc làm Agile lương cao trên TopDev

Ở class level thì giá trị được định nghĩa cho annotation này sẽ là prefix cho tất cả các request được định nghĩa bên trong class.

Ví dụ như mình có ứng dụng định nghĩa request URL như sau:

package com.huongdanjava.jaxrs;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/app")
public class HelloResource {

@GET
@Path("hello")
public String hello() {
return "Hello World!";
}

@GET
@Path("login")
public String login() {
return "Login";
}
}

thì các bạn có thể request tới ứng dụng này với các request URL như sau:

/app/hello
/app/login

Nếu các bạn không muốn định nghĩa prefix cho các request URL bên trong class thì có thể định nghĩa annotation @Path với giá trị @Path(“/”) hoặc @Path(“”) các bạn nhé!

Định nghĩa annotation @Path ở method level giúp chúng ta chỉ định rõ request URL nào được handle bởi method nào.

Để định nghĩa path parameter cho các request URL, chúng ta sẽ định nghĩa cặp “{“, “}” với tên parameter ở giữa cặp này trong annotation @Path.

Giá trị của parameter sẽ được bind vào request URL sử dụng annotation @PathParam, ví dụ như sau:

package com.huongdanjava.jaxrs;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;

@Path("/")
public class HelloResource {

@GET
@Path("/hello/{name}")
public String hello(@PathParam("name") String name) {
return "Hello, " + name;
}
}

Trong ví dụ trên, mình đã định nghĩa một path parameter tên là name.

Kết quả:
Để định nghĩa request parameter cho các request URL, chúng ta sẽ sử dụng annotation @QueryParam.

Ví dụ như sau:

package com.huongdanjava.jaxrs;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;

@Path("/")
public class HelloResource {

@GET
@Path("/hello")
public String hello(@QueryParam("name") String name) {
return "Hello, " + name;
}
}

Với ví dụ này thì mình đã định nghĩa một query parameter tên là name.

Kết quả:

Jakarta EE RESTful Web Service còn hỗ trợ chúng ta định nghĩa request URL với matrix param, sử dụng annotation @MatrixParam.

Matrix param giúp chúng ta có thể định nghĩa value của parameter phức tạp hơn, như là một collection chẳng hạn.

Các matrix parameter này ngăn cách nhau bởi dấu chấm phẩy.

Ví dụ, mình định nghĩa request URL như sau:

package com.huongdanjava.jaxrs;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.MatrixParam;
import jakarta.ws.rs.Path;
import java.util.List;

@Path("/")
public class HelloResource {

@GET
@Path("/hello")
public String hello(@MatrixParam("name") List<String> names,
@MatrixParam("address") List<String> address) {
return "Hello, " + names.toString() + ", " + address.toString();
}
}

Nếu bây giờ, mình request tới URL http://localhost:8080/hello;name=khanh,Java;address=A,B, các bạn sẽ thấy kết quả như sau: