The original link, reprint to indicate the source.

Code of this article:GitHub

Contents of this article:

Microservices Architecture

A single code base

When Laravel was used to do Web projects, the directory structure was divided according to MVC, that is, the Controller layer dealt with business logic, the Model layer dealt with CURD of database, and the View layer dealt with data rendering and page interaction. As well as MVP, MVVM is the entire project code is centralized in a code base, business processing. This single aggregated code approach is quick to implement the business in the early stages, but exposes many problems in the later stages:

  • Development and maintenance difficulties: With the increase of business complexity, the coupling degree of code tends to become high, and it is difficult to expand horizontally after multiple modules are coupled with each other
  • Low efficiency and reliability: Too much code will slow down the response time and application potential security problems will accumulate

Split code base

Microservice is a software architecture that disassembles a large and aggregated business project into several small and independent business modules. Modules are services, and each service uses an efficient protocol (protobuf, JSON, etc.) to call each other, namely RPC. This way of splitting the code base has the following characteristics:

  • Each service should run as a small, independent business module, similar to Unix’s Do One Thing and Do it well
  • Each service should be automated testing and (distributed) deployment without affecting other services
  • Careful error checking and handling within each service improves robustness

The two contrast

In essence, they are just different ways of aggregating and splitting code.

Reference: Strengths and weaknesses of microservices architecture

Building microservices

UserInfoService micro service

UserInfoService is a micro service that processes user information. The client queries the age, position and other details of the user to the server through name. GRPC and Protoc compiler should be installed first:

go get -u google.golang.org/grpc
go get -u github.com/golang/protobuf/protoc-gen-goCopy the code

The directory structure

├ ─ ─ proto │ ├ ─ ─ the user. The proto / / define the client request, the server response data format │ └ ─ ─ the user. The pb. Go / / protoc is the function of reading and writing data generated gRPC ├ ─ ─ for server go / / for implementing the server of a micro service ├ ─ └─ custom. go // Use a micro - service clientCopy the code

Calling process

Protobuf agreement

Each microservice has its own independent code base, which requires efficient protocols for communication between them and follows certain data structures to parse and encode data to be transmitted. Protobuf is often defined in microservices.

Protobuf (Protocal buffers) is a binary data encoding format developed by Google, which has advantages over XML and JSON text data encoding formats:

Faster reading and writing, smaller file size

It has no XML tag names or JSON field names, and is much lighter and more referential

Language neutrality

You only need to define a.proto file, which can be compiled using the corresponding protobuf compiler of each language. The generated file has functions to encode and decode message

For JSON
  • This is required in PHPjson_encode()json_decode()To encode and decode, Golang needs to use the JSON standard libraryMarshal()Unmarshal()… Each time parsing and coding is tedious
  • Advantages: good readability, low development cost
  • Disadvantages: Slower read/write speed and more storage space than Protobuf
For the Protobuf
  • Proto can be generated. PHP or *. Pb. Go… The encoding and decoding functions generated by the compiler in this file can be directly referenced in the project
  • Advantages: high efficiency and light weight, a definition of multiple use
  • Disadvantages: poor readability, high development cost

The user.proto file that defines the microservice

syntax = "proto3"; // Specify the syntax format. Note that Proto3 no longer supports Required and optional package proto for Proto2; // Specify the package name of the generated user.pb.go to prevent naming conflicts. UserInfoService microservice UserInfoService {// RPC defines GetUserInfo in the service to invoke remote returns of RPC GetUserInfo (UserRequest) (UserResponse) {}} // message corresponds to the generated code struct // define the client request data format message UserRequest {// [modifier] type field name = identifier; string name = 1; } // Define the data format of the server response message UserResponse {int32 id = 1; string name = 2; int32 age = 3; repeated string title = 4; // Repeated modifiers indicate that a field is a mutable array, i.e., slice}Copy the code

Compile the user.proto file

Message $protoc -i. --go_out=plugins= GRPC :. ./user.protoCopy the code

Generate user. Pb. Go

Package Proto import (context" golang.org/x/net/context" GRPC "google.golang.org/grpc") // Request structure type UserRequest Struct {Name string 'protobuf:"bytes,1,opt, Name = Name "json:" Name, omitEmpty"'} getFunc (m *UserRequest)  GetName() string { if m ! Type UserResponse struct {Id int32 'protobuf:"varint,1,opt,name= Id" json:"id,omitempty"` Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` Age int32 `protobuf:"varint,3,opt,name=age" json:"age,omitempty"` Title []string `protobuf:"bytes,4,rep,name=title" json:"title,omitempty"` } // ... Type UserInfoServiceClient Interface {GetUserInfo(CTX context. context, in *UserRequest, opts... grpc.CallOption) (*UserResponse, Type UserInfoServiceServer Interface {GetUserInfo(context. context, *UserRequest) (*UserResponse, The error)} / / micro service registry to GRPC func RegisterUserInfoServiceServer (s * GRPC. Server, srv UserInfoServiceServer) { s.RegisterService(&_UserInfoService_serviceDesc, Func _UserInfoService_GetUserInfo_Handler(SRV interface{}, CTX context.context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {... }Copy the code

The server implements microservices

The implementation process

Reference code

package main import (...) Type UserInfoService struct{} var u = UserInfoService{} GetUserInfo(ctx context.Context, req *pb.UserRequest) (resp *pb.UserResponse, Err error) {name := req. name if name == "wuYin" { resp = &pb.UserResponse{ Id: 233, Name: name, Age: 20, Title: Return} func main() {port := ":2333" L, err := net.Listen("tcp", port) if err ! = nil { log.Fatalf("listen error: %v\n", err) } fmt.Printf("listen %s\n", Port) s := grpc.newServer () // Register UserInfoService with GRPC // Note that the second parameter, UserInfoServiceServer, is an interface type variable // needs to take the address and pass the parameter pb.RegisterUserInfoServiceServer(s, &u) s.Serve(l) }Copy the code

Run listener:

Client call

The implementation process

Reference code

package main import (...) func main() { conn, err := grpc.Dial(":2333", grpc.WithInsecure()) if err ! = nil { log.Fatalf("dial error: %v\n", Err)} defer conn. Close () / / instantiate UserInfoService micro service client client: = pb. NewUserInfoServiceClient (conn) / / call the service the req: = new(pb.UserRequest) req.Name = "wuYin" resp, err := client.GetUserInfo(context.Background(), req) if err ! = nil { log.Fatalf("resp error: %v\n", err) } fmt.Printf("Recevied: %v\n", resp) }Copy the code

Run the call successfully:

conclusion

In the implementation process of UserInfoService microservice above, it can be found that each microservice needs to manage its own server listening port, which is called after the client is connected. When there are many microservices, port management will be more troublesome. Compared with gRPC, Go-micro implements Service Discovery to facilitate the management of microservices, which will be studied along with the dockification of services in the next section.

More references: Nginx microservices series tutorials