go 教程:使用 go-380玩彩网官网入口

在上一篇文章中,我们使用 kit 命令行工具搭建了一个本地环境。在本文中我们将继续使用这些代码。

让我们首先通过编写原型定义来实现 notificator 服务,它是一个 grpc 服务。我们已经生成了 notificator/pkg/grpc/pb/notificator.pb 文件,让我们简单的去实现它。

syntax = "proto3";
package pb;
service notificator {
    rpc sendemail (sendemailrequest) returns (sendemailreply);
}
message sendemailrequest {
    string email = 1;
    string content = 2;
}
message sendemailreply {
    string id = 1;
}

现在我们要生成服务端和客户端的存根,可以使用 kit 工具生成的 compile.sh 脚本,脚本已经包含 protoc 命令。

cd notificator/pkg/grpc/pb
./compile.sh

可以发现,notificator.pb.go 这个文件已经更新了。
现在我们需要实现服务本身了。我们用生成一个 uuid 代替发送真实的电子邮件。首先需要对服务进行调整以匹配我们的请求和响应格式(返回一个新的参数:id)。

notificator/pkg/service/service.go:

import (
    "context"
    uuid "github.com/satori/go.uuid"
)
// notificatorservice describes the service.
type notificatorservice interface {
    // add your methods here
    sendemail(ctx context.context, email string, content string) (string, error)
}
type basicnotificatorservice struct{}
func (b *basicnotificatorservice) sendemail(ctx context.context, email string, content string) (string, error) {
    id, err := uuid.newv4()
    if err != nil {
        return "", err
    }
    return id.string(), nil
}

notificator/pkg/service/middleware.go:

func (l loggingmiddleware) sendemail(ctx context.context, email string, content string) (string, error) {
    defer func() {
        l.logger.log("method", "sendemail", "email", email, "content", content)
    }()
    return l.next.sendemail(ctx, email, content)
}

notificator/pkg/endpoint/endpoint.go

// sendemailresponse 接受 sendemail 方法的响应
type sendemailresponse struct {
    id string
    e0 error `json:"e0"`
}
// makesendemailendpoint 返回 sendemail 调用的端点
func makesendemailendpoint(s service.notificatorservice) endpoint.endpoint {
    return func(ctx context.context, request interface{}) (interface{}, error) {
        req := request.(sendemailrequest)
        id, e0 := s.sendemail(ctx, req.email, req.content)
        return sendemailresponse{id: id, e0: e0}, nil
    }
}

如果我们搜索 todo grep -r "todo" notificator ,可以看到还需要实现 grpc 请求和响应的编码和解码。
notificator/pkg/grpc/handler.go:

func decodesendemailrequest(_ context.context, r interface{}) (interface{}, error) {
    req := r.(*pb.sendemailrequest)
    return endpoint.sendemailrequest{email: req.email, content: req.content}, nil
}
func encodesendemailresponse(_ context.context, r interface{}) (interface{}, error) {
    reply := r.(endpoint.sendemailresponse)
    return &pb.sendemailreply{id: reply.id}, nil
}

服务发现

sendemail 接口将由用户服务调用,所以用户服务必须知道通知服务的地址,这是典型的服务发现问题。在本地环境中,我们使用 docker compose 部署时知道如何连接服务,但是,在实际的分布式环境中,可能会遇到其他问题。

首先,在 etcd 中注册我们的通知服务。etcd 是一种可靠的分布式键值存储,广泛应用于服务发现。go-kit 支持使用其他的服务发现技术如:eureka、consul 和 zookeeper 等。

把它添加进 docker compose 中,这样380玩彩网官网入口的服务就可以使用它了。cv 工程师上线:

docker-compose.yml:

    etcd:
        image: 'quay.io/coreos/etcd:v3.1.7'
        restart: always
        ports:
            - '23791:2379'
            - '23801:2380'
        environment:
            etcd_name: infra
            etcd_initial_advertise_peer_urls: 'http://etcd:2380'
            etcd_initial_cluster: infra=http://etcd:2380
            etcd_initial_cluster_state: new
            etcd_initial_cluster_token: secrettoken
            etcd_listen_client_urls: 'http://etcd:2379,http://localhost:2379'
            etcd_listen_peer_urls: 'http://etcd:2380'
            etcd_advertise_client_urls: 'http://etcd:2379'

在 etcd 中注册通知服务 notificator/cmd/service/service.go:

registrar, err := registerservice(logger)
if err != nil {
    logger.log(err)
    return
}
defer registrar.deregister()
func registerservice(logger log.logger) (*sdetcd.registrar, error) {
    var (
        etcdserver = "http://etcd:2379"
        prefix     = "/services/notificator/"
        instance   = "notificator:8082"
        key        = prefix   instance
    )
    client, err := sdetcd.newclient(context.background(), []string{etcdserver}, sdetcd.clientoptions{})
    if err != nil {
        return nil, err
    }
    registrar := sdetcd.newregistrar(client, sdetcd.service{
        key:   key,
        value: instance,
    }, logger)
    registrar.register()
    return registrar, nil
}

当我们的程序停止或者崩溃时,应该要记得取消注册。现在 etcd 已经知道380玩彩网官网入口的服务了,这个例子中只有一个实例,实际环境中显然会有更多。
现在让我们来测试一下通知服务是否注册到了 etcd 中:

docker-compose up -d etcd
docker-compose up -d notificator

现在我们使用用户服务调用通知服务,当我们创建一个服务后它会发送一个虚构的通知给用户。

由于通知服务是一个 grpc 服务,在用户服务中,我们需要和客户端共用一个客户端存根。

protobuf 的客户端存根代码在 notificator/pkg/grpc/pb/notificator.pb.go ,我们把这个包引入我们的客户端

users/pkg/service/service.go:

import (
    "github.com/plutov/packagemain/13-go-kit-2/notificator/pkg/grpc/pb"
    "google.golang.org/grpc"
)
type basicusersservice struct {
    notificatorserviceclient pb.notificatorclient
}
func (b *basicusersservice) create(ctx context.context, email string) error {
    reply, err := b.notificatorserviceclient.sendemail(context.background(), &pb.sendemailrequest{
        email:   email,
        content: "hi! thank you for registration...",
    })
    if reply != nil {
        log.printf("email id: %s", reply.id)
    }
    return err
}
// newbasicusersservice 返回一个简单的、无状态的用户服务
func newbasicusersservice() usersservice {
    conn, err := grpc.dial("notificator:8082", grpc.withinsecure())
    if err != nil {
        log.printf("unable to connect to notificator: %s", err.error())
        return new(basicusersservice)
    }
    log.printf("connected to notificator")
    return &basicusersservice{
        notificatorserviceclient: pb.newnotificatorclient(conn),
    }
}

当通知服务注册到 etcd 中时,我们可以用 etcd 的地址替换它的硬编码地址。

var etcdserver = "http://etcd:2379"
client, err := sdetcd.newclient(context.background(), []string{etcdserver}, sdetcd.clientoptions{})
if err != nil {
    log.printf("unable to connect to etcd: %s", err.error())
    return new(basicusersservice)
}
entries, err := client.getentries("/services/notificator/")
if err != nil || len(entries) == 0 {
    log.printf("unable to get prefix entries: %s", err.error())
    return new(basicusersservice)
}
conn, err := grpc.dial(entries[0], grpc.withinsecure())

因为我们只有一个服务,所以只获取到了一个连接,但实际系统中可能有上百个服务,所以我们可以应用一些逻辑来进行实例选择,例如轮询。
现在让我们启动用户服务来测试一下:

docker-compose up users

调用 http 接口来创建一个用户:

curl -xpost http://localhost:8802/create -d '{"email": "test"}'

结论

这篇文章我们实现了虚构的 notificator grpc 服务,将它注册到 etcd 中,并在用户服务中调用它。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 cc 协议,如果我们的工作有侵犯到您的权益,请及时联系380玩彩网官网入口。

原文地址:

译文地址:https://learnku.com/go/t/36960

本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
讨论数量: 2

etcd注册通知服务那块需要导什么包啊

4年前
1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
网站地图