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("[服务调用结果]:\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
}

客户端调用结果:

最新文章

  1. Asp.Net跨平台:Ubuntu14.0+Mono+Jexus+Asp.Net
  2. 设计模式--享元模式Flyweight(结构型)
  3. C# 调用一个按钮的Click事件(利用反射)
  4. .NET下的并行开发
  5. 第三章 Models模块属性详解
  6. setTimeout()和setInterval() 何时被调用执行
  7. SQLServer中服务器角色和数据库角色权限详解
  8. CentOS6.5安装LAMP环境APACHE的安装
  9. Delphi COM编程学习笔记(1)
  10. Cocos2d-x3.0 lua捆绑C++分类
  11. 线上问题debug过程(cat,grep,tr,awk,sort,uniq,comm等工具的综合使用)
  12. Java生日计算年龄工具
  13. STL中vector、list、deque和map的区别
  14. python3入门教程(二)操作数据库(一)
  15. 20165215 2017-2018-2 《Java程序设计》第6周学习总结
  16. BZOJ 5424: 烧桥计划
  17. [华商韬略] 拉里&#183;埃里森(Larry Elison) 的传奇人生
  18. Windows Server Core Command (管理服务器核心的具体操作命令)
  19. 如何给一块新硬盘安装grub,让它成为一个只有一个内核的系统
  20. JS设计模式——2.初识接口

热门文章

  1. Ethical Hacking - NETWORK PENETRATION TESTING(5)
  2. Python Ethical Hacking - BeEF Framework(2)
  3. 在docker中写个Hello World
  4. python多进程之multiprocessing
  5. 循序渐进nginx(二):反向代理、负载均衡、缓存服务、静态资源访问
  6. C#中子类对基类方法的继承、重写和隐藏
  7. 在ASP.NET Core中创建自定义端点可视化图
  8. ANDROID自定义视图——onMeasure,MeasureSpec源码&#160;流程&#160;思路详解
  9. layui常用插件(二) 时间插件
  10. 让你的GitHub下载飞速提升到2M/s以上