1 - redis 客户端注入

简介

本示例展示了注入 Redis 客户端能力。

在应用开发过程中,通过 SDK 操作 Redis 是一个常见的诉求。

ioc-golang 框架提供了注入 Redis 连接的能力,开发者可以在配置文件中指定好 Redis 地址、密码、db名等信息,通过标签注入连接,无需手动创建、组装。

注入模型与结构

多例(normal)依赖注入模型

预定义的 redis 结构

关键代码

import(
	normalMysql "github.com/alibaba/ioc-golang/extension/normal/mysql"
)

// +ioc:autowire=true
// +ioc:autowire:type=singleton

type App struct {
	NormalRedis    normalRedis.Redis `normal:"github.com/alibaba/ioc-golang/extension/normal/redis.Impl"`
	NormalDB1Redis normalRedis.Redis `normal:"github.com/alibaba/ioc-golang/extension/normal/redis.Impl,db1-redis"`
	NormalDB2Redis normalRedis.Redis `normal:"github.com/alibaba/ioc-golang/extension/normal/redis.Impl,db2-redis"`
	NormalDB3Redis normalRedis.Redis `normal:"github.com/alibaba/ioc-golang/extension/normal/redis.Impl,address=127.0.0.1:6379&db=3"`
}
  • 被注入字段类型

    normalRedis.Redis 接口

  • 标签

    开发人员可以为 normalRedis.Redis 类型的字段增加 normal:"Impl,$(configKey),$(tableName)" 标签。从而注入Redis sdk。

    例子中的 normal:"github.com/alibaba/ioc-golang/extension/normal/redis.Impl" 的意义为,将配置文件内 autowire.normal.<github.com/alibaba/ioc-golang/extension/normal/redis.Impl>.param定义的值作为参数。

    例子中的 normal:"github.com/alibaba/ioc-golang/extension/normal/redis.Impl,db1-redis" 的意义为,将配置文件内 autowire.normal.<github.com/alibaba/ioc-golang/extension/normal/redis.Impl>.db1-redis.param定义的值作为参数。

    例子中的 normal:"github.com/alibaba/ioc-golang/extension/normal/redis.Impl,db2-redis"的意义为,将配置文件内 autowire.normal.<github.com/alibaba/ioc-golang/extension/normal/redis.Impl>.db2-redis.param定义的值作为参数。

    例子中的 normal:"github.com/alibaba/ioc-golang/extension/normal/redis.Impl,address=127.0.0.1:6379&db=3" 的意义为,使用标签内定义的 key-value 作为参数配置。

    默认参数加载策略详情请参阅参数加载器

    autowire:
      normal:
        github.com/alibaba/ioc-golang/extension/normal/redis.Impl:
          db1-redis:
            param:
              address: localhost:6379
              db: 1
          db2-redis:
            param:
              address: localhost:6379
              db: 2
          param:
            address: localhost:6379
            db: 0
    

运行示例

例子会注入多个位于127.0.0.1:6379 的Redis 客户端,数据库id分别为 0、1、2、3.

需要确保您本地运行了redis,可通过 docker run -p6379:6379 redis:latest 快速启动一个。

注入后,方可调用该接口提供的方法。可获取裸 Redis 连接,也可以直接使用封装好的 API 操作Redis。

cd example/autowire_redis_client/cmd
go run .
  ___    ___     ____            ____           _                         
 |_ _|  / _ \   / ___|          / ___|   ___   | |   __ _   _ __     __ _ 
  | |  | | | | | |      _____  | |  _   / _ \  | |  / _` | | '_ \   / _` |
  | |  | |_| | | |___  |_____| | |_| | | (_) | | | | (_| | | | | | | (_| |
 |___|  \___/   \____|          \____|  \___/  |_|  \__,_| |_| |_|  \__, |
                                                                    |___/ 
Welcome to use ioc-golang!
[Boot] Start to load ioc-golang config
[Config] Load config file from ../conf/ioc_golang.yaml
[Boot] Start to load debug
[Debug] Debug mod is not enabled
[Boot] Start to load autowire
[Autowire Type] Found registered autowire type singleton
[Autowire Struct Descriptor] Found type singleton registered SD main.App
[Autowire Type] Found registered autowire type normal
[Autowire Struct Descriptor] Found type normal registered SD github.com/alibaba/ioc-golang/extension/normal/redis.Impl
client0 get  db0
client1 get  db1
client2 get  db2
client3 get  db3

可看到打印出了已经写入四个 redis db 的值。

2 - GORM 客户端注入

简介

本示例展示了注入基于 GORM 的数据库客户端能力。

在应用开发过程中,通过 SDK 操作数据库是一个常见的诉求,GORM 是应用较广泛的 Go 数据库 sdk。

ioc-golang 框架提供了注入数据库连接的能力,开发者可以在配置文件中指定好数据库地址、密码信息,通过标签注入连接,无需手动创建、组装 GORM 客户端。

注入模型与结构

多例(normal)依赖注入模型

预定义的 mysql 结构

关键代码

import(
	normalMysql "github.com/alibaba/ioc-golang/extension/normal/mysql"
)

// +ioc:autowire=true
// +ioc:autowire:type=singleton

type App struct {
	MyDataTable normalMysql.Mysql `normal:"github.com/alibaba/ioc-golang/extension/normal.Impl,my-mysql,mydata"`
}
  • 被注入字段类型

    normalMysql.Mysql 接口

  • 标签

    开发人员可以为 normalMysql.Mysql 类型的字段增加 normal:"github.com/alibaba/ioc-golang/extension/normal.Impl,$(configKey),$(tableName)" 标签。从而注入指定数据库的指定表 sdk。

    例子中的 normal:"github.com/alibaba/ioc-golang/extension/normal.Impl,my-mysql,mydata" 的意义为,将配置文件内 autowire.normal.<github.com/alibaba/ioc-golang/extension/normal/mysql.Impl>.my-mysql.param定义的值作为参数。

    autowire:
      normal:
        github.com/alibaba/ioc-golang/extension/normal/mysql.Impl:
          my-mysql:
            param:
              host: "127.0.0.1"
              port: 3306
              username: "root"
              password: "root"
              dbname: "test"
    

    例子会建立一个位于127.0.0.1:3306 的数据库连接,用户名为root、密码为 root、数据库名为test、表名为mydata。

    注入后,方可调用该接口提供的方法,获取裸 gorm 连接,或者直接使用封装好的 API 操作数据表。

    type Mysql interface {
    	GetDB() *gorm.DB
    	SelectWhere(queryStr string, result interface{}, args ...interface{}) error
    	Insert(toInsertLines UserDefinedModel) error
    	Delete(toDeleteTarget UserDefinedModel) error
    	First(queryStr string, findTarget UserDefinedModel, args ...interface{}) error
    	Update(queryStr, field string, target interface{}, args ...interface{}) error
    }
    

3 - gRPC 客户端注入

简介

本示例展示了基于 ioc-golang 框架的 gRPC 客户端注入能力。

在进行微服务开发过程中,服务间通信尤为重要,gRPC 是被应用最为广泛的 RPC 框架之一。

在常规开发中,开发者需要从配置中手动读取下游主机名,启动 grpc 客户端。针对一个接口的网络客户端往往是单例模型,如果多个服务都需要使用同一客户端,则还需要开发者维护这个单例模型。

基于 ioc-golang 框架的 gRPC 客户端注入能力,我们可以将客户端的生命周期交付给框架管理,并赋予客户端调试能力,开发者只需要关注注册和使用。

示例介绍

本示例实现了以下拓扑

debug

在这个例子中,App 结构会依此调用所有依赖对象,进而调用一个单例模型注入的 gRPC 客户端,该客户端发起网络请求,并获得结果。

依赖注入模型

grpc 依赖注入模型

关键代码

import(
  "github.com/alibaba/ioc-golang/extension/autowire/grpc"
	googleGRPC "google.golang.org/grpc"
)
func init() {
	// register grpc client
	grpc.RegisterStructDescriptor(&autowire.StructDescriptor{
		Interface: new(api.HelloServiceClient),
		Factory: func() interface{} {
			return new(api.HelloServiceClient)
		},
		ParamFactory: func() interface{} {
			return &googleGRPC.ClientConn{}
		},
		ConstructFunc: func(impl interface{}, param interface{}) (interface{}, error) {
			conn := param.(*googleGRPC.ClientConn)
			fmt.Println("create conn target ", conn.Target())
			return api.NewHelloServiceClient(conn), nil
		},
	})
}

// +ioc:autowire=true
// +ioc:autowire:type=singleton

type App struct {
	HelloServiceClient api.HelloServiceClient `grpc:"hello-service"`
}

需要在代码中手动注册 gRPC 客户端。在需要使用的地方,增加 grpc:"xxx" 标签

框架会默认从 autowire.grpc.xxx 读取参数, 在例子中,为autowire.grpc.hello-service

autowire:
  grpc:
    hello-service:
      address: localhost:8080

运行示例

  1. 启动 grpc Server

    % cd example/denbug/grpc_server
    % go run .
    
  2. 新开一个终端,启动客户端。

    % cd example/autowire_grpc_client/cmd
    % go run .
      ___    ___     ____            ____           _                         
     |_ _|  / _ \   / ___|          / ___|   ___   | |   __ _   _ __     __ _ 
      | |  | | | | | |      _____  | |  _   / _ \  | |  / _` | | '_ \   / _` |
      | |  | |_| | | |___  |_____| | |_| | | (_) | | | | (_| | | | | | | (_| |
     |___|  \___/   \____|          \____|  \___/  |_|  \__,_| |_| |_|  \__, |
                                                                        |___/ 
    Welcome to use ioc-golang!
    [Boot] Start to load ioc-golang config
    [Config] Load config file from ../conf/ioc_golang.yaml
    [Boot] Start to load debug
    [Debug] Debug mod is not enabled
    [Boot] Start to load autowire
    [Autowire Type] Found registered autowire type grpc
    [Autowire Struct Descriptor] Found type grpc registered SD github.com/alibaba/ioc-golang/example/autowire_grpc_client/api.HelloServiceClient
    [Autowire Type] Found registered autowire type singleton
    [Autowire Struct Descriptor] Found type singleton registered SD github.com/alibaba/ioc-golang/example/autowire_grpc_client/cmd/struct1.Struct1
    [Autowire Struct Descriptor] Found type singleton registered SD main.App
    [Autowire Struct Descriptor] Found type singleton registered SD github.com/alibaba/ioc-golang/example/autowire_grpc_client/cmd/service1.Impl1
    [Autowire Struct Descriptor] Found type singleton registered SD github.com/alibaba/ioc-golang/example/autowire_grpc_client/cmd/service2.Impl1
    [Autowire Struct Descriptor] Found type singleton registered SD github.com/alibaba/ioc-golang/example/autowire_grpc_client/cmd/service2.Impl2
    create conn target  localhost:8080
    App call grpc get: Hello laurence
    ExampleService1Impl1 call grpc get :Hello laurence
    ExampleService2Impl1 call grpc get :Hello laurence_service2_impl1
    ExampleService2Impl2 call grpc get :Hello laurence_service2_impl2
    ExampleStruct1 call grpc get :Hello laurence_service1_impl1
    

小节

gRPC 客户端注入能力,所代表的是 ioc-golang 框架具备网络模型注入的能力。

针对特定网络框架,可以在其服务提供者接口处,提供客户端注入代码,从而便于客户端引入后直接注入。

4 - 配置注入

简介

本示例展示了从配置文件注入字段的能力。

在应用开发过程中,从配置文件中读入配置是一个常见的诉求。例如读取数据库的账号密码、下游服务的主机名,以及一些业务配置等。

ioc-golang 框架提供了便捷的基于文件注入配置的能力,使开发者无需手动解析配置文件,无需手动组装对象。

注入模型与结构

config 依赖注入模型

关键代码:

import (
  github.com/alibaba/ioc-golang/extension/config
)

// +ioc:autowire=true
// +ioc:autowire:type=singleton

type App struct {
	DemoConfigString *config.ConfigString `config:",autowire.config.demo-config.string-value"`
	DemoConfigInt    *config.ConfigInt    `config:",autowire.config.demo-config.int-value"`
	DemoConfigMap    *config.ConfigMap    `config:",autowire.config.demo-config.map-value"`
	DemoConfigSlice  *config.ConfigSlice  `config:",autowire.config.demo-config.slice-value"`
}
  • 被注入字段类型

    目前支持 ConfigString,ConfigInt,ConfigMap,ConfigSlice 四种类型。

    需要以 指针 的形式声明字段类型

  • 标签与注入位置

    开发人员可以给结构增加 ``config:",xxx" 标签, 标注需要注入的值类型,以及该字段位于配置文件的位置。

    例子中的

    config:",autowire.config.demo-config.string-value"

    的意义为,将配置文件内 autowire.config.demo-config.string-value 的值注入到该字段。

    对应配置文件:ioc_golang.yaml 中的字符串 “stringValue”

    autowire:
      config:
        demo-config:
          int-value: 123
          int64-value: 130117537261158665
          float64-value: 0.001
          string-value: stringValue
          map-value:
            key1: value1
            key2: value2
            key3: value3
            obj:
              objkey1: objvalue1
              objkey2: objvalue2
              objkeyslice: objslicevalue
          slice-value:
            - sliceValue1
            - sliceValue2
            - sliceValue3
            - sliceValue4
    

运行示例

cd example/autowire_config/cmd
go run .
  ___    ___     ____            ____           _                         
 |_ _|  / _ \   / ___|          / ___|   ___   | |   __ _   _ __     __ _ 
  | |  | | | | | |      _____  | |  _   / _ \  | |  / _` | | '_ \   / _` |
  | |  | |_| | | |___  |_____| | |_| | | (_) | | | | (_| | | | | | | (_| |
 |___|  \___/   \____|          \____|  \___/  |_|  \__,_| |_| |_|  \__, |
                                                                    |___/ 
Welcome to use ioc-golang!
[Boot] Start to load ioc-golang config
[Config] merge config map, depth: [0]
[Boot] Start to load debug
[Debug] Debug mod is not enabled
[Boot] Start to load autowire
[Autowire Type] Found registered autowire type singleton
[Autowire Struct Descriptor] Found type singleton registered SD main.App
[Autowire Type] Found registered autowire type normal
[Autowire Type] Found registered autowire type config
[Autowire Struct Descriptor] Found type config registered SD github.com/alibaba/ioc-golang/extension/config.ConfigInt64
[Autowire Struct Descriptor] Found type config registered SD github.com/alibaba/ioc-golang/extension/config.ConfigInt
[Autowire Struct Descriptor] Found type config registered SD github.com/alibaba/ioc-golang/extension/config.ConfigMap
[Autowire Struct Descriptor] Found type config registered SD github.com/alibaba/ioc-golang/extension/config.ConfigSlice
[Autowire Struct Descriptor] Found type config registered SD github.com/alibaba/ioc-golang/extension/config.ConfigString
[Autowire Struct Descriptor] Found type config registered SD github.com/alibaba/ioc-golang/extension/config.ConfigFloat64
2022/06/06 18:01:22 load config path autowire.config#demo-config#float64-value error =  property [autowire config#demo-config#float64-value]'s key config#demo-config#float64-value not found
stringValue
123
map[key1:value1 key2:value2 key3:value3 obj:map[objkey1:objvalue1 objkey2:objvalue2 objkeyslice:objslicevalue]]
[sliceValue1 sliceValue2 sliceValue3 sliceValue4]
130117537261158665
0
stringValue
123
map[key1:value1 key2:value2 key3:value3 obj:map[objkey1:objvalue1 objkey2:objvalue2 objkeyslice:objslicevalue]]
[sliceValue1 sliceValue2 sliceValue3 sliceValue4]
130117537261158665
0

可看到依次打印出了不同结构的注入配置。

5 - 通过 API 获取对象

简介

本示例展示了通过 API 调用的方式获取对象的能力。

在应用开发过程中,部分依赖通过注入的方式预置在字段中,也有一部分依赖是在程序运行过程中动态生成的。常规做法是通过手动拼装结构的方式,或者通过调用构造函数的方式获取对象。

ioc-golang 框架提供了中心化的获取对象 API。

  • 该 API 推荐被结构提供者封装,从而提供一个具象的 API 供用户调用。该 API 传入所需配置结构,返回具体的接口。

extension/normal/redis/redis.go

func GetRedis(config *Config) (Redis, error) {
	mysqlImpl, err := normal.GetImpl(SDID, config)
	if err != nil {
		return nil, err
	}
	return mysqlImpl.(Redis), nil
}
  • 如果结构提供者并没有提供上述 API,用户同样也可以直接调用,传入参数并获取对象。

对象获取 API

  • 多例(normal)

    autowire/normal/normal.go

    func GetImpl(sdID string, param interface{}) (interface{}, error) {}
    

    每次调用多例获取 API,将创建一个新对象。

  • 单例(singleton)

    autowire/singleton/singleton.go

    func GetImpl(sdID string) (interface{}, error) {}
    

    单例模型全局只拥有一个对象,通过 API 只能获取而不能创建,在框架启动时所有单例模型指针将基于配置/标签创建好。

关键代码

import(
	"github.com/alibaba/ioc-golang/autowire/normal"
	"github.com/alibaba/ioc-golang/autowire/singleton"
	"github.com/alibaba/ioc-golang/extension/normal/redis"
)


func (a *App) Run() {
  // 通过 normal 提供的全局 API,传递结构描述 ID 和配置结构,创建多例对象
	normalRedis, err := normal.GetImpl("github.com/alibaba/ioc-golang/extension/normal/redis.Impl", &redis.Config{
		Address: "localhost:6379",
		DB:      "0",
	})
  // 通过 redis 结构提供者定义好的 GetRedis 方法,传递配置结构,创建多例对象
	normalRedis2, err := redis.GetRedis(&redis.Config{
		Address: "localhost:6379",
		DB:      "0",
	})
  ...
}

func main() {
	if err := ioc.Load(); err != nil {
		panic(err)
	}
  // 通过 singleton 提供的全局 API,传递结构描述 ID 获取单例对象
	appInterface, err := singleton.GetImpl("main.App")
	if err != nil {
		panic(err)
	}
	app := appInterface.(*App)

	app.Run()
}

运行示例

需要确保您本地运行了redis,可通过 docker run -p6379:6379 redis:latest 快速启动一个。

例子会通过 API 获取 App 对象和 redis 对象,并调用 redis 对象提供的方法。

 cd example/get_impl_by_api/cmd
 go run .
  ___    ___     ____            ____           _                         
 |_ _|  / _ \   / ___|          / ___|   ___   | |   __ _   _ __     __ _ 
  | |  | | | | | |      _____  | |  _   / _ \  | |  / _` | | '_ \   / _` |
  | |  | |_| | | |___  |_____| | |_| | | (_) | | | | (_| | | | | | | (_| |
 |___|  \___/   \____|          \____|  \___/  |_|  \__,_| |_| |_|  \__, |
                                                                    |___/ 
Welcome to use ioc-golang!
[Boot] Start to load ioc-golang config
[Config] Load config file from ../conf/ioc_golang.yaml
Load ioc-golang config file failed. open ../conf/ioc_golang.yaml: no such file or directory
The load procedure is continue
[Boot] Start to load debug
[Debug] Debug mod is not enabled
[Boot] Start to load autowire
[Autowire Type] Found registered autowire type normal
[Autowire Struct Descriptor] Found type normal registered SD github.com/alibaba/ioc-golang/extension/normal/redis.Impl
[Autowire Type] Found registered autowire type singleton
[Autowire Struct Descriptor] Found type singleton registered SD main.App
get val =  db0

可看到打印出了 redis 中的数据。

6 - 使用基于monkey指针的调试功能

简介

本示例展示了 ioc-golang 框架提供的基于monkey指针的调试功能。

注入到接口的对象已经默认支持调试层。本例子针对注入到接口体指针的对象,可通过这种方式赋予其代理层运维能力。

基于monkey指针的调试功能对于程序性能有损耗,请您不要在追求性能的场景下开启调试能力。

本框架基于 AOP 的思路,为每个注册在框架的结构方法都封装了一组拦截器。基于这些拦截器,可以实现具有扩展性的调试功能。

调试能力包括:

  • 基于 ioc-debug 协议,暴露调试端口
  • 查看所有接口、实现、方法列表
  • 监听、修改任意方法的入参和返回值
  • 性能瓶颈分析【开发中】
  • 可观测性【开发中】

示例介绍

本示例实现了以下拓扑

debug

在这个例子中,App 结构会依此调用所有依赖对象,进而调用一个单例模型注入的 gRPC 客户端,该客户端发起网络请求,并获得结果。

我们将开启 debug 模式,通过 iocli 工具查看接口、实现、方法,并监听通过 gRPC Client 发送的所有请求和返回值。

运行示例

  1. 启动 grpc Server

    % cd example/denbug/grpc_server
    % go run .
    
  2. 新开一个终端,启动客户端。

    注意 GOARCH 环境变量和 ‘-gcflags="-N -l" -tags iocdebug’ 编译参数, amd机器无需指定 GOARCH 环境变量。

    正确在 ioc_golang.yaml 中开启debug模式后,会打印

    [Debug] Debug port is set to default :1999 的日志。

    % cd example/debug/cmd
    % GOARCH=amd64 go run -gcflags="-N -l" -tags iocdebug  .
      ___    ___     ____            ____           _                         
     |_ _|  / _ \   / ___|          / ___|   ___   | |   __ _   _ __     __ _ 
      | |  | | | | | |      _____  | |  _   / _ \  | |  / _` | | '_ \   / _` |
      | |  | |_| | | |___  |_____| | |_| | | (_) | | | | (_| | | | | | | (_| |
     |___|  \___/   \____|          \____|  \___/  |_|  \__,_| |_| |_|  \__, |
                                                                        |___/ 
    Welcome to use ioc-golang!
    [Boot] Start to load ioc-golang config
    [Config] Load default config file from ../conf/ioc_golang.yaml
    [Config] merge config map, depth: [0]
    [Boot] Start to load debug
    [Debug] Debug port is set to default :1999
    [Boot] Start to load autowire
    [Autowire Type] Found registered autowire type singleton
    [Autowire Struct Descriptor] Found type singleton registered SD github.com/alibaba/ioc-golang/example/debug/cmd/service2.Impl2
    [Autowire Struct Descriptor] Found type singleton registered SD github.com/alibaba/ioc-golang/example/debug/cmd/struct1.Struct1
    [Autowire Struct Descriptor] Found type singleton registered SD main.App
    [Autowire Struct Descriptor] Found type singleton registered SD github.com/alibaba/ioc-golang/example/debug/cmd/service1.Impl1
    [Autowire Struct Descriptor] Found type singleton registered SD github.com/alibaba/ioc-golang/example/debug/cmd/service2.Impl1
    [Autowire Type] Found registered autowire type grpc
    [Autowire Struct Descriptor] Found type grpc registered SD github.com/alibaba/ioc-golang/example/debug/api.HelloServiceClient
    [Debug] Debug server listening at :1999
    create conn target  localhost:8080
    App call grpc get: Hello laurence
    ExampleService1Impl1 call grpc get :Hello laurence_service1_impl1
    ExampleService2Impl1 call grpc get :Hello laurence_service2_impl1
    ExampleService2Impl2 call grpc get :Hello laurence_service2_impl2
    ExampleStruct1 call grpc get :Hello laurence_service1_impl1
    

    每隔 5s,所有的对象都会发起一次 gRPC 请求。

  3. 新开一个终端,查看所有接口、实现和方法。

    % iocli list
    github.com/alibaba/ioc-golang/example/debug/api.HelloServiceClient
    [SayHello]
    
    github.com/alibaba/ioc-golang/example/debug/cmd/service1.Impl1
    [Hello]
    
    github.com/alibaba/ioc-golang/example/debug/cmd/service2.Impl1
    [Hello]
    
    github.com/alibaba/ioc-golang/example/debug/cmd/service2.Impl2
    [Hello]
    
    github.com/alibaba/ioc-golang/example/debug/cmd/struct1.Struct1
    [Hello]
    
    main.App
    [Run]
    
  4. 监听 gRPC Client 的所有流量,每隔 5s 会打印出相关的请求、返回值信息。

    % iocli watch github.com/alibaba/ioc-golang/example/debug/api.HelloServiceClient  SayHello
    
    ========== On Call ==========
    github.com/alibaba/ioc-golang/example/debug/api.HelloServiceClient.SayHello()
    Param 1: (*context.emptyCtx)(0xc0000280e0)(context.Background)
    
    Param 2: (*api.HelloRequest)(0xc000260280)(name:"laurence")
    
    Param 3: ([]grpc.CallOption) (len=2 cap=2) {
    (grpc.MaxRecvMsgSizeCallOption) {
    MaxRecvMsgSize: (int) 1024
    },
    (grpc.MaxRecvMsgSizeCallOption) {
    MaxRecvMsgSize: (int) 1024
    }
    }
    
    
    ========== On Response ==========
    github.com/alibaba/ioc-golang/example/debug/api.HelloServiceClient.SayHello()
    Response 1: (*api.HelloResponse)(0xc000260380)(reply:"Hello laurence")
    
    Response 2: (interface {}) <nil>
    
    
    ========== On Call ==========
    github.com/alibaba/ioc-golang/example/debug/api.HelloServiceClient.SayHello()
    Param 1: (*context.emptyCtx)(0xc0000280e0)(context.Background)
    
    Param 2: (*api.HelloRequest)(0xc0003988c0)(name:"laurence_service1_impl1")
    
    Param 3: ([]grpc.CallOption) <nil>
    
    
    ========== On Response ==========
    github.com/alibaba/ioc-golang/example/debug/api.HelloServiceClient.SayHello()
    Response 1: (*api.HelloResponse)(0xc000398980)(reply:"Hello laurence_service1_impl1")
    
    Response 2: (interface {}) <nil>
    
    
    ========== On Call ==========
    github.com/alibaba/ioc-golang/example/debug/api.HelloServiceClient.SayHello()
    Param 1: (*context.emptyCtx)(0xc0000280e0)(context.Background)
    
    Param 2: (*api.HelloRequest)(0xc000260480)(name:"laurence_service2_impl1")
    
    Param 3: ([]grpc.CallOption) <nil>
    
    
    ========== On Response ==========
    github.com/alibaba/ioc-golang/example/debug/api.HelloServiceClient.SayHello()
    Response 1: (*api.HelloResponse)(0xc000260540)(reply:"Hello laurence_service2_impl1")
    
    Response 2: (interface {}) <nil>
    
    
    ========== On Call ==========
    github.com/alibaba/ioc-golang/example/debug/api.HelloServiceClient.SayHello()
    Param 1: (*context.emptyCtx)(0xc0000280e0)(context.Background)
    
    Param 2: (*api.HelloRequest)(0xc00041c200)(name:"laurence_service2_impl2")
    
    Param 3: ([]grpc.CallOption) <nil>
    
    
    ========== On Response ==========
    github.com/alibaba/ioc-golang/example/debug/api.HelloServiceClient.SayHello()
    Response 1: (*api.HelloResponse)(0xc00041c2c0)(reply:"Hello laurence_service2_impl2")
    
    Response 2: (interface {}) <nil>
    
    
    ========== On Call ==========
    github.com/alibaba/ioc-golang/example/debug/api.HelloServiceClient.SayHello()
    Param 1: (*context.emptyCtx)(0xc0000280e0)(context.Background)
    
    Param 2: (*api.HelloRequest)(0xc000260700)(name:"laurence_service1_impl1")
    
    Param 3: ([]grpc.CallOption) <nil>
    
    
    ========== On Response ==========
    github.com/alibaba/ioc-golang/example/debug/api.HelloServiceClient.SayHello()
    Response 1: (*api.HelloResponse)(0xc0002607c0)(reply:"Hello laurence_service1_impl1")
    
    Response 2: (interface {}) <nil>
    ...
    

小结

通过 Debug 能力,开发人员可以在测试环境内动态地监控流量,帮助排查问题。

也可以基于 ioc-golang 提供的拦截器层,注册任何自己期望的调试拦截器,扩展调试、可观测、运维能力。

7 - RPC 能力

使用 RPC 能力

简介

本示例展示了基于 IOC-golang 框架的 RPC 能力

在微服务开发过程中,暴露一些对象的方法给外部程序调用是一种常见的场景。

在 IOC-golang 的 RPC 能力的使用场景下,客户端可以直接注入下游接口的客户端存根,以方法调用的形式发起RPC请求。

关键代码

服务端

需要暴露的RPC服务结构使用 // +ioc:autowire:type=rpc 标注,例如 service.go:

import (
	"github.com/alibaba/ioc-golang/example/autowire_rpc/server/pkg/dto"
)

// +ioc:autowire=true
// +ioc:autowire:type=rpc

type ServiceStruct struct {
}

func (s *ServiceStruct) GetUser(name string, age int) (*dto.User, error) {
	return &dto.User{
		Id:   1,
		Name: name,
		Age:  age,
	}, nil
}

使用 iocli 工具生成相关代码。

sudo iocli gen

% tree
.
├── api
│   └── zz_generated.ioc_rpc_client_servicestruct.go
├── service.go
└── zz_generated.ioc.go

会在当前文件目录下生成 zz_generated.ioc.go 包含了服务提供者的结构描述信息。也会在当前目录下创建 api/ 文件夹,并创建当前结构的客户端存根文件 zz_generated.ioc_rpc_client_servicestruct.go

客户端

可以通过标签注入的方法,注入客户端存根,存根中给出下游地址。默认服务暴露端口为2022

import(
  "github.com/alibaba/ioc-golang/example/autowire_rpc/server/pkg/service/api"
)
// +ioc:autowire=true
// +ioc:autowire:type=singleton

type App struct {
	ServiceStruct *api.ServiceStructIOCRPCClient `rpc-client:",address=127.0.0.1:2022"`
}

func (a *App) Run() {
	for {
		time.Sleep(time.Second * 3)
		usr, err := a.ServiceStruct.GetUser("laurence", 23) // RPC调用
		if err != nil {
			panic(err)
		}
		fmt.Printf("get user = %+v\n", usr)
	}
}

运行示例

启动服务端

服务端需要确保引入对应服务提供者结构包: _ "github.com/alibaba/ioc-golang/example/autowire_rpc/server/pkg/service"

package main

import (
	"github.com/alibaba/ioc-golang"
	_ "github.com/alibaba/ioc-golang/example/autowire_rpc/server/pkg/service"
)

func main() {
	// start
	if err := ioc.Load(); err != nil {
		panic(err)
	}
	select {}
}

启动服务端进程,默认 rpc 服务监听 2022 端口

% cd server
% go run .
...
[negroni] listening on :2022

启动客户端

开启另一个终端,进入client目录,启动进程,不断发起调用,获得返回值。

% cd client
% go run .
...
get user = &{Id:1 Name:laurence Age:23}
get user = &{Id:1 Name:laurence Age:23}
get user = &{Id:1 Name:laurence Age:23}