Thông tin nhóm Riêng tư

administrators

Administrators

Danh sách thành viên

  • RE: Goroutines - Cách tiếp cận dễ hiểu

    Bài này anh thấy khá chi tiết, đầy đủ kèm ví dụ dễ hiểu với Goroutine, em tham khảo xem nha: https://vgolang.com/topic/37/bài-22-cơ-chế-truyền-dữ-liệu-giữa-các-goroutine

    được đăng trong Thảo luận chung
  • Socket server với Golang và socket.io - Ứng dụng chat realtime đơn giản

    maxresdefault (2).jpg

    Ứng dụng thực tiễn

    Áp dụng vào trong một bối cảnh cụ thể và chúng ta có thể thấy được websockets quan trọng như thế nào. Tưởng tượng rằng chúng ta có một ứng dụng chat có thể lấy về tất cả các tin nhắn gần nhất từ máy chủ duy nhất và đẩy lên tất cả các itn nhắn mới lên cùng một máy chủ.

    Sử dụng REST API?

    Để có thể làm được real-time chat, bạn phải gửi yêu cầu liên tục đến server API để lấy về những tin nhắn mới nhất.

    Điều này dẫn đến việc có khoảng 60 yêu cầu gửi lên mỗi phút cho một client. Nếu chúng ta có thể xây dựng thành công một dịch vụ và càng ngày chúng ta sẽ có nhiều traffic hơn và "khủng bố" ứng dụng của chúng ta, máy chủ của chúng ta sẽ bắt đầu bị quá tải bởi hàng triệu request API mỗi phút.

    Ví dụ về Socket

    Nếu chúng ta cân nhắc về viễn cảnh sử dụng websockets thay thế cho REST API:

    • Mỗi client sẽ duy trì chỉ duy nhất một kết nối đến máy chủ.
    • Với 1000 client chúng ta chỉ cần duy trì 1000 kết nối socket.
    • Nếu một người gửi một tin nhắn, sau đó máy chủ sẽ đẩy tin nhắn và cập nhật đến 1000 client khác.
    • Với phương thức này chúng ta làm giảm tối đa network traffic đến server. Chúng ta sẽ tiết kiệm được kinh phí cho số lượng ứng dụng server và chúng ta cần chạy và chúng ta có thể xử lý hàng ngàn clients mà không cần tốn quá nhiều công sức và chi phí.

    Triển khai Golang Server

    Để triển khai một websocket với Go, chungs ta có khá nhiều sự lựa chọn. Trong bài viết này mình sẽ sử dụng giải pháp phổ biến nhất đó là socket.io

    Cài đặt go-socket.io

    Chúng ta có thể cài đặt package sử dụng go get như bên dưới:

    go get github.com/googollee/go-socket.io
    

    Và sau đó chúng ta include nó vào chương trình Go như sau:

    import "github.com/googollee/go-socket.io"
    

    Server đơn giản

    Chúng ta hãy xem ví dụ mẫu dưới đây:

    package main
    
    import (
    	"log"
    	"net/http"
    
    	socketio "github.com/googollee/go-socket.io"
    )
    
    func main() {
    
    	server, err := socketio.NewServer(nil)
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	server.On("connection", func(so socketio.Socket) {
    
    		log.Println("on connection")
    
    		so.Join("chat")
    
    		so.On("chat message", func(msg string) {
    			log.Println("emit:", so.Emit("chat message", msg))
    			so.BroadcastTo("chat", "chat message", msg)
    		})
    
    		so.On("disconnection", func() {
    			log.Println("on disconnect")
    		})
    	})
    
    	server.On("error", func(so socketio.Socket, err error) {
    		log.Println("error:", err)
    	})
    
    	http.Handle("/socket.io/", server)
    
    	fs := http.FileServer(http.Dir("static"))
    	http.Handle("/", fs)
    
    	log.Println("Serving at localhost:5000...")
    	log.Fatal(http.ListenAndServe(":5000", nil))
    }
    

    Hãy cùng phân tích mã nguồn trên nào

    Ở mã nguồn mẫu trên chúng ta sẽ làm mọi thứ trong hàm main(). Đầu tiên chúng ta định nghĩa một socket.io server mới bằng cách gọi socketio.NewServẻ(nil) trước khi định nghĩa các phương thức hoạt động và xử lý lỗi.

    Bên trong hàm server.On('connection',...) chúng ta sẽ log ra những kết nối thành công trước sau đó join phòng chat sử dụng so.Join("chat").

    Sau đó chúng ta sẽ chỉ định điều gì sẽ xảy ra khi chúng ta nhận được một tin nhắn mới từ một socket đã kết nối. Mỗi khi server của chúng ta nhận được event này chúng ta gọi so.BroadcastTo("chat", "tin nhắn", msg) sẽ gửi message đến tất car các socket đang kết nối ở thời điểm hiện tại. Điều này có nghĩa là một client có thể thấy được tất cả các tin nhắn gửi đến từ các client khác.

    Cuối cùng chúng ta định nghĩa cho event "disconnection", trong ví dụ hày chúng ta chỉ đơn giản log ra client đã ngắt kết nối.

    Giao diện kết nối cho ứng dụng

    Okay, chúng ta cần một giao diện đơn giản để test xem ứng dụng có hoạt động như mong muốn không.

    Hãy tạo một file index.html bên trong thư mục dự án.

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>Go WebSocket Tutorial</title>
      </head>
      <body>
        <h2>Hello World</h2>
    
        <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.js"></script>
        <script>
          const socket = io("http://localhost:5000/socket.io/");
        </script>
      </body>
    </html>
    

    Sau đó chạy websocket server bằng cách gọi:

    $ go run main.go
    
    2019/03/12 07:54:06 Serving at localhost:5000...
    2019/03/12 07:54:15 on connection
    2019/03/12 07:54:16 on connection
    

    Server đang chạy ở cổng 5000 với URL http://localhost:5000. Bạn có thể mở URL này trên trình duyệt và xem những kết nối mới trong output log của server.

    Bây giờ, bạn đã xây dựng một giao diện kết nối trực tiếp đến Go websocket server thành công rùi đấy 😁

    Mì ăn liền

    https://github.com/TutorialEdge/Go/tree/master/go-websocket-tutorial

    được đăng trong Blogs
  • Hướng dẫn cài đặt và sử dụng Go Dep

    4e7edae6-594e-4067-ac92-3d451e98c2e4-image.png

    Go Dep là một công cụ chủ yếu dành cho các nhà phát triển, để hỗ trợ công việc cài đặt và phân phối mã nguồn. Nó không dành cho người dùng cuối đang cài đặt Go - đó là công việc của Go Get.

    Bài viết này sẽ giúp các bạn tìm hiểu cài đặt và sử dụng Go Dep cho dự án.

    Phiên bản

    Các bạn nên sử dụng phiên bản đã được released của dep sẽ ổn định và đảm bảo hơn.

    Cài đặt

    Các bản cài đặt có thể tải về trên trang chủ. Các bạn có thể sử dụng script install.sh để tự động cài đặt trên nền tảng local của các bạn.

    $ curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
    

    MacOS
    Cài đặt và cập nhật phiên bản mới nhất với Homebrew:

    $ brew install dep
    $ brew upgrade dep
    

    Arch Linux
    Cài đặt dep package:

    pacman -S dep
    

    Cài đặt từ mã nguồn

    Đoạn mã dưới đây sẽ cài đặt bản phát hành dep mới nhất từ mã nguồn và biên dịch sang nhị phân để hoạt động.

    Lưu ý rằng phương pháp này không được khuyến khích sử dụng bởi nó không đảm bảo sự ổn định mà chỉ phù hợp với những người sẵn sàng thử nghiệm và cung cấp các thông tin phản hồi nhanh!

    go get -d -u github.com/golang/dep
    cd $(go env GOPATH)/src/github.com/golang/dep
    DEP_LATEST=$(git describe --abbrev=0 --tags)
    git checkout $DEP_LATEST
    go install -ldflags="-X main.version=$DEP_LATEST" ./cmd/dep
    git checkout master
    

    Cài đặt ngay trên môi trường phát triển

    Nếu bạn muốn hack go dep, hãy thử cài đặt nó với go get

    go get -u github.com/golang/dep/cmd/dep
    

    Lưu ý rằng dep yêu cầu Go workspaceGOPATH hoạt động. Hãy đảm bảo bạn cấu hình tất cả những yêu cầu cần thiết trên trước khi cài đặt Go Dep

    Tạo một project mới

    Sau khi bạn đã cài đặt Go Dep, bạn cần chọn một thư mục root cho project của bạn. Bạn cần phải lựa chọn thư mục root phù hợp với yêu cầu phát triển của bạn. Có 4 trường hợp cơ bản sau:

    • Một dự án có thể được chia sẻ hay import bởi những dự án hay lập trình viên khác. Trong trường hợp này hãy lựa chọn đường dẫn import tương ứng với VCS root với vị trí network của nó, ví dụ:
    $GOPATH/src/github.com/golang/dep.
    
    • Một dự án hoàn toàn cục bộ, bạn không có ý định đẩy nó lên server trung tâm (như Github chẳng hạn). Trong trường hợp này, mọi thư mục con bên dưới $GOPATH/src đều hợp lý.

    • Một dự án cần một repository lớn sẽ phức tạp đôi chút. Go Dep hiện vẫn chưa hỗ trợ, có lẽ là trong tương lai

    • Hãy coi toàn bộ GOPATH là một dự án duy nhất, trong đó $GOPATH/src là root. Hiện tại Dep không hỗ trợ điều này - nó cần một đường dẫn nhập không rỗng để là gốc của namespace cho dự án của bạn.

    Chúng ta giả định rằng trường hợp đầu tiên là phổ biến nhất. Hãy tạo và di chuyển dự án vào đường dẫn như sau:

    $ mkdir -p $GOPATH/src/github.com/me/example
    $ cd $GOPATH/src/github.com/me/example
    

    Okay, chúng ta bắt đầu khởi tạo dự án:

    $ dep init
    $ ls
    Gopkg.toml Gopkg.lock vendor/
    

    Trong một dự án mới như này, tất cả những tập tin và thư mục vendor sẽ rỗng.

    Đây là thời điểm thích hợp để chúng ta thiết lập quản lý phiên bản, như git chẳng hạn. Mặc dù dep không có cách nào để kiểm soát, quản lý phiên bản của dự án nhưng nó có thể giúp việc kiểm tra các thay đổi được thực hiện dễ dàng hơn. Về cơ bản, đó là cách tốt nhất để phát triển các phần mềm hiện đại!

    Tới đây, dự án của chúng ta đã được khởi tạo, chúng ta đã sẵn sàng để bắt đầu lập trình. Bạn có thể mở một file .go lên vằ bắt đầu "hack" rồi. Hoặc bạn có thể thêm vào trước những project/package mà bạn đã biết là sẽ dùng đến sử dụng câu lệnh sau:

    $ dep ensure -add github.com/foo/bar github.com/baz/quux
    

    Okay, đến đây là kết thúc hướng dẫn căn bản cài đặt và sử dụng Go Dep rồi.
    Chi tiết về một số các câu lệnh căn bản thường dùng, các bạn có thể tham khảo thêm ở đây: https://golang.github.io/dep/docs/daily-dep.html

    Chúc các bạn sẽ tạo được các project "ngon lành" trong tương lai! 😂

    được đăng trong Blogs
  • RE: Tính dãy Fibonacci?

    @wuuyi awesome example bro 😉

    được đăng trong Blogs
  • RE: Suggest fullstack web framework nhanh

    @esoftcard Mình suggest Gingonic hoặc Echo 😁

    được đăng trong Thảo luận chung
  • RE: Mã màu và "struct màu"

    Bài chất quá bác 😂

    được đăng trong Blogs
  • RE: Các yêu cầu của junior golang

    @Nguyen-Thanh-Hai đã nói trong Các yêu cầu của junior golang:

    @admin junior mà đã nhiều skill vậy rồi á 🐶

    Chừng này chỉ là vừa đủ để vào dự án thui mà 😂

    được đăng trong Thảo luận chung
  • RE: Bài 11 - Kiểu dữ liệu trong Go: Slice và map

    @gopher đã nói trong Bài 11 - Kiểu dữ liệu trong Go: Slice và map:

    @admin e vẫn chưa hiểu tại sao khi gán 1 key/value mới (có value != 0) mà trong map không có thì cặp key/value có giá trị bằng 0 lại bị thay thế bởi cặp key/value mới này nhỉ ?. Vd ở trong bài này là "trúc" đã thay cho "Lan"

    "Lan" bị xóa rùi chớ hem phải "Trúc" thay cho "Lan"

    delete(age, student[i]) 
    
    được đăng trong Series nhập môn Golang
  • Replication Master-Slave với binlog trong Golang

    5f6b36022c1ef3bc5fad546457e21878--difference-german-shepherds.jpg

    Giới thiệu về MySql Replication

    MySQL Replication là một quá trình cho phép bạn dễ dàng duy trì nhiều bản sao của dữ liệu MySQL bằng cách cho họ sao chép tự động từ một master tạo ra một cơ sở dữ liệu slave. Điều này rất hữu ích vì nhiều lý do bao gồm việc tạo điều kiện cho sao lưu cho dữ liệu, một cách để phân tích nó mà không sử dụng các cơ sở dữ liệu chính, hoặc chỉ đơn giản là một phương tiện để mở rộng ra.

    Replication mặc định là không đồng bộ, slave không cần phải kết nối vĩnh viễn để nhận được cập nhật từ master. Tùy thuộc vào cấu hình, bạn có thể sao chép tất cả các cơ sở dữ liệu, cơ sở dữ liệu đã chọn, hoặc thậm chí bảng được lựa chọn trong một cơ sở dữ liệu. Thật vậy, Replication có ý nghĩa là nhân bản, là có một phiên bản giống hệt phiên bản đang tồn tại, đang sử dụng. Với một cơ sở dữ liệu có nhu cầu lưu trữ lớn, thì đòi hỏi cơ sở dữ liệu phải toàn vẹn, không bị mất mát trước những sự cố ngoài dự đoán là rất cao. Vì vậy, người ta nghĩ ra khái niệm (slave) “nhân bản”, tạo một phiên bản cơ sở dữ liệu giống hệt cơ sở dữ liệu đang tồn tại, và lưu trữ ở một nơi khác, đề phòng có sự cố.

    Server master lưu trữ phiên bản cơ sở dữ liệu phục vụ ứng dụng. Server slave lưu trữ phiên bản cơ sở dữ liệu “nhân bản”. Quá trình nhân bản từ master sang slave gọi là replication.

    Tất cả các thay đổi trên cơ sở dữ liệu master sẽ được ghi lại dưới dạng file log binary, slave đọc file log đó, thực hiện những thao tác trong file log, việc ghi, đọc và thực thi trong file log này dưới dạng binary được thực hiện rất nhanh.

    Ưu điểm của replication trong mysql

    • Giảm tải cho cơ sở dữ liệu server master, tải trọng của server được phân tải cho các con slave, cải thiện hiệu năng cho toàn hệ thống. Trong môi trường này, tất cả các quá trình ghi và cập nhật đều phải diễn ra trên server master, bên cạnh đó quá trình đọc được diễn ra trên một hoặc nhiều con slave. Chính vì vậy mô hình này giúp tăng đáng kể hiệu năng của toàn hệ thống.

    • Tính bảo mật dữ liệu cao - vì dữ liệu được sao chép đến các slave, và các slave có thể tạm dừng quá trình sao chép, nó có thể chạy các dịch vụ sao lưu trên các slave mà không làm hư hỏng dữ liệu tổng thể tương ứng.

    • Tính phân tích - dữ liệu trực tiếp có thể được tạo ra trên master, trong khi phân tích các thông tin có thể xảy ra trên các slave mà không ảnh hưởng đến hiệu suất của master.

    • Tính phân phối dữ liệu từ xa - bạn có thể sử dụng replication để tạo ra một bản sao của dữ liệu cho một trang web từ xa để sử dụng, mà không cần truy cập thường xuyên vào con master.
      Mô hình MySQL Replication và cách thức hoạt động
      mysql_replication_topology_threads.png

    Replication dựa trên các con master lưu giữ theo dõi tất cả những thay đổi cơ sở dữ liệu của nó (cập nhật, xóa, vv) trong bản ghi nhị phân của nó. Các bản ghi nhị phân phục vụ như là các record của tất cả các sự kiện làm thay đổi cấu trúc cơ sở dữ liệu hoặc nội dung (dữ liệu) từ thời điểm các máy chủ đã bắt đầu thực thi. Thông thường, câu SELECT không được ghi lại bởi vì chúng không phải thay đổi cấu trúc cũng như nội dung của cơ sở dữ liệu.

    Mỗi slave kết nối đến các master yêu cầu một bản sao của bản ghi nhị phân. Đó là, nó kéo các dữ liệu từ các master, chứ không phải là master đẩy dữ liệu đến các slave. Các slave cũng thực hiện các sự kiện từ các bản ghi nhị phân mà nó nhận được. Quá trình này lặp đi lặp lại những thay đổi ban đầu cũng giống như nó đã được thực hiện trên master. Bảng được tạo ra hoặc cấu trúc thay đổi và dữ liệu đã chèn hay đã xóa và kể cả cập nhật thì đều giống hệt theo những thay đổi mà ban đầu đã được thực hiện trên master.

    Chi tiết quá trình thực thi trong Replication

    Luồng Binlog dump: Các master tạo 1 luồng và gửi nội dung binary log đến một slave khi các slave kết nối với master. Luồng này có thể được xác định trong đầu ra của query SHOW PROCESSLIST trên master như là 1 luồng Binlog Dump. Các binary log có được trên bản ghi nhị phân của master đọc các sự kiện đó và gửi đi cho slave. Ngay sau khi sự kiện được đọc, khóa được phát hành ngay cả khi sự kiện được gửi tới slave.

    Luồng Slave I/O: Khi thông báo Slave được ban hành trên slave server, các slave tạo một luồng I/O, cái mà kết nối với server master và hỏi nó để nó gửi thông tin bản ghi cập nhật nó vào trong log nhị phân. Luồng slave I/O đọc sự cập nhật trên luồng Binlog Dump của master gửi và sao chép chúng vào 1 file local - file mà bao hàm cả những log trễ (Relay Log). Các trạng thái của luồng này được thể hiện như là Slave_IO_running trong output SHOW SLAVE STATUS hoặc là Slave_running trong ouput của SHOW STATUS.

    Luồng Slave SQL: Các slave tạo ra một luồng SQL để đọc cái log trễ, cái này sau đó sẽ được ghi vào luồng slave I/O và thực thi các sự kiện chứa trong đó

    Cách cài đặt MySQL Slave Replication

    Bước 1: Cấu hình Master Database

    sudo nano /etc/mysql/my.cnf
    

    Bước đầu tiên phải tìm đến phần trông như sau để binding server master localhost chẳng hạn:

    bind-address            = 127.0.0.1
    

    Thay thế địa chỉ IP local thành địa chỉ của server. Ví dụ:

    bind-address            = 12.34.56.789
    

    Thay đổi tiếp theo đề cập đến các server-id, nằm trong phần [mysqlId]. Bạn có thể chọn bất kì số nào, ví dụ đơn giản nhất có thể đặt là 1.

    server-id               = 1
    

    Tiếp theo đặt đường dẫn file log cho mysql, tất cả các sự kiện của slave được lưu trữ trong đường dẫn này. Tìm đến dòng log_bin:

    log_bin                 = /var/log/mysql/mysql-bin.log
    

    Cuối cùng chúng ta cần phải chỉ định cơ sở dữ liệu sẽ được nhân bản trên các máy slave. Bạn có thể chỉ định thay vì một mà là nhiều các slave bằng cách lặp lại dòng này cho tất cả các cơ sở dữ liệu mà bạn cần:

    binlog_do_db            = newdatabase
    

    Sau khi chỉnh sửa xong chúng ta cần lưu lại file cấu hình và khởi động lại mysql

    sudo service mysql restart
    

    Bước tiếp theo chúng ta cần phải mở MySQL mà cấp quyền cho các slave, bạn có thể đặt tên, mật khẩu cho các slave tùy ý:

    GRANT REPLICATION SLAVE ON *.* TO 'slave_user'@'%' IDENTIFIED BY 'password';
    

    Sau đó kiểm tra bằng cách:

    FLUSH PRIVILEGES;
    

    Một điều quan trọng nữa, bạn cần phải mở 1 tab mới và lựa chọn cơ sở dữ liệu của bạn.

    USE newdatabase;
    

    Sau đó, quan trọng nhất là bạn cần phải khóa cơ sở dữ liệu mà sau này các slave để ở chế độ chỉ đọc

    FLUSH TABLES WITH READ LOCK;
    

    Để kiểm tra cơ sở dữ liệu chúng ta gõ câu lệnh sau:

    mysql> SHOW MASTER STATUS;
    +------------------+----------+--------------+------------------+
    | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
    +------------------+----------+--------------+------------------+
    | mysql-bin.000001 |      107 | newdatabase  |                  |
    +------------------+----------+--------------+------------------+
    1 row in set (0.00 sec)
    

    Position ở đây có nghĩa là vị trí mà bắt đầu các slave sao lưu dữ liệu. Căn cứ vào cơ sở dữ liệu đã bị khóa, chúng ta export cơ sở dữ liệu ra 1 file sql để tiện dùng cho bước 2.

    mysqldump -u root -p --opt newdatabase > newdatabase.sql
    

    Bây giờ quay trở lại cửa sổ ban đầu và mở khóa cơ sở dữ liệu của bạn:

    UNLOCK TABLES;
    QUIT;
    

    Về cơ bản cấu hình máy chủ đã tạm ổn

    Bước 2: Cấu hình cơ sở dữ liệu slave

    Đăng nhập vào server slave, mở mysql là tạo cơ sở dữ liệu với tên giống hệt cơ sở dữ liệu master:

    CREATE DATABASE newdatabase;
    EXIT;
    

    Import cơ sở dữ liệu mà đã export ở bước 1.

    mysql -u root -p newdatabase < /path/to/newdatabase.sql
    

    Chúng ta cần cấu hình những con slave giống hệt như cách mà chúng ta cấu hình con master. Tuy nhiên cũng cần chỉnh sửa một số thông số cho phù hợp như server-id:

    server-id               = 2
    
    relay-log               = /var/log/mysql/mysql-relay-bin.log
    
    log_bin                 = /var/log/mysql/mysql-bin.log
    
    binlog_do_db            = newdatabase
    

    Khởi động lại mysql của con slave:

    sudo service mysql restart
    

    Bước tiếp theo chúng ta cần phải cấp quyền và cho phép nhân bản ở bên trong MySQL shell. Bật lại MySQL shell và thay thế các thông tin như sau:

    CHANGE MASTER TO MASTER_HOST='12.34.56.789',MASTER_USER='slave_user', MASTER_PASSWORD='password', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=  107;
    

    Nội dung command trên được hiểu như sau:

    • Chỉ định các máy chủ hiện tại như là slave của server master
    • Cung cấp thông tin đăng nhập chuẩn cho các máy chủ
    • Cuối cùng chỉ định cho các máy slave biết rằng cần phải sao lưu từ file log * nào và đăng nhập từ vị trí mà đã định nghĩa trong position nào.

    Sau đó chúng ta active server slave:

    START SLAVE;
    
    // Kiểm tra bằng cách:
    SHOW SLAVE STATUS\G
    
    // Nếu có vấn đề trong kết nối bạn có thể thử start slave bằng cách:
    SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1; SLAVE START;
    

    Mysql Binlog với Golang

    Đầu tiên chúng ta hãy làm cho nó chạy được trước đã:

    Chúng ta sẽ sử dụng thư viện này để làm việc với binlog: https://github.com/siddontang/go-mysql

    Hãy kết nối đến một channel mới. Chúng ta sử dụng thư viện https://mariadb.com/kb/en/library/binary-log-formats dùng cho binlog ROW format.

    func binLogListener() {
    c, err := getDefaultCanal()
    if err == nil {
    coords, err := c.GetMasterPos()
    if err == nil {
       c.SetEventHandler(&binlogHandler{})
       c.RunFrom(coords)
      }
     }
    }
    func getDefaultCanal() (*canal.Canal, error) {
        cfg := canal.NewDefaultConfig() 
        cfg.Addr = fmt.Sprintf("%s:%d", "127.0.0.1", 3306)
        cfg.User = "root"
        cfg.Password = "root"
        cfg.Flavor = "mysql"
        cfg.Dump.ExecutionPath = ""
        
    return canal.NewCanal(cfg)
    }
    

    Hãy tạo một cái wrapper:

    type binlogHandler struct {
    canal.DummyEventHandler // Dummy handler from external lib
    BinlogParser // Our custom helper
    }
    func (h *binlogHandler) OnRow(e *canal.RowsEvent) error {return nil}
    func (h *binlogHandler) String() string {return "binlogHandler"}
    

    Sử dụng thư viện này để parse Binlog: https://github.com/JackShadow/go-binlog-example/blob/master/src/parser.go

    Sau đó thêm một ít muối 🍳 ... à nhầm logic vào phương thức OnRow()

    func (h *binlogHandler) OnRow(e *canal.RowsEvent) error {
    var n int //starting value
    var k int // step
    switch e.Action {
    case canal.DeleteAction:
      return nil  // not covered in example
    case canal.UpdateAction:
     n = 1
     k = 2
    case canal.InsertAction:
     n = 0
     k = 1
    }
    for i := n; i < len(e.Rows); i += k {
    key := e.Table.Schema + "." + e.Table.Name
    switch key {
     case User{}.SchemaName() + "." + User{}.TableName():
     /*
      Real data parsing
     */
     }
    }
    return nil
    }
    

    Mục đích chính của wrapper này là phân tách dữ liệu nhận được. Chúng ta đang lấy dữ liệu bởi 2 mục cập nhật (hàng đầu tiên chứa dữ liệu dược khởi tạo, hàng thứ 2 là dữ liệu đã được cập nhật). Và ở đây chúng ta sẽ hỗ trợ multi insertsmulti updates. Trong trường hợp này cho UPDATE chúng ta sẽ sẽ lấy mọi mục thứ 2. Với INSERT chúng ta sẽ lấy cả row, sử dụng biến kn cho mục đích này.

    Hãy tạo một model struct để lấy dữ liệu về từ binlog. Chúng ta sử dụng nó để lấy data từ rows.

    type User struct {
     Id      int       `gorm:"column:id"`
     Name    string    `gorm:"column:name"`
     Status  string    `gorm:"column:status"`
     Created time.Time `gorm:"column:created"`
    }
    func (User) TableName() string {
     return "User"
    }
    func (User) SchemaName() string {
     return "Test"
    }
    

    Tạo bảng với lệnh như này:

    CREATE TABLE Test.User(
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(40) NULL ,
    status ENUM("active","deleted") DEFAULT "active",
    created TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP
    )
    ENGINE =InnoDB;
    

    Tạo đối tượng User mới và gọi phương thức để lấy dữ liệu từ rows:

    user := User{}
    h.GetBinLogData(&user, e, i)
    

    Print ra xem cho nó nuột:

    if e.Action == canal.UpdateAction {
      oldUser := User{}
      h.GetBinLogData(&oldUser, e, i-1)
      fmt.Printf("User %d is updated from name %s to name %s\n", user.Id, oldUser.Name, user.Name, )
     } else {
      fmt.Printf("User %d is created with name %s\n", user.Id, user.Name, )
    }
    

    Thêm tí code vào main để test phát:

    func main() {
    go binLogListener()
    // placeholder for your handsome code
    time.Sleep(2 * time.Minute)
    fmt.Print("Thx for watching")
    }
    

    Thêm và cập nhật users:

    INSERT INTO Test.User (`id`,`name`) VALUE (1,"Huy");
    UPDATE Test.User SET name="Huy Huynh" WHERE id=1;
    

    Kết quả xuất ra:

    User 1 is created with name Huy
    User 1 name changed from Huy to Huy Huynh
    

    Code trên đã hoạt động với binlog và parse dữ liệu được từ rows mới. Khi chúng ta lấy dữ liệu từ bảng chúng ta cần, code trên sẽ parse dữ liệu vào trong struct và in ra kết quả.

    Okie đến đây là hết bài rùi, các bạn cố gắng đọc hiểu vì Replication Master-Slave với binlog thường rất hay gặp trong các dự án lớn.

    Chúc các bạn có một cái Tết ấm no và vui vẻ bên gia đình 👯 👯 👯

    Reference from medium, viblo

    được đăng trong Blogs
  • RE: Vấn đề khi sử dụng sendgrid để gửi email

    @Nguyễn-Trung-Đức nếu đang sử dụng tk test của sendgrid thì ko gửi đc nha

    được đăng trong Thảo luận chung