Возможность перезагрузить (reload) конфигурацию без перезапуска (restart) программы стало не излишеством, а производственной необходимостью. Например, такая функция есть в Nginx и Postgresql (не для всех настроек, но для большинства).
Основная проблема при решении подобных задач это обеспечить одновременный доступ для чтения и записи к общему объекту содержащему конфигурацию. Существует много способов сделать это. В Go идеально подойдёт подход с использованием мьютекса чтения/записи.
Во-первых, базовая структура конфигурации, глобальная (для пакета) переменная и аналогичная блокировка:
import (
"os"
"log"
"sync"
"syscall"
"os/signal"
"io/ioutil"
"encoding/json"
)
type Config struct {
Mode string
CacheSize int
}
var (
config *Config
configLock = new(sync.RWMutex)
)
Затем нужна функция, которая загружает конфигурацию:
func loadConfig(fail bool) {
file, err := ioutil.ReadFile("config.json")
if err != nil {
log.Println("open config: ", err)
if fail { os.Exit(1) }
}
temp := new(Config)
if err = json.Unmarshal(file, temp); err != nil {
log.Println("parse config: ", err)
if fail { os.Exit(1) }
}
configLock.Lock()
config = temp
configLock.Unlock()
}
Чтобы минимизировать продолжительность блокировки записи можно использовать временную переменную для хранения новых значений конфигурации.
Далее нужен способ доступа к текущей конфигурации:
func GetConfig() *Config {
configLock.RLock()
defer configLock.RUnlock()
return config
}
Теперь осталось сделать начальную нагрузку, а также вызвать перезагрузку. Для этого слушаем сигнал:
// go calls init on start
func init() {
loadConfig(true)
s := make(chan os.Signal, 1)
signal.Notify(s, syscall.SIGUSR2)
go func() {
for {
<-s
loadConfig(false)
log.Println("Reloaded")
}
}()
}