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
按照官方的说法:
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 }
客户端调用结果:
相关文章推荐
- spring cloud eureka服务调用出现feign.codec.EncodeException: Could not write request: no suitable HttpMessa
- SpringBoot发布HttpClient服务和客户端调用HttpClient服务
- 个人PC 调用RO 服务时报 http 500
- go微服务框架go-micro深度学习(五) stream 调用过程详解
- 解析Go 标准库 http.FileServer 实现静态文件服务
- 实现jquery.ajax及原生的XMLHttpRequest跨域调用WCF服务的方法
- 使用Spring Cloud Feign作为HTTP客户端调用远程HTTP服务
- Spring Cloud系列教程三 :声明式服务调用Spring Cloud Fegin(F版)
- CXF系列之JAX-RS:CXF发布与调用REST服务
- gomicro微服务系列之一
- AFNetworking3.1.0源码分析(九)AFHTTPRequestSerializer 之解决调用Amazon S3 服务出现的bug
- dubbo泛化调用 http接口 随意调用dubbo服务
- 【Dubbo源码阅读系列】之远程服务调用(上)
- WebService系列博客{一}[发布、调用一个简单的服务]
- 高并发 Nginx+Lua OpenResty系列(9)——HTTP服务
- WCF系列教程之WCF客户端调用服务
- go微服务框架go-micro深度学习(五) stream 调用过程详解
- 使用Spring Cloud Feign作为HTTP客户端调用远程HTTP服务的方法(推荐)
- Web服务初探:用Demo学Web服务系列(9)——用B/S程序调用Web服务
- go 原生http web 服务跨域restful api 写法