Created
January 17, 2013 07:29
-
-
Save icub3d/4554320 to your computer and use it in GitHub Desktop.
A complete example of creating a service in go using RPC.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// greeter/config.go | |
package main | |
import ( | |
"encoding/json" | |
"fmt" | |
"io/ioutil" | |
"sync" | |
) | |
// This is the config file we read from. Our flags from the command | |
// line will set this appropriately. | |
var ConfigFile string | |
// This is the system configuration. | |
var Config map[string]string | |
// This is the lock we'll use for the config when we are updating it. | |
var ConfigLock sync.RWMutex | |
// Reads the contents of ConfigFile and makes a map[string]string out | |
// of it which is saved to Config. The contents of the file should be | |
// a json object with both they keys and values as strings. | |
func ReadConfig() error { | |
// Read the contents of the config file. | |
bytes, err := ioutil.ReadFile(ConfigFile) | |
if err != nil { | |
return fmt.Errorf("reading config file: %v", err) | |
} | |
// Lock the config so we can change it. | |
ConfigLock.Lock() | |
defer ConfigLock.Unlock() | |
// Change the config. | |
Config = make(map[string]string) | |
err = json.Unmarshal(bytes, &Config) | |
if err != nil { | |
return fmt.Errorf("unmarshalling config: %v", err) | |
} | |
return nil | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"greeting": "Howdy Doodie!" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// greeter/flags.go | |
package main | |
import ( | |
"flag" | |
) | |
func init() { | |
// Prepare the flags to be read from the command line. | |
flag.StringVar(&ConfigFile, "config", "/tmp/config.json", | |
"The location of the configuration file.") | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# /etc/systemd/system/greeter.service | |
[Unit] | |
Description=A simple greeting service | |
After=network.target | |
[Service] | |
ExecStart=/home/jmarsh/go/bin/greeter | |
ExecStop=/home/jmarsh/go/bin/servicer stop | |
ExecReload=/home/jmarsh/go/bin/servicer reload |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// greeter/main.go | |
package main | |
import ( | |
"fmt" | |
"flag" | |
"net" | |
"net/http" | |
) | |
// The following is the handler for the request "/". | |
func printGreeting(w http.ResponseWriter, r *http.Request) { | |
if greeting, ok := Config["greeting"]; ok { | |
fmt.Fprintln(w, greeting) | |
} else { | |
fmt.Fprintln(w, "Hello, default greeting!") | |
} | |
} | |
func main() { | |
flag.Parse() | |
// Load the initial configuration. | |
err := ReadConfig() | |
if err != nil { | |
fmt.Println("reading config:", err) | |
return | |
} | |
// Start the listener | |
listener, err := net.Listen("tcp", "127.0.0.1:9000") | |
if err != nil { | |
fmt.Println("opening listening socket:", err) | |
return | |
} | |
// Initialize the RPC Interface. | |
err = StartRPC(listener) | |
if err != nil { | |
fmt.Println("StartRPC:", err) | |
return | |
} | |
// Create your web app as normal. | |
http.HandleFunc("/", printGreeting) | |
// Start listening http request. If I were using fastcgi, I'd simply | |
// do the same but with fcgi.Serve. | |
err = http.Serve(listener, nil) | |
if err != nil { | |
fmt.Println("serve:", err) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// greeter/rpc.go | |
package main | |
import ( | |
"fmt" | |
"log" | |
"net" | |
"net/http" | |
"net/rpc" | |
) | |
// This is our RPC object we'll use to do things to the http service. | |
type ServiceHandler struct { | |
// The listener we'll close with when stopping. | |
listener net.Listener | |
} | |
// This singles the server to stop if it's running. incoming ars are | |
// nil here because we don't use them and the replay is simply a | |
// boolean to signify success or failure. | |
func (s *ServiceHandler) Stop(args int, reply *bool) error { | |
// We should only stop it if we have one. | |
if s.listener != nil { | |
log.Println("Stopping listener") | |
s.listener.Close() | |
*reply = true | |
return nil | |
} | |
*reply = false | |
return fmt.Errorf("listener is not open") | |
} | |
// This signals the server to reload it's config. | |
func (r *ServiceHandler) Reload(args int, reply *bool) error { | |
err := ReadConfig() | |
if err != nil { | |
*reply = false | |
return err | |
} | |
*reply = true | |
return nil | |
} | |
// Initialize the RPC service and start listening for connections. | |
func StartRPC(listener net.Listener) error { | |
// Create the ServiceHandler, register it, and set it to be handled. | |
rpcsh := &ServiceHandler{listener: listener} | |
rpc.Register(rpcsh) | |
rpc.HandleHTTP() | |
l, err := net.Listen("tcp", ":9001") | |
if err != nil { | |
return fmt.Errorf("opening rpc listener: %v", err) | |
} | |
// It seems a bit unituitive to me, but this is how we start | |
// listening (i.e. using the http package). | |
go http.Serve(l, nil) | |
return nil | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// servicer/servicer.go | |
package main | |
import ( | |
"flag" | |
"fmt" | |
"net/rpc" | |
"os" | |
) | |
func main() { | |
// Get the action the user wants us to perform | |
flag.Parse() | |
action := flag.Arg(0) | |
// Translate the action into an RPC method. | |
method := "" | |
switch action { | |
case "stop": | |
method = "ServiceHandler.Stop" | |
case "reload": | |
method = "ServiceHandler.Reload" | |
} | |
if method == "" { | |
fmt.Println("unknown action:", action) | |
os.Exit(1) | |
} | |
// Connect to the server. | |
client, err := rpc.DialHTTP("tcp", ":9001") | |
if err != nil { | |
fmt.Println("dialing:", err) | |
os.Exit(1) | |
} | |
defer client.Close() | |
// Issue the method | |
var reply bool | |
err = client.Call(method, 1, &reply) | |
if err != nil { | |
fmt.Println("calling:", err) | |
os.Exit(1) | |
} | |
// Send the proper exit code to the system. How you respond to your | |
// service manager may differ slightly. | |
if !reply { | |
os.Exit(1) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ cd servicer | |
$ go install | |
$ cd ../greeter | |
$ go install | |
$ cd .. | |
$ sudo cp greeter.services /etc/systemd/system | |
$ cp config.json /tmp | |
$ sudo systemctl start greeter | |
$ curl http://localhost:9000 | |
Howdy Doodie! | |
$ emacs /tmp/config.json | |
$ sudo systemctl reload greeter | |
$ curl http://localhost:9000 | |
Hey there, partner! | |
$ sudo systemctl stop greeter | |
$ curl http://localhost:9000 | |
curl: (7) couldn't connect to host at localhost:9000 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment