This article is not about GRPC, but about API calls between services through HTTP. All examples use Go Mod to manage dependency packages.

1. Introduction to go-Micro Micro service framework

Go-micro is similar to SpringCloud in Java, with some ecological differences, but can be compared by analogy. The Github address of Guan Dang is github.com/micro/go-mi… , download the installation dependency commands:

go get -u github.com/micro/go-micro
Copy the code

2. Create a Web service

Golang itself provides rich HTTP packages, and Web frameworks such as Gin implement a Web service, but to fit into the go-Micro framework’s uniform specification, So you need to create a Web service by using go-Micro’s github.com/micro/go-micro/web package.

The first way:

Directly through the Web package to create a service, parameters optional, also relatively easy to understand, please Ctrl+ left click to view the source code. Once created, the service can be used directly like a native HTTP package.

func main(a) {
	service := web.NewService(
		web.Name("cas"),
		web.Address(": 8001"),
	)
	service.HandleFunc("/hello".func(writer http.ResponseWriter, request *http.Request) {
		// handler
	})
	service.Run()
}
Copy the code

The second way: the native HTTP package is not very efficient in many cases and only provides some basic functionality, so Go-Micro provides integration with third party Web frameworks such as Gin. First download and install:

go get -u "github.com/gin-gonic/gin"
Copy the code

Examples of GO-Micro integrated Gin:

func main(a) {
	engine := gin.Default()
	engine.GET("/hello".func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "hello,world",
		})
	})
	service := web.NewService(
		web.Name("cas"),
		web.Address(": 8001"),
		web.Handler(engine),
	)
	service.Run()
}
Copy the code

In addition to specifying the parameters of the Web service directly, we can also start the Web service through the command line parameters, so that we can quickly open the same service by specifying different service names and ports on the command line. While this approach is rarely used in real development deployment environments, it can be useful when we debug service registration and discovery ourselves. For example, just call Init(), you can view the source code, you can view the command function parameter name:

func main(a) {
  engine := gin.Default()
  engine.GET("/hello".func(c *gin.Context) {
  	c.JSON(http.StatusOK, gin.H{
  		"msg": "hello,world",
  	})
  })
  service := web.NewService(
  	//web.Name("cas"),
  	//web.Address(":80"),
  	web.Handler(engine),
  )
  service.Init()
  service.Run()
}
Copy the code

Start command:

go run main.go -server_name cas -server_address :8001
Copy the code

3. Service registration

3.1 Consul

Consul as registry, the official download address: www.consul.io/downloads.h… .

Windows installation:

  1. Direct download after willconsul.exeDecompress;
  2. Add the file name of the directory to which it was extracted to the path environment variable;
  3. In a CMD command window:consul agent -devCommand to start Consul service
  4. Consul has its own UI management interface, and access address:http://localhost:8500, you can see the currently registered service:

Linux installation:

  1. Download it from the Linux Version Download address on the official website:Wget HTTP: / / https://releases.hashicorp.com/consul/1.9.0/consul_1.9.0_linux_amd64.zip;
  2. Extract:Unzip consul_1. 9.0 _linux_amd64. Zip;
  3. Modify permission:chmod 777 consul;
  4. Let’s get this sorted outconsulFolder copy to/usr/local/bin/Directory:cp consul /usr/local/bin/;
  5. Enter consul to check whether the installation is successful:consul version;
  6. Launch Consul and pass-clientConfigure extranet host access:Consul agent - dev - client = 0.0.0.0;

Of course, the most convenient is to pull a mirror directly through docker, directly map the port to run

Register services with Consul:

After installing Consul and starting the service, download the Go-Plugins package provided by Go-Micro:

go get -u github.com/micro/go-plugins
Copy the code

This package includes Consul and a number of other plug-ins such as Eureka, and is basically used to create a registry object using the Consul.newRegistry () interface and then integrate it into the Web service configuration provided by Go Micro:

package main

import (
	"github.com/gin-gonic/gin"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/web"
	"github.com/micro/go-plugins/registry/consul"
	"net/http"
)

func main(a) {
	consulReg := consul.NewRegistry(registry.Addrs(": 8500"))
	engine := gin.Default()
	engine.GET("/hello".func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "hello,world",
		})
	})
	service := web.NewService(
		web.Name("cas"),
		web.Address(": 8001"),
		web.Registry(consulReg),
		web.Handler(engine),
	)
	service.Init()
	service.Run()
}
Copy the code

You can view that the service has been successfully registered on Consul’s administration UI

3.2 etcd

4 Service Discovery

The way to get a service from the registry is also relatively simple, just a few steps to get the service information:

  1. As with service registrations, first create Consul registry objects:consul.NewRegistry();
  2. Called from the Consul objectGetService("cas")Method to get the service node slice, the parameter is the registered service name, so the service name specified in the above registration is very important;
  3. Courtesy of Go-MicroselectorThe package is delivered to a specific service node, which provides two common load balancing algorithms,RoundRobin (polling)andThe Random (Random). The corresponding method returns oneNextMethod,NextMethod call before returning to the specific service node, you can view the source code;
  4. After getting the specific service node structure object, you can initiate HTTP API calls through the IP and other information of the service.

Sample code and results:

package main

import (
	"github.com/micro/go-micro/client/selector"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-plugins/registry/consul"
	"log"
)

func main(a) {
	consulReg := consul.NewRegistry(registry.Addrs(": 8500"))
	service, err := consulReg.GetService("cas")
	iferr ! =nil {
		log.Fatal("get service from consul err :",err)
	}
	node, err := selector.RoundRobin(service)()
	iferr ! =nil {
		log.Fatal("get service node err :",err)
	}
	log.Printf("service node : %+v", node)
}
Copy the code
// Run the printed log contents
2020- 12- 09 10:50:46.501937 I | service node : &{Id:ec20b3c9- 7531.- 4921.- 89.bc- 73.ee7e233525 Address:192.168152.1.:8002 Metadata:map[]}
Copy the code

If a dependency error occurs during startup, please refer to another of my blog posts on how to resolve this issue: Go-Micro Startup dependency error Resolution. To see the effect of polling or randomization, you can write a for loop interval to get nodes and see how the node information changes

5. Service invocation

5.1 Basic HTTP Invocation Modes

The most basic is to make an HTTP request through the HTTP package provided by Golang. Directly on the basis of the service discovery sample code above, after getting the service node information, the estrus API request.

package main

import (
	"fmt"
	"github.com/micro/go-micro/client/selector"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-plugins/registry/consul"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

func main(a) {
	consulReg := consul.NewRegistry(registry.Addrs(": 8500"))
	// Service discovery
	service, err := consulReg.GetService("cas")
	iferr ! =nil {
		log.Fatalf("get service from consul err : %+v",err)
	}
	node, err := selector.RoundRobin(service)()
	iferr ! =nil {
		log.Fatalf("get service node err : %+v",err)
	}
	log.Printf("service node : %+v", node)
	// Service invocation
	url := fmt.Sprintf("http://%s/hello", node.Address)
	reqBody := strings.NewReader("")
	request, err := http.NewRequest(http.MethodGet, url, reqBody)
	iferr ! =nil {
		log.Fatalf("create request err : %+v",err)
	}
	response, err := http.DefaultClient.Do(request)
	iferr ! =nil {
		log.Fatalf("send request err : %+v",err)
	}
	defer response.Body.Close()
	rspBody, err := ioutil.ReadAll(response.Body)
	iferr ! =nil {
		log.Fatalf("read response body err : %+v",err)
	}
	log.Printf("rsp body : %+v".string(rspBody))
}

// Result:
/ / the 2020-12-09 11:45:16. 422562 I | service node: & {Id: bda22dae - 9 bc2-46 db - b325 - bcc9ab2b6778 Address: 192.168.152.1:8001 Metadata: map []}
/ / the 2020-12-09 11:45:16. 436525 I | RSP body: {" MSG ":" hello, world "}
Copy the code

5.1 HTTP Invocation Modes in Go-Micro (Recommended)

This method also uses HTTP packages, but is different from the above Golang official provided, this method uses go-Micro plug-in package Go-plugins, specifically: “github.com/micro/go-plugins/client/http” package, go-plugins package in addition to the basic functions of HTTP client, but also support Selector parameters, automatic selection of services, and support json, protobuf and other data formats.

Call the basis of the above ways can be found obviously calls or are tedious, and this method simplified the process on a lot of, plug-in for some steps in the encapsulation and automated processing, steps to put the registry in the Selector Selector, and then create httpClient Selector, initiated by the client request, Invoke the service. Note that by default, the plugin assumes that all service calls are POST requests, so the specified interface must be POST:

package main

import (
	"context"
	"github.com/micro/go-micro/client"
	"github.com/micro/go-micro/client/selector"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-plugins/client/http"
	"github.com/micro/go-plugins/registry/consul"
	"log"
)

func main(a) {
	consulReg := consul.NewRegistry(registry.Addrs(": 8500"))
	selector := selector.NewSelector(
		selector.Registry(consulReg),
		selector.SetStrategy(selector.RoundRobin),
	)
	httpClient := http.NewClient(
		/ / selector
		client.Selector(selector),
		// Response format The default format is protobuf, set to JSON
		client.ContentType("application/json"),
		)
	req := map[string]string{}
	request := httpClient.NewRequest("cas"."/hello", req)
	rsp := map[string]interface{}{}
	err := httpClient.Call(context.Background(), request, &rsp)
	iferr ! =nil {
		log.Fatalf("request err: %+v", err)
	}
	log.Printf("%+v",rsp)
}

Copy the code

‘{“id”:”go.micro. Client “,”code”:500,”detail”:”none available”,”status”:”Internal Server Error”}

You can check out my other blog post: Go-Micro client request 500 error resolution for a detailed analysis and resolution of the problem.

Note that only JSON parameters are supported in service calls, so in Gin, please use Bind() to obtain request parameters:

engine := gin.Default()
	engine.POST("/hello".func(c *gin.Context) {
		req := struct {
			Name string `json:"name"`
		}{}
		c.BindJSON(&req)
		c.JSON(http.StatusOK, gin.H{
			"msg": "hello,world"."name": req.Name,
		})
	})
Copy the code