Introduction
As shown in the title, DREAMEMO is a distributed cache with out-of-the-box, high-scalability, modular-design features.The groupcache implementation is referenced, and re-structured, specific module differentiation is as follows:
The main modules will be introduced in detail in the design module.
Quick Start
Install
Execute following command to install DREAMEMO:
go get github.com/B1NARY-GR0UP/dreamemo
Run with standalone mode
DREAMEMO provides a function dream.StandAlone
used default configuration to help user to start in standalone mode swiftly. All you need to do is configure the source.Getter
of the corresponding data source.
package main
import (
"context"
"fmt"
"net/http"
"github.com/B1NARY-GR0UP/dreamemo/common/constant"
"github.com/B1NARY-GR0UP/dreamemo/dream"
"github.com/B1NARY-GR0UP/dreamemo/guidance"
"github.com/B1NARY-GR0UP/dreamemo/source"
log "github.com/B1NARY-GR0UP/inquisitor/core"
"github.com/B1NARY-GR0UP/piano/core"
"github.com/B1NARY-GR0UP/piano/core/bin"
)
var db = map[string]string{
"binary": "dreamemo",
"hello": "world",
"ping": "pong",
}
func getFromDB(_ context.Context, key string) ([]byte, error) {
log.Info("Get from DB")
if v, ok := db[key]; ok {
return []byte(v), nil
}
return nil, fmt.Errorf("key %v is not exist", key)
}
// go run .
// curl localhost:8080/hello?key=ping
func main() {
dream.StandAlone(source.GetterFunc(getFromDB))
p := bin.Default(core.WithHostAddr(":8080"))
p.GET("/hello", func(ctx context.Context, pk *core.PianoKey) {
key := pk.Query("key")
g := guidance.GetGroup(constant.DefaultGroupName)
value, _ := g.Get(ctx, key)
pk.JSON(http.StatusOK, core.M{
key: value.String(),
})
})
p.Play()
}
Here in the form of a map
simulation data source database, and use PIANO HTTP framework as a front-end server instead of using other HTTP frameworks like Hertz, Gin, etc. You can start the server with go run .
and then simply visit the URL to retrieve the key.
go run .
curl localhost:8080/hello?key=ping
Run with cluster mode
DREAMEMO also provides a dream.Cluster
function that runs in Cluster mode using the default configuration. All you need to do is configure the address of the corresponding cluster node and the data source.
package main
import (
"context"
"fmt"
"net/http"
"github.com/B1NARY-GR0UP/dreamemo/common/constant"
"github.com/B1NARY-GR0UP/dreamemo/common/util"
"github.com/B1NARY-GR0UP/dreamemo/dream"
"github.com/B1NARY-GR0UP/dreamemo/guidance"
"github.com/B1NARY-GR0UP/dreamemo/source"
log "github.com/B1NARY-GR0UP/inquisitor/core"
"github.com/B1NARY-GR0UP/piano/core"
"github.com/B1NARY-GR0UP/piano/core/bin"
)
var db = map[string]string{
"binary": "dreamemo",
"hello": "world",
"ping": "pong",
}
func getFromDB(_ context.Context, key string) ([]byte, error) {
log.Info("Get from DB")
if v, ok := db[key]; ok {
return []byte(v), nil
}
return nil, fmt.Errorf("key %v is not exist", key)
}
// go run . --addrs=http://localhost:7246,http://localhost:7247,http://localhost:7248 --api
// go run . --addrs=http://localhost:7247,http://localhost:7248,http://localhost:7246
// go run . --addrs=http://localhost:7248,http://localhost:7246,http://localhost:7247
// curl localhost:8080/hello?key=ping
func main() {
addrs, api := util.ParseFlags()
e := dream.Cluster(source.GetterFunc(getFromDB), addrs...)
if api {
go func() {
p := bin.Default(core.WithHostAddr(":8080"))
p.GET("/hello", func(ctx context.Context, pk *core.PianoKey) {
key := pk.Query("key")
g := guidance.GetGroup(constant.DefaultGroupName)
value, _ := g.Get(ctx, key)
pk.JSON(http.StatusOK, core.M{
key: value.String(),
})
})
p.Play()
}()
}
e.Run()
}
Again, the database data source is simulated as map
, and three cache nodes are configured at localhost:7246
, localhost:7247
and localhost:7248
, the frontend server is configured on port 8080
. To retrieve the value of the key, run the following command and visit the URL:
go run . --addrs=http://localhost:7246,http://localhost:7247,http://localhost:7248 --api
go run . --addrs=http://localhost:7247,http://localhost:7248,http://localhost:7246
go run . --addrs=http://localhost:7248,http://localhost:7246,http://localhost:7247
curl localhost:8080/hello?key=ping
Custom Assemble
dream.StandAlone
and dream.Cluster
functions configured by DREAMEMO were used earlier, here we'll look at a more customized way of running the assembly.
- Configure Engine
Here we configure the engine, util.ParseFlags
is a utility method provided to parse flag arguments. We use app.WithHostAddr
to configure the engine to listen to and app.WithThrift0
to use Thrift as the serialization protocol and register other nodes.
addrs, api := util.ParseFlags()
e := server.NewEngine(app.WithHostAddr(addrs[0]), app.WithThrift0())
e.RegisterInstances(addrs...)
- Configure cache eliminate strategy
Configure LFU
l := lfu.NewLFUCore()
m := memo.NewMemo(l)
- Configure cache group
We configure the cache group name with guidance.WithGroupName
, configure Thrift as the serialization protocol with guidance.WithThrift1
, be consistent with the engine, and configure the data source with source.Getter
guidance.NewGroup(m, e, guidance.WithGroupName("hello"), guidance.WithThrift1(), guidance.WithGetter(source.GetterFunc(getFromDB)))
The complete code is as follows:
package main
import (
"context"
"fmt"
"net/http"
"github.com/B1NARY-GR0UP/dreamemo/app"
"github.com/B1NARY-GR0UP/dreamemo/app/server"
"github.com/B1NARY-GR0UP/dreamemo/common/util"
"github.com/B1NARY-GR0UP/dreamemo/guidance"
"github.com/B1NARY-GR0UP/dreamemo/memo"
"github.com/B1NARY-GR0UP/dreamemo/source"
"github.com/B1NARY-GR0UP/dreamemo/strategy/eliminate/lfu"
log "github.com/B1NARY-GR0UP/inquisitor/core"
"github.com/B1NARY-GR0UP/piano/core"
"github.com/B1NARY-GR0UP/piano/core/bin"
)
var db = map[string]string{
"binary": "dreamemo",
"hello": "world",
"ping": "pong",
}
func getFromDB(_ context.Context, key string) ([]byte, error) {
log.Info("Get from DB")
if v, ok := db[key]; ok {
return []byte(v), nil
}
return nil, fmt.Errorf("key %v is not exist", key)
}
// go run . --addrs=http://localhost:7246,http://localhost:7247,http://localhost:7248 --api
// go run . --addrs=http://localhost:7247,http://localhost:7248,http://localhost:7246
// go run . --addrs=http://localhost:7248,http://localhost:7246,http://localhost:7247
// curl localhost:8080/hello?key=ping
func main() {
addrs, api := util.ParseFlags()
e := server.NewEngine(app.WithHostAddr(addrs[0]), app.WithThrift0())
e.RegisterInstances(addrs...)
l := lfu.NewLFUCore()
m := memo.NewMemo(l)
guidance.NewGroup(m, e, guidance.WithGroupName("hello"), guidance.WithThrift1(), guidance.WithGetter(source.GetterFunc(getFromDB)))
if api {
go func() {
p := bin.Default(core.WithHostAddr(":8080"))
p.GET("/hello", func(ctx context.Context, pk *core.PianoKey) {
key := pk.Query("key")
g := guidance.GetGroup("hello")
value, _ := g.Get(ctx, key)
pk.JSON(http.StatusOK, core.M{
key: value.String(),
})
})
p.Play()
}()
}
e.Run()
}
Run and get the cache value with the following command:
go run . --addrs=http://localhost:7246,http://localhost:7247,http://localhost:7248 --api
go run . --addrs=http://localhost:7247,http://localhost:7248,http://localhost:7246
go run . --addrs=http://localhost:7248,http://localhost:7246,http://localhost:7247
curl localhost:8080/hello?key=ping
Design
Eliminate strategy
In terms of cache elimination strategy, DREAMEMO supports LRU and LFU cache elimination algorithms, and users can choose and assemble them by themselves:
- LRU
c := lru.NewLRUCore()
- LFU
c := lfu.NewLFUCore()
DREAMEMO also provides an interface for uses to extend other eliminate strategies. Just implement the interface and pass the object to memo.NewMemo
as follows:
type ICore interface {
Add(Key, Value)
Get(Key) (Value, bool)
Remove(Key)
Clear()
Name() string
}
Serialization
DREAMEMO supports both Thrift and Protobuf serialization protocols.The default is Protobuf, and to use Thrift, you need to enable Thrift in both the configuration engine and the cache group:
e := server.NewEngine(app.WithThrift0()) // engine
guidance.NewGroup(m, e, guidance.WithThrift1()) // group
Data Source
DREAMEMO provides a default configuration to use Redis as a data source:
s := redis.NewSource()
DREAMEMO also provides an interface to configure more data sources of your own:
type Getter interface {
Get(ctx context.Context, key string) ([]byte, error)
}
Consistent Hash
DREAMEMO, like groupcache, uses consistent hashing for distributed node selection, while also providing an interface to configure more policies.
TODO
DREAMEMO is still very young, and there are many changes that can be made to it. In the future, more node selection algorithms and quick configuration tools will be provided.
Summary
That's all for this article. Hope this can help you. I would be appreciate if you could give DREAMEMO a star.
If you have any questions, please leave them in the comments or as issues. Thanks for reading.
Top comments (0)