14

我正在编写一些 Go 网络服务(也在 Go 中使用 http.ListenAndServe 实现网络服务器)。我有一个结构图,我想将其保存在内存中(数据大小约为 100Kb),以供不同的 HTTP 请求使用。

在 Go 中如何实现这一点?我正在考虑使用全局包变量或缓存系统(如 memcache/groupcache)。

4

2 回答 2

13

除了您已经收到的答案之外,请考虑使用接收者柯里化方法值http.HandlerFunc

如果您的数据是在流程开始之前加载的数据,您可以使用以下内容:

type Common struct {
    Data map[string]*Data
}

func NewCommon() (*Common, error) {
    // load data
    return c, err
}

func (c *Common) Root(w http.ResponseWriter, r *http.Request) {
    // handler
}

func (c *Common) Page(w http.ResponseWriter, r *http.Request) {
    // handler
}

func main() {
    common, err := NewCommon()
    if err != nil { ... }

    http.HandleFunc("/", common.Root)
    http.HandleFunc("/page", common.Page)

    http.ListenAndServe(...)
}

Common如果所有数据都是只读的,这会很好地工作。如果Common数据是读/写的,那么你会想要更多的东西:

type Common struct {
    lock sync.RWMutex
    data map[string]Data // Data should probably not have any reference fields
}

func (c *Common) Get(key string) (*Data, bool) {
    c.lock.RLock()
    defer c.lock.RUnlock()
    d, ok := c.data[key]
    return &d, ok
}

func (c *Common) Set(key string, d *Data) {
    c.lock.Lock()
    defer c.lock.Unlock()
    c.data[key] = *d
}

其余的基本相同,除了不是直接通过接收者的字段访问数据,而是通过 getter 和 setter 访问它们。在读取大部分数据的网络服务器中,您可能需要一个 RWMutex,以便可以同时执行读取操作。第二种方法的另一个优点是您已经封装了数据,因此如果您的应用程序有这样的需求,您可以在将来添加对 memcache 或 groupcache 或类似性质的透明写入和/或读取。

我真正喜欢将我的处理程序定义为对象上的方法的一件事是,它使对它们进行单元测试变得更加容易:您可以轻松定义一个表驱动的测试,其中包括您想要的值和您期望的输出,而不必搞砸与全局变量有关。

于 2013-08-30T01:35:10.287 回答
2

不要沉迷于过早的优化。定义一个 Go 包 API 来封装数据,然后您可以随时更改实现。例如,只是涂鸦,

package data

type Key struct {
    // . . .
}

type Data struct {
    // . . .
}

var dataMap map[Key]Data

func init() {
    dataMap = make(map[Key]Data)
}

func GetData(key Key) (*Data, error) {
    data := dataMap[key]
    return &data, nil
}
于 2013-08-28T12:54:41.303 回答