go-zero微服务实战——服务构建

这篇具有很好参考价值的文章主要介绍了go-zero微服务实战——服务构建。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录介绍

接上一节go-zero微服务实战——基本环境搭建。搭建好了微服务的基本环境,开始构建整个微服务体系了,将其他服务也搭建起来。

order的目录结构,如下

go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

  • 根目录
    • api服务
    • rpc服务
    • 自定义逻辑层logic
    • 自定义参数层models
    • 自定义工具层util

api服务和rpc服务都是基于goctl一键生成的,当然这是小编的目录,各位到也可以自定义目录结构,或者参考其他优秀的目录结构。go-zero官网也提供了官方的目录结构go-zero项目结构

  • api服务
    • config
    • handler
    • logic
    • svc
    • types

go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

  • rpc服务
    • etc
    • intenel
    • rpcservice
    • rpcserviceclient

go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

首先解释一个各个目录是干什么的,两个服务api和rpc是go-zero生成的,其内部目录都是对接服务本身的。logic和models,util是公共的部分。

**公共logic和服务内部的logic是不一样的,公共部分是公用的,例如返回订单列表,完成查询返回结果等,而服务的logic则是进一步对公共logic的私有化封装,主要表现是返回的数据不通用,对于api服务来说,logic最后返回结构体或结构体数组等数据即可,因为zero的api封装httpx对序列化,这些是框架完成的。但是对于rpc服务来说,好需要将这些数据转化为字符串的格式才可以传输,所以服务内部的logic就在于将公共logic数据转化为便于传输的格式。**其他目录就不再介绍了go-zero.dev官网上都有。

服务构建

前一节构建了order服务,本节将构建user和product服务器,项目和order基本一样。唯一的区别是user中存在一个登录即用户名认证过程,该过程需要从rpc客户端传递数据到rpc服务端。

user数据库表
go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

公共logic代码

// 验证账户
func Ideatify(account string, pass string) error {
	var user models.User
	b, err := db.Engine.Where("username = ?", account).Get(&user)

	if err != nil {
		fmt.Printf("logic list err%v", err)
		return err
	} else if b && (err != nil) {
		return errors.New("用户不存在")
	} else if user.Password == pass {
		return nil
	} else {
		return errors.New("密码错误")
	}
}

api的handler函数

api服务部分,路由此处省略,挂载到/login下即可。

func UserIdentify() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var req models.User
		err := httpx.ParseJsonBody(r, &req)
		if err != nil {
			httpx.WriteJson(w, 500, fmt.Sprintf("err%v", err))
			return
		}
		err = orderlogic.Ideatify(req.Username, req.Password)
		if err != nil {
			//
			httpx.WriteJson(w, 500, map[string]string{"code": "200", "message": err.Error()})
			return
		}
		httpx.OkJson(w, map[string]string{"code": "200", "message": "登录成功"})
	}
}

rpc的logic重写封装部分

// 继承rpc服务器方法
func Identify(in *rpcservice.Request) (*rpcservice.Response, error) {
	reqstr := in.GetReqJson()
	var req models.User
	_ = json.Unmarshal([]byte(reqstr), &req)
	err := orderlogic.Ideatify(req.Username, req.Password)
	if err != nil {
		fmt.Printf("rpc err:%v", err)
		return &rpcservice.Response{ResJson: err.Error()}, err
	}
	//o 赚json字符串
	return &rpcservice.Response{ResJson: "true"}, nil
}

rpc服务方法重写(方法注册)

//继承
func (s *RpcserviceServer) List(ctx context.Context,in *rpcservice.Request) (*rpcservice.Response, error) {
	return logic.Identify(in)
}

客户端调用

注意修改端口,user端口改为9001。

import (
	"context"
	"fmt"
	"rpcclient/rpcservice"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {
	//配置连连接参数(无加密)
	dial, _ := grpc.Dial("localhost:9001", grpc.WithTransportCredentials(insecure.NewCredentials()))
	defer dial.Close()
	//创建客户端连接
	client := rpcservice.NewRpcserviceClient(dial)
	//通过客户端调用方法
	res, err := client.Ping(context.Background(), &rpcservice.Request{ReqJson: "xiaoxu"})
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(res)

	//order list
	str := `{
		"id":0,
		"username":"xiaoxu",
		"password":"1234567",
		"status":0
	}`
	r, err := client.List(context.Background(), &rpcservice.Request{ReqJson: str})
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(r.ResJson)

}

别忘了_grpc.pbpb两个文件。

错误返回
go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc
正确返回
go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

传入数据通过&rpcservice.Request{ReqJson: "xiaoxu"}Request结构体完成的。在pb文件下,这个客户端和服务端共有的。

go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

以此方法逐个完成其他方法的封装和注册,另外完成product服务的构建。三个服务端口不同注意修改,api为8000系,rpc为9000系列。

product的api服务
go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc
rpc客户端代码完全一样改一下端接口9002即可

go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

rpc远程调用

到上一小结三个服务就构建完成了。服务之间是应该可以互相调用的,就像客户端调用服务端一样。在其他服务调用其本身就是客户端,被调用的服务就相当于服务端。

在api和rpc服务的目录下都有一个主程序,都启动即可。如下图所示,注意三个服务一种药开6个终端分别启动。

go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

在三个独立的api服务和rpc服务中,各自都只能操作相应的数据库,但是涉及多表查询是就需要rpc远程调用了。

在goctl目录下,存在XXXclent目录该目录提供了构造client实例的代码。
go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

// Code generated by goctl. DO NOT EDIT.
// Source: rpcservice.proto

package rpcserviceclient

import (
	"context"

	"demo/rpcservice/rpcservice"

	"github.com/zeromicro/go-zero/zrpc"
	"google.golang.org/grpc"
)

type (
	Request  = rpcservice.Request
	Response = rpcservice.Response

	Rpcservice interface {
		Ping(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
	}

	defaultRpcservice struct {
		cli zrpc.Client
	}
)

func NewRpcservice(cli zrpc.Client) Rpcservice {
	return &defaultRpcservice{
		cli: cli,
	}
}

func (m *defaultRpcservice) Ping(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
	client := rpcservice.NewRpcserviceClient(m.cli.Conn())
	return client.Ping(ctx, in, opts...)
}

对比上一章节自定义的客户端,如下:

package main
import (
	"context"
	"fmt"
	"rpcclient/rpcservice"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {
	//配置连连接参数(无加密)
	dial, _ := grpc.Dial("localhost:9002", grpc.WithTransportCredentials(insecure.NewCredentials()))
	defer dial.Close()
	//创建客户端连接
	client := rpcservice.NewRpcserviceClient(dial)
	//通过客户端调用方法
	res, err := client.Ping(context.Background(), &rpcservice.Request{ReqJson: "xiaoxu"})
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(res)

	// //order list
	// str := `{
	// 	"id":0,
	// 	"username":"xiaoxu",
	// 	"password":"123456",
	// 	"status":0
	// }`
	r, err := client.List(context.Background(), &rpcservice.Request{})
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(r.ResJson)

}

rpcclient/rpcservice包是存放_grpc.pb和pb文件的目录

创建rpc服务端的方法是来源于_grpc.pb的NewRpcserviceClient

func NewRpcserviceClient(cc grpc.ClientConnInterface) RpcserviceClient {
	return &rpcserviceClient{cc}
}

对比可以看出,都是使用该方法构建的客户端实例,唯一的不同在于,自定义的客户端时通过grpc.Dial返回客户端对象,但是官方提供的代码通过返回zrpc.Client(内置连接对象)。但是官方提供的并未配置端口的直接入口。

从参数入手,由于都是调用的NewRpcserviceClient方法,那么参数都是*grpc.ClientConn类型。

func (m *defaultRpcservice) Ping(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
	client := rpcservice.NewRpcserviceClient(m.cli.Conn())
	return client.Ping(ctx, in, opts...)
}

回到源码,看到Conn()方法指向如下图所示的结构体。

go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc
导航到该结构体的定义处,其是*grpc.ClientConn的一个实现类。

go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc
该实现类继承了Conn方法同时也扩展了另一个眼熟的方法dial如下,那么到这就知道该如何使用了吧。

go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

直接调用dial方法配置端口,配置*grpc.ClientConn对象。注意这个方法和自定义的不一样,
自定义是直接调用grpc.Dial来自于grpc库,直接返回连接对象实例。而前者只是连接对象的一个配置端口和参数的方法。

// NewClient returns a Client.
func NewClient(target string, middlewares ClientMiddlewaresConf, opts ...ClientOption) (Client, error) {
	cli := client{
		middlewares: middlewares,
	}

	svcCfg := fmt.Sprintf(`{"loadBalancingPolicy":"%s"}`, p2c.Name)
	balancerOpt := WithDialOption(grpc.WithDefaultServiceConfig(svcCfg))
	opts = append([]ClientOption{balancerOpt}, opts...)
	if err := cli.dial(target, opts...); err != nil {
		return nil, err
	}

	return &cli, nil
}

上述源码来自zrpc提供了创建api构建zrpc.Client实例,作为官方提供的NewRpcservice方法的参数,于是请求的地址和端口就能配置了。如下:

得到的r就是_grpc.pb的RpcserviceClient对象,就可以实现方法调用了。

func GetRpcClientData() (string, error) {
	c, err := zrpc.NewClient(zrpc.RpcClientConf{
		Etcd: discov.EtcdConf{
			Hosts: []string{"127.0.0.1:9000"},
		},
	})
	if err != nil {
		return "", errors.New("rpc connect failed")
	}
	r := rpcserviceclient.NewRpcservice(c)
	r2, err := r.Ping(context.Background(), &rpcservice.Request{ReqJson: "xiaoxu"})
	if err != nil {
		return "", errors.New("rpc method anlyse failed")
	}
	return r2.ResJson, nil

}

上述方法使用了微服务的服务注册,下一章节讲,因此需要将服务再注册到服务中心中。到此函数已经注册两次了,第一次是继承服务器函数(服务器注册函数),第二次是客户端使用服务注册时将函数注册到服务中心。

上述代码构建使用discov.EtcdConf就是服务发现etcd的配置,上述代码是无法直接调用的,应为本地没有服务中心。

无服务中心服务注册的调用

func GetRpcClientPing() (string, error) {
	c, err := zrpc.NewClient(zrpc.RpcClientConf{
		Target: "127.0.0.1:9000",
	})
	if err != nil {
		return "", err
	}
	r := rpcserviceclient.NewRpcservice(c)
	r2, err := r.Ping(context.Background(), &rpcservice.Request{ReqJson: "xiaoxu"})
	if err != nil {
		return "", errors.New("rpc method anlyse failed")
	}
	return r2.ResJson, nil

}

使用Tartget属性就跳过服务中心。

import (
	"fmt"
	"testing"
)

func TestGetData(t *testing.T) {
	str, err := GetRpcClientPing()
	if err != nil {
		panic(err)
	}
	t.Log(str)
	fmt.Println(str)
}

go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

rpcclient中注册自定义函数:

func TestGetList(t *testing.T) {
	str, err := GetRpcClientList()
	if err != nil {
		panic(err)
	}
	t.Log(str)
	fmt.Println(str)
}

go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

测试通过,返回数据。该数据是字符串,还需要经过反序列化得到结构体数据数据。

部分参考自:https://juejin.cn/post/7041907188972912676。

gRPC Client 的开发

服务发现

在rpc远程调用时,连接的套接字是直接写在代码中的,如下图所示:

func GetRpcClientPing() (string, error) {
	c, err := zrpc.NewClient(zrpc.RpcClientConf{
		Target: "127.0.0.1:9000",
	})
	if err != nil {
		return "", err
	}
	r := rpcserviceclient.NewRpcservice(c)
	r2, err := r.Ping(context.Background(), &rpcservice.Request{ReqJson: "xiaoxu"})
	if err != nil {
		return "", errors.New("rpc method anlyse failed")
	}
	return r2.ResJson, nil

}

这样的弊端在于当分布部署或者服务器更换时需要修改源代码的套接字,这样时非常不方便的。服务发现的是微服务治理的一种手段,功能在于使用服务注册后只需记录服务的名称,有注册中心自动查找该名称的服务,这样就脱离ip的强绑定了。

zero默认的服务中心是Etcd。服务etcd是一个注册与发现服务器,当然功能不止如此,首先在电脑上下载服务器。

  1. apt install etcd下载etcd
    go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

  2. etcd启动服务

go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

默认启动端口为2379。

go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

启动会报错,那么如何将服务以名称的形式注册到etcd中呢?

官方教程

搭建etcd服务器

etcd官网

用 go-grpc 使用etcd发现

etcd服务注册与发现的原理和实现

  1. 服务注册

go-zero集成了etcd,在core/discov包下提供了注册与发现的方法。
go-zero微服务实战——服务构建,# go-zero,golang,微服务,go-zero,rpc

章节到此结束,具体使用方法请看下一章节go-zero微服务实战——etcd服务注册与发现文章来源地址https://www.toymoban.com/news/detail-627120.html

到了这里,关于go-zero微服务实战——服务构建的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • go-zero/grpc的rpc服务间传递额外数据

    go-zero/grpc的rpc服务间传递额外数据 2024/02/18 客户端: 初始化 md 也可如下方式: 追加新的如下: 也可使用 md 的 Set 和 Append 方法追加: 服务端: 注意 key 都会被转为小写,即使客户端为大写: 而且 key 只能由 数字、字母和三个特殊字符“-_.”组成,大写字母会自动被转为小写

    2024年02月19日
    浏览(62)
  • GoZero微服务个人探索之路(三)Go-Zero官方rpc demo示例探究

    两个文件均为protoc-gen-go-grpc自动生成 构成一个完整的 gRPC 服务的定义和实现 demo.go goctl生成的客户端代码 Request 和 Response 别名: 定义了 Request 和 Response 两个别名,实际上是从 demo 包中导入的对应的消息类型。 Demo 接口: 定义了一个 Demo 接口,其中包含了调用 gRPC 服务中 P

    2024年01月18日
    浏览(57)
  • 使用go-zero快速构建微服务

    本文是对 使用go-zero快速构建微服务 [1] 的亲手实践 编写API Gateway代码 mkdir api goctl api -o api/bookstore.api cd api goctl api go -api bookstore.api -dir . go run bookstore.go -f etc/bookstore-api.yaml 启动API Gateway服务,默认侦听在8888端口 因为默认生成的 api/etc/bookstore-api.yml 为: 按提示下载,再次运行

    2024年02月13日
    浏览(65)
  • go-zero的服务发现源码阅读

    服务发现原理与grpc源码解析_wangxiaoangg的博客-CSDN博客   go-zero rpc demo官方文档:rpc编写与调用 | go-zero 目录 一 服务注册 1. 创建rpc服务 2. 启动rpc服务 3. registerEtcd做了什么 4. discov.NewPublisher 服务发布者 二 服务发现 1.定义注册resolver 2.解析etcd地址创建链接 3.update方法 在看rp

    2024年02月06日
    浏览(61)
  • go-zero学习 第三章 微服务

    1.1 API服务模块 goctl 使用 api 文件生成 api服务 命令: 1.2 RPC服务模块 goctl 使用 protoc 文件生成 rpc服务 命令: 注意: --go_out 、 --go-grpc_out 、 --zrpc_out 三者配置的路径需要完全一致,否则会报下列错误。 基础代码:已生成基本的API服务、RPC服务。 这里以API服务调用RPC服务的登

    2024年02月16日
    浏览(72)
  • 【go-zero】go-zero阿里云oss 前端上传文件到go-zero API服务 并在k8s pod中创建文件 并推送到阿里云oss 最佳实践

    问题:在本地通过上传文件,然后将文件推送到aliyun的oss中,是没问题的 但是部署到了k8s中,则出现了问题,一直报错没有创建的权限 思路:开始认为应该将该文件挂载到configmap中,然后通过这种方式修改了deployment和dockerfile。最终发现应该是go的创建文件路径方式搞错了,

    2024年02月13日
    浏览(48)
  • go-zero踩坑:在api层逻辑代码中设置context超时时间,传递到rpc层逻辑代码时设置的context超时时间消失 + api层和rpc层Timeout配置说明

    在api层逻辑代码中设置context超时时间,传递到rpc层逻辑代码时设置的context超时时间消失 我在用 go-zero 时,在 api 层传递 context 到 rpc 层,但报错: rpc error:DeadlineExceeded desc = context deadline exceeded ,这是 上下文超时 导致的(客户端用的上下文是 context.WithTimeout 超时时间 小于

    2024年02月11日
    浏览(62)
  • [golang gin框架] 39.Gin商城项目-微服务实战之微服务架构

    单体架构在 中小企业内部 用的是非常多的,当 业务不复杂 , 团队规模不大 的时候,单体架构比微服务架构具有 更高的生产率 单体架构 当 业务比较复杂 , 并发量 比较大, 团队规模扩大的时候, 就需要引入微服务架构了,它比单体架构 具有 更高的生产率, 可以 节省成本 , 解

    2024年02月12日
    浏览(43)
  • Go-Zero微服务快速入门和最佳实践(一)

    并发编程和分布式微服务 是我们Gopher升职加薪的关键。 毕竟Go基础很容易搞定,不管你是否有编程经验,都可以比较快速的入门Go语言进行简单项目的开发。 虽说好上手,但是想和别人拉开差距,提高自己的竞争力, 搞懂分布式微服务和并发编程还是灰常重要的,这也是我

    2024年04月28日
    浏览(41)
  • 微服务框架 go-zero logx 日志组件剖析

    上一篇我们说到咱们还剩下 addTenant 功能还未实现,不知道有没有兄弟感兴趣去实验一波的,本篇文章进行简要补充 根据上一篇文章分析,其实我们只需要执行如下几步即可: 编写 tenant.api,提供外部 addTenant 的 http 接口 编写 tenant.api 提供一个 POST http 的接口 / api /tenant/addt

    2024年02月11日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包