gRPC微服務 library-book-grpc-service ,書籍管理內部服務,提供書籍管理的 rpc 介面,主要實作了根據用戶ID獲取書籍串列的功能,用戶管理服務通過 GRPC 呼叫此服務介面,
完整代碼:
https://github.com/Justin02180218/micro-kit
包結構

各個包的含義與上兩篇基本一樣,這里就不一一說明了,
代碼實作
gRPC Server
撰寫 book.proto 檔案
syntax = "proto3";
package book;
option go_package = "/book";
message BookInfo {
uint64 id = 1;
string bookname = 2;
}
message BooksByUserIDRequest {
uint64 userID = 1;
}
message BooksResponse {
repeated BookInfo books = 1;
}
service Book {
// 根據用戶ID查找書籍串列
rpc FindBooksByUserID (BooksByUserIDRequest) returns (BooksResponse) {}
}
下載 protoc 可執行程式:
在 https://github.com/protocolbuffers/protobuf/releases 下,找到作業系統對應的版本下載安裝,
安裝代碼生成工具:
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1
生成 go 程式檔案:
進入工程下的 protos 目錄下執行:
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative book/book.proto
生成的檔案如圖:

組態檔
同 book.yaml ,埠與微服務的名稱不同,
server:
port: 10088
mode: debug
name: "book-rpc-service"
mysql:
host: "localhost"
port: 3306
db: "library"
username: "root"
password: "123456"
debug: true
資料庫表
在 library 資料庫建立 user_book 表,存盤用戶與書籍的對應關系,
CREATE TABLE `user_book` (
`user_id` bigint(20) NOT NULL,
`book_id` bigint(20) NOT NULL,
PRIMARY KEY (`user_id`,`book_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
dao層
在 dao 層創建與資料庫互動的 book_dao.go
定義 BookDao 介面及實作:
type BookDao interface {
FindBooksByUserID(userID uint64) ([]models.Book, error)
}
type BookDaoImpl struct{}
func NewBookDaoImpl() BookDao {
return &BookDaoImpl{}
}
-
FindBooksByUserID:根據用戶ID查詢所借書籍串列
BookDao 介面的函式實作
func (b *BookDaoImpl) FindBooksByUserID(userID uint64) ([]models.Book, error) {
books := new([]models.Book)
sql := "select b.* from book b, user_book ub where b.id = ub.book_id and ub.user_id = ?"
err := databases.DB.Raw(sql, userID).Scan(books).Error
if err != nil {
return nil, err
}
return *books, nil
}
service層
在 service 層創建 book_service.go
定義 BookService 介面及實作:
type BookService interface {
FindBooksByUserID(ctx context.Context, req *pbbook.BooksByUserIDRequest) (*pbbook.BooksResponse, error)
}
type BookServiceImpl struct {
bookDao dao.BookDao
}
func NewBookServiceImpl(bookDao dao.BookDao) BookService {
return &BookServiceImpl{
bookDao: bookDao,
}
}
BookService 介面的函式實作
func (b *BookServiceImpl) FindBooksByUserID(ctx context.Context, req *pbbook.BooksByUserIDRequest) (*pbbook.BooksResponse, error) {
books, err := b.bookDao.FindBooksByUserID(req.UserID)
if err != nil {
return &pbbook.BooksResponse{}, err
}
pbbooks := new([]*pbbook.BookInfo)
for _, book := range books {
*pbbooks = append(*pbbooks, &pbbook.BookInfo{
Id: book.ID,
Bookname: book.Bookname,
})
}
return &pbbook.BooksResponse{
Books: *pbbooks,
}, nil
}
endpoint層
在 endpoint 層創建 book_endpoint.go,
定義 BookEndpoints struct,只有一個請求,所以對應的只有一個endpoint
type BookEndpoints struct {
FindBooksByUserIDEndpoint endpoint.Endpoint
}
func NewFindBooksByUserIDEndpoint(bookService service.BookService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
req := request.(*pbbook.BooksByUserIDRequest)
res, err := bookService.FindBooksByUserID(ctx, req)
if err != nil {
return nil, err
}
return res, nil
}
}
transport層
在 transport 層定義 grpcServer struct,實作在 book.proto 中定義的介面 book 和函式 FindBooksByUserID
type grpcServer struct {
pbbook.UnimplementedBookServer
findBooksByUserID kitrpc.Handler
}
func NewBookServer(ctx context.Context, endpoints endpoint.BookEndpoints) pbbook.BookServer {
return &grpcServer{
findBooksByUserID: kitrpc.NewServer(
endpoints.FindBooksByUserIDEndpoint,
decodeFindBooksByUserIDRequest,
encodeFindBooksByUserIDResponse,
),
}
}
func (g grpcServer) FindBooksByUserID(ctx context.Context, r *pbbook.BooksByUserIDRequest) (*pbbook.BooksResponse, error) {
_, res, err := g.findBooksByUserID.ServeGRPC(ctx, r)
if err != nil {
return nil, err
}
return res.(*pbbook.BooksResponse), nil
}
啟動服務
main.go
var configFile = flag.String("f", "book_rpc.yaml", "book rpc config file")
var quiteChan = make(chan error, 1)
func main() {
flag.Parse()
err := configs.Init(*configFile)
if err != nil {
panic(err)
}
err = databases.InitMySql(configs.Conf.MySQLConfig)
if err != nil {
fmt.Println("load mysql failed")
}
ctx := context.Background()
bookDao := dao.NewBookDaoImpl()
bookService := service.NewBookServiceImpl(bookDao)
endpoints := endpoint.BookEndpoints{
FindBooksByUserIDEndpoint: endpoint.NewFindBooksByUserIDEndpoint(bookService),
}
go func() {
handler := transport.NewBookServer(ctx, endpoints)
listener, err := net.Listen("tcp", fmt.Sprintf(":%s", strconv.Itoa(configs.Conf.ServerConfig.Port)))
if err != nil {
fmt.Println("listen tcp err", err)
quiteChan <- err
return
}
gRPCServer := grpc.NewServer()
pbbook.RegisterBookServer(gRPCServer, handler)
err = gRPCServer.Serve(listener)
if err != nil {
fmt.Println("gRPCServer Serve err", err)
quiteChan <- err
return
}
}()
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP, syscall.SIGQUIT)
quiteChan <- fmt.Errorf("%s", <-c)
}()
fmt.Println(<-quiteChan)
}
gRPC Client
在 library-user-service 中增加 FindBooksByUserID 函式,通過 gRPC Client 訪問 library-book-grpc-service 中 gRPC Server 提供的 FindBooksByUserID 函式,

修改 user_service.go
type UserService interface {
Register(ctx context.Context, vo *dto.RegisterUser) (*dto.UserInfo, error)
FindByID(ctx context.Context, id uint64) (*dto.UserInfo, error)
FindByEmail(ctx context.Context, email string) (*dto.UserInfo, error)
FindBooksByUserID(ctx context.Context, id uint64) (interface{}, error)
}
type UserServiceImpl struct {
userDao dao.UserDao
bookClient pbbook.BookClient
}
func (u *UserServiceImpl) FindBooksByUserID(ctx context.Context, id uint64) (interface{}, error) {
req := &pbbook.BooksByUserIDRequest{UserID: id}
res, err := u.bookClient.FindBooksByUserID(ctx, req)
if err != nil {
return nil, err
}
return res, nil
}
修改 library-user-service的 main.go ,建立 gRPC Client 的鏈接
conn, err := grpc.Dial("127.0.0.1:10088", grpc.WithInsecure())
if err != nil {
log.Println("連接user rpc 錯誤", err)
panic(err)
}
defer conn.Close()
bookClient := pbbook.NewBookClient(conn)
userDao := dao.NewUserDaoImpl()
userService := service.NewUserServiceImpl(userDao, bookClient)
啟動
進入 library-book-grpc-service 目錄,執行 go run main.go

進入 library-user-service 目錄,執行 go run main.go

介面測驗
使用postman進行介面測驗

回傳書籍串列,gRPC呼叫回傳成功,等后面加入呼叫的鏈路追蹤就可以清晰的看到呼叫鏈路,
下一篇文章,我們給微服務加入限流功能,
完整代碼:
https://github.com/Justin02180218/micro-kit
更多【分布式專輯】【架構實戰專輯】系列文章,請關注公眾號

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/301483.html
標籤:其他
上一篇:SpringBoot+Vue搭建一個WebSocket的實時聊天室
下一篇:ONAP — 系統架構
