Author’s brief introduction

Asim Aslam, former Google engineer, linkedin address. Technology is rapidly evolving. Cloud computing now gives us almost unlimited scale, however leveraging that scale with existing tools is still difficult. Micro is solving this problem with a developer first focus.

Asim said in the promotional video that he is very familiar with Proto, GRPC and Golang because of his 6 years of experience working in Google. The framework of Micro is also relatively perfect, and many enterprises are using it, so I feel relieved. Moreover, it should be very reliable.

I learned from the use of time is not long, there are some objective evaluation of the author

  • Asim is currently working on the micro project full-time. He has registered a company also called Micro, which has a commercial version (official website) and is serious about making money. The difference between the open source version and the commercial version
  • Both the micro project and the company seem to be Asim alone
  • The issue on Github can be solved very quickly, which gives me the impression that he is online 24 hours a day. However, he likes to close the issue, and the issue in open state can hardly be seen
  • Versions change quickly
  • Documents are few and not detailed, and there are many pits (maybe the author knows too little knowledge).

helloWorld

Proto. Name it greeter.proto

syntax = "proto3";

service Greeter {
	rpc Hello(HelloRequest) returns (HelloResponse) {}
}

message HelloRequest {
	string name = 1;
}

message HelloResponse {
	string greeting = 2;
}

Copy the code

Proto protoc -i. –go_out=. –micro_out=. Proto /greeter.proto generates two files, Greeter.pb. go Native Proto go file, Greeter.micro. Go micro service Proto go file

Microservice code

package main

import (
	"context"
	"log"

	"github.com/micro/go-micro"// Reference the proto file proto generated above"micro-blog/helloworld/proto"
)

type Greeter struct{}

func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error {
	rsp.Greeting = "Hello " + req.Name
	return nil
}

func main() {// NewService := micro.NewService(micro."greeter"),
		micro.Version("latest"), / / optional parsing command line service. The Init () / / registered handler proto. RegisterGreeterHandler (service. The Server (), new (Greeter)) / / start the serviceiferr := service.Run(); err ! = nil { log.Fatal(err) } }Copy the code

Start the microservice go run main. Go such a simple microservice is OK, is not very simple, we try to call again

package main

import (
	"context"
	"fmt"// Reference the proto file generated above"github.com/micro/go-micro"
	proto "micro-blog/helloworld/proto"
)

func main() {// new a service service := micro-.newService () // parse the command line flag service.init () // create a client with proto cl := proto.NewGreeterService("greeter", service.client ()) // Make a request RSP, err := cl.hello (context.background (), &proto.helloRequest {Name:"John",})iferr ! = nil { fmt.Println(err)return
	}

	fmt.Println(rsp.Greeting)
}

Copy the code

As you can see by printing Hello John, calling microservices is also very simple, because Micro does a lot of things for us, as we’ll see later

Install the micro

Micro is a toolkit collection, The micro version used in this article is V0.22.0. Other versions may be different. You can install it by running go get -u github.com/micro/micro. It is recommended that “FanQiang” test install micro –version

Micro Tool Package introduction

  • Micro CLI Interactive mode

Micro CLI enters interactive mode. The commands in interactive mode are similar to but may be different from those in the following sections. See help for details. Run microproxy on the remote machine. Run MICRO_PROXY_ADDRESS=staging. Micro

For example, I would run Micro on a desktop while developing for my company and then type code on my laptop

  • List the service

Micro List Services If you are also running the helloWorld service, greeter is displayed

  • Access to services

Micro Get Service [Servicename] Obtains details about a service

For example, micro Get Service greeter might output:

service  greeter The service name is filled in above

version latest The version number of the service

Service ID, IP of the server, port number, protocol, registration mode, transport, broker, etc. If you start the same server process again, two records will appear below
ID	Address	Port	Metadata
greeter-8f9ea45b-d8ce-43f6-97b5-a41d8514cd79 65217 protocol = mucp 192.168.1.107, registry = MDNS, server = RPC, transport = HTTP, broker = HTTPAn RPC interface
Endpoint: Greeter.Hello
Metadata: stream=false

Greeter.Hello request structure
Request: {
	name string
	- 
	- []uint8 {
		uint8 uint8
	}
	- int32
}

Greeter.Hello response structure
Response: {
	greeting string
	- 
	- []uint8 {
		uint8 uint8
	}
	- int32
}
Copy the code
  • Call the service

Micro Call [Servicename] [Endpoint] [data] Immediately invokes a method of a service

For example, micro Call greeter greeter.Hello ‘{“name”: “John”}’, is displayed

{
	"greeting": "Hello John"
}
Copy the code

The result is the same as the call in the code. In fact, whether the call in the micro CLI or the call in the code, the process is the same, which will be explained in detail later

  • Service health inspection

Micro Health [Servicename] Gets the health status of a service, for example, Micro Health Greeter

service  greeter

version latest

node		address:port		status
greeter-8f9ea45b-d8ce-43f6-97b5-a41d8514cd79		192.168.1.107:65217		ok
Copy the code
  • Quickly generate service templates

micro new [servicename] [arguments…] When a new service is written, you can use this command to quickly generate a template, which by default is generated in the relative directory of $GOPATH

For example, micro new test –gopath=false

Creating service go.micro.srv.test in test. ├ ─ ─ main. Go ├ ─ ─ plugin. Go ├ ─ ─ handler │ └ ─ ─ example. Go ├ ─ ─ the subscriber │ └ ─ ─ example. Go ├ ─ ─ proto/example │ └ ─ ─ ├─ ├─ └ └ └ └ └ ├─ Makefile └for micro:

brew install protobuf
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
go get -u github.com/micro/protoc-gen-micro

compile the proto file example.proto:

cd test
protoc --proto_path=. --go_out=. --micro_out=. proto/example/example.proto

Copy the code
  • Web Management Console

micro web [arguments…] To open the web management interface, enter localhost:8082 in the browser. The management interface provides many functions similar to the CLI, which is more convenient and intuitive. The Micro Web itself is also a micro service to view Web resources

Call the service

View service details

Interactive command line

  • API gateway

micro api [arguments…] As detailed below, the Micro API is itself a micro service

API gateway

  • Everything service

In the micro system, there are many resource types. What the author understands is an abstract classification of services, such as API, FNC (function), SRV and Web. These resources are defined in the form of services. The service “greeter” in HelloWorld above is just the simplest service. How can resources be classified? The answer is to add a prefix, such as “greeter” for a back-end service. Let’s define it as “srV. Greeter”, so micro knows that the service is AN SRV classification, modifying the code

Service := micro.NewService(micro.Name())"srv.greeter"),
		micro.Version("latest"),Copy the code
  • The namespace

Once the types of services are classified, sometimes you need to classify them again by project, service characteristics, and so on, where namespaces are needed. For example, if you change “srv.greeter” to “proj1.srv.greeter”, the resource belongs to the namespace “proj1”. The default namespace is “go.micro”.

  • Three layer architecture

In Micro, it is recommended to organize many microservices in a three-tier architecture

Micro API: HTTP access some API: exposed API service Some SRV: Intranet background serviceCopy the code

  • Start API Gateway

Micro API to start API a gateway, the default port is 8080 can be changed by –address=0.0.0.0:8080flag or setting the environment MICRO_API_ADDRESS=0.0.0.0:8080

  • Use the ACME protocol

Automatic Certificate Management Environment (ACME) is a security protocol developed by Let’s Encrypt Use — enable_ACme =true or set the environment MICRO_ENABLE_ACME=true

Can choose whether configuration white list – acme_hosts=example.com or MICRO_ACME_HOSTS=example.com, and api.example.com

  • Setting the TLS Certificate

Micro — enabLE_TLS =true — TLs_cert_file =/ PATH /to/cert — TLs_KEY_file =/ PATH /to/key API or MICRO_ENABLE_TLS=true MICRO_TLS_CERT_FILE=/path/to/cert MICRO_TLS_KEY_FILE=/path/to/key micro api

  • Setting the namespace

Micro — API_namespace =namespace API or MICRO_API_NAMESPACE=namespace micro API Notice The namespace set when the API is started must be the same as the namespace of the resource to be accessed; otherwise, the namespace cannot be accessed. This is similar to the Web management console

  • Direct access service

The fixed URL/RPC allows direct access to the service, bypassing the RPC processor, for example

curl -d 'service=go.micro.srv.greeter' \
     -d 'endpoint=Greeter.Hello' \
     -d 'request={"name": "Bob"}' \
     http://localhost:8080/rpc
Copy the code

Will be output

{"greeting":"Hello Bob"}
Copy the code

This is not recommended, however, because micro is typically a three-tier architecture that can be used during debugging. To disable RPC calls, you need to use the Go-plugin, which is described later

  • Use the processor to access the service

By processor, I understand how HTTP requests are handled when accessing a service through an API gateway. Micro provides several processors, and the above fixed route via/RPC bypassed the processor to access the service directly using the sent JSON serialization request. Using the processor is typically used in the three-tier architecture mentioned above, where the processor provides a layer of API services that in turn access other back-end microservices

    - /[service]/[method]	# HTTP paths are dynamically mapped to services
    - /rpc			# Explicitly call a backend service by name and method
Copy the code
  • API processor

The API is the default handler that receives HTTP requests and serializes the HTTP Request/ Response information into api.request /api.Response format

Content-Type: Any Body: Any Forward Format: api.Request/api.Response Path: /[service]/[method] Resolver: Request the parser, and the path will be resolved into services and methods. Configure: configuration, specified at startup –handler= API or the environment variable MICRO_API_HANDLER= API before starting the command

For example, add an API structure to the HelloWorld service described above

package main

import (
	"encoding/json"
	"log"
	"strings"// Reference the proto file generated above"github.com/micro/go-micro"
	"github.com/micro/go-micro/errors"
	api "github.com/micro/go-api/proto"
	proto "micro-blog/helloworld/proto"

	"context"
)

type Say struct {
	Client proto.GreeterService
}

func (s *Say) Hello(ctx context.Context, req *api.Request, rsp *api.Response) error {
	log.Print("Received Say.Hello API request")

	name, ok := req.Get["name"]
	if! ok || len(name.Values) == 0 {return errors.BadRequest("go.micro.api.greeter"."Name cannot be blank")
	}

	response, err := s.Client.Hello(ctx, &proto.HelloRequest{
		Name: strings.Join(name.Values, ""})),iferr ! = nil {return err
	}

	rsp.StatusCode = 200
	b, _ := json.Marshal(map[string]string{
		"message": response.Greeting,
	})
	rsp.Body = string(b)

	return nil
}

func main() {// new a micro service out of the resource type set to API service := micro.NewService(micro."go.micro.api.greeter"Init() // Register handler service.server ().handle (service.server ().newhandler (&say {Client: proto.NewGreeterService("go.micro.srv.greeter", service.Client())},
		),
	)

	iferr := service.Run(); err ! = nil { log.Fatal(err) } }Copy the code

Call through the API gateway

curl -H 'Content-Type: application/json' \
    -s "http://localhost:8080/greeter/say/hello? name=Pengju"
Copy the code

Will output:

{
    "message": "Hello Pengju"
}
Copy the code

Greeter /say/hello is resolved as a Request to service=go.micro. Srv. greeter endpoint= greeter. Get[“name”] := req.Get[“name”]

  • The RPC handler

Similar to API handlers, except that the structure of the serialized data can be customized using, for example, proto below

Content-type: application/json or application/protobuf Body: json or protobuf Forward Format: Json-rpc or proto-rpc, related to content-type. Path: /[service]/[method] Resolver: Configuration that specifies –handler= RPC at startup or the environment variable MICRO_API_HANDLER= RPC before starting the command

syntax = "proto3";

service Example {
	rpc Call(CallRequest) returns(CallResponse) {};
}

service Foo {
	rpc Bar(EmptyRequest) returns(EmptyResponse) {};
}

message CallRequest {
	string name = 1;
}

message CallResponse {
	string message = 2;
}

message EmptyRequest {
}

message EmptyResponse {
}

Copy the code

Change the code for the HelloWorld API

package main

import (
	"log"

	"github.com/micro/go-micro"
	proto "micro-blog/helloworld/api/rpc/proto"
	greeter "micro-blog/helloworld/proto"

	"context"
)

type Greeter struct {
	Client greeter.GreeterService
}

func (g *Greeter) Hello(ctx context.Context, req *proto.Request, rsp *proto.Response) error {
	log.Print("Received Greeter.Hello API request")

	// make the request
	response, err := g.Client.Hello(ctx, &greeter.HelloRequest{Name: req.Name})
	iferr ! = nil {return err
	}

	// set api response
	rsp.Msg = response.Greeting
	return nil
}

func main() {
	// Create service
	service := micro.NewService(
		micro.Name("go.micro.api.greeter"),
	)

	// Init to parse flags
	service.Init()

	// Register Handlers
	proto.RegisterGreeterHandler(service.Server(), &Greeter{
		// Create Service Client
		Client: greeter.NewGreeterService("go.micro.srv.greeter", service.Client()),
	})

	// for handler use

	// Run server
	iferr := service.Run(); err ! = nil { log.Fatal(err) } }Copy the code

A call

curl -H 'Content-Type: application/json' \
         -X POST \
         -d "{\"name\": \"PengJu\"}" \
         http://localhost:8080/greeter/greeter/hello

Copy the code

The output

{"msg":"Hello PengJu"}
Copy the code

As you can see, the request has been serialized according to the custom PROTO. If an API type of service uses handler, the endpoint of the service is the lower case of the class name + “.” + the lower case of the method name, for example, the Greeter Hello method. Then the whole route is http://localhost:8080/greeter/greeter/hello, is the first greeter service Name, micro. The Name (” go. Micro. API. The greeter “)

  • Web processor

HTTP reverse proxy, web socket support, will only match the Path: /[service] layer, the rest is up to the developer, you can use your favorite Web framework, custom middleware, etc., relatively free, is my final choice of processor

Content-type: any Type Body: any Format Forward Format: HTTP reverse proxy, including Web socket Path: /[service] Resolver: Request the parser, and the path is resolved to the service name Configure: configuration, specified at startup –handler=web or the environment variable MICRO_API_HANDLER=web before starting the command

Using GIN for example

package main

import (
	"log"

	"github.com/gin-gonic/gin"

	"context"
	"github.com/micro/go-micro/client"
	"github.com/micro/go-web"
	proto "micro-blog/helloworld/proto"
)

type Say struct{}

var (
	cl proto.GreeterService
)

func (s *Say) Anything(c *gin.Context) {
	log.Print("Received Say.Anything API request")
	c.JSON(200, map[string]string{
		"message": "Hi, this is the Greeter API",
	})
}

func (s *Say) Hello(c *gin.Context) {
	log.Print("Received Say.Hello API request")

	name := c.Param("name")

	response, err := cl.Hello(context.TODO(), &proto.HelloRequest{
		Name: name,
	})

	iferr ! = nil { c.JSON(500, err) } c.JSON(200, response) } funcmainNewService := web.newService (web.name ()) {// Create service ();"go.micro.api.greeter"),
	)

	service.Init()

	// setup Greeter Server Client
	cl = proto.NewGreeterService("go.micro.srv.greeter", client.DefaultClient)

	// Create RESTful handler (using Gin)
	say := new(Say)
	router := gin.Default()
	router.GET("/greeter", say.Anything)
	router.GET("/greeter/:name", say.Hello)

	// Register Handler
	service.Handle("/", router)

	// Run server
	iferr := service.Run(); err ! = nil { log.Fatal(err) } }Copy the code

A call

curl -H 'Content-Type: application/json' \
    -s "http://localhost:8080/greeter/Pengju"
Copy the code

The output

{"greeting":"Hello Pengju"}
Copy the code

I have learned golang, Micro, K8S, GRPC, Protobuf and other knowledge for a short time, if there is any misunderstanding, welcome criticism and correction, you can add my wechat to discuss learning