• go微服务系列(三)


    1. 关于服务调用

    这里的服务调用,我们调用的可以是http api也可以是gRPC等。主要意思就是调用我们从consul获取到的服务的API。

    下面的所有示例以RESTful HTTP API为例

    2. 基本方式调用服务

    我们在服务发现之后,肯定要调用发现之后的服务,这里的服务可以是http的RESTful API也可以是RPC服务等,这里以前面的定义的productServiceRESTful API作为被调用者

    其实就是用获取到的服务地址,调API

    下面要演示的是使用标准库net/httphttpclient进行的比较原始的请求API的方法

    被调用的API

    • EndPoint

    /v1/list

    服务调用的代码

    func main() {
    	// 1.连接到consul
    	cr := consul.NewRegistry(registry.Addrs("47.100.220.174:8500"))
    
    	// 2.根据service name获取对应的微服务列表
    	services, err := cr.GetService("productService")
    	if err != nil {
    		log.Fatal("cannot get service list")
    	}
    
    	// 3.使用random随机获取其中一个实例
    	next := selector.RoundRobin(services)
    	svc, err := next()
    	if err != nil {
    		log.Fatal("cannot get service")
    	}
    
    	fmt.Println("[测试输出]:", svc.Address)
        
        // 4. 请求获取到的服务的API方法
    	resp, err := RequestApi(http.MethodGet, svc.Address, "/v1/list", nil)
    	if err != nil {
    		log.Fatal("request api failed")
    	}
    	fmt.Println("[请求API结果]:", resp)
    }
    
    // 简单封装一个请求api的方法
    func RequestApi(method string, host string, path string, body io.Reader) (string, error) {
        // 1.如果没有http开头就给它加一个
    	if !strings.HasPrefix(host, "http://") && !strings.HasPrefix(host, "https://") {
    		host = "http://" + host
    	}
    	// 2. 新建一个request
    	req, _ := http.NewRequest(method, host+path, body)
    
        // 3. 新建httpclient,并且传入request
    	client := http.DefaultClient
    	res, err := client.Do(req)
    	if err != nil {
    		return "", err
    	}
    
    	defer res.Body.Close()
    
        // 4. 获取请求结果
    	buff, err := ioutil.ReadAll(res.Body)
    	if err != nil {
    		return "", err
    	}
    
    	return string(buff), nil
    }
    

    如下可以调用成功:

    3. 服务调用正确姿势(初步)

    上面我们调用api的方式是没什么问题,但是有缺点就是

    • 但是假如有多个微服务,每个微服务都会有很多重复的基础设施,go-micro就把这部分抽取出来,弄了一个plugin

    https://github.com/micro/go-plugins

    按照官方的说法:

    go-plugins使您可以交换基础设施结构,而不必重写所有代码。这样就可以在多个环境中运行相同的软件,而无需进行大量工作

    查看go-plugins的组成部分,client中有http api

    3.1 服务端代码

    服务端代码跟之前的差不太多

    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"
    	"gomicro-quickstart/product_service/model"
    	"net/http"
    )
    
    func main() {
    	// 添加consul地址
    	cr := consul.NewRegistry(registry.Addrs("127.0.0.1:8500"))
    
    	// 使用gin作为路由
    	router := gin.Default()
    	v1 := router.Group("v1")
    	{
    		v1.POST("list", func(c *gin.Context) {
    			var req ProdRequest
    			if err := c.Bind(&req); err != nil {
    				c.JSON(http.StatusBadRequest, gin.H{
    					"data": "模型绑定失败",
    				})
    				c.Abort()
    				return
    			}
    
    			c.JSON(http.StatusOK, gin.H{
    				"data": model.NewProductList(req.Size),
    			})
    		})
    	}
    
    	server := web.NewService(
    		web.Name("ProductService"),                          // 当前微服务服务名
    		web.Registry(cr),                                    // 注册到consul
    		web.Address(":8001"),                                // 端口
    		web.Metadata(map[string]string{"protocol": "http"}), // 元信息
    		web.Handler(router)) // 路由
    
    	_ = server.Init()
    
    	_ = server.Run()
    }
    
    type ProdRequest struct {
    	Size int `json:"size"`
    }
    
    

    下面是返回的model对象代码

    package model
    
    import "strconv"
    
    type Product struct {
    	Id   int
    	Name string
    }
    
    func NewProduct(id int, name string) *Product {
    	return &Product{
    		Id:   id,
    		Name: name,
    	}
    }
    
    func NewProductList(count int) []*Product {
    	products := make([]*Product, 0)
    	for i := 0; i < count; i++ {
    		products = append(products, NewProduct(i+1, "productName"+strconv.Itoa(i+1)))
    	}
    
    	return products
    }
    

    3.2 客户端调用(重要)

    这里使用了go-pluginsclient下的http的包,优点是

    • 可以直接通过服务名来调用服务,省去了getService的步骤
    package main
    
    import (
    	"context"
    	"fmt"
    	"log"
    
    	"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"
    )
    
    func main() {
    	// 1. 注册consul地址
    	cr := consul.NewRegistry(registry.Addrs("47.100.220.174:8500"))
    
    	// 2. 实例化selector
    	mySelector := selector.NewSelector(
    		selector.Registry(cr),                     // 传入上面的consul
    		selector.SetStrategy(selector.RoundRobin), // 指定获取实例的算法
    	)
    
    	// 3. 请求服务
    	resp, err := callByGoPlugin(mySelector)
    	if err != nil {
    		log.Fatal("request API failed", err)
    	}
    
    	fmt.Printf("[服务调用结果]:
     %v", resp)
    }
    
    func callByGoPlugin(s selector.Selector) (map[string]interface{}, error) {
    	// 1. 调用`go-plugins/client/http`包的函数获取它们提供的httpClient
    	gopluginClient := http.NewClient(
    		client.Selector(s),                     // 传入上面的selector
    		client.ContentType("application/json"), // 指定contentType
    	)
    
    	// 2. 新建请求对象,传入: (1)服务名 (2)endpoint (3)请求参数
    	req := gopluginClient.NewRequest("ProductService", "/v1/list", map[string]interface{}{"size": 6})
    
    	// 3. 新建响应对象,并call请求,获取响应
    	var resp map[string]interface{}
    	err := gopluginClient.Call(context.Background(), req, &resp)
    	if err != nil {
    		return nil, err
    	}
    
    	return resp, nil
    }
    
    

    客户端调用结果:

  • 相关阅读:
    1.18
    人月神话读后感
    疯狂学java的第45天
    学Java的第46天
    JAVA学习日记150720
    JAVA学习日记140719
    JAVA学习日记160721
    JAVA学习日记130718
    Windows DOS窗体下Oracle 数据库的导入导出(IMP/EXP)命令
    IntelliJ IDEA自动清除没用的import
  • 原文地址:https://www.cnblogs.com/baoshu/p/13473007.html
Copyright © 2020-2023  润新知