go-zeroを使用して、高い同時実行性をサポートするマイクロサービスをすばやく構築します

0.マイクロサービスをうまく行うことが難しいのはなぜですか。

マイクロサービスをうまく活用するには、いくつかの側面から多くの知識ポイントを理解し、習得する必要があります。

  • 基本機能レベル

    1. サービスがバーストトラフィックに圧倒されないようにするための同時実行制御と現在の制限
    2. 増加または減少するノードの動的検出を確実にするためのサービス登録およびサービス検出
    3. 負荷分散は、ノードの容量に応じてトラフィックを分散する必要があります
    4. タイムアウトしたリクエストでの無駄な作業を回避するためのタイムアウト制御
    5. ヒューズ設計、高速障害、障害が発生したノードの回復能力を保証
  • 高レベルの機能レベル

    1. 各ユーザーが自分のデータにのみアクセスできるように認証をリクエストします
    2. リンクトラッキング。システム全体を理解し、特定のリクエストに関する問題をすばやく特定するために使用されます
    3. データ収集と問題の場所のログ
    4. 可観測性、測定なしの最適化なし

これらの各ポイントについて、その原則と実装を説明するために長いスペースを使用する必要があるため、バックエンド開発者がこれらの知識ポイントを習得してビジネスシステムに実装することは非常に困難です。 、しかし、私たちは大規模なトラフィックによって検証されたフレームワークシステムに頼ることができます。このために、go-zeroマイクロサービスフレームワークが誕生しました。

さらに、私たちは常に、ツールは慣習や文書よりも優れているという哲学を守ります開発者の精神的負担を極力減らし、ビジネス価値を生み出すコードに力を注ぎ、繰り返しコードの記述を減らしたいと考え、goctlツールを開発しました。

以下では、ショートチェーンマイクロサービスを使用して、go-zeroを介してマイクロサービスすばやく作成するプロセスを示します。それをウォークスルーすると、マイクロサービスの作成が非常に簡単であることがわかります。

1.ショートチェーンサービスとは何ですか?

ショートチェーンサービスは、プログラムの計算やその他の方法で、長いURLURLを短いURL文字列に変換することです。

このショートチェーンサービスは、完全なマイクロサービス全体をゼロで構築するプロセスを示すために作成されています。アルゴリズムと実装の詳細は可能な限り簡素化されているため、これは高レベルのショートチェーンサービスではありません。

2.ショートチェーンマイクロサービスアーキテクチャ図

  • ここでは、短縮と拡張を2つのマイクロサービスに分けます。これは、リモートコールを1つのマイクロサービスに分割する必要があるという意味ではなく、複数のマイクロサービスの最も単純なデモンストレーションのためだけです。
  • 後者のredisとmysqlも共有されますが、実際のプロジェクトでは、各マイクロサービスは可能な限り独自のデータベースを使用する必要があり、データの境界は明確である必要があります

3.準備

  • etcd、mysql、redisをインストールします
  • goctlツールを準備する
  • https://github.com/tal-tech/go-zero/releases最新バージョンを直接ダウンロードすると、自動更新が後で追加されます
    • ソースコードから任意のディレクトリでコンパイルすることもできます。目的はgoctlツールをコンパイルすることです。

1.  git clone https://github.com/tal-tech/go-zero 2.tools/goctlディレクトリのgoctlツールコンパイルします。3 go build goctl.go 。生成されたgoctlを$PATH下に置き、goctlコマンドを実行できることを確認します。

  • 作業ディレクトリを作成するshorturl
  • shorturlディレクトリでgo mod init shorturl初期化実行しますgo.mod

4.APIゲートウェイコードを記述します

  • goctlによって生成shorturl.apiおよび編集されます。簡潔にするために、ファイルの先頭が削除されinfoます。コードは次のとおりです。
type (
  shortenReq struct {
      url string `form:"url"`
  }

  shortenResp struct {
      shortUrl string `json:"shortUrl"`
  }
)

type (
  expandReq struct {
      key string `form:"key"`
  }

  expandResp struct {
      url string `json:"url"`
  }
)

service shorturl-api {
  @server(
      handler: ShortenHandler
  )
  get /shorten(shortenReq) returns(shortenResp)

  @server(
      handler: ExpandHandler
  )
  get /expand(expandReq) returns(expandResp)
}

タイプの使用法はgoの使用法と同じです。サービスは、get / post / head / deleteなどのapi要求を定義するために使用されます。説明は次のとおりです。

  • service shorturl-api { この行はサービス名を定義します
  • @serverその一部は、サーバー側で使用される属性を定義するために使用されます
  • handlerサーバーハンドラーの名前が定義されています
  • get /shorten(shortenReq) returns(shortenResp)getメソッドのルーティング、要求パラメーター、戻りパラメーターなどを定義します

    • goctlを使用してAPIゲートウェイコードを生成します
goctl api go -api shorturl.api -dir api

生成されるファイル構造は次のとおりです。

.
├── api
│   ├── etc
│   │   └── shorturl-api.yaml         // 配置文件
│   ├── internal
│   │   ├── config
│   │   │   └── config.go             // 定义配置
│   │   ├── handler
│   │   │   ├── expandhandler.go      // 实现expandHandler
│   │   │   ├── routes.go             // 定义路由处理
│   │   │   └── shortenhandler.go     // 实现shortenHandler
│   │   ├── logic
│   │   │   ├── expandlogic.go        // 实现ExpandLogic
│   │   │   └── shortenlogic.go       // 实现ShortenLogic
│   │   ├── svc
│   │   │   └── servicecontext.go     // 定义ServiceContext
│   │   └── types
│   │       └── types.go              // 定义请求、返回结构体
│   └── shorturl.go                   // main入口定义
├── go.mod
├── go.sum
└── shorturl.api
  • デフォルトでポート8888でリッスンしているAPIGatewayサービスを開始します
go run api/shorturl.go -f api/etc/shorturl-api.yaml
  • APIゲートウェイサービスをテストします
curl -i "http://localhost:8888/shorten?url=http://www.xiaoheiban.cn"

返品は次のとおりです。

HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 27 Aug 2020 14:31:39 GMT
Content-Length: 15

{"shortUrl":""}

API Gatewayがnull値を返しただけであることがわかります。次に、rpcサービスにビジネスロジックを実装します。

  • internal/svc/servicecontext.goサービスの依存関係を渡すように変更できます(必要な場合)

  • 実装ロジックのinternal/logic下の対応するファイルは変更できます

  • goctlコード、さまざまなクライアント言語でAPIを生成することで呼び出すことができます

  • この時点で、goctlを使用して、クライアント側の学生が並行して開発し、複数の言語をサポートするためのクライアントコードを生成できます。詳細については、ドキュメントを参照してください。

5.より短いrpcサービスを作成します

  • rpc/shortenディレクトリにshorten.protoファイル書き込む

プロトファイルテンプレートはコマンドで生成できます

goctl rpc template -o shorten.proto

変更されたファイルの内容は次のとおりです。

syntax = "proto3";

package shorten;

message shortenReq {
    string url = 1;
}

message shortenResp {
    string key = 1;
}

service shortener {
    rpc shorten(shortenReq) returns(shortenResp);
}
  • goctlコードrpcを生成することによりrpc/shorten、ディレクトリ内のコマンド
goctl rpc proto -src shorten.proto

ファイル構造は次のとおりです。

rpc/shorten
├── etc
│   └── shorten.yaml               // 配置文件
├── internal
│   ├── config
│   │   └── config.go              // 配置定义
│   ├── logic
│   │   └── shortenlogic.go        // rpc业务逻辑在这里实现
│   ├── server
│   │   └── shortenerserver.go     // 调用入口, 不需要修改
│   └── svc
│       └── servicecontext.go      // 定义ServiceContext,传递依赖
├── pb
│   └── shorten.pb.go
├── shorten.go                     // rpc服务main函数
├── shorten.proto
└── shortener
    ├── shortener.go               // 提供了外部调用方法,无需修改
    ├── shortener_mock.go          // mock方法,测试用
    └── types.go                   // request/response结构体定义

次のように直接実行できます。

$ go run shorten.go -f etc/shorten.yaml
Starting rpc server at 127.0.0.1:8080...

etc/shorten.yamlリスニングポートの構成はファイルで変更できます

6.拡張rpcサービスを記述します

  • rpc/expandディレクトリにexpand.protoファイル書き込む

プロトファイルテンプレートはコマンドで生成できます

goctl rpc template -o expand.proto

変更されたファイルの内容は次のとおりです。

syntax = "proto3";

package expand;

message expandReq {
    string key = 1;
}

message expandResp {
    string url = 1;
}

service expander {
    rpc expand(expandReq) returns(expandResp);
}
  • goctlコードrpcを生成することによりrpc/expand、ディレクトリ内のコマンド
goctl rpc proto -src expand.proto

ファイル構造は次のとおりです。

rpc/expand
├── etc
│   └── expand.yaml                // 配置文件
├── expand.go                      // rpc服务main函数
├── expand.proto
├── expander
│   ├── expander.go                // 提供了外部调用方法,无需修改
│   ├── expander_mock.go           // mock方法,测试用
│   └── types.go                   // request/response结构体定义
├── internal
│   ├── config
│   │   └── config.go              // 配置定义
│   ├── logic
│   │   └── expandlogic.go         // rpc业务逻辑在这里实现
│   ├── server
│   │   └── expanderserver.go      // 调用入口, 不需要修改
│   └── svc
│       └── servicecontext.go      // 定义ServiceContext,传递依赖
└── pb
    └── expand.pb.go

変更ポートetc/expand.yamlの内側をListenOn通り8081、それがされて8080すでにshorten占有によってサービス

次のように、変更後に実行します。

$ go run expand.go -f etc/expand.yaml
Starting rpc server at 127.0.0.1:8081...

etc/expand.yamlリスニングポートの構成はファイルで変更できます

7. API Gatewayコードを変更して、短縮/拡張rpcサービスを呼び出します

  • 構成ファイルshorter-api.yaml変更し、次のコンテンツを追加します
Shortener:
  Etcd:
    Hosts:
      - localhost:2379
    Key: shorten.rpc
Expander:
  Etcd:
    Hosts:
      - localhost:2379
    Key: expand.rpc

etcdを介して利用可能な短縮/拡張サービスを自動的に検出します

  • internal/config/config.go次のように修正し、サービスの依存関係を短縮/拡張します
type Config struct {
  rest.RestConf
  Shortener rpcx.RpcClientConf     // 手动代码
  Expander  rpcx.RpcClientConf     // 手动代码
}
  • internal/svc/servicecontext.go次のように修正します。
type ServiceContext struct {
  Config    config.Config
  Shortener rpcx.Client                                 // 手动代码
  Expander  rpcx.Client                                 // 手动代码
}

func NewServiceContext(config config.Config) *ServiceContext {
  return &ServiceContext{
      Config:    config,
      Shortener: rpcx.MustNewClient(config.Shortener),    // 手动代码
      Expander:  rpcx.MustNewClient(config.Expander),     // 手动代码
  }
}

ServiceContextを介して異なるビジネスロジック間で依存関係を渡す

  • internal/logic/expandlogic.go次のように修正します。
type ExpandLogic struct {
  ctx context.Context
  logx.Logger
  expander rpcx.Client            // 手动代码
}

func NewExpandLogic(ctx context.Context, svcCtx *svc.ServiceContext) ExpandLogic {
  return ExpandLogic{
      ctx:    ctx,
      Logger: logx.WithContext(ctx),
      expander: svcCtx.Expander,    // 手动代码
  }
}

func (l *ExpandLogic) Expand(req types.ExpandReq) (*types.ExpandResp, error) {
  // 手动代码开始
  resp, err := expander.NewExpander(l.expander).Expand(l.ctx, &expander.ExpandReq{
      Key: req.Key,
  })
  if err != nil {
      return nil, err
  }

  return &types.ExpandResp{
      Url: resp.Url,
  }, nil
  // 手动代码结束
}

増加expanderの依存関係をのサービス、およびURLへの短鎖回復を実現呼び出すことによってメソッドをexpanderExpand

  • internal/logic/shortenlogic.go次のように修正します。
type ShortenLogic struct {
  ctx context.Context
  logx.Logger
  shortener rpcx.Client             // 手动代码
}

func NewShortenLogic(ctx context.Context, svcCtx *svc.ServiceContext) ShortenLogic {
  return ShortenLogic{
      ctx:    ctx,
      Logger: logx.WithContext(ctx),
      shortener: svcCtx.Shortener,    // 手动代码
  }
}

func (l *ShortenLogic) Shorten(req types.ShortenReq) (*types.ShortenResp, error) {
  // 手动代码开始
  resp, err := shortener.NewShortener(l.shortener).Shorten(l.ctx, &shortener.ShortenReq{
      Url: req.Url,
  })
  if err != nil {
      return nil, err
  }

  return &types.ShortenResp{
      ShortUrl: resp.Key,
  }, nil
  // 手动代码结束
}

shortener依存サービスを追加しshortenerShorten実現URLを呼び出しショートチェーンアプローチを変換する

この時点で、API Gatewayが変更されました。投稿されたコードはたくさんありますが、中期的な変更のごく一部しか行われていません。コンテキストの理解を容易にするために、完全なコードを投稿してから、CRUD + cacheを処理します。

8.データベーステーブル構造を定義し、CRUD +キャッシュコードを生成します

  • shorturlの下にrpc / modelディレクトリを作成します。mkdir -p rpc/model
  • 次のように、SQLファイルを記述してrpc / modelディレクトリshorturl.sqlshorturlテーブルを作成します。
CREATE TABLE `shorturl`
(
  `shorten` varchar(255) NOT NULL COMMENT 'shorten key',
  `url` varchar(255) NOT NULL COMMENT 'original url',
  PRIMARY KEY(`shorten`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  • DBとテーブルを作成する
create database gozero;
source shorturl.sql;
  • rpc/modelディレクトリで次のコマンド実行して、CRUD +キャッシュコードを生成-cします。redis cache
goctl model mysql ddl -c -src shorturl.sql -dir .

datasource代わりddlコマンドを使用して、データベースリンクがスキーマから直接生成されるように指定することもできます。

生成されるファイル構造は次のとおりです。

rpc/model
├── shorturl.sql
├── shorturlmodel.go              // CRUD+cache代码
└── vars.go                       // 定义常量和变量

9.短縮/拡張rpcコードを変更して、crud + cacheコードを呼び出します

  • 変更しrpc/expand/etc/expand.yaml、次のコンテンツを追加します。
DataSource: root:@tcp(localhost:3306)/gozero
Table: shorturl
Cache:
  - Host: localhost:6379

複数のredisをキャッシュとして使用し、redisシングルポイントまたはredisクラスターをサポートできます

  • rpc/expand/internal/config.go次のように修正します。
type Config struct {
  rpcx.RpcServerConf
  DataSource string             // 手动代码
  Table      string             // 手动代码
  Cache      cache.CacheConf    // 手动代码
}

mysqlおよびredisキャッシュ構成を追加しました

  • rpc/expand/internal/svc/servicecontext.go次のように修正します。
type ServiceContext struct {
  c     config.Config
  Model *model.ShorturlModel   // 手动代码
}

func NewServiceContext(c config.Config) *ServiceContext {
  return &ServiceContext{
      c:     c,
      Model: model.NewShorturlModel(sqlx.NewMysql(c.DataSource), c.Cache, c.Table), // 手动代码
  }
}
  • rpc/expand/internal/logic/expandlogic.go次のように修正します。
type ExpandLogic struct {
  ctx context.Context
  logx.Logger
  model *model.ShorturlModel          // 手动代码
}

func NewExpandLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ExpandLogic {
  return &ExpandLogic{
      ctx:    ctx,
      Logger: logx.WithContext(ctx),
      model:  svcCtx.Model,             // 手动代码
  }
}

func (l *ExpandLogic) Expand(in *expand.ExpandReq) (*expand.ExpandResp, error) {
  // 手动代码开始
  res, err := l.model.FindOne(in.Key)
  if err != nil {
      return nil, err
  }

  return &expand.ExpandResp{
      Url: res.Url,
  }, nil
  // 手动代码结束
}
  • 変更しrpc/shorten/etc/shorten.yaml、次のコンテンツを追加します。
DataSource: root:@tcp(localhost:3306)/gozero
Table: shorturl
Cache:
  - Host: localhost:6379

複数のredisをキャッシュとして使用し、redisシングルポイントまたはredisクラスターをサポートできます

  • rpc/shorten/internal/config.go次のように修正します。
type Config struct {
  rpcx.RpcServerConf
  DataSource string            // 手动代码
  Table      string            // 手动代码
  Cache      cache.CacheConf   // 手动代码
}

mysqlおよびredisキャッシュ構成を追加しました

  • rpc/shorten/internal/svc/servicecontext.go次のように修正します。
type ServiceContext struct {
  c     config.Config
  Model *model.ShorturlModel   // 手动代码
}

func NewServiceContext(c config.Config) *ServiceContext {
  return &ServiceContext{
      c:     c,
      Model: model.NewShorturlModel(sqlx.NewMysql(c.DataSource), c.Cache, c.Table), // 手动代码
  }
}
  • rpc/shorten/internal/logic/shortenlogic.go次のように修正します。
const keyLen = 6

type ShortenLogic struct {
  ctx context.Context
  logx.Logger
  model *model.ShorturlModel          // 手动代码
}

func NewShortenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ShortenLogic {
  return &ShortenLogic{
      ctx:    ctx,
      Logger: logx.WithContext(ctx),
      model:  svcCtx.Model,             // 手动代码
  }
}

func (l *ShortenLogic) Shorten(in *shorten.ShortenReq) (*shorten.ShortenResp, error) {
  // 手动代码开始,生成短链接
  key := hash.Md5Hex([]byte(in.Url))[:keyLen]
  _, err := l.model.Insert(model.Shorturl{
      Shorten: key,
      Url:     in.Url,
  })
  if err != nil {
      return nil, err
  }

  return &shorten.ShortenResp{
      Key: key,
  }, nil
  // 手动代码结束
}

この時点で、コードの変更は完了し、手動で変更されたコードにマークを付けました。

10.フルコールデモ

  • api呼び出しを短くする
~ curl -i "http://localhost:8888/shorten?url=http://www.xiaoheiban.cn"

返品は次のとおりです。

HTTP/1.1 200 OK
Content-Type: application/json
Date: Sat, 29 Aug 2020 10:49:49 GMT
Content-Length: 21

{"shortUrl":"f35b2a"}
  • api呼び出しを展開します
curl -i "http://localhost:8888/expand?key=f35b2a"

返品は次のとおりです。

HTTP/1.1 200 OK
Content-Type: application/json
Date: Sat, 29 Aug 2020 10:51:53 GMT
Content-Length: 34

{"url":"http://www.xiaoheiban.cn"}

11.ベンチマーク

書き込みはmysqlの書き込み速度に依存するため、mysqlを押すのと同じです。したがって、圧力テストでは、mysqlからの読み取りとキャッシュの使用に相当する、拡張インターフェイスのみをテストしました。shorten.luaでは、dbからランダムに100を取得します。圧力テスト要求を生成するためのホットキー

私のMacBookProでは30,000以上のqpsに到達できることがわかります。

12.まとめ

私たちは常に、ツールは慣習や文書よりも優れていることを強調してきました

go-zeroは単なるフレームワークではなく、マイクロサービスの構築全体を簡素化および標準化するフレームワークとツールに基づく技術システムでもあります。

シンプルに保ちながら、マイクロサービスガバナンスの複雑さを可能な限りフレームワークにカプセル化することで、開発者の精神的負担を大幅に軽減し、迅速なビジネス開発を可能にします。

go-zero + goctlによって生成されたコードには、同時実行制御、適応ヒューズ、適応負荷制限、自動キャッシュ制御など、マイクロサービスガバナンスのさまざまなコンポーネントが含まれており、これらを簡単に展開して大量のトラフィックを伝送できます。

12. WeChat Exchange Group

 

 

おすすめ

転載: blog.csdn.net/qq8864/article/details/108327384