SOA (VS SERVERS)
WHAT ARE SERVICES
▸ Logical Entities
▸ Independent
▸ Comprised of more services
▸ Multiple servers for load and tolerance
▸ Scalability
▸ Flexibility
WHO DUN IT?
WHY SERVICES?
▸ Software Problem
▸ Scalability
▸ Management Problem
▸ Authority
▸ Responsibility.
REQUEST RESPONSE
CALLER SERVICE
HELLO?
ALOHA!WRONG NUMBER
Message {“Data”nterface{} `json:"data"`
//Actual Payload
Code int `json:"code"` //Response code if this is a response. This uses HTTP error codes
Sender string `json:"sender"` //Origin of the request (unique for each request)}
SENDER_ID
CODE
REQUEST
data := G.NewMessage().SetData("hello?")resp := g.Request(“echo", data, nil)
var msg stringreq.Data(&msg);fmt.Println("Echoclient: received", msg)
opts := G.NewHandlerOpts().SetGroup("exclusive")g.ReplyTo("echo", func(req *G.Request, resp *G.Message) { var msg string req.Data(&msg) resp.SetData(fmt.Sprintf("Pong %v", msg))}, opts)
RESPONSE
REQUEST
ASYNC: SIGNALS AND SLOTS
Slots
Signal
ITEM.BUY.500
ITEM.BUY.*
ITEM.BUY.*
ITEM.BUY.*
GROUP: PUSH_MSG
FP SCALINGPUSH MESSAGE
SERVERPUSH MESSAGE SERVER
WEB GILMOUR
EMAIL NOTIFIER
SMS NOTIFIER
PUSH MESSAGE SERVER
ITEM PURCHASED
VIP ITEM PURCHASED
ITEM.BUY.420
ASYNC: SLOTS
g := G.Get(redis)g.Slot("example.log", func(req *G.Request) {
var msg stringreq.Data(&msg)log.Println(req.Sender(), "->", msg)
})g.Start()
g.Signal("example.log", G.NewMessage().SetData("log message emitted"))
SIGNAL
RECEIVER / SLOT
ASYNC VS SYNC
WHAT TO USE WHEN?
▸ Request-Response
▸ Confirmed delivery.
▸ Response or Error Guaranteed.
▸ HTTP Like.
▸ Sender responsible for errors.
ASYNC VS SYNC
WHAT TO USE WHEN?
▸ Signal-Slots
▸ UDP-like.
▸ Broadcasting
▸ Wildcard topics
▸ Unreliable delivery.
▸ Receiver responsible for errors.
NOTIFICATION SERVER 1
REPLICATED LOAD
BALANCER
X.Y.Y.Z?CALLER SERVICE
REGISTER
I, AM
HEALTHY?
NOTIFICATION SERVER 2
NOTIFICATION SERVER 3
NOTIFICATION SERVER 2
GILMOUR
MANAGER.NOTIFICATION
NOTIFICATION SERVER 1
NOTIFICATION SERVER 3
MANAGER.NOTIFICATION
MANAGER.NOTIFICATION
CALLER SERVICE
MANAGER.NOTIFICATION
{“NOTIFICATION”: “DATA”}
DISCOVERY AND LOAD BALANCING
LOOK MA, NO SERVICE DISCOVERY
▸ Gilmour works on Redis Subscriber Publisher.
▸ No querying discovery process.
▸ Call service and not servers.
DISCOVERY AND LOAD BALANCING
LOOK MA, NO LOAD BALANCING
▸ Has Capacity, Will Serve.
▸ Message delivered to one and all.
▸ Fittest node acquires lock first.
GILMOUR ERRORS
ERROR DETECTION
▸ Timeouts
▸ server side - execution timed out.
▸ client side - response too late
▸ confirm_subscribers
GILMOUR ERRORS
CALLER SERVICE
HELLO?
WRONG NUMBER
SENDER_ID
CODE
ERROR REPORTER
type gilmourError struct {topic string //topic to which the request was sentrequestData string //request payloaduserData string //implementation dependent informationsender string //sender from the requesttimestamp string //timestamp of errorbacktrace interface{} //backtrace of the errorcode int //response code (non-200)
}
GILMOUR ERRORS
ERROR HANDLING
▸ Ignore
▸ Publish
▸ Queue
func main() {redis := backends.MakeRedis("127.0.0.1:6379", "pass")g := G.Get(redis)
//g.SetErrorPolicy(protocol.ErrorPolicyQueue)//g.SetErrorPolicy(protocol.ErrorPolicyIgnore)g.SetErrorPolicy(protocol.ErrorPolicyPublish)
}
OPS: MONITORING
SERVERS
▸ Log forwarding & aggregation
▸ Error Reporting
▸ Server and Health Monitoring
OPS: MONITORING
SERVICES
▸ Are the services servicing?
▸ Do services have spare capacity?
▸ Do services respond in time?
▸ Health Bulletin: github.com/gilmour-libs/health-bulletin
OPS: MONITORING
Feb 23 12:25:14 qa-next-backend-manager0.datascale.io gilmour_health: D, [2016-02-23T06:55:12.930435 #22111] DEBUG -- : Ensuring topics have at least one subscriber
Feb 23 12:25:14 qa-next-backend-manager0.datascale.io gilmour_health: D, [2016-02-23T06:55:12.931160 #22111] DEBUG -- : Ensuring that gilmour servers respond to health ping
Feb 23 12:26:14 qa-next-backend-manager0.datascale.io backend_manager: D, [2016-02-23T06:56:12.932775 #9605] DEBUG -- : Executing handler for gilmour.health.ip-10-0-0-146-pid-9605-uuid-9d0484dd-08e5-4ec3-a3e4-9a109634712b from 2881867d41f24228b462b9eff0b7e88e
2016-04-03 00:06:56 d0e4ea929645600e2fa97657091613a4 -> Cleared VPC jenkins-backend-manager-pipeline-qa-172016-04-03 00:06:56 d0e4ea929645600e2fa97657091613a4 -> Clearing databag customer_jenkins-backend-manager-pipeline-qa-17_config::vpc_config2016-04-03 00:06:57 d0e4ea929645600e2fa97657091613a4 -> Deleted data_bag[customer_jenkins-backend-manager-pipeline-qa-17_config]2016-04-03 00:06:57 d0e4ea929645600e2fa97657091613a4 -> Clearing DNS entries2016-04-03 00:07:00 d0e4ea929645600e2fa97657091613a4 -> Cleaning Chef environment2016-04-03 00:07:01 d0e4ea929645600e2fa97657091613a4 -> Deleted customer_jenkins-backend-manager-pipeline-qa-17
REQUEST LOGGING
COMPOSITION
▸ compose - service1 | service2 | composition3
▸ andand - service1 && service2 && composition3
▸ oror - service1 || composition2 || service3
▸ batch
▸ (service1; service2; service3) > out
▸ service1; composition2; service3 > out
▸ parallel
▸ lambda functions
COMPOSITION
popular := g.NewParallel(g.NewRequestComposition("example.popular3"),g.NewRequestComposition("example.popular4"),
)
batch := g.NewPipe(g.NewRequestComposition("example.fetch"),g.NewRequestComposition("example.words"),g.NewRequestComposition("example.count"),popular,
)
data := G.NewMessage()data.SetData("https://s3.amazonaws.com/ds-data-sample/test.txt")msg := <-batch.Execute(data)
COMPOSITION
popular := g.NewParallel(g.NewRequestComposition("example.popular3"),g.NewRequestComposition(“example.popular4"),g.NewRequestComposition("example.popular5"),
)
batch := g.NewPipe(g.NewRequestComposition("example.fetch"),g.NewRequestComposition("example.words"),g.NewRequestComposition("example.stopfilter"),g.NewRequestComposition("example.count"),popular,&MyComposer{},
)data := G.NewMessage()data.SetData("https://s3.amazonaws.com/ds-data-sample/test.txt")msg := <-batch.Execute(data)
COMPOSITION
batch := e.NewPipe(e.NewRequest("weather.fetch"),e.NewRequest("weather.group"),e.NewParallel(
e.NewPipe(e.NewLambda(monthLambda("jan")),e.NewParallel(
e.NewRequest("weather.min"),e.NewRequest("weather.max"),
),),e.NewPipe(
e.NewLambda(monthLambda("feb")),e.NewParallel(
e.NewRequest("weather.min"),e.NewRequest("weather.max"),
),),
),)
EXAMPLES: POPULAR WORD COUNT
▸ Given
▸ A S3 URL pointing to a file that has (lot of) random text.
▸ Goal
▸ Eliminate stop words from the file.
▸ Find most popular 3, 4, and 5 letter words.
▸ Find these in parallel.
EXAMPLES: WEATHER AGGREGATION
▸ Output
▸ Popular 3 letter words: [way, for]
▸ Popular 4 letter words: [Text, copy]
▸ Popular 5 letter words: [Blind, blind]
EXAMPLES: WEATHER AGGREGATION
▸ Given
▸ Weather data as per hour, per day, per month of form:
▸ {"Day": 10, "Month": "jan", "Hour": 13, "Degrees": 56}
EXAMPLES: WEATHER AGGREGATION
▸ Goal
▸ For the months of Jan & Feb
▸ Find the maximum temperature recorded.
▸ Find the minimum temperature recorded.
▸ If possible do 3a and 3b in parallel, and in parallel for both months
EXAMPLES: WEATHER AGGREGATION
▸ Output
▸ Temperature on 19th Feb at 2100 hours was 50ºF
▸ Temperature on 21st Feb at 0500 hours was 94ºF
▸ Temperature on 19th Jan at 1900 hours was 50ºF
▸ Temperature on 17th Jan at 2200 hours was 94ºF
GILMOUR
RECAP
▸ Library only; no external process.
▸ No Load Balancer
▸ No Service Discovery
▸ Signal, Slots.
▸ Broadcast, Wildcard Topics.
▸ Exclusion groups.
▸ Sync - Request, Reply
▸ Error and Health Monitoring - HealthBulletin.
▸ Failure Detection
▸ Composition - Real micro-services.
▸ No message persistence.
GILMOUR
CREDITS & LINKS
▸ Aditya Godbole
▸ Datascale.IO
▸ datascale.io
▸ Repository
▸ github.com/gilmour-libs