当前位置: 首页 > 工具软件 > M3O > 使用案例 >

micro、M3O微服务系列(二)

穆高澹
2023-12-01

micro构建博客

确保micro server在另一个终端中运行,并且我们已连接到它,即运行微服务器

[root@master1 M3O]# micro server
2022-07-15 20:49:01  file=server/server.go:88 level=info Starting server
2022-07-15 20:49:01  file=server/server.go:116 level=info Registering registry
2022-07-15 20:49:01  file=server/server.go:116 level=info Registering broker
2022-07-15 20:49:01  file=server/server.go:116 level=info Registering network
2022-07-15 20:49:01  file=server/server.go:116 level=info Registering runtime
2022-07-15 20:49:01  file=server/server.go:116 level=info Registering config
2022-07-15 20:49:01  file=server/server.go:116 level=info Registering store
2022-07-15 20:49:01  file=server/server.go:116 level=info Registering events
2022-07-15 20:49:01  file=server/server.go:116 level=info Registering auth
2022-07-15 20:49:01  file=server/server.go:116 level=info Registering proxy
2022-07-15 20:49:01  file=server/server.go:116 level=info Registering api
2022-07-15 20:49:01  file=server/server.go:116 level=info Registering web
2022-07-15 20:49:01  file=server/server.go:206 level=info Starting server runtime

查看我们当地的环境

[root@master1 ~]# micro env
* local      127.0.0.1:8081    Local running Micro Server
  platform   proxy.m3o.com     Cloud hosted Micro Platform

如果不是本地环境,运行

micro env set local

创建为服务项目:

[root@master1 ~]# micro new ports
Creating service ports

.
├── micro.mu
├── main.go
├── generate.go
├── handler
│   └── ports.go
├── proto
│   └── ports.proto
├── Dockerfile
├── Makefile
├── README.md
├── .gitignore
└── go.mod


download protoc zip packages (protoc-$VERSION-$PLATFORM.zip) and install:

visit https://github.com/protocolbuffers/protobuf/releases

compile the proto file ports.proto:

cd ports
make init
go mod vendor
make proto

[root@master1 ~]# ls ports
Dockerfile  generate.go  go.mod  handler  main.go  Makefile  micro.mu  proto  README.md

需要 3 种方法:
Save用于博客插入和更新
Query用于阅读和列出
Delete删除

从ports开始:
ports.proto:

syntax = "proto3";
//option go_package="./;posts";
package posts;

service Posts {
        rpc Save(SaveRequest) returns (SaveResponse) {}
        rpc Query(QueryRequest) returns (QueryResponse) {}
        rpc Delete(DeleteRequest) returns (DeleteResponse) {}
}

message SaveRequest {
        string id = 1;
        string title = 2;
        string slug = 3;
        string content = 4;
        int64 timestamp = 5;
        repeated string tags = 6;
}

message SaveResponse {
        string id = 1;
}

message Post {
        string id = 1;
        string title = 2;
        string slug = 3;
        string content = 4;
        int64 created = 5;
        int64 updated = 6;
        string author = 7;
        repeated string tags = 8;
}

// Query posts. Acts as a listing when no id or slug provided.
// Gets a single post by id or slug if any of them provided.
message QueryRequest {
        string id = 1;
        string slug = 2;
        string tag = 3;
        int64 offset = 4;
        int64 limit = 5;
}

message QueryResponse {
        repeated Post posts = 1;
}

message DeleteRequest {
        string id = 1;
}

message DeleteResponse {}

虽然我们已经定义了Post消息类型,但我们仍然将一些字段重新定义为消息类型的顶级字段SaveRequest。主要原因是我们不想要动态命令

如果我们嵌入Post post = 1 到 SaveRequest,我们将通过以下方式调用帖子服务:

micro posts save --post_title=Title --post_content=Content

但我们不想重复post,我们首选的方式是:

micro posts save --title=Title --content=Content

要重新生成proto,我们必须make proto在项目根目录中发出命令。

接下来到main.go:

package main
 
import (
	"posts/handler"
 
	"github.com/micro/micro/v3/service"
	"github.com/micro/micro/v3/service/logger"
)
 
func main() {
	// Create the service
	srv := service.New(
		service.Name("posts"),
	)
 
	// Register Handler
	srv.Handle(handler.NewPosts())
 
	// Run service
	if err := srv.Run(); err != nil {
		logger.Fatal(err)
	}
}

posts.go:

package handler

import (
        "context"
        "time"

        "github.com/micro/dev/model"
        "github.com/micro/go-micro/errors"
        "github.com/micro/micro/v3/service/logger"
        "github.com/micro/micro/v3/service/store"

        proto "posts/proto"

        "github.com/gosimple/slug"
)

type Posts struct {
        db           model.Model
        idIndex      model.Index
        createdIndex model.Index
        slugIndex    model.Index
}

func NewPosts() *Posts {
        createdIndex := model.ByEquality("created")
        createdIndex.Order.Type = model.OrderTypeDesc

        slugIndex := model.ByEquality("slug")

        idIndex := model.ByEquality("id")
        idIndex.Order.Type = model.OrderTypeUnordered

        return &Posts{
                db: model.New(
                        store.DefaultStore,
                        "posts",
                        model.Indexes(slugIndex, createdIndex),
                        &model.ModelOptions{
                                IdIndex: idIndex,
                        },
                ),
                createdIndex: createdIndex,
                slugIndex:    slugIndex,
                idIndex:      idIndex,
        }
}

func (p *Posts) Save(ctx context.Context, req *proto.SaveRequest, rsp *proto.SaveResponse) error {
        logger.Info("Received Posts.Save request")
        post := &proto.Post{
                Id:      req.Id,
                Title:   req.Title,
                Content: req.Content,
                Slug:    req.Slug,
                Created: time.Now().Unix(),
        }
        if req.Slug == "" {
                post.Slug = slug.Make(req.Title)
        }
        return p.db.Save(post)
}

func (p *Posts) Query(ctx context.Context, req *proto.QueryRequest, rsp *proto.QueryResponse) error {
        var q model.Query
        if len(req.Slug) > 0 {
                logger.Infof("Reading post by slug: %v", req.Slug)
                q = p.slugIndex.ToQuery(req.Slug)
        } else if len(req.Id) > 0 {
                logger.Infof("Reading post by id: %v", req.Id)
                q = p.idIndex.ToQuery(req.Id)
        } else {
                q = p.createdIndex.ToQuery(nil)

                var limit uint
                limit = 20
                if req.Limit > 0 {
                        limit = uint(req.Limit)
                }
                q.Limit = int64(limit)
                q.Offset = req.Offset
                logger.Infof("Listing posts, offset: %v, limit: %v", req.Offset, limit)
        }

        posts := []*proto.Post{}
        err := p.db.List(q, &posts)
        if err != nil {
                return errors.BadRequest("proto.query.store-read", "Failed to read from store: %v", err.Error())
        }
        rsp.Posts = posts
        return nil
}

func (p *Posts) Delete(ctx context.Context, req *proto.DeleteRequest, rsp *proto.DeleteResponse) error {
        logger.Info("Received Post.Delete request")
        return p.db.Delete(model.Equals("id", req.Id))
}

上面这段代码使用了Model Package。它设置了索引,使我们能够查询数据,并告诉模型维护这些索引。

1.需要id索引才能通过id读取
2.需要创建索引,因此当我们列出帖子时,帖子的顺序将根据创建的字段降序
3.需要 slug 索引才能通过 slug 阅读帖子(即。myblog.com/post/awesome-post-url)
此时micro run .项目根目录中应该部署我们的后期服务。让我们验证一下micro logs posts(确切的输出可能取决于实际的配置格式配置)

保存帖子

func (p *Posts) Save(ctx context.Context, req *proto.SaveRequest, rsp *proto.SaveResponse) error {
        logger.Info("Received Posts.Save request")
        post := &proto.Post{
                Id:      req.Id,
                Title:   req.Title,
                Content: req.Content,
                Slug:    req.Slug,
                Created: time.Now().Unix(),
        }
        if req.Slug == "" {
                post.Slug = slug.Make(req.Title)
        }
        return p.db.Save(post)
}
[root@master1 handler]# micro posts save --id=1 --title="Post one" --content="First saved post"
{}
[root@master1 handler]# micro posts save --id=2 --title="Post two" --content="Second saved post"
{}

查询帖子

查询处理程序能够通过 id、slug 进行查询并启用帖子列表

func (p *Posts) Query(ctx context.Context, req *proto.QueryRequest, rsp *proto.QueryResponse) error {
        var q model.Query
        if len(req.Slug) > 0 {
                logger.Infof("Reading post by slug: %v", req.Slug)
                q = p.slugIndex.ToQuery(req.Slug)
        } else if len(req.Id) > 0 {
                logger.Infof("Reading post by id: %v", req.Id)
                q = p.idIndex.ToQuery(req.Id)
        } else {
                q = p.createdIndex.ToQuery(nil)

                var limit uint
                limit = 20
                if req.Limit > 0 {
                        limit = uint(req.Limit)
                }
                q.Limit = int64(limit)
                q.Offset = req.Offset
                logger.Infof("Listing posts, offset: %v, limit: %v", req.Offset, limit)
        }

        posts := []*proto.Post{}
        err := p.db.List(q, &posts)
        if err != nil {
                return errors.BadRequest("proto.query.store-read", "Failed to read from store: %v", err.Error())
        }
        rsp.Posts = posts
        return nil
}

(修改proto文件,或者重新修改代码,就make proto或者micro update .)

[root@master1 handler]# micro posts query
{
        "posts": [
                {
                        "id": "2",
                        "title": "Post two",
                        "slug": "post-two",
                        "content": "Second saved post",
                        "created": "1657894638"
                },
                {
                        "id": "1",
                        "title": "Post one",
                        "slug": "post-one",
                        "content": "First saved post",
                        "created": "1657894626"
                }
        ]
}

删除帖子

func (p *Posts) Delete(ctx context.Context, req *proto.DeleteRequest, rsp *proto.DeleteResponse) error {
        logger.Info("Received Post.Delete request")
        return p.db.Delete(model.Equals("id", req.Id))
}

放下完整的项目链接:https://github.com/micro/dev/tree/master/blog/v1-posts

 类似资料: