面试题答案
一键面试设计思路
- 配置加载:从文件(如JSON、YAML等)或环境变量中读取配置信息,然后解析并转换为
map[string]interface{}
结构。不同的配置格式需要不同的解析库,如encoding/json
用于JSON,gopkg.in/yaml.v2
用于YAML。 - 配置修改:提供函数,根据配置路径(类似
database.master.pool.size
)来定位并修改map
中的值。 - 配置验证:针对不同的配置部分定义验证规则,例如数据库连接字符串的格式验证,连接池大小的范围验证等。
- 并发处理:使用
sync.RWMutex
来保证配置在并发读取和修改时的线程安全。读操作使用读锁,写操作使用写锁。
关键代码示例
package main
import (
"encoding/json"
"fmt"
"sync"
)
// ConfigManager 配置管理结构体
type ConfigManager struct {
config map[string]interface{}
mutex sync.RWMutex
}
// NewConfigManager 创建新的配置管理实例
func NewConfigManager() *ConfigManager {
return &ConfigManager{
config: make(map[string]interface{}),
}
}
// LoadConfig 加载配置
func (cm *ConfigManager) LoadConfig(data []byte) error {
cm.mutex.Lock()
defer cm.mutex.Unlock()
return json.Unmarshal(data, &cm.config)
}
// GetValue 根据路径获取配置值
func (cm *ConfigManager) GetValue(path string) (interface{}, bool) {
cm.mutex.RLock()
defer cm.mutex.RUnlock()
keys := splitPath(path)
current := cm.config
for _, key := range keys {
value, exists := current[key]
if!exists {
return nil, false
}
if len(keys) == 1 {
return value, true
}
if subMap, ok := value.(map[string]interface{}); ok {
current = subMap
} else {
return nil, false
}
}
return nil, false
}
// SetValue 根据路径设置配置值
func (cm *ConfigManager) SetValue(path string, value interface{}) {
cm.mutex.Lock()
defer cm.mutex.Unlock()
keys := splitPath(path)
current := cm.config
for i, key := range keys {
if i == len(keys)-1 {
current[key] = value
return
}
subMap, exists := current[key]
if!exists {
subMap = make(map[string]interface{})
current[key] = subMap
}
var ok bool
current, ok = subMap.(map[string]interface{})
if!ok {
return
}
}
}
// 拆分路径
func splitPath(path string) []string {
return strings.Split(path, ".")
}
// Validate 验证配置
func (cm *ConfigManager) Validate() bool {
// 示例验证:假设数据库主库连接池大小需大于0
size, exists := cm.GetValue("database.master.pool.size")
if!exists {
return false
}
if poolSize, ok := size.(float64); ok && poolSize <= 0 {
return false
}
return true
}
并发问题处理
- 读操作:使用
sync.RWMutex
的读锁(RLock
),允许多个读操作同时进行,提高并发读取性能。例如在GetValue
方法中使用读锁。 - 写操作:使用
sync.RWMutex
的写锁(Lock
),写操作时会阻止其他读和写操作,保证数据一致性。例如在LoadConfig
和SetValue
方法中使用写锁。
通过这种方式,既保证了配置在并发环境下的安全访问,又尽可能提高了读操作的并发性能。