您的位置:首页 > 理论基础 > 计算机网络

go微服务系列(三) - 服务调用(http)

2020-08-10 23:35 1271 查看

1. 关于服务调用

这里的服务调用,我们调用的可以是

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

下面的所有示例以

RESTful HTTP API
为例

2. 基本方式调用服务

我们在服务发现之后,肯定要调用发现之后的服务,这里的服务可以是http的

RESTful API
也可以是
RPC
服务等,这里以前面的定义的
productService
RESTful API
作为被调用者

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

下面要演示的是使用标准库

net/http
httpclient
进行的比较原始的请求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-plugins
client
下的
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("[服务调用结果]:\r\n %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
}

客户端调用结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: