Saturday, April 15, 2023

Set up replica set trong mongodb

 

  1. Giới thiệu về replica set trong MongoDB

    Replica set trong MongoDB là một group các mongod processes để duy trì cùng một cơ sở dữ liệu. Replica set cung cấp khả năng dự phòng và tính sẵn sàng cao.

    Tính dự phòng và sẵn sàng cao

    • Replication cung cấp tính dự phòng và nâng cao tính khả dụng của mongoDB. Với nhiều bản sao chép trên nhiều database server khác nhau, replication nâng cao mức độ chịu lỗi chống lại việc mất dữ liệu trên signle database.
    • Trong một số trường hợp, replication có thể cải thiện khả năng đọc dữ liệu vì client có thể đọc dữ liệu trên nhiều database server khác nhau. Duy trì các bản sao dữ liệu làm tăng tính cục bộ và khả dụng của dữ liệu.
    • Các replica cũng có thể duy trì cho các mục đích như khôi phục dữ liệu, sao lưu.

    Replication trong MongoDB

    • Replica set là một group các mongod instances để duy trì cùng một bộ cơ sở dữ liệu. Trong replica set bao gồm một số node mang dữ liệu, trong các nút này chỉ duy nhất một nút là nút chính để ghi dữ liệu trong khi các nút còn lại là các nút phụ được sao chép dữ liệu từ nút chính. Nếu nút chính bị lỗi một trong những nút phụ sẽ được thay thế thành một nút chính mới.

  2. Cấu hình replica trong MongoDB trên môi trường ubuntu

  • Cài đặt MongoDB: reference
  • Set up replica set với 3 node (1primary và 2 slave)
    • đầu tiền cần tạo 3 thư mục cho 3 node của replica. Ở đây mình sẽ chạy 3 node trên 3 port 27017, 27018, 27019 nên mình tạo 3 thư mục tương ứng.

  • Lần lượt run 3 node bằng lệnh:

    sudo mongod --dbpath mongo27017 --port 27017 --replSet "rs0"

    sudo mongod --dbpath mongo27018 --port 27018 --replSet "rs0"

    sudo mongod --dbpath mongo27019 --port 27019 --replSet "rs0"

    Trong đó:

    • --dbpath là đường dẫn đến thư mục vừa tạo.
    • --port là port để run node của của mongo.
    • --replSet là tên của replication.
  • Access vào mongo để config replica bằng lệnh mongo --port 27017

  • Run lệnh: rs.initiate()

  • Add lần lượt các node còn lại vào replica set:

    rs.addArb('127.0.0.1:27018')

    rs.add('127.0.0.1:27019')

    • rs.addArb: để add 1 node làm trọng tài.
    • rs.add: để add 1 member vào replica.

  • Xem lại cấu hình của replica ta thấy lần có 3 node với 3 port 27017, 27018, 27019.

  • Access vào các node còn lại và run rs.slaveOk() để hoàn thành cấu hình replica set.
  • Set up replica với authentication.

    • Tạo file key để authen giữa các node trong replica.

      openssl rand -base64 756 > mongodb.key

      chmod 400 mongodb.key

    • Access primary node để tạo mới user.

    • run: use admin db.createUser({ user: "admin", pwd:"admin", roles: ["userAdminAnyDatabase"] }) ở đây mình tạo mới user admin với full quyền trên tất cả db.

  • Restart lại 3 node với --keyFile để yêu cầu authen.

    sudo mongod --dbpath mongo27017 --keyFile mongodb.key --port 27017 --replSet "rs0"

    sudo mongod --dbpath mongo27018 --keyFile mongodb.key --port 27018 --replSet "rs0"

    sudo mongod --dbpath mongo27019 --keyFile mongodb.key --port 27019 --replSet "rs0"

reference replica

Thursday, April 13, 2023

001: Message-driven programming với Message broker và Apache Kafka

1) Asynchrounous và Message-driven programming

Nếu đọc bài viết này thì chắc chắn chúng ta đã nắm chắc các khái niệm cơ bản về async programming và message-driven. Còn nếu chưa thì.. đọc tiếp nhé.

1.1) Asynchronous programming

Làm một chiếc ví dụ cho sôi động:

Sau khi release project thành công, leader quyết định cho cả team đi ăn... KFC. Thôi cũng được, đi ăn không mất tiền là sướng rồi.

Các anh em nhanh chóng phi đến nơi và tự phân chia vào các hàng chờ để đến lượt để gọi món. Người đến sau chờ người đến trước, ông nào lớ ngớ ra khỏi hàng cái là lại xuống cuối, mất chỗ như chơi.

Ông nào mà vã quá có khi lại đứng tán tỉnh luôn em bán hàng mất đến 15p thì đúng là.. há mồm.

Thấy nhân viên công ty F đông quá, giám đốc chi nhánh quyết định tăng gấp đôi số lượng line, tốc độ cải thiện rõ rệt. Tuy nhiên vẫn không tránh được việc một số anh em tranh thủ xin số em nhân viên...

Hãy coi các anh em công ty F là client và các em nhân viên là server. Trong ví dụ này, client phải tương tác trực tiếp với server, và đó là hình ảnh của synchronous programming - lập trình đồng bộ. Client cần chờ cho đến khi server xử lý xong request để tiếp tục chạy. Như vậy, mấu chốt của synchronous programming không nằm ở số lượng client, số lượng request hay số lượng server mà nằm ở cách client và server tương tác với nhau.

Async programming mình đang đề cập là trong việc giao tiếp giữa nhiều service với nhau, không phải trong single application với multi-thread. Anw, idea của nó na ná nhau, đều muốn tránh blocking khi xử lý request.

Nếu chỉ cần cải thiện tốc độ thì không khó, cứ làm theo giải pháp của giám đốc chi nhánh là ok, tăng số lượng nhân viên bằng số lượng khách hàng.

Và dĩ nhiên trong thực tế chẳng ai làm như thế 🔨. Và nó cũng không giải quyết được vấn đề giao tiếp trực tiếp, chờ đợi lẫn nhau:

  • Nhân viên: ...
  • Khách hàng: ...
  • Nhân viên: ...
  • Khách hàng: ...
  • Nhân viên: ...

Mọi chuyện tưởng chừng không có vấn đề gì, nhưng sẽ có 2 bất lợi chính:

  • Nếu server có gặp sự cố, client phải chờ cho đến khi server phục hồi và trả lời request hoặc quá timeout, và không chỉ một mà còn rất nhiều client phía sau cũng phải chờ.
  • Client phải chờ phản hồi từ server mới có thể tiếp tục thực hiện các công việc khác.

Chuyện gì cũng có cách giải quyết. Vậy giám đốc chi nhánh xử lý thế nào?

Nhà hàng nhận thấy điểm yếu trong khâu order. Họ quyết định chuyển sang hình thức khác. Sau khi lựa chọn được các món ăn ưa thích, khách hàng sẽ thông báo cho nhân viên. Trong lúc nhân viên ghi chép tính toán thì khách hàng có thể đi tìm chỗ ngồi và làm việc cá nhân. Sau khi xác nhận đơn hàng hàng công, nhân viên báo lại cho khách hàng ra thanh toán và nhận đồ.

Vấn đề được giải quyết, client và server vẫn giao tiếp trực tiếp nhưng không chờ đợi nhau. Client có thể làm việc riêng tùy thích trong khoảng thời gian đó, và được thông báo khi server xử lý xong yêu cầu.

Đó là ví dụ của asynchronous programming. Như vậy, việc giải quyết bài toán không nằm ở cách thức giao tiếp hay kênh giao tiếp mà giải pháp làtách biệt request và response.

Tách biệt hai luồng request và response, nghĩa là hoàn toàn có thể thực hiện giao tiếp trực tiếp giữa nhiều service với nhau.

  • Service A gửi request đến Service B.
  • Service B nhận request và phản hồi đã nhận được request. Để đó xử lý sau.
  • Trong lúc đó, Service A hoàn toàn rảnh rang thực hiện các công việc khác.
  • Sau khi Service B xử lý xong request sẽ gửi ngược lại cho Service A.

Tuy nhiên chưa thực sự triệt để, vẫn có thể gặp 2 vấn đề:

  • Nếu server quá tải từ chối nhận thêm request, hoặc server.. die. Client cần cơ chế retry để gửi lại request.
  • Trong trường hợp muốn order ở tất cả cửa hàng tại Hà Nội. Khách hàng cần gửi request đến từng cửa hàng.

1.2) Message-driven programming

Ca này hơi khó, nhà hàng quyết định thuê các chuyên gia về tìm giải pháp gỡ rối.

Sau một hồi xem xét, các chuyên gia đều đồng tình phán một câu xanh rờn: việc gì mà phức tạp thì outsource cho nhanh.

Những tưởng chỉ là câu nói đùa nhưng vấn đề đã được giải quyết êm đẹp. Nhà hàng thay đổi cách thức hoạt động, khách hàng thực hiện order qua hệ thống SMS. Sau đó hệ thống SMS điều phối request đến các cửa hàng, nhân viên để thực hiện nhiệm vụ. Không lo việc chẳng may một nhà hàng bị đóng cửa thì đã có nhà hàng khác phục vụ. Hoặc muốn thử đồ ăn ở nhiều nơi thì không cần gửi một list các request mà chỉ cần gửi một request. Các nhiệm vụ còn lại là công việc của hệ thống SMS.

Ngoài ra, khách hàng và nhân viên không cần biết nhau, tránh việc khách hàng đánh nhân viên u đầu vì món ăn quá tệ 🔨.

Đấy là mình lấy ví dụ thôi chứ nhà hàng nào mà áp dụng hình thức này chắc phá sản 😂. Trong thực tế hệ thống message broker là Grab, Now, Baemin... với các chàng shipper đẹp zai lực lưỡng.

Message-driven programming bắt nguồn từ tư tưởng trên. Server và client không giao tiếp trực tiếp với nhau nữa. Tất cả các request sẽ được gửi dưới dạng message cho bên thứ 3. Bên thứ 3 có nhiệm vụ điều hướng các message đến địa chỉ cụ thể với 2 mục tiêu:

  • Đảm bảo gửi message thành công.
  • Và gửi đến đúng địa chỉ.

2) Message broker

Với ví dụ trên, hệ thống SMS là một Message Broker với mục đích điều hướng, trung chuyển message từ người gửi đến người nhận, với 4 ưu điểm:

  • Giảm tải cho các server bằng việc giảm các tương tác trực tiếp.
  • Lưu trữ request, trong trường hợp server gặp sự cố.
  • Phân phối request đến các nhiều server trong các bài toán cụ thể.
  • Đơn giản hóa quá trình gửi nhận message trong môi trường multi-services.

No silver bullet, nhìn có vẻ gọn gàng nhưng cũng có hạn chế nhất định tuy nhiên không đề cập quá kĩ trong bài viết này 🙉.

  • Đảm bảo việc gửi nhận message đến đích.
  • Cần monitor thêm cả hệ thống Message broker.
  • Xử lý vấn đề khi Message broker gặp lỗi.
  • Tăng latency, giảm performance.
  • Vân vân và mây mây...

Hiện nay, có khá nhiều Message broker hoạt động dựa trên cách thức và nền tảng khác nhau, nhưng tựu chung lại đều chung mục đích điều hướng, trung chuyển message:

  • Apache ActiveMQ.
  • Apache RocketMQ.
  • RabbitMQ.
  • Apache Kafka.
  • IronMQ.
  • ZeroMQ.
  • Redis, thực tế hiếm khi sử dụng. Chẳng ai đi KIA Morning trên cao tốc trong khi đã có Lamborghini.

Nói chung là, nhiều loại vcđ... Chọn cái gì cũng cần đau đầu để nghĩ. Chọn sai là phải trả giá, đắt hay rẻ tùy thuộc túi tiền của bạn.

3) Message distribution patterns

Tương tự với ví dụ cửa hàng KFC, Message broker cung cấp 2 patterns chính để cung cấp việc điều hướng message:

  • Point-to-point messaging: hay còn gọi là Queue. Hiểu đơn giản đó là dạng phân phối message có quan hệ 1 - 1 giữa client và server, tao chỉ nói cho.. một mình mày thôi đấy. Mỗi message chỉ được gửi đến một endpoint duy nhất. Ví dụ là cuộc trò chuyện trên Skype giữa 2 người với nhau.
  • Broadcast messaging: một message có thể được gửi tới nhiều địa chỉ khác nhau, chỉ những người subcribe nội dung đó mới nhận được message. Ví dụ như khi follow mình, hệ thống chỉ gửi thông báo khi mình có bài viết mới đến các followers. Pattern này được gọi là Topic.

Với từng bài toán khác nhau ta sẽ linh hoạt sử dụng queue hoặc topic để xử lý vấn đề.

  • Nhắn tin hai người có thể dùng queue.
  • Khi nhắn tin trong group thì dùng topic.

4) Mô hình sử dụng Message broker

Message broker được đặt trong hệ thống bao gồm:

  • Producer/Publisher: nơi gửi message.
  • Message broker: hệ thống điều hướng message.
  • Consumer/Subcriber: nơi nhận message.

Không còn khái niệm client và server mà thay vào đó là producer/publisher và consumer/subscriber. Về bản chất vẫn như nhau, một bên gửi và một bên nhận message.

5) Phân chia Message broker theo cách thức hoạt động

Các chuyên gia phân chia Message broker ra thành 2 loại dựa trên cách thức hoạt động của chúng:

  • Message base.
  • Data pipline.
Message baseData pipeline
ActiveMQ, RabbitMQ, ZeroMQRocketMQ, Kafka
Lưu trạng thái của Consumer để đảm bảo tất cả đều nhận được message từ topic đang subscribe.Không lưu trạng thái của Consumer.
Message bị xóa sau khi các Consumer nhận được message.Message chưa bị xóa ngay sau khi Consumer nhận message.
Khi có message mới, Consumer chỉ lấy được duy nhất message đó.Consumer có thể tùy ý lựa chọn lấy về một danh sách các message, bao gồm cả message cũ.

Có thể quyết định sử dụng loại Message broker nào dựa trên bảng so sánh trên:

  • Với các bài toán yêu cầu đảm bảo Consumer đều nhận được một message và duy nhất một lần nhận là quan trọng nhất, ta sử dụng Message base.
  • Các bài toán yêu cầu sự chính xác cao và đảm bảo không lost message thì cân nhắc sử dụng Data pipeline.

Mạnh miệng thế chứ hiện tại chúng ta đa số đều sử dụng Apache Kafka, với hai lý do chính:

  • Mạnh mẽ, được quảng cáo performance tốt. Feature được nhấn mạnh là không mất message.
  • Công nghệ mới, tội gì không làm.

Reference

Reference in series https://viblo.asia/s/apache-kafka-tu-zero-den-one-aGK7jPbA5j2

Wednesday, November 30, 2016

Cài đặt thuật toán Dijskstra bằng java

Bố cuc:
1. Lớp Bài toán: Cài đặt thuật toán Dijstra
2.Lớp test

1 .Lớp cài đặt thuật toán Dijkstra
BaiToan.java
package baocaovantruhoc_nhom13;

/**
 *
 * @TranVanLinh
 */
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;

public class BaiToan
{
    int soDinh;//so dinh cua do thi
    int G[][];//ma tran trong so
    int diemDau;//diem dau
    int doDai[];//do dai toi dinh i
    int daXet[];// danh dau la dinh co duong di la ngan nhat
    int dinhTruoc[];//luu vet dinh truoc
    int oo = 0;//gia tri vo cung
    public BaiToan()
    {
        soDinh = 0;
        G = null;
        doDai = null;
        daXet=null;
        dinhTruoc = null;
        diemDau = 0;
        oo = 0;
    }
    //Doc du lieu
    public boolean getDuLieuTuFile(String tenfile)
    {
        String chuoifile[]=tenfile.split(".");
        String path="G://"+tenfile;
        try
        {
            File file=new File(path);
            if(!file.exists())
            {
                return false;
            }
            FileInputStream input = new FileInputStream(path);
            InputStreamReader istream=new InputStreamReader(input);
            BufferedReader reader=new BufferedReader(istream);
            String sc;
            //doc so dinh cua do thi , dinh bat dau , dinh dich
            sc = reader.readLine();
            String temp[] = sc.split(" ");
            soDinh = Integer.parseInt(temp[0]);
            diemDau = Integer.parseInt(temp[1]);
            G=new int[soDinh][soDinh];
            int row=0;
            //doc matran do thi
            while ((sc = reader.readLine()) != null)
            {
                temp = sc.split(" ");
                for (int col = 0; col < soDinh; col++)
                {
                     G[row][col] = Integer.parseInt(temp[col]);
                }
                row++;
            }
          
            //dong file
            reader.close();
            istream.close();
            input.close();
        }
        catch(FileNotFoundException ex)
        {
            System.err.println("LOi file");
        }
        catch (IOException ex)
        {
                System.err.println("Ngoai le xay ra.!");
        }
        return true;
    }
    public void thuatToan_Dijkstra()
    {
        oo=999999999;
        //Gan trong so cho cac canh khong co duong di la vong cung
        for (int i = 0; i < soDinh; i++)
        {
            for (int j = 0; j < soDinh; j++)
            {
                if (i != j && G[i][j] == 0)
                {
                    G[i][j] = oo;
                }
            }
        }
        diemDau--;
        doDai= new int[soDinh];
        daXet=new int[soDinh];
        dinhTruoc=new int[soDinh];
        for (int i = 0; i < soDinh; i++)
        {
            doDai[i] = oo;                   // khoi tao do dai tu a toi moi dinh la vo cung
            daXet[i] = 0;                       // danh sach cac diem da xet
            dinhTruoc[i] = diemDau;                   // dat diem bat dau cua moi diem la a
        }
        int i=0;
         //Khoi tao d(diemDau,diemDau)=0;
        doDai[diemDau] = 0;
        for(int dinh=0;dinh<soDinh;dinh++)
        {
               for (i = 0; i < soDinh; i++)   // tim mot diem chua xet ma co duong di < vo cung
               {
                   if (daXet[i] != 1 && doDai[i] < oo)
                   {
                       break;
                   }
               }
               if(i==soDinh)//khong co dinh nao thoa man
               {
                   break;
               }
               for (int j = 0; j < soDinh; j++)// tim dinh ma co do dai la nho nhat
               {  
                   if (daXet[j]!=1 && doDai[i] > doDai[j])
                   {
                       i = j;
                   }
               }
               daXet[i] = 1;    // cho i vao danh sach xet roi
         
               for (int j = 0; j < soDinh; j++) // tinh lai do dai cua cac diem chua xet
               {  
                   if (daXet[j]!=1 && doDai[i] + G[i][j] < doDai[j])
                   {
                       doDai[j] = doDai[i] + G[i][j];   // thay doi do dai cua d[i,j]
                       dinhTruoc[j] = i;                       // danh dau diem truoc j la i
                   }
               }
        }
    }
    public  void inDuongDi()
    {
        for(int dinh=0;dinh<soDinh;dinh++)
        if(dinh!=diemDau)
        {
             //in duong di
            if(doDai[dinh] < oo)
            {
                System.out.print("\t\tĐộ dài đường đi từ đỉnh " + ((char)(diemDau+65)) + " đến đỉnh " +  ((char)(dinh+65)) + " là: " + doDai[dinh]+"\t----");
                int mang[] = new int[soDinh];
                int dem = 0;
                int i=dinh;
                while (i != diemDau)
                {
                    mang[dem++] = i;
                    i = dinhTruoc[i];
                }
                System.out.print("\tChi tiết: " +  ((char)(diemDau+65)));
                for (int k = dem - 1; k >= 0; k--) {
                    System.out.print("-->" + (char)(mang[k]+65));
                }
            }
            else
            {
                System.out.println("\t\tKhông có đường đi từ đỉnh "+( ((char)(diemDau+65)))+" đến đỉnh " + ( ((char)(dinh+65))));
            }
            System.out.println("\n");
        }
    }
    public  void inDuong2Di()
    {
        System.out.println("Nhap dinh dich: ");
        Scanner sc=new Scanner(System.in);
        int diemCuoi=sc.nextInt();
      
     
             //in duong di
            if(doDai[diemCuoi] < oo)
            {
                System.out.print("\t\tĐộ dài đường đi từ đỉnh " + ((char)(diemDau+65)) + " đến đỉnh " +  ((char)(diemCuoi+65)) + " là: " + doDai[diemCuoi]+"\t----");
                int mang[] = new int[soDinh];
                int dem = 0;
                int i=diemCuoi;
                while (i != diemDau)
                {
                    mang[dem++] = i;
                    i = dinhTruoc[i];
                }
                System.out.print("\tChi tiết: " +  ((char)(diemDau+65)));
                for (int k = dem - 1; k >= 0; k--) {
                    System.out.print("-->" + (char)(mang[k]+65));
                }
            }
            else
            {
                System.out.println("\t\tKhông có đường đi từ đỉnh "+( ((char)(diemDau+65)))+" đến đỉnh " + ( ((char)(diemCuoi+65))));
            }
            System.out.println("\n");
      
    }
    public void inMatran()
    {
        for(int i=0;i<soDinh;i++)
        {
            System.out.print("\t\t");
            for(int j=0;j<soDinh;j++)
            {
                if(G[i][j]==0)
                {
                    System.out.print("oo\t");
                }
                else
                {
                    System.out.print(G[i][j]+"\t");
                }
            }
            System.out.println();
        }
        System.out.println();
    }
}

2. Lớp test 
BaoCaoVanTruHoc_Nhom13.java
 package baocaovantruhoc_nhom13;
import java.util.Scanner;

/**
 *
 * @TranVanLinh
 *
 */
public class BaoCaoVanTruHoc_Nhom13 {

    public static void main(String[] args)
    {
        System.out.println("\t\t\tBáo Cáo Bài Tập Lớn Môn Vận Trù Học");
        System.out.println("\t\tNhóm 13\tĐề tài: Cài đặt thuật toán Dijkstra\n");
       
        String tenTapTin;
        BaiToan thuatToan;
        do
        {
            System.out.print("\t\tNhập tên tập tin (.txt) : ");
            tenTapTin=(new Scanner(System.in)).nextLine();
            System.out.println("");
            thuatToan=new BaiToan();
            if(!thuatToan.getDuLieuTuFile(tenTapTin))
            {
                System.out.println("\t\tTập tin không tồn tại, Nhập lại.!");
            }
        }while(!thuatToan.getDuLieuTuFile(tenTapTin));
        System.out.println("\t\tMa trận kề : ");
        thuatToan.inMatran();
        thuatToan.thuatToan_Dijkstra();
        thuatToan.inDuongDi();
    }
}

Vd: Tìm đường đi của từ đỉnh x1 đến tất cả các đỉnh của đồ thị sau:



File input2.txt

 7 4
0 3 5 4 2 7 5
3 0 0 0 2 0 0
5 0 0 4 0 3 0
4 0 4 0 0 0 2
2 2 0 0 0 0 4
7 0 3 0 0 0 6
5 0 0 2 4 6 0


Kết quả: