How to created an unbounded input for Beam in Go? - go

I'm trying to use the Go Beam Sdk to create a pipeline processing pubsub messages.
github.com/apache/beam/sdks/v2/go/pkg/beam
I understand that the pubsubio connector is doing external calls working only on dataflow runner.
What if I want to test my pipeline locally ? How would you do that ?
I need to understand what is preventing me to write my own pubsub unbounded source ? (I may not understand how Beam works under the hood, like how does it serialize user defined code to send it to the runner ?)
Tried to do something like that:
package pubsubio
import (
"context"
"fmt"
cloud_pubsub "cloud.google.com/go/pubsub"
"github.com/apache/beam/sdks/v2/go/pkg/beam"
"github.com/apache/beam/sdks/v2/go/pkg/beam/log"
"github.com/apache/beam/sdks/v2/go/pkg/beam/register"
"github.com/apache/beam/sdks/v2/go/pkg/beam/util/pubsubx"
)
func init() {
register.DoFn3x1[context.Context, string, func(*cloud_pubsub.Message), error](&readFn{})
register.Emitter1[*cloud_pubsub.Message]()
}
type ReadConfig struct {
ProjectID string
TopicName string
SubscriptionName string
}
func Read(
scope beam.Scope,
cfg ReadConfig,
) beam.PCollection {
scope = scope.Scope("pubsubio.Read")
col := beam.Create(scope, cfg.SubscriptionName)
return beam.ParDo(scope, newReadFn(cfg.ProjectID, cfg.TopicName), col)
}
type readFn struct {
pubsubFn
TopicName string
}
func newReadFn(projectID, topicName string) *readFn {
return &readFn{
pubsubFn: pubsubFn{
ProjectID: projectID,
},
TopicName: topicName,
}
}
func (fn *readFn) ProcessElement(
ctx context.Context,
subscriptionName string,
emit func(message *cloud_pubsub.Message),
) error {
log.Info(ctx, "[pubsubio.ProcessElement] Reading from pubsub")
_, err := pubsubx.EnsureTopic(ctx, fn.client, fn.TopicName)
if err != nil {
return fmt.Errorf("cannot get topic: %w", err)
}
sub, err := pubsubx.EnsureSubscription(ctx, fn.client, fn.TopicName, subscriptionName)
if err != nil {
return fmt.Errorf("cannot get subscription: %w", err)
}
return sub.Receive(ctx, func(ctx context.Context, message *cloud_pubsub.Message) {
emit(message)
log.Debugf(ctx, "[pubsubio.ProcessElement] Emit msg: %s", message.ID)
message.Ack()
})
}
So basically I created a Read fn that never return, but the rest of my pipeline is never triggered (I must miss something)

Related

Invalid type while writing to BigQuery from Apache Beam Go SDK

While I try to write to BigQuery using Apache Beam Go SDK, I got :
panic: element type is struct { TaskId string; Connector string; ... },
want struct { TaskId string "json:\"task_id\" bigquery:\"task_id\""; Connector string "json:\"connector\" bigquery:\"connector\""; ... }`
Here's my code:
package main
import (
"context"
"encoding/json"
"flag"
"github.com/apache/beam/sdks/go/pkg/beam/io/bigqueryio"
"github.com/apache/beam/sdks/go/pkg/beam/x/debug"
"time"
"github.com/apache/beam/sdks/go/pkg/beam"
"github.com/apache/beam/sdks/go/pkg/beam/io/pubsubio"
"github.com/apache/beam/sdks/go/pkg/beam/log"
"github.com/apache/beam/sdks/go/pkg/beam/x/beamx"
)
type (
ProcessedTask struct {
TaskId string `json:"task_id" bigquery:"task_id"`
Connector string `json:"connector" bigquery:"connector"`
// ... other fields
}
)
func buildPipeline(s beam.Scope) {
rawProcessedTasks := pubsubio.Read(s, "project", "topic", &pubsubio.ReadOptions{Subscription: "subscription"})
processedTasks := beam.ParDo(s, func(ctx context.Context, data []byte) (ProcessedTask, error) {
var task ProcessedTask
if err := json.Unmarshal(data, &task); err != nil {
log.Error(ctx, err)
return task, err
}
return task, nil
}, rawProcessedTasks)
debug.Printf(s, "Task : %#v", processedTasks)
bigqueryio.Write(s, "project", "table", processedTasks)
}
func main() {
flag.Parse()
beam.Init()
p, s := beam.NewPipelineWithRoot()
buildPipeline(s)
ctx := context.Background()
if err := beamx.Run(ctx, p); err != nil {
log.Exitf(ctx, "Failed to execute pipeline: %v", err)
}
}
While looking at the logs of Dataflow I see from my debug.Printf:
Task : struct { TaskId string "json:\"task_id\" bigquery:\"task_id\""; Connector string "json:\"connector\" bigquery:\"connector\""; ... }
{TaskId:"342ebd19-4bd8-4271-9970-fd4615ddd725", Connector:"optin-to-mailjet", ...}}
Why did I lose the correct typing in the bigqueryio.Write call ?
I tried to register the type to beam without success. Did I have to explicit the type somewhere ?
I took example of that official example : https://github.com/apache/beam/blob/master/sdks/go/examples/cookbook/tornadoes/tornadoes.go
You're missing type registration at "init"
func init() {
beam.RegisterType(reflect.TypeOf((*ProcessedTask)(nil)).Elem())
}

HTTP request fails when executed asynchronously

I'm trying to write a tiny application in Go that can send an HTTP request to all IP addresses in hopes to find a specific content. The issue is that the application seems to crash in a very peculiar way when the call is executed asynchronously.
ip/validator.go
package ip
import (
"io/ioutil"
"net/http"
"regexp"
"time"
)
type ipValidator struct {
httpClient http.Client
path string
exp *regexp.Regexp
confirmationChannel *chan string
}
func (this *ipValidator) validateUrl(url string) bool {
response, err := this.httpClient.Get(url)
if err != nil {
return false
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return false
}
bodyBytes, _ := ioutil.ReadAll(response.Body)
result := this.exp.Match(bodyBytes)
if result && this.confirmationChannel != nil {
*this.confirmationChannel <- url
}
return result
}
func (this *ipValidator) ValidateIp(addr ip) bool {
httpResult := this.validateUrl("http://" + addr.ToString() + this.path)
httpsResult := this.validateUrl("https://" + addr.ToString() + this.path)
return httpResult || httpsResult
}
func (this *ipValidator) GetSuccessChannel() *chan string {
return this.confirmationChannel
}
func NewIpValidadtor(path string, exp *regexp.Regexp) ipValidator {
return newValidator(path, exp, nil)
}
func NewAsyncIpValidator(path string, exp *regexp.Regexp) ipValidator {
c := make(chan string)
return newValidator(path, exp, &c)
}
func newValidator(path string, exp *regexp.Regexp, c *chan string) ipValidator {
httpClient := http.Client{
Timeout: time.Second * 2,
}
return ipValidator{httpClient, path, exp, c}
}
main.go
package main
import (
"./ip"
"fmt"
"os"
"regexp"
)
func processOutput(c *chan string) {
for true {
url := <- *c
fmt.Println(url)
}
}
func main() {
args := os.Args[1:]
fmt.Printf("path: %s regex: %s", args[0], args[1])
regexp, regexpError := regexp.Compile(args[1])
if regexpError != nil {
fmt.Println("The provided regexp is not valid")
return
}
currentIp, _ := ip.NewIp("172.217.22.174")
validator := ip.NewAsyncIpValidator(args[0], regexp)
successChannel := validator.GetSuccessChannel()
go processOutput(successChannel)
for currentIp.HasMore() {
go validator.ValidateIp(currentIp)
currentIp = currentIp.Increment()
}
}
Note the line that says go validator.ValidateIp(currentIp) in main.go. Should I remove the word "go" to execute everything within the main routine, the code works as expected -> it sends requests to IP addresses starting 172.217.22.174 and should one of them return a legitimate result that matches the regexp that the ipValidator was initialized with, the URL is passed to the channel and the value is printed out by processOutput function from main.go. The issue is that simply adding go in front of validator.ValidateIp(currentIp) breaks that functionality. In fact, according to the debugger, I never seem to go past the line that says response, err := this.httpClient.Get(url) in validator.go.
The struggle is real. Should I decide to scan the whole internet, there's 256^4 IP addresses to go through. It will take years, unless I find a way to split the process into multiple routines.

go 1.8 plugin use custom interface

I want to use custom interface based on go plugin, but I found it's not support.
Definition of filter.Filter
package filter
import (
"net/http"
"github.com/valyala/fasthttp"
)
// Context filter context
type Context interface {
SetStartAt(startAt int64)
SetEndAt(endAt int64)
GetStartAt() int64
GetEndAt() int64
GetProxyServerAddr() string
GetProxyOuterRequest() *fasthttp.Request
GetProxyResponse() *fasthttp.Response
NeedMerge() bool
GetOriginRequestCtx() *fasthttp.RequestCtx
GetMaxQPS() int
ValidateProxyOuterRequest() bool
InBlacklist(ip string) bool
InWhitelist(ip string) bool
IsCircuitOpen() bool
IsCircuitHalf() bool
GetOpenToCloseFailureRate() int
GetHalfTrafficRate() int
GetHalfToOpenSucceedRate() int
GetOpenToCloseCollectSeconds() int
ChangeCircuitStatusToClose()
ChangeCircuitStatusToOpen()
RecordMetricsForRequest()
RecordMetricsForResponse()
RecordMetricsForFailure()
RecordMetricsForReject()
GetRecentlyRequestSuccessedCount(sec int) int
GetRecentlyRequestCount(sec int) int
GetRecentlyRequestFailureCount(sec int) int
}
// Filter filter interface
type Filter interface {
Name() string
Pre(c Context) (statusCode int, err error)
Post(c Context) (statusCode int, err error)
PostErr(c Context)
}
// BaseFilter base filter support default implemention
type BaseFilter struct{}
// Pre execute before proxy
func (f BaseFilter) Pre(c Context) (statusCode int, err error) {
return http.StatusOK, nil
}
// Post execute after proxy
func (f BaseFilter) Post(c Context) (statusCode int, err error) {
return http.StatusOK, nil
}
// PostErr execute proxy has errors
func (f BaseFilter) PostErr(c Context) {
}
This pkg is in my go app project.
load plugin file
package proxy
import (
"errors"
"plugin"
"strings"
"github.com/fagongzi/gateway/pkg/conf"
"github.com/fagongzi/gateway/pkg/filter"
)
var (
// ErrKnownFilter known filter error
ErrKnownFilter = errors.New("unknow filter")
)
const (
// FilterHTTPAccess access log filter
FilterHTTPAccess = "HTTP-ACCESS"
// FilterHeader header filter
FilterHeader = "HEAD" // process header fiter
// FilterXForward xforward fiter
FilterXForward = "XFORWARD"
// FilterBlackList blacklist filter
FilterBlackList = "BLACKLIST"
// FilterWhiteList whitelist filter
FilterWhiteList = "WHITELIST"
// FilterAnalysis analysis filter
FilterAnalysis = "ANALYSIS"
// FilterRateLimiting limit filter
FilterRateLimiting = "RATE-LIMITING"
// FilterCircuitBreake circuit breake filter
FilterCircuitBreake = "CIRCUIT-BREAKE"
// FilterValidation validation request filter
FilterValidation = "VALIDATION"
)
func newFilter(filterSpec *conf.FilterSpec) (filter.Filter, error) {
if filterSpec.External {
return newExternalFilter(filterSpec)
}
input := strings.ToUpper(filterSpec.Name)
switch input {
case FilterHTTPAccess:
return newAccessFilter(), nil
case FilterHeader:
return newHeadersFilter(), nil
case FilterXForward:
return newXForwardForFilter(), nil
case FilterAnalysis:
return newAnalysisFilter(), nil
case FilterBlackList:
return newBlackListFilter(), nil
case FilterWhiteList:
return newWhiteListFilter(), nil
case FilterRateLimiting:
return newRateLimitingFilter(), nil
case FilterCircuitBreake:
return newCircuitBreakeFilter(), nil
case FilterValidation:
return newValidationFilter(), nil
default:
return nil, ErrKnownFilter
}
}
func newExternalFilter(filterSpec *conf.FilterSpec) (filter.Filter, error) {
p, err := plugin.Open(filterSpec.ExternalPluginFile)
if err != nil {
return nil, err
}
s, err := p.Lookup("NewExternalFilter")
if err != nil {
return nil, err
}
sf := s.(func() (filter.Filter, error))
return sf()
}
This is the code of load plugin in my go app project
package main
import (
"C"
"strings"
"time"
"github.com/CodisLabs/codis/pkg/utils/log"
"github.com/fagongzi/gateway/pkg/filter"
"github.com/valyala/fasthttp"
)
// AccessFilter record the http access log
// log format: $remoteip "$method $path" $code "$agent" $svr $cost
type AccessFilter struct {
}
// NewExternalFilter create a External filter
func NewExternalFilter() (filter.Filter, error) {
return &AccessFilter{}, nil
}
// Name return name of this filter
func (f *AccessFilter) Name() string {
return "HTTP-ACCESS"
}
// Pre pre process
func (f *AccessFilter) Pre(c filter.Context) (statusCode int, err error) {
return 200, nil
}
// Post execute after proxy
func (f *AccessFilter) Post(c filter.Context) (statusCode int, err error) {
cost := (c.GetStartAt() - c.GetEndAt())
log.Infof("%s %s \"%s\" %d \"%s\" %s %s",
GetRealClientIP(c.GetOriginRequestCtx()),
c.GetOriginRequestCtx().Method(),
c.GetProxyOuterRequest().RequestURI(),
c.GetProxyResponse().StatusCode(),
c.GetOriginRequestCtx().UserAgent(),
c.GetProxyServerAddr(),
time.Duration(cost))
return 200, nil
}
// PostErr post error process
func (f *AccessFilter) PostErr(c filter.Context) {
}
// GetRealClientIP get read client ip
func GetRealClientIP(ctx *fasthttp.RequestCtx) string {
xforward := ctx.Request.Header.Peek("X-Forwarded-For")
if nil == xforward {
return strings.SplitN(ctx.RemoteAddr().String(), ":", 2)[0]
}
return strings.SplitN(string(xforward), ",", 2)[0]
}
This is the definition of plugin, it's in my plugin project. The plugin project and go app project are different projects.
I found errors:
panic: interface conversion: plugin.Symbol is func() (filter.Filter, error), not func() (filter.Filter, error)
You can find code in this project https://github.com/fagongzi/gateway/tree/go18-plugin-support.
filter.Filter is in pkg/filter package.
load plugin file in proxy/factory.go
plugin go file is in another project.
Custom interfaces work just fine.
But one important thing: you can only type assert types from values looked up from plugins that are defined outside of the plugin (you can't refer types defined in plugins). This also applies to each component of "composite types", for example you can only type assert a function type whose parameter and result types are also defined outside of the plugin.
1. With a common package outside of the plugin
One solution is to define the interface in a package outside of the plugin, and both the plugin and your app can import it and refer to it.
Define it in package filter:
package filter
type Filter interface {
Name() string
Age() int
}
The plugin is in package pq and imports package filter:
package main
import (
"fmt"
"filter"
)
type plgFilter struct{}
func (plgFilter) Name() string { return "Bob" }
func (plgFilter) Age() int { return 23 }
func GetFilter() (f filter.Filter, err error) {
f = plgFilter{}
fmt.Printf("[plugin GetFilter] Returning filter: %T %v\n", f, f)
return
}
And the main app that also imports (the same) package filter, loads the plugin, looks up GetFilter(), calls it and also uses the returned Filter:
package main
import (
"fmt"
"filter"
"plugin"
)
func main() {
p, err := plugin.Open("pg/pg.so")
if err != nil {
panic(err)
}
GetFilter, err := p.Lookup("GetFilter")
if err != nil {
panic(err)
}
filter, err := GetFilter.(func() (filter.Filter, error))()
fmt.Printf("GetFilter result: %T %v %v\n", filter, filter, err)
fmt.Println("\tName:", filter.Name())
fmt.Println("\tAge:", filter.Age())
}
Output:
[plugin GetFilter] Returning filter: main.plgFilter {}
GetFilter result: main.plgFilter {} <nil>
Name: Bob
Age: 23
2. With plugin returning interface{}, and interface defined in main app
Another solution is to have the plugin function return a value of type interface{}. Your main app can define the interface it expects, and it can use type assertion on the interface{} value returned by the plugin.
No filter package this time.
The plugin is in package pq:
package main
import (
"fmt"
)
type plgFilter struct{}
func (plgFilter) Name() string { return "Bob" }
func (plgFilter) Age() int { return 23 }
func GetFilterIface() (f interface{}, err error) {
f = plgFilter{}
fmt.Printf("[plugin GetFilterIface] Returning filter: %T %v\n", f, f)
return
}
And the main app:
package main
import (
"fmt"
"plugin"
)
func main() {
p, err := plugin.Open("pg/pg.so")
if err != nil {
panic(err)
}
GetFilterIface, err := p.Lookup("GetFilterIface")
if err != nil {
panic(err)
}
filterIface, err := GetFilterIface.(func() (interface{}, error))()
fmt.Printf("GetFilterIface result: %T %v %v\n", filterIface, filterIface, err)
myfilter := filterIface.(MyFilter)
fmt.Println("\tName:", myfilter.Name())
fmt.Println("\tAge:", myfilter.Age())
}
type MyFilter interface {
Name() string
Age() int
}
Output:
[plugin GetFilterIface] Returning filter: main.plgFilter {}
GetFilterIface result: main.plgFilter {} <nil>
Name: Bob
Age: 23
Also see related question: How do Go plugin dependencies work?

How to implement dependency injection in Go

I'm porting an app from Play (Scala) to Go and wondering how to implement dependency injection. In Scala I used the cake pattern, while in Go I implemented a DAO interface along with an implementation for Mongo.
Here below is how I tried to implement a pattern that let me change the DAO implementation as needed (e.g. test, different DB, etc.):
1. entity.go
package models
import (
"time"
"gopkg.in/mgo.v2/bson"
)
type (
Entity struct {
Id bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty" bson:"createdAt,omitempty"`
LastUpdate time.Time `json:"lastUpdate,omitempty" bson:"lastUpdate,omitempty"`
}
)
2. user.go
package models
import (
"time"
)
type (
User struct {
Entity `bson:",inline"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
BirthDate time.Time `json:"birthDate,omitempty" bson:"birthDate,omitempty"`
}
)
3. dao.go
package persistence
type (
DAO interface {
Insert(entity interface{}) error
List(result interface{}, sort string) error
Find(id string, result interface{}) error
Update(id string, update interface{}) error
Remove(id string) error
Close()
}
daoFactory func() DAO
)
var (
New daoFactory
)
4. mongoDao.go (DB info and collection name are hard-coded since it's just an example)
package persistence
import (
"fmt"
"time"
"errors"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"github.com/fatih/structs"
"cmd/server/models"
)
type (
mongoDAO struct{
session *mgo.Session
}
)
func NewMongoDAO() DAO {
dialInfo := &mgo.DialInfo{
Addrs: []string{"localhost:27017"},
Timeout: 60 * time.Second,
Database: "test",
}
session, err := mgo.DialWithInfo(dialInfo)
if err != nil {
panic(err)
}
session.SetMode(mgo.Monotonic, true)
return &mongoDAO{session}
}
func (dao *mongoDAO) Insert(entity interface{}) error {
doc := entity.(*models.User)
doc.Id = bson.NewObjectId()
doc.CreatedAt = time.Now().UTC()
doc.LastUpdate = time.Now().UTC()
return dao.session.DB("test").C("users").Insert(doc)
}
func (dao *mongoDAO) List(result interface{}, sort string) error {
return dao.session.DB("test").C("users").Find(nil).Sort(sort).All(result)
}
func (dao *mongoDAO) Find(id string, result interface{}) error {
if !bson.IsObjectIdHex(id) {
return errors.New(fmt.Sprintf("%s is not a valid hex id", id))
}
oid := bson.ObjectIdHex(id)
return dao.session.DB("test").C("users").FindId(oid).One(result)
}
func (dao *mongoDAO) Update(id string, update interface{}) error {
if !bson.IsObjectIdHex(id) {
return errors.New(fmt.Sprintf("%s is not a valid hex id", id))
}
oid := bson.ObjectIdHex(id)
doc := update.(*models.User)
doc.LastUpdate = time.Now().UTC()
return dao.session.DB("test").C("users").Update(oid, bson.M{"$set": structs.Map(update)})
}
func (dao *mongoDAO) Remove(id string) error {
if !bson.IsObjectIdHex(id) {
return errors.New(fmt.Sprintf("%s is not a valid hex id", id))
}
oid := bson.ObjectIdHex(id)
return dao.session.DB("test").C("users").RemoveId(oid)
}
func (dao *mongoDAO) Close() {
dao.session.Close()
}
func init() {
New = NewMongoDAO
}
Finally, here is how I use the types above:
5. userController.go
package controllers
import (
"net/http"
"github.com/labstack/echo"
"cmd/server/models"
"cmd/server/persistence"
)
type (
UserController struct {
dao persistence.DAO
}
)
func NewUserController(dao persistence.DAO) *UserController {
return &UserController{dao}
}
func (userController *UserController) CreateUser() echo.HandlerFunc {
return func(context echo.Context) error {
user := &models.User{}
if err := context.Bind(user); err != nil {
return err
}
if err := userController.dao.Insert(user); err != nil {
return err
}
return context.JSON(http.StatusCreated, user)
}
}
func (userController *UserController) UpdateUser() echo.HandlerFunc {
return func(context echo.Context) error {
user := &models.User{}
if err := context.Bind(user); err != nil {
return err
}
id := context.Param("id")
if err := userController.dao.Update(id, user); err != nil {
return err
}
return context.JSON(http.StatusOK, user)
}
}
....
The code above is 90% fine... I've just a problem in mongoDao.go with methods Insert and Update where the compiler forces me to cast input entity to a specific type (*models.User), but this prevents me from having a generic DAO component that works for all types. How do I fix this issue?
How about creating an interface that you implement for the Entity struct?
type Entitier interface {
GetEntity() *Entity
}
The implementation would simply return a pointer to itself that you can now use in the Insert and Update methods of your DAO. This would also have the added benefit of letting you be more specific in the declarations of your DAO methods. Instead of simply stating that they take an arbitrary interface{} as argument you could now say that they take an Entitier.
Like so:
func (dao *mongoDAO) Update(id string, update Entitier) error
Here's a minimal complete example of what I mean:
http://play.golang.org/p/lpVs_61mfM
Hope this gives you some ideas! You might want to adjust naming of Entity/Entitier/GetEntity for style and clarity once you've settled on the pattern to use.
This generalization
DAO interface {
Insert(entity interface{}) error
looks over-helming
You both assert to *models.User for mongo
doc := entity.(*models.User)
and do
user := &models.User{}
userController.dao.Insert(user)
when use your generic DAO interface.
Why don't you just define interface more precisely?
DAO interface {
Insert(entity *models.User) error

Sending a message to set of hosts with acknowledgements

Function is like:
func Message(worker_ID int, message string, args *Args , reply *int) chan bool {
}
This function resides at host which is called by client when it want to send the message to hosts, hosts are located at different place, so both IP and port required to send message right? which mechanism can be helpful net.dial() or gob or rpc?
If you want something simple then check out net/rpc which wraps gob and networking into a remote procedure call framework which should do what you want.
Server
From the docs a server running over HTTP
type Args struct {
A, B int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":1234")
if e != nil {
log.Fatal("listen error:", e)
}
go http.Serve(l, nil)
Client
At this point, clients can see a service "Arith" with method "Arith.Multiply". To invoke one, dial the server then make a call. You can also make asynchronous calls where the result comes back in a channel.
client, err := rpc.DialHTTP("tcp", serverAddress + ":1234")
if err != nil {
log.Fatal("dialing:", err)
}
args := &server.Args{7,8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply)
A slight oddity of the framework is that each remote call can have only one input argument and one output argument which means that you need to wrap all your arguments in a struct.
//server.go will provide the interface for communicating and handling hosts
// workerDead(message string), send the message and wait for ack, if no ack means worker dead
package
main
import(
"fmt"
"io"
"net"
"net/http"
"net/rpc"
"path"
"os"
)
type Flag int
type Args struct{
message string
}
func main() {
flag := new(Flag)
rpc.Register(flag)
rpc.HandleHTTP()
err := http.ListenAndServe(":1234", nil) //ListenAndServe starts an HTTP server with a given address and handler.
//The handler is usually nil, which means to use DefaultServeMux.
if err != nil {
fmt.Println(err.Error())
}
}
//Worker counts the number of hosts
func workerCount() int
{
return db.runCommand( { count: 'id' } ) //mongodb command to count the id
}
// Returns an array of the distinct values of field id from all documents in the workers collection
//In mongodb document is analogous to rdbms table and collection is record
func Worker(worker int) []string{
return db.runCommand ({ distinct: 'workers', key: 'id' } ) //mongodb command to get the array of list of
//workers for column id
}
func Message(worker int, message string, args *Args , reply *int) chan bool {
server, err :=rpc.Dial("tcp","192.168.23.12") //Serve Dials here to send message to host, IP address here is of host
if(err!=nil){
log.Fatal("Dialing", err)
}
var reply bool
args:=Args{message};
err = server.Call(args,&reply);
if(err!=nil){
log.Fatal("Dialing", err)
replaceWorker(); // No reply from worker then call to replace worker
}
fmt.Println("Reply from host", reply);
}
return nil
}
//Replace dead worker of particular project with fresh worker
func replaceWorker(worker_id int,project_id int)
{
db.workers.update( //this query updates workers collection with id=worker_id(id of dead worker)
{ _id: worker_id, _project_id: project_id },
{
//$set: { ' ': 'Warner' },
}
)
}

Resources