确保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