使用Golang实现一套流程可配置,适用于广告、推荐系统的业务性框架——构建

2023-08-09 18:49:21 浏览数 (2)

在这个框架中,构建可以分为两部分。一是对象的构建,二是关系的构建。

对象的构建

自动构建

自动构建是指框架依据配置文件,自行创建出其描述的对象。 在自动构建前,我们需要向对象工厂注册各个自定义的类型。比如example_mix例子中

代码语言:javascript复制
func main() {
	factory := factory.NewFactory()
	factory.Register(reflect.TypeOf(examplelayera.ExampleA1Handler{}))
	factory.Register(reflect.TypeOf(examplelayera.ExampleA2Handler{}))
	factory.Register(reflect.TypeOf(examplelayera.ExampleADivider{}))
	……

Factory在底层维护了一个类型名和其反射Type的映射。

代码语言:javascript复制
func (f *Factory) Register(concreteType reflect.Type) (err error) {
	concreteName := concreteType.Name()
	if _, ok := f.concretesType[concreteName]; !ok {
		f.concretesType[concreteName] = concreteType
	} else {
		err = fmt.Errorf(concreteName   " is already registered.Please modify name.")
		panic(err.Error())
	}
	return nil
}

后续,框架在读取配置文件的过程中,会根据type字段的值构建对象。

代码语言:javascript复制
func (f *Factory) Create(concreteTypeName string, configure []byte, constructorInterface any) (concrete any, err error) {
	concreteType, ok := f.concretesType[concreteTypeName]
	if !ok {
		err = fmt.Errorf("concrete name not found for %s", concreteTypeName)
		panic(err.Error())
	}

	concrete = reflect.New(concreteType).Interface()
	concreteInterface, ok := concrete.(frame.ConcreteInterface)
	if !ok || concreteInterface == nil {
		err = fmt.Errorf("concrete %s conver to ConcreteInterface error", concreteTypeName)
		panic(err.Error())
	}

	constructorSetterInterface, ok := concrete.(frame.ConstructorSetterInterface)
	if ok && constructorSetterInterface != nil && constructorInterface != nil {
		constructorSetterInterface.SetConstructorInterface(constructorInterface)
	}

	loadConfigFromMemoryInterface, ok := concrete.(frame.LoadConfigFromMemoryInterface)
	if ok && loadConfigFromMemoryInterface != nil {
		err := loadConfigFromMemoryInterface.LoadConfigFromMemory(configure)
		if err != nil {
			err = fmt.Errorf("concrete LoadConfigFromMemory error for %s .error: %v", concreteTypeName, err)
			panic(err.Error())
		}
	}

	loadEnvironmentConfInterface, ok := concrete.(frame.LoadEnvironmentConfInterface)
	if ok && loadEnvironmentConfInterface != nil {
		err := loadEnvironmentConfInterface.LoadEnvironmentConf()
		if err != nil {
			err = fmt.Errorf("concrete LoadEnvironmentConf error for %s .error: %v", concreteTypeName, err)
			panic(err.Error())
		}
	}

	concreteName := concreteInterface.Name()
	if concreteName == "" {
		err = fmt.Errorf("concrete's Name is empty for %s", concreteTypeName)
		panic(err.Error())
	}
	if _, ok := f.concretes[concreteName]; !ok {
		f.concretes[concreteName] = concreteInterface
	} else {
		err = fmt.Errorf("concrete's Name  %s has already been used, type is %s", concreteName, concreteTypeName)
		panic(err.Error())
	}
	return
}

被自动构建的对象会自动保存起来,并通过下面的方法获取

代码语言:javascript复制
func (f *Factory) Get(concreteName string) (frame.ConcreteInterface, error) {
	if concrete, ok := f.concretes[concreteName]; ok {
		return concrete, nil
	} else {
		return nil, nil
	}
}

举个例子,配置文件目录下存在layer_center.yaml文件

代码语言:javascript复制
# layer_center.yaml
type: LayerCenter
name: layer_center
layers: 
  - layer_a
  - layer_b

构建器会通过Get方法检查名字为layer_center的组件是否存在。如果不存在,就调用Create方法创建type为LayerCenter、名字为layer_center的组件。LayerCenter在创建后会自动读取上述配置,发现其layers下有两个组件layer_a和layer_b。然后会检查这两个组件是否存在。如果不存在,则会在构建器中通过组件名,寻找对应的配置文件——这就要求组件名和其配置名强一致。比如layer_a的配置名为layer_a.yaml,layer_b的配置名为layer_b.yaml。

代码语言:javascript复制
# layer_a.yaml
name: layer_a
type: Layer
divider: ExampleADivider
handlers: 
  - ExampleA1Handler
  - ExampleA2Handler
代码语言:javascript复制
# layer_b.yaml
name: layer_b
type: Layer
divider: ExampleBDivider
handlers: 
  - ExampleB1Handler
  - ExampleB2Handler

这个创建过程通过下面函数实现

代码语言:javascript复制
func (c *Constructor) createConcreteByObjectName(name string) error {
	confPath, err := c.concreteConfManager.GetConfPath(name)
	if err != nil {
		return err
	}

	data, err := os.ReadFile(confPath)
	if err != nil {
		return err
	}

	var constructorType ConstructorType
	if err := yaml.Unmarshal(data, &constructorType); err != nil {
		return err
	}

	switch constructorType.Type {
	case TypeNameHandler:
		_, err := c.handlerConstructorInterface.GetHandler(name)
		if err != nil {
			return c.handlerConstructorInterface.CreateHandlerWithConfPath(confPath)
		}
	case TypeNameDivider:
		_, err := c.dividerConstructorInterface.GetDivider(name)
		if err != nil {
			return c.dividerConstructorInterface.CreateDividerWithConfPath(confPath)
		}
	case TypeNameLayer:
		_, err := c.layerConstructorInterface.GetLayer(name)
		if err != nil {
			return c.layerConstructorInterface.CreateLayerWithConfPath(confPath)
		}
	case TypeNameLayerCenter:
		_, err := c.layerCenterConstructorInterface.GetLayerCenter(name)
		if err != nil {
			return c.layerCenterConstructorInterface.CreateLayerCenterWithConfPath(confPath)
		}
	case TypeNameHandlerGroup:
		_, err := c.handlerGroupConstructorInterface.GetHandlerGroup(name)
		if err != nil {
			return c.handlerGroupConstructorInterface.CreateHandlerGroupWithConfPath(confPath)
		}
	case TypeNameAsyncHandlerGroup:
		_, err := c.asyncHandlerGroupConstructorInterface.GetAsyncHandlerGroup(name)
		if err != nil {
			return c.asyncHandlerGroupConstructorInterface.CreateAsyncHandlerGroupWithConfPath(confPath)
		}
	default:
		err := c.createConcreteByTypeName(constructorType.Type, data)
		if err != nil {
			return err
		}
		return nil
	}

	return fmt.Errorf("class name  %s not found", name)
}

对于框架自有组件,如LayerCenter、HandlerGroup等,它们会由其构建器构建。对于其他自定义的组件,比如自定义的各种Handler,则通过default逻辑中createConcreteByTypeName实现。

代码语言:javascript复制
func (c *Constructor) createConcreteByTypeName(name string, data []byte) error {
	if strings.HasSuffix(name, TypeNameAsyncHandlerGroup) {
		_, err := c.asyncHandlerGroupConstructorInterface.GetAsyncHandlerGroup(name)
		if err != nil {
			asyncHandlerGroupInterface, err := c.Create(name, data, c)
			if err != nil {
				return err
			}
			if namedInterface, ok := asyncHandlerGroupInterface.(frame.ConcreteInterface); ok {
				return c.asyncHandlerGroupConstructorInterface.RegisterAsyncHandlerGroup(namedInterface.Name(), asyncHandlerGroupInterface.(frame.AsyncHandlerGroupBaseInterface))
			} else {
				return c.asyncHandlerGroupConstructorInterface.RegisterAsyncHandlerGroup(name, asyncHandlerGroupInterface.(frame.AsyncHandlerGroupBaseInterface))
			}
		}
		return nil
	}
	if strings.HasPrefix(name, TypeNameHandlerGroup) {
		_, err := c.handlerGroupConstructorInterface.GetHandlerGroup(name)
		if err != nil {
			handlerGroupInterface, err := c.Create(name, data, c)
			if err != nil {
				return err
			}
			if namedInterface, ok := handlerGroupInterface.(frame.ConcreteInterface); ok {
				return c.handlerGroupConstructorInterface.RegisterHandlerGroup(namedInterface.Name(), handlerGroupInterface.(frame.HandlerGroupBaseInterface))
			} else {
				return c.handlerGroupConstructorInterface.RegisterHandlerGroup(name, handlerGroupInterface.(frame.HandlerGroupBaseInterface))
			}
		}
		return nil
	}
	if strings.HasSuffix(name, TypeNameDivider) {
		_, err := c.dividerConstructorInterface.GetDivider(name)
		if err != nil {
			dividerInterface, err := c.Create(name, data, c)
			if err != nil {
				return err
			}
			if namedInterface, ok := dividerInterface.(frame.ConcreteInterface); ok {
				return c.dividerConstructorInterface.RegisterDivider(namedInterface.Name(), dividerInterface.(frame.DividerBaseInterface))
			} else {
				return c.dividerConstructorInterface.RegisterDivider(name, dividerInterface.(frame.DividerBaseInterface))
			}
		}
		return nil
	}
	if strings.HasSuffix(name, TypeNameHandler) {
		_, err := c.handlerConstructorInterface.GetHandler(name)
		if err != nil {
			handlerInterface, err := c.Create(name, data, c)
			if err != nil {
				return err
			}
			if namedInterface, ok := handlerInterface.(frame.ConcreteInterface); ok {
				return c.handlerConstructorInterface.RegisterHandler(namedInterface.Name(), handlerInterface.(frame.HandlerBaseInterface))
			} else {
				return c.handlerConstructorInterface.RegisterHandler(name, handlerInterface.(frame.HandlerBaseInterface))
			}
		}
		return nil
	}
	if strings.HasSuffix(name, TypeNameLayer) {
		_, err := c.layerConstructorInterface.GetLayer(name)
		if err != nil {
			layerInterface, err := c.Create(name, data, c)
			if err != nil {
				return err
			}
			if namedInterface, ok := layerInterface.(frame.ConcreteInterface); ok {
				return c.layerConstructorInterface.RegisterLayer(namedInterface.Name(), layerInterface.(frame.LayerBaseInterface))
			} else {
				return c.layerConstructorInterface.RegisterLayer(name, layerInterface.(frame.LayerBaseInterface))
			}
		}
		return nil
	}
	if strings.HasSuffix(name, TypeNameLayerCenter) {
		_, err := c.layerCenterConstructorInterface.GetLayerCenter(name)
		if err != nil {
			layerCenterInterface, err := c.Create(name, data, c)
			if err != nil {
				return err
			}
			if namedInterface, ok := layerCenterInterface.(frame.ConcreteInterface); ok {
				return c.layerCenterConstructorInterface.RegisterLayerCenter(namedInterface.Name(), layerCenterInterface.(frame.LayerCenterBaseInterface))
			} else {
				return c.layerCenterConstructorInterface.RegisterLayerCenter(name, layerCenterInterface.(frame.LayerCenterBaseInterface))
			}
		}
		return nil
	}

	return fmt.Errorf("object name  %s not found", name)
}

在底层,我们需要设计一种规则用于标志这个自定义组件是哪个框架基础组件的子类。这儿就引出这个框架的第二个强制性约定——自定义类型的名称需要以框架基础组件名结尾。比如自定义的ExampleA1Handler是以Handler结尾,这样在底层我们就知道将其构造成一个Handler对象。 所有的自动构建,都依赖于配置文件。于是我们设计了ConcreteConfManager来遍历配置文件目录,这个目录在我们创建构建器时传入的。

代码语言:javascript复制
	……
	runPath, errGetWd := os.Getwd()
	if errGetWd != nil {
		fmt.Printf("%v", errGetWd)
		return
	}
	concretePath := path.Join(runPath, "conf")
	constructor := constructorbuilder.BuildConstructor(factory, concretePath)
	……

然后我们告诉构建器初始组件名,它就会自动像爬虫一样,通过配置文件和之前注册的反射类型,将对象和关系都构建出来。

代码语言:javascript复制
	……
	mainProcess := "layer_center"

	run(constructor, mainProcess)
}

func run(constructor *constructor.Constructor, mainProcess string) {
	if err := constructor.CreateConcrete(mainProcess); err != nil {
		fmt.Printf("%v", err)
	}

单一对象

单一对象是指一个类型只有一个对象。 我们在写业务时,往往需要一个简单的逻辑单元处理一个单一的事情,即在任何场景下,它只需要存在一份——属性一样,且不会改变。 这个时候,对象名变得不太重要。我们只要让其Name方法返回其类型名,而不需要再搞一个配置文件,就能实现自动构建。这种场景占绝大多数。

代码语言:javascript复制
func (e *ExampleA1Handler) Name() string {
	return reflect.TypeOf(*e).Name()
}

多个对象

有时候,我们希望一个类可以处理一种逻辑,但是其在不同场景下,其属性不一样。这样我们就需要通过配置文件来描述它——描述它不同的名字和对应的属性。比如下面的例子就是从配置文件中读取了名字和其相应属性。

代码语言:javascript复制
package samplehandler

import (
	"fmt"
	"ghgroups/frame"
	ghgroupscontext "ghgroups/frame/ghgroups_context"

	"gopkg.in/yaml.v2"
)

// 自动构建handler,它会自动从配置文件中读取配置,然后根据配置构建handler
// 因为系统使用名称作为唯一检索键,所以自动构建handler在构建过程中,就要被命名,而名称应该来源于配置文件
// 这就要求配置文件中必须有一个名为name的字段,用于指定handler的名称
// 下面例子中confs配置不是必须的,handler的实现者,需要自行解析配置文件,以确保Name方法返回的名称与配置文件中的name字段一致

type SampleAutoConstructHandlerConf struct {
	Name  string                              `yaml:"name"`
	Confs []SampleAutoConstructHandlerEnvConf `yaml:"confs"`
}

type SampleAutoConstructHandlerEnvConf struct {
	Env         string                                 `yaml:"env"`
	RegionsConf []SampleAutoConstructHandlerRegionConf `yaml:"regions_conf"`
}

type SampleAutoConstructHandlerRegionConf struct {
	Region          string `yaml:"region"`
	AwsRegion       string `yaml:"aws_region"`
	AccessKeyId     string `yaml:"access_key_id"`
	SecretAccessKey string `yaml:"secret_access_key"`
	IntKey          int32  `yaml:"int_key"`
}

type SampleAutoConstructHandler struct {
	frame.HandlerBaseInterface
	frame.LoadConfigFromMemoryInterface
	conf SampleAutoConstructHandlerConf
}

func NewSampleAutoConstructHandler() *SampleAutoConstructHandler {
	return &SampleAutoConstructHandler{}
}

// ///
// LoadConfigFromMemoryInterface
func (s *SampleAutoConstructHandler) LoadConfigFromMemory(configure []byte) error {
	sampleHandlerConf := new(SampleAutoConstructHandlerConf)
	err := yaml.Unmarshal([]byte(configure), sampleHandlerConf)
	if err != nil {
		return err
	}
	s.conf = *sampleHandlerConf
	return nil
}

// ///
// ConcreteInterface
func (s *SampleAutoConstructHandler) Name() string {
	return s.conf.Name
}

// ///
// HandlerBaseInterface
func (s *SampleAutoConstructHandler) Handle(*ghgroupscontext.GhGroupsContext) bool {
	fmt.Sprintln(s.conf.Name)
	return true
}

// ///

于是我们在不同组件关系中,通过该类型的不同对象名来组织关系,从而实现一套逻辑,不同配置的应用场景。 比如下面的两个配置,描述了同一个类型的不同配置

代码语言:javascript复制
# sample_handler_a.yaml
type: SampleAutoConstructHandler
name: sample_handler_a
confs: 
  - env: Online
    regions_conf:
    - region: us-east-1
      aws_region: us-east-1
      int_key: 1
    - region: us-east-2
      aws_region: us-east-2
代码语言:javascript复制
# sample_handler_b.yaml
type: SampleAutoConstructHandler
name: sample_handler_b
confs: 
  - env: Online
    regions_conf:
    - region: us-east-1
      aws_region: us-east-1
      int_key: 2
    - region: us-east-2
      aws_region: us-east-2

然后在下面关系中予以区分调用

代码语言:javascript复制
name: Sample
divider: divider_sample_a
handlers: 
  - handler_sample_a
  - handler_sample_b

手工构建

如果由于某些原因,自动构建不能满足需求,我们可以手工构建对象。这个时候我们不需要向对象工厂注册其反射(Register),只要手工构建出对象后,调用工厂的注册对象方法(比如RegisterHandler),告诉框架某个名字的组件存在。这样在构建关系时,就会自动识别——这就要求手工构建要在自动构建之前完成,否则框架无法识别它们。

代码语言:javascript复制
package samplehandler

import (
	"fmt"
	"ghgroups/frame"
	ghgroupscontext "ghgroups/frame/ghgroups_context"
)

// 这是相对简单的handler,它只用实现HandlerInterface两个接口
// 系统使用名称作为唯一检索键,通过构造不同的对象拥有不同的名字,可以在系统中有多个该名字的handler实例,即一个类型(struct)可以有多个该名字的handler实例

type SampleSelfConstructHandlerMulti struct {
	frame.HandlerBaseInterface
	name string
}

func NewSampleSelfConstructHandlerMulti(name string) *SampleSelfConstructHandlerMulti {
	return &SampleSelfConstructHandlerMulti{
		name: name,
	}
}

// ///
// ConcreteInterface
func (s *SampleSelfConstructHandlerMulti) Name() string {
	return s.name
}

// ///
// HandlerBaseInterface
func (s *SampleSelfConstructHandlerMulti) Handle(*ghgroupscontext.GhGroupsContext) bool {
	fmt.Sprintln(s.Name())
	return true
}

// ///

注册代码如下

代码语言:javascript复制
	……
	constructor := utils.BuildConstructor("")

	sampleSelfConstructHandlerMultiNameA := "sample_self_construct_handler_multi_a"
	sampleSelfConstructHandlerMultiA := NewSampleSelfConstructHandlerMulti(sampleSelfConstructHandlerMultiNameA)
	constructor.RegisterHandler(sampleSelfConstructHandlerMultiA.Name(), sampleSelfConstructHandlerMultiA)

	sampleSelfConstructHandlerMultiNameB := "sample_self_construct_handler_multi_b"
	sampleSelfConstructHandlerMultiB := NewSampleSelfConstructHandlerMulti(sampleSelfConstructHandlerMultiNameB)
	constructor.RegisterHandler(sampleSelfConstructHandlerMultiB.Name(), sampleSelfConstructHandlerMultiB)
	……

关系的构建

自动构建

关系的自动构建依赖于配置文件的描述。 每个组件在读取配置文件后,会构建不存在的子组件,并加载其配置。 在这个递归过程中,整个关系网就会被构建起来。 比如LayerCenter的构建过程

代码语言:javascript复制
func (l *LayerCenter) LoadConfigFromMemory(configure []byte) error {
	var layerCenterConf LayerCenterConf
	err := yaml.Unmarshal(configure, &layerCenterConf)
	if err != nil {
		return err
	}
	l.conf = layerCenterConf
	return l.init()
}

func (l *LayerCenter) init() error {
	for _, layerName := range l.conf.Layers {
		if err := l.constructorInterface.CreateConcrete(layerName); err != nil {
			return err
		}

		if someInterface, err := l.constructorInterface.GetConcrete(layerName); err != nil {
			return err
		} else {
			if layerBaseInterface, ok := someInterface.(frame.LayerBaseInterface); !ok {
				return fmt.Errorf("layer %s is not frame.LayerBaseInterface", layerName)
			} else {
				l.layers = append(l.layers, layerBaseInterface)
			}
		}
	}

	return nil
}

其在底层会创建Layer对象,进而触发Layer子组件的构建

代码语言:javascript复制
func (l *Layer) LoadConfigFromMemory(configure []byte) error {
	var layerConf LayerConf
	err := yaml.Unmarshal(configure, &layerConf)
	if err != nil {
		return err
	}
	l.conf = layerConf
	return l.init()
}

func (l *Layer) init() error {
	if l.handlers == nil {
		l.handlers = make(map[string]frame.HandlerBaseInterface)
	}
	err := l.initDivider(l.conf.Divider)
	if err != nil {
		return err
	}

	err = l.initHandlers(l.conf.Handlers)
	if err != nil {
		return err
	}
	return nil
}

func (l *Layer) initDivider(dividerName string) error {
	if err := l.constructorInterface.CreateConcrete(dividerName); err != nil {
		return err
	}

	if someInterface, err := l.constructorInterface.GetConcrete(dividerName); err != nil {
		return err
	} else {
		if dividerInterface, ok := someInterface.(frame.DividerBaseInterface); !ok {
			return fmt.Errorf("handler %s is not frame.DividerBaseInterface", dividerName)
		} else {
			err = l.SetDivider(dividerName, dividerInterface)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func (l *Layer) initHandlers(handlersName []string) error {
	for _, handlerName := range handlersName {

		if err := l.constructorInterface.CreateConcrete(handlerName); err != nil {
			return err
		}

		if someInterface, err := l.constructorInterface.GetConcrete(handlerName); err != nil {
			return err
		} else {
			if handlerInterface, ok := someInterface.(frame.HandlerBaseInterface); !ok {
				return fmt.Errorf("handler %s is not frame.HandlerBaseInterface", handlerName)
			} else {
				err = l.AddHandler(handlerName, handlerInterface)
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}

手工构建

手工构建是不推荐的形式,因为它可能会让维护成本上升,但是框架仍然支持这种形式。 这儿只是做个简单介绍,如下例子

代码语言:javascript复制
	constructor := utils.BuildConstructor("")

	layerCenter := NewLayerCenter(constructor)
	testLayer := layer.NewLayer("test_layer", constructor)
	layerCenter.Add(testLayer)

layerCenter通过Add新增了一个Layer。 更多具体例子可以参考源码中的单测文件。

0 人点赞