Permalink
Browse files

connection pool for clients to services, as well as smart pool functi…

…onality so that it self updates as it sees instances registered/unregistered from the cluster that match it's internal query
  • Loading branch information...
1 parent 8636fc3 commit e121188de91d1b95d569041e4ec4a7e4d1d79817 @erikstmartin erikstmartin committed Jun 1, 2012
Showing with 760 additions and 67 deletions.
  1. +8 −4 README.md
  2. +33 −1 examples/client/client.go
  3. +14 −4 examples/doozer_cluster.sh
  4. +34 −10 sky/sky.go
  5. +167 −30 skylib/client.go
  6. +3 −0 skylib/config.go
  7. +34 −4 skylib/doozer.go
  8. +6 −0 skylib/query.go
  9. +25 −14 skylib/service.go
  10. +254 −0 skylib/util/roundrobin.go
  11. +182 −0 skylib/util/roundrobin_test.go
View
@@ -29,6 +29,8 @@ SkyNet uses Doozer to store configuration data about the available services. Co
## Doozer
Skynet makes heavy usage of Doozer. Both clients and services will take a DoozerConfig so that it knows how to communicate with doozer. In the examples directory there is a shell script to startup a cluster of doozer instances locally for testing.
+We recommend using at least 5 instances of doozer in your cluster, if you have 3, and loose 1, if an additonal doozer instance goes down the doozer cluster doesn't reject it.
+
<pre>
type DoozerConfig struct {
Uri string
@@ -86,6 +88,9 @@ Checkout the examples/service directory for a full example, also a call to skyli
##Clients
Clients are just as simple. They start with a ClientConfig:
+#####Smart Connection Pools
+ServiceClients's contain a pool of connections to a given service, up to a specified size to load balance requests across. Instances are removed from skynet when they crash, the pools are smart enough to remove any connections to any instances that are no longer available and replace them with connections to valid instances to maintain the pool.
+
<pre>
type ClientConfig struct {
Log *log.Logger `json:"-"`
@@ -134,6 +139,7 @@ Commands:
-version - limit results to instances of the specified version of service
-region - limit results to instances in the specified region
-host - limit results to instances on the specified host
+ -registered - (true, false) limit results to instances that are registered (accepting requests)
regions: List all regions available that meet the specified criteria
services: List all services available that meet the specified criteria
-host - limit results to the specified host
@@ -163,11 +169,12 @@ type Query struct {
Version string
Host string
Region string
+ Registered bool
DoozerConn *DoozerConnection
}
</pre>
-The only required field here is a pointer to a doozer connection. All other fields are optional, any field not supplied will be considered as any/all.
+The only required field here is a pointer to a doozer connection. All other fields are optional, any field not supplied will be considered as any/all. Keep in mind if you're going to make requests to an instance you'll want to ensure the Registered attribute is true, you don't want your code responsibile for sending requests to a server that's trying to shut down.
From here you can use any of the following
@@ -196,9 +203,6 @@ query.ServiceVersions()
</pre>
## Work In Progress
-#####Smart Connection Pools
-ServiceClients's will have a pool of connections to a given service, to load balance across. Instances are already removed from skynet when they crash, but local pools will be smart enough to remove any connections to any instances that are no longer available and replace them with connections to valid instances to maintain pool size.
-
#####Process Monitoring / Restarting
Services will restart themselves a specified number of times after crashing and add themselves back to the pool.
View
@@ -2,20 +2,52 @@ package main
import (
"github.com/bketelsen/skynet/skylib"
+ "os"
+ "os/signal"
+ "syscall"
"fmt"
)
func main() {
+ c := make(chan os.Signal, 1)
+
config := &skylib.ClientConfig {
DoozerConfig: &skylib.DoozerConfig {
Uri: "127.0.0.1:8046",
+ AutoDiscover: true,
},
}
client := skylib.NewClient(config)
+
+ // This will not fail if no services currently exist, as connections are created on demand
+ // this saves from chicken and egg issues with dependencies between services
service := client.GetService("TestService", "", "", "") // any version, any region, any host
- ret, _ := service.Send("Upcase", "Upcase me!!")
+ // This on the other hand will fail if it can't find a service to connect to
+ ret, err := service.Send("Upcase", "Upcase me!!")
+
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
fmt.Println(ret)
+
+ watchSignals(c)
+}
+
+func watchSignals(c chan os.Signal) {
+ signal.Notify(c, syscall.SIGINT, syscall.SIGKILL, syscall.SIGQUIT, syscall.SIGSEGV, syscall.SIGSTOP, syscall.SIGTERM)
+
+ for {
+ select {
+ case sig := <-c:
+ switch sig.(syscall.Signal) {
+ // Trap signals for clean shutdown
+ case syscall.SIGINT, syscall.SIGKILL, syscall.SIGQUIT, syscall.SIGSEGV, syscall.SIGSTOP, syscall.SIGTERM:
+ syscall.Exit(0)
+ }
+ }
+ }
}
View
@@ -14,8 +14,8 @@ START_WEB_PORT=8080
START_DZNS_PORT=10000
START_DZNS_WEB_PORT=11000
-DZNS_INSTANCES=3
-CLUSTER_INSTANCES=3
+DZNS_INSTANCES=5
+CLUSTER_INSTANCES=5
if [ $# -lt 1 ]
then
@@ -25,7 +25,7 @@ fi
echo "Using Doozerd: $DOOZERD_PATH"
-if [ $1 = "start" ]; then
+function start {
if [ $# -gt 1 ]; then
CLUSTER_INSTANCES=$3
fi
@@ -80,7 +80,17 @@ if [ $1 = "start" ]; then
dz_web_port=$(( $dz_web_port + 1 ))
dz_count=$(( $dz_count + 1 ))
done
+}
-elif [ $1 = "stop" ]; then
+function stop {
killall doozerd
+}
+
+if [ $1 = "start" ]; then
+ start
+elif [ $1 = "stop" ]; then
+ stop
+elif [ $1 = "restart" ]; then
+ stop
+ start
fi
View
@@ -12,15 +12,15 @@ var VersionFlag *string = flag.String("version", "", "service version")
var ServiceNameFlag *string = flag.String("service", "", "service name")
var HostFlag *string = flag.String("host", "", "host")
var RegionFlag *string = flag.String("region", "", "region")
+var RegisteredFlag *string = flag.String("registered", "", "registered")
var DC *skylib.DoozerConnection
func main() {
flag.Parse()
- Connect()
query := &skylib.Query{
- DoozerConn: DC,
+ DoozerConn: Connect(),
Service: *ServiceNameFlag,
Version: *VersionFlag,
Host: *HostFlag,
@@ -48,7 +48,7 @@ func main() {
}
}
-func Connect() {
+func Connect() (*skylib.DoozerConnection) {
defer func() {
if r := recover(); r != nil {
fmt.Println("Failed to connect to Doozer")
@@ -57,18 +57,35 @@ func Connect() {
}()
// TODO: This needs to come from command line, or environment variable
- DC = &skylib.DoozerConnection{
- Config: &skylib.DoozerConfig {
- Uri: "127.0.0.1:8046",
- },
- }
+ conn := skylib.NewDoozerConnection("127.0.0.1:8046", "", false, nil) // nil as the last param will default to a Stdout logger
+ conn.Connect()
+
+ return conn
}
func ListInstances(q *skylib.Query) {
+ var regFlag *bool
+
+ if *RegisteredFlag == "true" {
+ b := true
+ regFlag = &b
+ } else if *RegisteredFlag == "false" {
+ b := false
+ regFlag = &b
+ }
+
+ q.Registered = regFlag
+
results := q.FindInstances()
for _, instance := range *results {
- fmt.Println(instance.Config.ServiceAddr.IPAddress + ":" + strconv.Itoa(instance.Config.ServiceAddr.Port) + " - " + instance.Config.Name + " (" + instance.Config.Version + ")")
+ registered := ""
+
+ if(instance.Registered){
+ registered = " [Registered]"
+ }
+
+ fmt.Println(instance.Config.ServiceAddr.IPAddress + ":" + strconv.Itoa(instance.Config.ServiceAddr.Port) + " - " + instance.Config.Name + " " + instance.Config.Version + registered)
}
}
@@ -149,7 +166,13 @@ func PrintTopology(q *skylib.Query) {
fmt.Println("\t\t\tVersion: " + versionName)
for _, instance := range version {
- fmt.Println("\t\t\t\t" + instance.Config.ServiceAddr.IPAddress + ":" + strconv.Itoa(instance.Config.ServiceAddr.Port) + " - " + instance.Config.Name + " (" + instance.Config.Version + ")")
+ registered := ""
+
+ if(instance.Registered){
+ registered = " [Registered]"
+ }
+
+ fmt.Println("\t\t\t\t" + instance.Config.ServiceAddr.IPAddress + ":" + strconv.Itoa(instance.Config.ServiceAddr.Port) + registered)
}
}
}
@@ -172,6 +195,7 @@ func Help() {
"\n\t\t-version - limit results to instances of the specified version of service" +
"\n\t\t-region - limit results to instances in the specified region" +
"\n\t\t-host - limit results to instances on the specified host" +
+ "\n\t\t-registered - (true, false) limit results to instances that are registered (accepting requests)" +
"\n\tregions: List all regions available that meet the specified criteria" +
Oops, something went wrong.

0 comments on commit e121188

Please sign in to comment.