Go语言网络爬虫组件注册器
在讲解下载器接口设计时,我们介绍过组件注册方面的设计和组件注册器接口 Registrar,它声明在 module 包中。根据前面的接口描述,我们会让组件注册器按照类型存储已注册的组件。该接口的声明如下:
//细件注册器的实现类型
type myRegistrar struct {
//组件类型与对应组件实例的映射
moduleTypeMap map[Type]map[MID]Module
//组件注册专用读写锁
rwlock sync.RWMutex
}
在组件注册器的实现类型 myRegistrar 中只有两个字段,一个用于分类存储组件实例,另一个用于读写保护。由于注册和注销组件实例的动作肯定不会太频繁,所以这里简单地使用读写锁实施保护就足够了。
moduleTypeMap 表示一个双层的字典结构,其中第一层提供了分类型注册和获取组件实例集合的能力,而第二层则负责存储组件实例集合。
Registrar 接口的 Register 方法只需做两件事:检查参数和注册组件实例。在检查参数时,Register 方法用到了 module 包中的一些工具类方法和变量。该方法的实现如下:
func (registrar *myRegistrar) Register(module Module) (bool, error) {
if module == nil {
return false, errors.NewIllegalParameterError("nil module instance")
}
mid := module.ID()
parts, err := SplitMID(mid)
if err != nil {
return false, err
}
moduleType := legalLetterTypeMap[parts[0]]
if !CheckType(moduleType, module) {
errMsg := fmt.Sprintf("incorrect module type: %s", moduleType)
return false, errors.NewIllegalParameterError(errMsg)
}
//省略部分代码
}
前面已经介绍过 NewIllegalParameterError 和 SplitMID 函数。legalLetterTypeMap 变量是一个以组件类型字母为键、以组件类型为元素的字典,是前面介绍的 legalTypeLetterMap 的反向映射。CheckType 函数的功能是检查从组件 ID 解析出来的组件类型与组件实例的实际类型是否相符。
如果所有检查都通过了,那么 Register 方法就会把组件实例存储在 moduleTypeMap 中。当然,肯定会在 rwlock 的保护之下操作 moduleTypeMap。
Unregister 方法会把与给定的组件 ID 对应的组件实例从 moduleTypeMap 删除掉。在真正进行查找和删除操作前,它会先通过调用 SplitMID 函数检查那个组件 ID 的合法性。
Get 方法的实现包含负载均衡的策略,并返回最“空闲”的那个组件实例:
// Get用于获取一个指定类型的组件的实例。
//本函数会基于负载均衡策略返回实例
func (registrar *myRegistrar) Get(moduleType Type) (Module, error) {
modules, err := registrar.GetAllByType(moduleType)
if err != nil {
return nil, err
}
minScore := uint64(0)
var selectedModule Module
for _, module := range modules {
SetScore(module)
if err != nil {
return nil, err
}
score := module.Score()
if minScore == 0 || score < minScore {
selectedModule = module
minScore = score
}
}
return selectedModule, nil
}
该方法先调用注册器的 GetAllByType 方法以获得指定类型的组件实例的集合,然后在遍历它们时计算其评分,并找到评分最低者,最后返回。其中 SetScore 是一个工具类函数,它通过组件实例的 ScoreCalculator 方法获得它的评分计算器。若该方法返回 nil,则使用默认的计算函数。计算其评分后,再通过组件实例的 SetScore 方法设置评分。
为了让 *myRegistrar 类型成为 Registrar 接口的实现类型,还需要实现它的 GetAllByType、GetAll 和 Clear 方法。不过这几个方法的实现都非常简单,这里就不展示了。这里需要注意的是对 rwlock 字段的合理运用。
一旦把所有方法都编写好,下面这个函数就可以编译通过了:
//用于创建一个组件注册器的实例
func NewRegistrar() Registrar {
return &myRegistrar{
moduleTypeMap: map[Type]map[MID]Module{},
}
}
还没有评论,来说两句吧...