Go Cache Server using gRPC and Kubernetes

ahmet aşık
4 min readMay 5, 2020

--

Summary

In this article, I demonstrate one of my project named gocache. Gocache is a simple in-memory caching tool and it provides Cache Server implementation which is using gRPC.

Before we start let’s highlight some points.

Prerequisites

See the below examples and gocache.

What is gRPC?

“gRPC is a modern open-source high-performance RPC framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in the last mile of distributed computing to connect devices, mobile applications and browsers to backend services.”[1]

What is Kubernetes?

Kubernetes (K8s) is an open-source system for automating deployment, scaling, and management of containerized applications. see this link

Gocache

Functions

NewCache(defaultLife, gbcInterval time.Duration) *Cache
Add(key, content interface{}, life time.Duration) (bool, string)
Get(key string) (interface{}, bool)
Set(key string, content interface{}, life time.Duration)
DeleteExpired()
ClearCache()

How to initialize cache?

cache:= NewCache(defautlCacheItemLife, garbeceCollectorWorkInterval)

it creates pointer, starts garbage collector and returns the pointer from now on you can use this pointer to anywhere and it uses a mutex to ensure that race condition will never occur.

cache.mutex.RLock()item, found := cache.items[key]if !found {
cache.mutex.RUnlock()
return nil, false
}
cache.mutex.RUnlock()return item.Content, true

Garbage Collector deletes expired items periodically

ticker := time.NewTicker(j.interval)
for {
select {
case <-ticker.C:
cache.DeleteExpired()
case <-j.stop:
ticker.Stop()
return
}
}

How to add to the cache?

cache.Add("key", {value as interface}, (Eternal or time.Duration))

How to get a cached item?

cache, success := cache.Get("key")

You can examine other functions and return values from the source.

So far briefly explained the gocache library so what else?

Gocache Server And Client

Gocache provides server enhancement that you can use as a cache server. It also provides client functions so that you can talk directly with the server.

It uses gRPC to communicate. You can find the proto file from this link. So right now it only needs port and cache instance.

cache := NewCache(5*time.Minute, 5*time.Second)serverConfig := ServerConfig{
CachePtr: cache,
Port: "10001",
}
err := gocache.NewCacheServer(serverConfig)

You can deploy to your gocache-server-example to your minikube by running deploy.sh. It only runs one instance. You can change deployment yaml to run more servers.

pods
services

How do you communicate with the servers?

Client Functions

GetCache(address, key string, cacheResponse interface{}) error
SaveCache(address string, cache AddCacheRequest) error
ClearCache(address string) error

The key point is the address. It can be IP address or we use Kubernetes env right now so we can use service discovery too.

Let’s add Cache

for the sake of our example, we defined the “gocache-server” as a service name so we can be able to use service discovery. Kubernetes handles the rest.

request := AddCacheRequest{Key: "key", Value: "value",Life: second}
err := gocache.SaveCache("gocache-server", request)

that’s it. Item has been cached. It uses gRPC inside of the SaveCache function and calls the gocache server protocols by using service discovery.

func SaveCache(address string, cache AddCacheRequest) error {cc, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
return err
}
defer cc.Close()
client := NewCacheServiceClient(cc)cacheBytes, bodyError := json.Marshal(&cache.Value)if bodyError != nil {
return bodyError
}
request := &SaveCacheRequest{Key: cache.Key, Value: cacheBytes, Life: cache.Life}_, clientError := client.SaveCache(context.Background(), request)
if clientError != nil {
return clientError
}
return nil
}

Other methods do a similar thing.

Let’s deploy client example too

The client example uses the echo framework to handle the HTTP requests. It has two endpoints. The first one “POST /cache” it gets any item from the request body and calls gocache-server by using SaveCache function. The second one gets cache from gocache-server if it’s not found it returns “service-response” as a static string.

run deploy.sh.
it will create client service and expose it like below.

copy that URL and run this curl

curl --location --request POST 'http://192.168.99.101:31989/cache' \
--header 'Content-Type: application/json' \
--data-raw '{
"key":"test",
"life":60,
"value": {
"a": 1,
"b": "b"
}
}'

it saves the value field’s content. Then run this curl

curl --location --request GET 'http://192.168.99.101:31989/cache/test'

it returns the same value in sixty seconds. Then GC will delete it if it’s not found in the cache, “GET /cache/test” returns service-response as a static value.

Thank you for your time to examine this post and the library. I hope that will be useful.

TODO

  • Worker pool that saves cache for you so you don’t have to wait for saving operation
  • Override Eternal Cached Item
  • Dynamic GC interval

Reference

  1. https://grpc.io/

--

--