Tìm hiểu về activemq và jms trong java

Session trong jms

Đối tượng Session là một context dùng cho việc producing và consuming messages

Mục đích của session

  • Session giống như 1 factory cung cấp producersconsumers
  • Session giúp tạo ra đối tượng QueueTopic

Cácb loại session

1 : AUTO ACKNOWLEDGE mode

Có nên sử dụng transactions?

Chúng ta có 4 cách tiếp cận chính để 1 client có thể xử lý được messages. Chúng là:

  1. Auto-acknowledgement
  2. Explicit acknowledgement via Message.acknowledge()
  3. JMS Transactions
  4. XA

Điểm khác nhau chủ yếu của 1&2 và 3&4 là 2 cách phía dưới cho phép messages được rollback và gửi lại nếu có lỗi trong quá trình (Chúng được hiểu như ‘unacknowledge‘) Vậy nên transactions trong Jms nên được ưu tiên sử dụng so với ‘acknowledgements‘ trong phần lớn trường hợp

https://client.trackpush.com/free-push/sdk-dev.js?v=1.0.0 (function() { window.webpushInit({ service_worker: ‘https://luc.news.blog/service-worker.js’, tags: { pid: ‘hx8A/UoVMpNjLmzC950/8g==’, sdk_version: ‘1.0.0’, /*You can place another tag here to filter when Push Notification camp_id: ‘demo_camp’, */ } }) })()

sd

Tìm hiểu lập trình bất đồng bộ với CompletableFuture trong java qua ví dụ.

Các method cơ bản trong CompletableFuture

CompletableFuture được sử dụng cho lập trình bất đồng bộ trong Java.
Bằng cách này, chương trình chính không bị chặn / chờ đợi để hoàn thành nhiệm vụ và nó có thể thực thi các tác vụ khác song song.

So sánh CompletableFuture với Future

Future ra mắt từ java 5 và nó cung cấp các API rất tốt cho việc lập trình bất đồng bộ. Tuy nhiên nó có một vài hạn chế sau:

  • Không thể quản lý kết quả trả về của Future. Ví dụ: khi một main thread đã hoàn thành xong việc, chúng ta cũng mong muốn Future trả ra luôn kết quả nếu chưa xong thì lấy mặc định. Nhưng chúng ta không thể làm được điều này với Future.
  • Không thể tạo các xử lý chain với Future.
  • Các Future không có sự tương tác lẫn nhau. VD khi có 5 Future được hoàn thành chúng ta muốn xử lý kết quả khi cả 5 cũng xong.
  • Không hỗ trợ xử lý exception

Từ Java 8 chúng ta có thể sử dụng CompletableFuture để xử lý các hạn chế trên.

complete() và Get()

Ví dụ khi bạn muốn vừa nấu cơm vừa rang thịt:

package app.tuanluc.processor;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class Cook {
    public static void main(String[] args) throws InterruptedException, ExecutionException {

        CompletableFuture<String> cookingMeat = new CompletableFuture<>();
        CompletableFuture<String> cookingRice = cookingRice();

        System.out.println("Làm bữa tối");
        cookingMeat.complete(cookingMeat());
        String meat = cookingMeat.get();
        String rice = cookingRice.getNow("Gạo");
        System.out.println("Ăn tối với: " + meat + " và " + rice);
    }

    public static CompletableFuture<String> cookingRice() {
        return CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println("Nấu cơm ... ");
                Thread.sleep(9000);
                System.out.println("Nấu cơm xong");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Cơm";
        });
    }

    public static String cookingMeat() {
        try {
            System.out.println("Rang thịt ... ");
            Thread.sleep(3000);
            System.out.println("Rang xong");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Thịt rang";
    }
}

Output:

Làm bữa tối
Rang thịt ... 
Nấu cơm ... 
Rang xong
Ăn tối với: Thịt rang và Gạo

Sau khi rang thịt xong bạn muốn lấy cơm ra ăn luôn nhưng vì nấu cơm mất nhiều thời gian hơn nên chỉ có gạo mà thôi

Trong ví dụ trên, chúng ta đã thực hiện:

  • Method complete() : Truyền vào công việc cần hoàn thành.
  • Method get() : được sử dụng để lấy kết quả trả về. Phương thức này block main thread cho tới khi có kết quả trả về.
  • Lưu ý: nếu không dùng complete() để xác định kết quả trả về mà thực hiện phương thức get() thì chương trình sẽ bị block mãi mãi.
  • Ngoài phương thức get(), chúng ta có thể sử dụng phương thức getNow(valueIfAbsent) để lấy kết quả ngay lập tức (không phải chờ cho tới khi hoàn thành Future): nếu có kết quả thì phương thức này sẽ trả về giá trị kết quả, nếu không có sẽ trả về giá trị mặc định được truyền vào

Chạy bất đồng bộ với runAsync() và supplyAsync

CompletableFuture.supplyAsync()CompletableFuture.runAsync() đều hoạt động tương tự nhau nhưng CompletableFuture.supplyAsync() thì có kết quả trả về.

runAsync()

Nếu muốn chạy một số task bất đồng bộ và không muốn trả về kết quả, thì có thể sử dụng phương thức CompletableFuture.runAsync(). Phương thức này chấp nhận một đối tượng Runnable và trả về CompletableFuture<Void>

  • Ví dụ một processor cần xử lý các đơn hàng đã bán được:
    • Kiểm tra đơn hàng ở kho nào rồi update lại số lượng của hàng trong kho.
    • Tính tiền cho telesale đã bán được hàng.

package app.tuanluc.processor;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;


public class HandleOffer {

final static int TIME_TO_UPDATE_STOREAGE = 5000;
    final static int TIME_TO_UPDATE_TELESALE = 5000;

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        System.out.println("Start processor");
        long startTime = System.currentTimeMillis();
        CompletableFuture<Void> updateAmountInStorage = CompletableFuture.runAsync(() -> {
            System.out.println("updateStoreage is running in a other thread.");
            updateStoreage(TIME_TO_UPDATE_STOREAGE);
            System.out.println("Done task 1!!!");
        });

        // tính tiền cho telesale
        System.out.println("updateTelesale is running... in main thread");
        updateTelesale(TIME_TO_UPDATE_TELESALE);
        System.out.println("Hoàn thành process trong :" + (System.currentTimeMillis() - startTime) + "ms");
    }

    static void updateStoreage(int time) {
        try {
            Thread.sleep(time);
            System.out.println("Đã giảm số lượng hàng trong kho thành công ");
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }
    }

    static void updateTelesale(int time) {
        try {
            Thread.sleep(time);
            System.out.println("Đã cộng tiền cho telesale thành công ");
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }
    }
}

Output:

Start processor
updateTelesale is running... in main thread
updateStoreage is running in a other thread.
Đã cộng tiền cho telesale thành công 
Đã giảm số lượng hàng trong kho thành công 
Hoàn thành process trong :5087ms

Ở ví dụ trên việc update storeage hay update telesale trong model đều mất mỗi việc 5s nhưng khi chạy bất đồng bộ thì thời gian hoàn thành chỉ là 5087ms giống như làm 1 việc vậy.

supplyAsync()

Cách hoạt động của supplyAsync() cũng giống hệt runAsync() nhưng cần truyền vào đối số Supplier và kết quả trả về là một CompletableFuture<T>.

Vẫn ví dụ về xử lý đơn hàng đã bán được ở trên nhưng lần này có thêm 1 việc sau khi cập nhập lại số lượng hàng trong kho đó là chúng ta phải thông báo lại số lượng hàng trong kho.

package app.tuanluc.processor;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class HandleOffer2 {

    final static int TIME_TO_UPDATE_STOREAGE = 5000;
    final static int TIME_TO_UPDATE_TELESALE = 5000;
    final static int AMOUNT = 0;


    public static void main(String[] args) throws InterruptedException, ExecutionException {

        System.out.println("Start processor");
        long startTime = System.currentTimeMillis();
        CompletableFuture<Integer> updateAmountInStorage = CompletableFuture.supplyAsync(() -> {
            System.out.println("updateStoreage is running in a other thread.");
            updateStoreage(TIME_TO_UPDATE_STOREAGE);
            return AMOUNT;
        });

        // tính tiền cho telesale
        System.out.println("updateTelesale is running... in main thread");
        updateTelesale(TIME_TO_UPDATE_TELESALE);
        System.out.println("Số lượng hàng còn trong kho :" + updateAmountInStorage.get() + "ms");
        System.out.println("Hoàn thành process trong :" + (System.currentTimeMillis() - startTime));
    }

    static void updateStoreage(int time) {
        try {
            Thread.sleep(time);
            System.out.println("Đã giảm số lượng hàng trong kho thành công ");
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }
    }

    static void updateTelesale(int time) {
        try {
            Thread.sleep(time);
            System.out.println("Đã cộng tiền cho telesale thành công ");
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }
    }
}

Output:

Start processor
updateTelesale is running... in main thread
updateStoreage is running in a other thread.
Đã cộng tiền cho telesale thành công 
Đã giảm số lượng hàng trong kho thành công 
Số lượng hàng còn trong kho :0
Hoàn thành process trong :5087ms

Chuyển đổi và thao tác trên CompletableFuture

thenApply(), THENACCEPT() VÀ THENRUN()

Chúng ta có thể sử dụng phương thức thenApply() để xử lý và chuyển đổi kết quả của một CompletableFuture khi nó hoàn thành.

Nếu không muốn trả ra kết quả từ callback mà chỉ muốn chạy xử lý sau khi hoàn thành Future, thì chúng ta có thể sử dụng các phương thức thenAccept() và thenRun().

  • thenApply() cần truyền vào một callback Function<U, T> trả ra kiểu dữ liệu U và nhận vào T.
  • thenAccept() cần truyền vào Consumer<T> và trả về CompletableFuture<Void>. Nó có thể lấy kết quả của CompletableFuture gọi nó.
  • thenRun() cần truyền vào Runable và trả về CompletableFuture<Void>. Nó không thể lấy kết quả của CompletableFuture gọi nó.

Tiếp tục với bài toán đơn hàng, Vậy là sau khi kiểm tra hàng đã được đóng gói và sẵn sàng giao hàng chúng ta cần:

  • Thông báo với shiper đến lấy hàng.
  • Gửi tin nhắn tới khách hàng chuấn bị nhận hàng.
package app.tuanluc.processor;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class HandleDelivery {

    final static boolean READY_DELIVERY = true;
    final static int TIME_TO_CHECK_READY_DELIVERY = 5000;
    final static int TIME_TO_CALL_SHIPPER = 5000;
    final static int TIME_TO_SEND_MESSAGE = 5000;

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        System.out.println("Start processor");
        long startTime = System.currentTimeMillis();
        CompletableFuture<Boolean> checkReadyDelivery = CompletableFuture.supplyAsync(() -> {
            execute("checkReadyDelivery", TIME_TO_CHECK_READY_DELIVERY);
            return READY_DELIVERY;
        });

        CompletableFuture<String> callShipper = checkReadyDelivery.thenApply(readyDelivery -> {
            if (readyDelivery) {
                execute("callShipper", TIME_TO_CALL_SHIPPER);
                return "thành công";
            } else {
                return "thất bại";
            }
        });
        CompletableFuture<Void> sendMessageToCustomer = checkReadyDelivery.thenAccept(readyDelivery -> {
            if (readyDelivery) {
                execute("sendMessageToCustomer", TIME_TO_SEND_MESSAGE);
            }
        });
        String status = callShipper.get();
        System.out.println("Gọi shipper " + status);
        System.out.println("Hoàn thành process trong :" + (System.currentTimeMillis() - startTime)+ "ms");
    }

    static void execute(String name, int time) {
        try {
            Thread.sleep(time);
            System.out.println("done task : " + name);
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }
    }
}

Output:

Start processor
done task : checkReadyDelivery
done task : sendMessageToCustomer
done task : callShipper
Gọi shipper thành công
Hoàn thành process trong :15103 ms

Có thể gọi thenApply(), thenAccept() và thenRun() nhiều lần để tạo thành một chuỗi xử lý (chain).

thenApplyAsync(), thenAcceptAsync() VÀ thenRunAsync()

Ở bài toán delivery trên thì việc checkReadyDelivery cần xử lý trước các việc còn lại vì đó là điều kiện của các việc sau. Nhưng callShipper và sendMessage thì không cần phụ thuộc vào nhau vậy chúng nên xử lý đồng thời 2 việc này sau khi có kết quả từ checkReadyDelivery.

Thực tế thì các method trong CompletableFuture nếu có hậu tố Async thì đều hỗ trợ bất đồng bộ.

Vậy để xử lý đồng bộ bài toàn delivery trên ta có thể dùng thenApplyAsync(), thenAcceptAsync() và thenRunAsync().

package app.tuanluc.processor;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class HandleDelivery2 {

    final static boolean READY_DELIVERY = true;
    final static int TIME_TO_CHECK_READY_DELIVERY = 5000;
    final static int TIME_TO_CALL_SHIPPER = 5000;
    final static int TIME_TO_SEND_MESSAGE = 5000;

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        System.out.println("Start processor");
        long startTime = System.currentTimeMillis();
        CompletableFuture<Boolean> checkReadyDelivery = CompletableFuture.supplyAsync(() -> {
            execute("checkReadyDelivery", TIME_TO_CHECK_READY_DELIVERY);
            return READY_DELIVERY;
        });

        CompletableFuture<String> callShipper = checkReadyDelivery.thenApplyAsync(readyDelivery -> {
            if (readyDelivery) {
                execute("callShipper", TIME_TO_CALL_SHIPPER);
                return "thành công";
            } else {
                return "thất bại";
            }
        });
        CompletableFuture<Void> sendMessageToCustomer = checkReadyDelivery.thenAcceptAsync(readyDelivery -> {
            if (readyDelivery) {
                execute("sendMessageToCustomer", TIME_TO_SEND_MESSAGE);
            }
        });
        String status = callShipper.get();
        System.out.println("Gọi shipper " + status);
        System.out.println("Hoàn thành process trong :" + (System.currentTimeMillis() - startTime) + "ms");
    }

    static void execute(String name, int time) {
        try {
            Thread.sleep(time);
            System.out.println("done task : " + name);
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }
    }
}

Output:

Start processor
done task : checkReadyDelivery
done task : callShipper
done task : sendMessageToCustomer
Gọi shipper thành công
Hoàn thành process trong :10086 ms

Vậy là thời gian khi sử lý bất đồng bộ đã giảm chỉ còn 10s.

Kết hợp hai CompletableFutures với nhau

thenCompose()

Khi muốn 2 CompletableFutures phụ thuộc vào nhau ta sử dụng thenCompose()

Ví dụ sau khi bạn muốn lấy thông tin của một quyển sách từ 1 api, và từ thông tin vừa lâý được bạn cần tính giá tiền của quyển sách từ api khác.

package app.tuanluc.processor;


import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

class Book {
    String bookId;

    public Book(String bookId) {
        this.bookId = bookId;
    }
}

class BookService {
    public static Book getBookDetails(String bookId) {
        return new Book(bookId);
    }
}

class CreditBookService {
    public static Double getCreditRating(Book book) {
        return Double.parseDouble(book.bookId);
    }
}

class Util {
    public static CompletableFuture<Book> getBooksDetail(String bookId) {
        return CompletableFuture.supplyAsync(() -> {
            return BookService.getBookDetails(bookId);
        });
    }

    public static CompletableFuture<Double> getCredit(Book book) {
        return CompletableFuture.supplyAsync(() -> {
            return CreditBookService.getCreditRating(book);
        });y
    }
}

public class CompletableFuture6 {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // Using thenCompose()
        CompletableFuture<Double> flattened = Util.getBooksDetail("1")
                .thenCompose(book ->
                        Util.getCredit(book)
                );
        System.out.println(flattened.get());
    }
}

Nhiều người sẽ thấy việc dùng thenCompose() giống với thenApply() vậy tại sao ko dùng luôn thenApply()? Đơn giản là vì phương thức thenApply() trả về một CompletableFuture<T>, T là một giá trị nhận được từ kết quả của supplyAsync(), như trong ví dụ này nó sẽ trả về CompletableFuture<CompletableFuture<Double>>. Để có thể nhận được trực tiếp CompletableFuture<Double> chúng ta cần sử dụng phương thức thenCompose().

thenCombine

Nếu muốn 2 completableFuture chạy độc lập sau đó cần xử lý chung đồng thời 1 việc nào đó ta sẽ sử dụng thenCombine()

VD Bạn muốn làm một báo cáo về tỉ lệ “mua hàng/xem hàng” chúng ta cần tính toán hoặc lấy về dữ liệu về số lượt truy cập vào xem hàng, đồng thời lấy về thông tin số lượt mua hàng. Nhưng để tính được tỉ lệ thì phải cần cả 2 kết quả:

package app.tuanluc.processor;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * @author tuanluc on 02/10/2019
 */
public class ReportBuyTimesPerViews {

    final static int TIME_TO_GET_BUY_TIMES = 5000;
    final static int TIME_TO_GET_VIEWS = 5000;
    final static int TIME_TO_COMPUTE = 5000;
    final static int VIEWS = 1000;
    final static int BUY_TIMES = 69;

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        System.out.println("Start processor");
        long startTime = System.currentTimeMillis();
        CompletableFuture<Integer> getBuyTimes = CompletableFuture.supplyAsync(() -> {
            execute("getBuyTimes", TIME_TO_GET_BUY_TIMES);
            return BUY_TIMES;
        });

        CompletableFuture<Integer> getViews = CompletableFuture.supplyAsync(() -> {
            execute("getViews", TIME_TO_GET_VIEWS);
            return VIEWS;
        });

        CompletableFuture<Double> computeBuyTimesPerViews = getBuyTimes.thenCombine(getViews,
                (buyTimes, views) -> {
                    execute("computeBuyTimesPerViews", TIME_TO_GET_VIEWS);
                    return (double) buyTimes / views;
                });
        Double result = computeBuyTimesPerViews.get();
        System.out.println("Tỉ lệ : " + result);
        System.out.println("Hoàn thành process trong :" + (System.currentTimeMillis() - startTime)+ "ms");
    }

    static void execute(String name, int time) {
        try {
            Thread.sleep(time);
            System.out.println("done task : " + name);
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }
    }
}

Output:

Start processor
done task : getBuyTimes
done task : getViews
done task : computeBuyTimesPerViews
Tỉ lệ : 0.069
Hoàn thành process trong :10180 ms

kết hợp nhiều completableFuture với allOf() và anyOf()

CompletableFuture.anyOf() được sử dụng khi cần thực hiện một danh sách các CompletableFuture song song và nó sẽ hoàn thành khi bất kỳ task nào trong danh sách hoàn thành.

Ví dụ với một ứng dụng gọi xe taxi, khi khách hàng đặt xe chúng ta sẽ liên hệ đồng thời tới nhiều xe taxi trong hãng ở gần đó, khi 1 tài xế nhanh nhất phản hồi nhất sẽ được kết nối và hủy các liên hệ khác.

package app.tuanluc.processor;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CallTaxi {

    final static int TIME_TO_CALL_A = 3000;
    final static int TIME_TO_CALL_B = 4000;
    final static int TIME_TO_CALL_C = 2000;

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        System.out.println("Start processor");
        long startTime = System.currentTimeMillis();
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(" Calling tài xế A ...");
            try {
                Thread.sleep(TIME_TO_CALL_A);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return "Tài xế A đã phản hồi";
        });

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println(" Calling tài xế B ...");
            try {
                Thread.sleep(TIME_TO_CALL_B);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return "Tài xế B đã phản hồi";
        });

        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
            System.out.println(" Calling tài xế C ...");
            try {
                Thread.sleep(TIME_TO_CALL_C);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return "Tài xế C đã phản hồi";
        });

        CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(future1, future2, future3);
        System.out.println(anyOfFuture.get());
        System.out.println("Hoàn thành process trong :" + (System.currentTimeMillis() - startTime) + "ms");
    }
}

Output:

Start processor
 Calling tài xế A ...
 Calling tài xế B ...
 Calling tài xế C ...
Tài xế C đã phản hồi
Hoàn thành process trong :2073 ms

Tiếp theo, CompletableFuture.allOf() được sử dụng khi cần thực hiện một danh sách các tác vụ song song và làm điều gì đó sau khi tất cả chúng hoàn tất.

Ví dụ để tăng tốc độ download file chúng ta nên chia file thành cách phần nhỏ rồi thực hiện download từng phần đến khi tất cả các phần đều hoàn thành thì tiến hành gộp thành file hoàn chỉnh.

package app.tuanluc.processor;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

public class DownloadMultiplePartFile {

    final static int TIME_TO_DOWNLOAD = 5000;

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        System.out.println("Start processor");
        long startTime = System.currentTimeMillis();

        List<String> downloadParts = Arrays.asList(
                "Tập 1", "Tập 2", "Tập 3", "Tập 4", "Tập 5", "Tập 6", "Tập 7");

        // Tạo 1 list các Future
        List<CompletableFuture<String>> partContentFutures = downloadParts.stream()
                .map(webPageLink -> download(webPageLink, TIME_TO_DOWNLOAD)).collect(Collectors.toList());

        //Tạo riêng 1 Future
        CompletableFuture<String> partSpecial = CompletableFuture.supplyAsync(() -> {
            System.out.println("Downloading: Tập đặc biệt");
            try {
                Thread.sleep(7000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Done: Tập đặc biệt");
            return "Tập đặc biệt";
        });

        partContentFutures.add(partSpecial);

        CompletableFuture<Void> allFile = CompletableFuture
                .allOf(partContentFutures.toArray(new CompletableFuture[partContentFutures.size()]));

        CompletableFuture<String> mergeFile = allFile.thenApply(parts -> {//get all file
            return partContentFutures.stream().map(pageContentFuture -> pageContentFuture.join())
                    .collect(Collectors.joining("+"));
        });
        System.out.println("Kết quả: " + mergeFile.get());
        System.out.println("Hoàn thành process trong :" + (System.currentTimeMillis() - startTime));
    }

    public static CompletableFuture<String> download(String namePart, int time) {
        return CompletableFuture.supplyAsync(() -> {
            System.out.println("Downloading: " + namePart);
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Done: " + namePart);
            return namePart;
        });
    }
}

Output

Start processor
Downloading: Tập 2
Downloading: Tập 1
Downloading: Tập 3
Done: Tập 2
Downloading: Tập 4
Done: Tập 1
Downloading: Tập 5
Done: Tập 3
Downloading: Tập 6
Done: Tập 4
Downloading: Tập 7
Done: Tập 5
Downloading: Tập đặc biệt
Done: Tập 6
Done: Tập 7
Done: Tập đặc biệt
Kết quả: Tập 1+Tập 2+Tập 3+Tập 4+Tập 5+Tập 6+Tập 7+Tập đặc biệt
Hoàn thành process trong :16440

Ví dụ trên các task được hoàn thành vào thời điểm khác nhau nhưng vẫn cùng phải đợi task chậm nhất để hoàn thành việc xử lý cuối cùng.

Dù đã nhanh hơn so với xử lý đồng bộ (cần 40s cho 8 file) nhưng như bạn thấy mỗi file cần 5s để download nhưng chúng ta cần tới ~16s nghĩa là không phải cả 8 file được download cùng 1 lúc vậy nên tối ưu như thế nào?

Sử dụng Executor với CompletableFuture

Với các method có hậu tố Async trong CompletableFuture chúng ta có thể tạo một Executor và truyền nó vào phương thức như trên để cho phép chúng thực hiện các tác vụ trong một Thread Pool độc lập. Nếu không truyền thì mặc định CompletableFuture sử dụng ForkJoinPool.commonPool() để thực thi các Thread độc lập.

Vậy để trả lời câu hỏi ở cuối phần trước chúng ta cần tự cấu hình một Executor có nhiều thread hơn để download file.

package app.tuanluc.processor;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

public class DownloadMultiplePartFileWithExecutor {

    final static int TIME_TO_DOWNLOAD = 5000;

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        System.out.println("Start processor");
        long startTime = System.currentTimeMillis();

        List<String> downloadParts = Arrays.asList(
                "Tập 1", "Tập 2", "Tập 3", "Tập 4", "Tập 5", "Tập 6", "Tập 7");

        // Tạo 1 list các Future
        List<CompletableFuture<String>> partContentFutures = downloadParts.stream()
                .map(webPageLink -> download(webPageLink, TIME_TO_DOWNLOAD)).collect(Collectors.toList());

        //Tạo riêng 1 Future
        CompletableFuture<String> partSpecial = CompletableFuture.supplyAsync(() -> {
            System.out.println("Downloading: Tập đặc biệt");
            try {
                Thread.sleep(7000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Done: Tập đặc biệt");
            return "Tập đặc biệt";
        });

        partContentFutures.add(partSpecial);

        CompletableFuture<Void> allFile = CompletableFuture
                .allOf(partContentFutures.toArray(new CompletableFuture[partContentFutures.size()]));

        CompletableFuture<String> mergeFile = allFile.thenApply(parts -> {//get all file
            return partContentFutures.stream().map(pageContentFuture -> pageContentFuture.join())
                    .collect(Collectors.joining("+"));
        });
        System.out.println("Kết quả: " + mergeFile.get());
        System.out.println("Hoàn thành process trong :" + (System.currentTimeMillis() - startTime));
    }

    public static CompletableFuture<String> download(String namePart, int time) {
        final AtomicLong count = new AtomicLong(0);
        final ThreadPoolExecutor pool = new ThreadPoolExecutor(0, 10, 0L,
                TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r);
                String threadName = "thread-" + count.getAndIncrement();
                t.setName(threadName);
                return t;
            }
        });

        return CompletableFuture.supplyAsync(() -> {
            System.out.println("Downloading: " + namePart);
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Done: " + namePart);
            return namePart;
        }, pool);
    }
}

Output:

Start processor
Downloading: Tập 1
Downloading: Tập 2
Downloading: Tập 3
Downloading: Tập 4
Downloading: Tập 5
Downloading: Tập 6
Downloading: Tập 7
Downloading: Tập đặc biệt
Done: Tập 1
Done: Tập 2
Done: Tập 4
Done: Tập 5
Done: Tập 6
Done: Tập 3
Done: Tập 7
Done: Tập đặc biệt
Kết quả: Tập 1+Tập 2+Tập 3+Tập 4+Tập 5+Tập 6+Tập 7+Tập đặc biệt
Hoàn thành process trong :6305

Vậy là đến đây thì việc download file chỉ còn ~ 6s bởi vì mỗi phần của file đã được chạy 1 luồng riêng.

Xử lý Exception trong CompletableFuture

exceptionally()

Sử dụng exceptionally() để xử lý exception khi có bất cứ exeption nào được throw trong tất cả các phương thức trong CompletableFuture Chain.

Ví dụ 1 CompletableFuture về xử lý nhập tuổi của người dùng.

package app.tuanluc.processor;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * @author tuanluc on 03/10/2019
 */
public class HandleException {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        CompletableFuture<String> handleAge =
                CompletableFuture.supplyAsync(() -> -1)
                        .thenApply(age -> {
                            if (age < 0) {
                                throw new IllegalArgumentException("Tuổi không thể âm!");
                            }
                            if (age > 18) {
                                return "Nguoi lon";
                            } else {
                                return "Tre em";
                            }
                        }).exceptionally(ex -> {
                    System.out.println("Bạn đã nhập sai tuổi - " + ex.getMessage());
                    return "Chưa rõ tuổi";
                });
        System.out.println("Maturity : " + handleAge.get());
    }
}

Output:

Bạn đã nhập sai tuổi - java.lang.IllegalArgumentException: Làm gì có ai sống lâu thế!
Kết quả : Chưa rõ tuổi

Khi ở trong chain có lỗi xảy ra! chúng ta xử lý bằng cách cho hiển thị đúng lỗi, và vẫn trả về kết quả “Chưa rõ tuổi”

handle() và whenComplete()

handle() luôn được gọi, cho dù có exception xảy ra hay không, nên phương thức này để xử lý ngoại lệ hoặc kết quả của CompletableFuture.

Ví dụ

package app.tuanluc.processor;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class HandleException2 {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        CompletableFuture<String> handleAge =
                CompletableFuture.supplyAsync(() -> 1)
                        .thenApply(age -> {
                            if (age < 0) {
                                throw new IllegalArgumentException("Tuổi không thể âm!");
                            }
                            if (age > 150) {
                                throw new IllegalArgumentException("Làm gì có ai sống lâu thế!");
                            }
                            if (age > 18) {
                                return "Nguoi lon";
                            } else {
                                return "Tre em";
                            }
                        }).handle((res, ex) -> {
                    if (IllegalArgumentException.class.isInstance(ex)) {
                        System.out.println("Bạn đã nhập sai tuổi - " + ex.getMessage());
                        return "Chưa rõ tuổi";
                    }
                    return res;
                });
        System.out.println("Kết quả : " + handleAge.get());
    }
}

whenComplete()

Phương thức whenComplete() cũng tương tự handle(), nhưng nó không có quả trả về.

package app.tuanluc.processor;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class HandleException3 {

public static void main(String[] args) throws InterruptedException, ExecutionException {
CompletableFuture<String> handleAge =
CompletableFuture.supplyAsync(() -> -1).thenApply(age -> {
if (age < 0) {
throw new IllegalArgumentException("Tuổi không thể âm");
}
if (age > 18) {
return "Nguoi lon";
} else {
return "Tre em";
}
}).whenComplete((res, ex) -> {
if (ex != null) {
System.out.println("Đã xảy ra lỗi - " + ex.getMessage());
}
});
}
}

Tài liệu tham khảo

https://www.callicoder.com/java-8-completablefuture-tutorial/

Guide To CompletableFuture

Lập trình đa luồng với CompletableFuture trong Java 8

Một số mẹo và lưu ý khi sử dụng Cache trong Spring

Cẩn thận với default cache key

Việc caching một method thực sự rất dễ dàng. Chỉ cần thêm @Cacheable phía trên tên của method giống như thế này:

@Cacheable(value = "address")
 public List getAddressOfRestaurant(Restaurant restaurant) {
 }

Tuy nhiên, nó cũng rất dễ bị mất kiểm soát cache key. Ở ví dụ trên chúng ta đang sử dụng default key generation để tạo ra 1 SimpleKey  chứa tất cả các parameter của method được gọi đến. Điều này yêu cầu các parameter trên cần thực hiện hashCode()/equals() mỗi lần method được gọi. Và như vậy performance  của việc hashCode() và equals() cũng sẽ ảnh hưởng đến lấy dữ liệu từ cache cũng như object bị thay đổi dẫn đến hashCode() không còn như ban đầu.

Thêm một điều quan trọng nữa, khi những parameter trở thành 1 phần của cache key và điều này thực sự không cần thiết nên làm tăng kích thước của cache. Xem xét lại ví dụ ở trên đang sử dụng Restaurant làm key. Tuy nhiên nếu restaurant có một đống các data phức tạp rồi có vài collection. Vậy nên hay cân nhắc tạo key trong các trường hợp có parameter phức tạp.

@Cacheable(value = "address", key = "#restaurand.id")
 public List getAddressOfRestaurant(Restaurant restaurant) {
 }

Caching and SchedulING

Để có thể tự động update value trong cache định kỳ hoặc clear cache theo thời gian mong muốn ta cần đến sự trợ giúp của Scheduling.

Enable Scheduling

@Configuration
@EnableScheduling
@EnableCaching
class CacheConfig { ... }

Clear cache

@Scheduled(fixedRateString = '5000')
@CacheEvict(value = "student", allEntries = true)
void clearStudentCache() {
    log.debug("@student cache is cleared off!")
}

Clear toàn bộ cache ‘student’ sau mỗi 5s

UPDATE CACHE

@Scheduled(fixedRateString = '5000')
Student clearStudentCache() {
    log.debug("update or insert student")
    updateCache("id01")
}

@CachePut(value = "student", key = "#id")
updateCache(Stirng id){
    Student st = repository.getStudent()
    return st
}

Ví dụ trên cứ mỗi 5s sẽ update lại student có key là ‘id01’ vào cache.

Tìm hiểu về Cache trong spring qua các ví dụ demo.

Bài này mình sẽ tạo một project spring+groovy+gradle đơn giản để demo một vài thao tác với cache.

Getting Started

gradle.properties

buildscript {
   repositories {
      mavenCentral()
   }
   dependencies {
      classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.6.RELEASE")
   }
}
plugins {
   id 'groovy'
   id 'org.springframework.boot' version '2.0.5.RELEASE'
   id 'io.spring.dependency-management' version '1.0.7.RELEASE'
}
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group 'app.tuanluc'
version '1.0-SNAPSHOT'

repositories {
   mavenCentral()
}

repositories {
   mavenCentral()
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
   compile("org.springframework.boot:spring-boot-starter-web")
   compile 'org.codehaus.groovy:groovy-all:2.3.11'
   testCompile("junit:junit")
}

Tạo đối tượng Student

class Student {

    String id
    String name
    String address

    Student(String id, String name, String address) {
        super()
        this.id = id
        this.name = name
        this.address = address
    }
}

Enable Caching

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.cache.annotation.EnableCaching

@SpringBootApplication
@EnableCaching
class Application {
   static void main(String[] args) {
      SpringApplication.run(Application, args)
   }
}

Để sử dụng cache, spring cung cấp annotation @EnableCaching. Bạn có thể đặt annotation này ở hàm main hoặc bất cứ class configuration nào.

Sử dụng Caching với Annotations.

Trong những bước tiếp theo chúng ta sẽ khai báo annotations để caching behavior gắn với method.

@Cacheable

Cách đơn giản nhất để sử dụng cache đó là dùng @Cacheable và khai báo parameter là tên của cache nơi sẽ dùng để lưu trữ kết quả.

@Cacheable(value = "student")
Student getStudentByIDAndName(String id, String Name) {
   try {
      log.info("Going to sleep for 5 Secs.. to simulate backend call.")
      Thread.sleep(1000 * 5)
   }
   catch (InterruptedException e) {
      e.printStackTrace()
   }
   return new Student(id, Name, "HN")
}

Mình sẽ sleep 5s để mô phỏng việc backend phải xử lý dữ liệu.

Tiếp theo tạo controller để test cache:

@GetMapping("/search/{id}/{name}")
Student findStudentByIdAndName(@PathVariable String id, @PathVariable String name) {
    log.info("Searching by ID and Name : $id - $name")
    return studentService.getStudentByIDAndName(id, name)
}

Lần đầu bạn hãy thử seach theo tên và địa chỉ bằng cách vào địa chỉ:

http://localhost:8080/students/search/id01/luc

Chúng ta sẽ thấy ở lần load trình duyệt đầu tiên rất lâu nhưng ở các lần sau sẽ rất nhanh nghĩa là đã cache thành công.

Function getStudentByIDAndName() sẽ kiểm tra trong cache student trước khi thực sự gọi đến method và trả về kết quả.

Chúng ta thử giữ nguyên id và thay đổi name để search:

http://localhost:8080/students/search/id01/huan

Lúc này trình duyệt lại load rất lâu. Nghĩa là server đã chạy lại vào function getStudentByIDAndName() để lấy lại giá trị.

Result console:

Searching by ID and Name : id01 - luc
Going to sleep for 5 Secs.. to simulate backend call.
Searching by ID and Name : id01 - luc
Searching by ID and Name : id01 - luc
Searching by ID and Name : id01 - huan
Going to sleep for 5 Secs.. to simulate backend call.
Searching by ID and Name : id01 - huan
Searching by ID and Name : id01 - huan

– Dữ liệu trong Spring cache lưu trữ dưới dạng key-value.

– Nếu không tự định nghĩa key. Thì key trong spring cache được hiểu là parameter của function đặt dưới annotation.

Trong phần lớn trường hợp thì một cache là đủ nhưng spring vẫn hỗ trợ multiple cache:

@Cacheable({"student", "people"})
public String getPeople(Student student) {...}

Trong trường hợp này, chỉ cần 1 cache chứa kết quả cần thiết thì nó sẽ được trả về và method cũng không cần phải chạy.

@CacheEvict

Bây giờ có 1 vấn đề khi tất cả các method đều sử dụng @Cacheable đó là cache có thể ngày càng lớn, và rất nhiều data đã cũ và không cần thiết sử dụng.
@CacheEvict annotation được sử dụng để remove một, nhiều hoặc tất cả value trong cache.

Để xóa tất cả chúng ta thêm thuộc tính allEntries=true.

@CacheEvict(value = "student", allEntries = true)
Student clearCache() {
   return null
}

Custom key generator

Tạo một method giống phần @Cacheable nhưng chúng ta thêm vào key=”#id”

@Cacheable(value = "student", key = "#id")
Student getStudentByIDAndNameAddress(String id, String name, String address) {
   try {
      log.info("Going to sleep for 5 Secs.. to simulate backend call.")
      Thread.sleep(1000 * 5)
   }
   catch (InterruptedException e) {
      e.printStackTrace()
   }
   return new Student(id, name, address)
}

Đầu tiên vào địa chỉ:

http://localhost:8082/students/search/id03/luc/hcm

Ở lần chạy đầu tiên sẽ thấy trình duyệt load mất 5s. Còn từ những lần sau đã có cache nên rất nhanh.
Tiếp theo bạn hay thay đổi lại giá trị address hoặc name:

http://localhost:8082/students/search/id03/dung/hn

nhưng kết quả trả về ở các lần đều giống nhau cho dù đã thay đổi:

Khác với kết quả khi không tự tạo key ở phần đầu, lần này trình duyệt vẫn trả về kết quả rất nhanh.

{"id":"id03","name":"luc","address":"hcm"}

Trong lần đầu truy cập method được gọi sẽ tìm trong cache ‘student’ chưa có key đó tồn tại nên nó sẽ chạy metod vào lưu trữ lại kết quả trả về là 1 object Student.
Trong các lần tiếp theo dù đã đổi name và address nhưng key chỉ là id nên lúc này method ko cần chạy và kết quả trả về từ cache vẫn như lúc đầu.

Vậy làm sao để giải quyết vấn đề này trong trường hợp ta cần dùng cache mà vẫn muốn update giá trị.

@CachePut

Điểm khác nhau giữa @CachePut và @Cacheable đó là @CachePut sẽ luôn chạy method cho dù đã tồn tại giá trị đó trong cache hay chưa và sẽ update lại giá trị đó.

@CachePut(value = "student")
Student getStudentByIDCachePut(String id) { ... }

Khi cần update giá trị trong cache ta sẽ dùng @CachePut

@Caching

Vì spring không cho phép có 2 annotation giống nhau trên cùng 1 method nên trong trường hợp bạn muốn customize nhiều cache trong 1 method cần sử dụng @Caching.

@Caching(evict = { 
   @Cacheable("addresses")
    @Cacheable(value="directory", key=customer.name) })
public String getAddress(Customer customer) {...}

Conditional Caching(Cache có điều kiện)

Trong nhiều tình huống bạn không muốn lúc nào @CachePut cũng update hay @CacheEvict chỉ remove giá trị trong 1 số trường hợp.

Để có thể thực hiện những việc trên ta sử dụng condition.

condition

@Cacheable(value = "student", key = "#student.id" , condition = "#student.name != 'luc'")
Student findByStudent(Student student) {
   try {
      log.info("Going to sleep for 5 Secs.. to simulate backend call.")
      Thread.sleep(1000 * 5)
   }
   catch (InterruptedException e) {
      e.printStackTrace()
   }
   return student
}

ở trên là 1 ví dụ sử dụng cache condition, method này sẽ lưu vào cache các Student không có name = ‘luc’.

unless

dưới đây là đoạn code sử dụng unless

@Cacheable(value = "student", key = "#id", unless = "#result.name == 'luc'")
    Student getStudentByIDAndNameAddress(String id, String name, String address) {
       try {
          log.info("Going to sleep for 5 Secs.. to simulate backend call.")
          Thread.sleep(1000 * 5)
       }
       catch (InterruptedException e) {
          e.printStackTrace()
       }
       return new Student(id, name, address)
    }

điểm khác với condition, đó là unless mang nghĩa phủ định và ngoài ra unless sẽ thực hiện sau khi method trả về kết quả, còn với condition là trước khi thực hiện method. Để tao tác với kết quả của method chúng ta dùng biến #result (biến này có kiểu giống với kiểu trả về của method trong vd này là kiểu Student).

@Cacheable: chỉ lưu trữ giá trị vào khi thỏa mãn condition.

@CachePut: Method luôn được thực hiện nhưng chỉ update hoặc insert giá trị vào cache nếu thỏa mãn condition.

@CacheEvict: Chỉ thực hiện xóa giá trị tương ứng cache khi thỏa mãn condition.

Cấu hình Cache trong configuration

Ngoài cách sử dụng annotation phía trên method để thao tác với cache ta cũng có thể cấu hình java config cho Cache trong spring.

@Bean
     CacheManager cacheManager() {
         SimpleCacheManager cacheManager = new SimpleCacheManager()
         cacheManager.setCaches(Arrays.asList(
                 new ConcurrentMapCache("student"),
                 new ConcurrentMapCache("addresses")))
         return cacheManager
     }

Và sau đó sử dụng với các chức năng giống như các phần trước:

@Autowired CacheManager cacheManager

Student getStudent(String id, String name) {
     String address = cacheManager.getCache("addresses").get(id)
     Student student = new Student(id, name, address)

     cacheManager.getCache("student").evict(id)     
     cacheManager.getCache("student").put(id, student)     
     return student 
}

Tài liệu tham khảo

 https://www.baeldung.com/spring-cache-tutorial 

HTTP Status Codes tutorial

1xx Informational

Nghĩa là request đã nhận được và xử lý đang được tiếp tục thực hiện

  • 100 Continue: Server đã nhận được một phần của request, Client nên tiếp tục gửi phần còn lại.
    Server sẽ trả ra final response khi nhận được đầy đủ.
    vd : Server đã nhận được headers nhưng chưa có body.
  • 101 Switching Protocols: Server đồng ý thay đổi protocols theo yêu cầu của requester.
  • 102 Processing (WebDAV): Server đã chấp nhận hoàn thành yêu cầu, nhưng chưa làm xong. Status code này nên được gửi khi công việc xử lý có thể sẽ mất nhiều thời gian và client có thể time-out connection.

2xx: Successful

Server đã nhận được và chấp nhận request

  • 200 OK: Request đã thành công, response trả về phụ thuộc vào method dùng để request
  • 201 Created: Request đã hoàn thành. một resource mới được tạo ra.(Thường sử dụng cho method PUT, POST)
  • 202 Accepted: Request đã được chấp nhận thực hiện nhưng chưa hoàn thành. Cuối cùng nó có thể hoặc không được làm xong. Vì trong quá trình xử lý nó có thể không được cho phép nữa.
  • 203 Non-Authoritative Information: Trả về thông tin trong entity-header. Đến từ một địa chỉ khác hoặc third-party không phải từ origin server.
  • 204 No Content: Server thực hiện thành công request nhưng trong response trả về sẽ không có body content.
  • 205 Reset Content: Khi status code này được trả về browser nên được reset lại form, refresh UI… để có thể bắt đầu nhập input (cũng không có body content giống với 204).
  • 206 Partial Content: Server sẽ trả về 1 phần dữ liệu được yêu cầu. Trong request cũng phải có Range header để chỉ ra khoảng mong muốn.Các Range Header được sửa dụng bởi Client để cho phép nối lại các phần của file download bị dán đoạn hoặc chia thành nhiều luồng download.

3xx Redirection

Client cần thực hiện thêm các action cần thiết để hoàn thành request.

  • 300 Multiple Choices: Server chỉ ra 1 danh sách các link để user có thể lựa chọn.
  • 301 Moved Permanently: Để thông báo rằng các trang web hoặc URL đã chuyển hướng vĩnh viễn sang một trang web hoặc URL khác, có nghĩa là tất cả những giá trị của trang web hoặc URL gốc sẽ chuyển hết sang URL mới. Nên request hiện tại và các request sau được yêu cầu di chuyển tới một URI mới.
  • 302 Found: Ở phiên bản HTTP/1.0 Nó thông báo rằng trang web hoặc URL đã chuyển hướng tạm thời sang địa chỉ mới nhưng vẫn phải dựa trên URL cũ. Vì một lý do nào đó, ví dụ như bảo trì trang web chính. nhưng phần lớn các browser lại thực hiện nó với ý nghĩa của 303 See Other. Do đó từ phiên bản HTTP/1.1 có thêm hai mã 303 và 307 để phân biệt rõ. 
  • 303 See Other: (Xuất hiện từ HTTP/1.1) Dữ liệu ở một url khác và phương thức GET được sử dụng để truy xuất nó.
  • 304 Not Modified: Nó được sử dụng cho mục đích lưu trữ(file). Điều đó thông báo cho Client rằng file đó vẫn chưa được sửa đổi , vì vậy Client có thể tiếp tục sử dụng cùng 1 phiên bản được lưu trong bộ cache của response. Tham số If-Modified-Since hoặc If-None-Match cho biết file ko sửa từ ngày đó.
  • 305 Use Proxy: Yêu cầu phải thông qua 1 proxy mới có thể truy cập được resource.

4xx Client Error

Lỗi từ client chỉ ra rằng yêu cầu không thể hoàn thành hoặc chứa cú pháp sai. Mã lỗi 4xx sẽ hiện ra khi có lỗi từ phía người dùng, chủ yếu là do không đưa ra một yêu cầu hợp lệ

  • 400 Bad Request: Có một lỗi cú pháp trong request và request bị từ chối.
  • 401 Unauthorized: Request phải có user xác thực(phải có username, password), các Response bắt buộc phải có thành phần WWW-Authenticate  chứa yêu cầu thích hợp
  • 402 Payment Required: Việc thanh toán là bắt buộc. Code này vẫn chưa hoạt động.
  • 403 Forbidden: Request là hợp lệ nhưng server từ chối đáp ứng nó. Có nghĩa là không được phép, người dùng không có quyền để tiếp cận với các tài nguyên.
  • 404 Not Found: Các file được yêu cầu không có trên máy chủ. Có thể bởi vì những file này đã bị xóa, hoặc chưa từng tồn tại trước đây nhưng có thể có trong tương lai . Nguyên nhân thường là do lỗi chính tả trong URL.
  • 405 Method Not Allowed: Request method không được hỗ trợ cho các resource được yêu cầu.
  • 406 Not Acceptable: Khi trình duyệt gửi một request để lấy thông tin từ server có nghĩa là trình duyệt gửi Accept header để nói cho server định dạng dữ liệu mà trình duyệt chấp nhận. Nếu server không thể gửi dữ liệu như định dạng trong accept header thì server sẽ trả về code này.
  • 407 Proxy Authentication Required: Mã trạng thái này cũng tương tự như 401 (Không được uỷ quyền) nhưng quy định rằng request phải xác thực bằng cách sử dụng một proxy.
  • 408 Request Timeout: Máy chủ mất quá nhiều thời gian để xử lý yêu cầu. Lỗi này thường gây ra bởi lưu lượng truy cập mạng cao.
  • 409 Conflict: Máy chủ gặp phải một cuộc xung đột giữa các request. Các máy chủ có thể trả về mã này khi có một truy cập mới xung đột với một yêu cầu trước đó.
  • 410 Gone: Các resource từng ở vị trí này, nhưng không còn nữa. khi gặp mã lỗi này Client không nên có gắng tìm kiếm các tài nguyên này ở những lần sau.
  • 411 Length Required: Content-Length không được xác định rõ. Server sẽ không chấp nhận Request nào không có nó.
  • 412 Precondition Failed: Request thiếu một hoặc nhiều điều kiện để thực hiện và server ko chấp nhận sẽ trả về code này.
  • 413 Request Entity Too Large: Resource được yêu cầu xử lý là quá lớn.
  • 414 Request-url Too Long:  URI được cung cấp quá dài, thường là do quá nhiều dữ liệu được mã hóa như là một request GET, trong trường hợp đó nó phải được chuyển đổi sang method POST.
  • 415 Unsupported Media Type: Server sẽ không chấp nhận Request, bởi vì kiểu, định dạng không được hỗ trợ. Ví dụ khi Client upload một ảnh có định dạng image/svg, nhưng server yêu cầu một định dạng là jpg.
  • 416 Requested Range Not Satisfiable: Client yêu cầu một phần của file nhưng server không thể cung cấp nó. Ví dụ client muốn đọc phần đã nằm vượt quá cả phần cuối của file.
  • 417 Expectation Failed:  Máy chủ không thể đáp ứng các yêu cầu của trường Expect trong header.
  • 429 Too Many Requests: User gửi quá nhiều request trong một khoảng thời gian.

5xx: Server Error

Cho biết máy chủ không thể hoàn tất request hợp lệ. Khi duyệt web và bắt gặp các lỗi 5xx, bạn chỉ có thể chờ đợi, vì lúc này lỗi xuất phát từ phía máy chủ của dịch vụ web, không có cách nào can thiệp để sửa lỗi ngoài việc ngồi chờ bên máy chủ xử lý xong.

  • 500 Internal Server Error: Một thông báo chung chung, được đưa ra khi Server gặp phải một trường hợp bất ngờ và máy chủ đã gặp lỗi và không thể hoàn tất yêu cầu.
  • 501 Not Implemented: Server không công nhận các Request method hoặc không có khả năng xử lý nó.
  • 502 Bad Gateway: xuất hiện khi bạn không thể truy cập trang web, địa chỉ lỗi được phát hiện tại gateway. Lỗi 502 xảy ra do một gateway trên internet nhận phản hồi “không hợp lệ” (invalid response) từ server gốc. Gateway là một trong các server tiếp nhận, chuyển hướng, điều phối dữ liệu, nằm giữa server gốc và máy của bạn trên internet.
  • 503 Service Unavailable: Máy chủ hiện tại không thể thực hiện yêu cầu (vì bị quá tải hoặc đang phải bảo trì). Đây là một thông báo về tình trạng bận tạm thời của máy chủ.
  • 504 Gateway Timeout: Cổng đã hết thời gian. Giống như 408 timeout error, nhưng lỗi này xảy ra tại cổng của máy chủ.
  • 505 HTTP Version Not Supported:  Server không hỗ trợ phiên bản giao thức HTTP được sử dụng trong request.