Skip to content

Instantly share code, notes, and snippets.

@momer
Last active February 7, 2024 00:44
Show Gist options
  • Star 35 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save momer/ac20357abd331e23b8ea to your computer and use it in GitHub Desktop.
Save momer/ac20357abd331e23b8ea to your computer and use it in GitHub Desktop.
A pattern I created to maintain connections to an RPC server in Go. Since net/rpc does not provide any methods for automatically reconnecting to an RPC server, I needed a work-around. Additionally, rpc.Client does not export any state variables (rpc.Client.shutdown and rpc.Client.closing) nor does it export the Client's Mutex. So, we wrap the rp…
package main
import (
"myapp/webserver/app/common"
"github.com/golang/glog"
"github.com/gorilla/mux"
"encoding/json"
"strconv"
"flag"
"fmt"
"log"
"net/http"
"net/rpc"
"time"
"sync"
)
type RpcConnection struct {
sync.Mutex
rpcClient *rpc.Client
}
var dataHost *string // endpoint for cache frontend service
var browserCacheControl *int
var router *mux.Router
var rpcConnection RpcConnection
func RpcConnect() (err error) {
rpcConnection.rpcClient, err = rpc.DialHTTP("tcp", *dataHost)
return err
}
// http://stackoverflow.com/a/6391185/1162491
func RpcConnectionCheck(rpcCallError error) {
var err error
if rpcConnection.rpcClient == nil {
log.Println("Restarting RPC Connection due to nil connection")
err = RpcConnect()
} else if rpcCallError == rpc.ErrShutdown || reflect.TypeOf(rpcCallError) == reflect.TypeOf((*rpc.ServerError)(nil)).Elem() {
log.Println("Restarting RPC Connection due to error")
rpcConnection.Lock()
err = RpcConnect()
rpcConnection.Unlock()
}
if err != nil {
log.Fatal("Unable to initialize connection to RPC")
}
}
func HttpInterceptor(w http.ResponseWriter, r *http.Request) {
startTime := time.Now()
router.ServeHTTP(w, r)
finishTime := time.Now()
elapsedTime := finishTime.Sub(startTime)
common.LogAccess(w, r, elapsedTime)
}
func main() {
dataHost = flag.String("dl", "localhost:9001", "dataaccess endpoint")
flag.Parse()
defer glog.Flush()
err := RpcConnect()
if err != nil {
log.Fatal("Unable to initialize connection to RPC")
}
router = mux.NewRouter()
router.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) {
accessCall := rpcConnection.rpcClient.Go(...)
replyCall := <-accessCall.Done
if replyCall.Error != nil {
http.Error(w, replyCall.Error.Error(), http.StatusInternalServerError)
// Return the error no matter what - you could write your own reattempt loop and try here
RpcConnectionCheck(replyCall.Error)
return
}
fmt.Fprintf(w, "success")
})
http.HandleFunc("/", HttpInterceptor)
http.ListenAndServe("0.0.0.0:"+*port, nil)
}
@chespinoza
Copy link

I'm wondering about rpcConnection.rpcClient.Go(...) what should be some valid args for this function call?

@dedo1911
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment