Go语言网络爬虫组件注册器
在讲解下载器接口设计时,我们介绍过组件注册方面的设计和组件注册器接口 Registrar,它声明在 module 包中。根据前面的接口描述,我们会让组件注册器按照类型存储已注册的组件。该接口的声明如下:
moduleTypeMap 表示一个双层的字典结构,其中第一层提供了分类型注册和获取组件实例集合的能力,而第二层则负责存储组件实例集合。
Registrar 接口的 Register 方法只需做两件事:检查参数和注册组件实例。在检查参数时,Register 方法用到了 module 包中的一些工具类方法和变量。该方法的实现如下:
如果所有检查都通过了,那么 Register 方法就会把组件实例存储在 moduleTypeMap 中。当然,肯定会在 rwlock 的保护之下操作 moduleTypeMap。
Unregister 方法会把与给定的组件 ID 对应的组件实例从 moduleTypeMap 删除掉。在真正进行查找和删除操作前,它会先通过调用 SplitMID 函数检查那个组件 ID 的合法性。
Get 方法的实现包含负载均衡的策略,并返回最“空闲”的那个组件实例:
为了让 *myRegistrar 类型成为 Registrar 接口的实现类型,还需要实现它的 GetAllByType、GetAll 和 Clear 方法。不过这几个方法的实现都非常简单,这里就不展示了。这里需要注意的是对 rwlock 字段的合理运用。
一旦把所有方法都编写好,下面这个函数就可以编译通过了:
//细件注册器的实现类型 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{}, } }
所有教程
- socket
- Python基础教程
- C#教程
- MySQL函数
- MySQL
- C语言入门
- C语言专题
- C语言编译器
- C语言编程实例
- GCC编译器
- 数据结构
- C语言项目案例
- C++教程
- OpenCV
- Qt教程
- Unity 3D教程
- UE4
- STL
- Redis
- Android教程
- JavaScript
- PHP
- Mybatis
- Spring Cloud
- Maven
- vi命令
- Spring Boot
- Spring MVC
- Hibernate
- Linux
- Linux命令
- Shell脚本
- Java教程
- 设计模式
- Spring
- Servlet
- Struts2
- Java Swing
- JSP教程
- CSS教程
- TensorFlow
- 区块链
- Go语言教程
- Docker
- 编程笔记
- 资源下载
- 关于我们
- 汇编语言
- 大数据
- 云计算
- VIP视频