47
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								README.md
									
									
									
									
									
								
							| @@ -20,14 +20,15 @@ Go Micro abstracts way the details of distributed systems. Here are the main fea | ||||
| - **Message Encoding** - Micro services can encode requests in a number of encoding formats and seamlessly decode based on the Content-Type header. | ||||
| - **RPC Client/Server** - The client and server leverage the above features and provide a clean simple interface for building microservices. | ||||
|  | ||||
| Go Micro supports both the Service and Function programming models. Read on to learn more. | ||||
|  | ||||
| ## Docs | ||||
|  | ||||
| For more detailed information on the architecture, installation and use of go-micro checkout the [docs](https://micro.mu/docs). | ||||
|  | ||||
| ## Learn By Example | ||||
|  | ||||
| An example service can be found in [**examples/service**](https://github.com/micro/examples/tree/master/service). The [**examples**](https://github.com/micro/examples) directory contains many more examples for using things such as middleware/wrappers, selector filters, pub/sub and code generation.  | ||||
|  | ||||
| An example service can be found in [**examples/service**](https://github.com/micro/examples/tree/master/service) and function in [**examples/function**](https://github.com/micro/examples/tree/master/function). The [**examples**](https://github.com/micro/examples) directory contains many more examples for using things such as middleware/wrappers, selector filters, pub/sub and code generation.  | ||||
| For the complete greeter example look at [**examples/greeter**](https://github.com/micro/examples/tree/master/greeter). Other examples can be found throughout the GitHub repository. | ||||
|  | ||||
| Check out the blog post to learn how to write go-micro services [https://micro.mu/blog/2016/03/28/go-micro.html](https://micro.mu/blog/2016/03/28/go-micro.html) or watch the talk from the [Golang UK Conf 2016](https://www.youtube.com/watch?v=xspaDovwk34). | ||||
| @@ -228,6 +229,48 @@ go run client.go | ||||
| Hello John | ||||
| ``` | ||||
|  | ||||
| ## Writing a Function | ||||
|  | ||||
| Go Micro includes the Function programming model. This is the notion of a one time executing Service which operates much like a service except exiting  | ||||
| after completing a request. A function is defined much like a service and called in exactly the same way. | ||||
|  | ||||
| ### Defining a Function | ||||
|  | ||||
| ```go | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	proto "github.com/micro/examples/function/proto" | ||||
| 	"github.com/micro/go-micro" | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
|  | ||||
| type Greeter struct{} | ||||
|  | ||||
| func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error { | ||||
| 	rsp.Greeting = "Hello " + req.Name | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	// create a new function | ||||
| 	fnc := micro.NewFunction( | ||||
| 		micro.Name("go.micro.fnc.greeter"), | ||||
| 	) | ||||
|  | ||||
| 	// init the command line | ||||
| 	fnc.Init() | ||||
|  | ||||
| 	// register a handler | ||||
| 	fnc.Handle(new(Greeter)) | ||||
|  | ||||
| 	// run the function | ||||
| 	fnc.Run() | ||||
| } | ||||
| ``` | ||||
|  | ||||
| It's that simple. | ||||
|  | ||||
| ## How does it work? | ||||
|  | ||||
| <p align="center"> | ||||
|   | ||||
							
								
								
									
										81
									
								
								function.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								function.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| package micro | ||||
|  | ||||
| import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/micro/go-micro/server" | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
|  | ||||
| type function struct { | ||||
| 	cancel context.CancelFunc | ||||
| 	Service | ||||
| } | ||||
|  | ||||
| func fnHandlerWrapper(f Function) server.HandlerWrapper { | ||||
| 	return func(h server.HandlerFunc) server.HandlerFunc { | ||||
| 		return func(ctx context.Context, req server.Request, rsp interface{}) error { | ||||
| 			defer f.Done() | ||||
| 			return h(ctx, req, rsp) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func fnSubWrapper(f Function) server.SubscriberWrapper { | ||||
| 	return func(s server.SubscriberFunc) server.SubscriberFunc { | ||||
| 		return func(ctx context.Context, msg server.Publication) error { | ||||
| 			defer f.Done() | ||||
| 			return s(ctx, msg) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func newFunction(opts ...Option) Function { | ||||
| 	ctx, cancel := context.WithCancel(context.Background()) | ||||
|  | ||||
| 	// force ttl/interval | ||||
| 	fopts := []Option{ | ||||
| 		RegisterTTL(time.Minute), | ||||
| 		RegisterInterval(time.Second * 30), | ||||
| 	} | ||||
|  | ||||
| 	// prepend to opts | ||||
| 	fopts = append(fopts, opts...) | ||||
|  | ||||
| 	// make context the last thing | ||||
| 	fopts = append(fopts, Context(ctx)) | ||||
|  | ||||
| 	service := newService(fopts...) | ||||
|  | ||||
| 	fn := &function{ | ||||
| 		cancel:  cancel, | ||||
| 		Service: service, | ||||
| 	} | ||||
|  | ||||
| 	service.Server().Init( | ||||
| 		// ensure the service waits for requests to finish | ||||
| 		server.Wait(true), | ||||
| 		// wrap handlers and subscribers to finish execution | ||||
| 		server.WrapHandler(fnHandlerWrapper(fn)), | ||||
| 		server.WrapSubscriber(fnSubWrapper(fn)), | ||||
| 	) | ||||
|  | ||||
| 	return fn | ||||
| } | ||||
|  | ||||
| func (f *function) Done() error { | ||||
| 	f.cancel() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (f *function) Handle(v interface{}) error { | ||||
| 	return f.Service.Server().Handle( | ||||
| 		f.Service.Server().NewHandler(v), | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (f *function) Subscribe(topic string, v interface{}) error { | ||||
| 	return f.Service.Server().Subscribe( | ||||
| 		f.Service.Server().NewSubscriber(topic, v), | ||||
| 	) | ||||
| } | ||||
							
								
								
									
										55
									
								
								function_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								function_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| package micro | ||||
|  | ||||
| import ( | ||||
| 	"sync" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/micro/go-micro/registry/mock" | ||||
| 	proto "github.com/micro/go-micro/server/debug/proto" | ||||
|  | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
|  | ||||
| func TestFunction(t *testing.T) { | ||||
| 	var wg sync.WaitGroup | ||||
| 	wg.Add(1) | ||||
|  | ||||
| 	// create service | ||||
| 	fn := NewFunction( | ||||
| 		Name("test.function"), | ||||
| 		Registry(mock.NewRegistry()), | ||||
| 		AfterStart(func() error { | ||||
| 			wg.Done() | ||||
| 			return nil | ||||
| 		}), | ||||
| 	) | ||||
|  | ||||
| 	// we can't test fn.Init as it parses the command line | ||||
| 	// fn.Init() | ||||
|  | ||||
| 	go func() { | ||||
| 		// wait for start | ||||
| 		wg.Wait() | ||||
|  | ||||
| 		// test call debug | ||||
| 		req := fn.Client().NewRequest( | ||||
| 			"test.function", | ||||
| 			"Debug.Health", | ||||
| 			new(proto.HealthRequest), | ||||
| 		) | ||||
|  | ||||
| 		rsp := new(proto.HealthResponse) | ||||
|  | ||||
| 		err := fn.Client().Call(context.TODO(), req, rsp) | ||||
| 		if err != nil { | ||||
| 			t.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		if rsp.Status != "ok" { | ||||
| 			t.Fatalf("function response: %s", rsp.Status) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// run service | ||||
| 	fn.Run() | ||||
| } | ||||
							
								
								
									
										17
									
								
								go-micro.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								go-micro.go
									
									
									
									
									
								
							| @@ -22,6 +22,18 @@ type Service interface { | ||||
| 	String() string | ||||
| } | ||||
|  | ||||
| // Function is a one time executing Service | ||||
| type Function interface { | ||||
| 	// Inherits Service interface | ||||
| 	Service | ||||
| 	// Done signals to complete execution | ||||
| 	Done() error | ||||
| 	// Handle registers an RPC handler | ||||
| 	Handle(v interface{}) error | ||||
| 	// Subscribe registers a subscriber | ||||
| 	Subscribe(topic string, v interface{}) error | ||||
| } | ||||
|  | ||||
| // Publisher is syntactic sugar for publishing | ||||
| type Publisher interface { | ||||
| 	Publish(ctx context.Context, msg interface{}, opts ...client.PublishOption) error | ||||
| @@ -49,6 +61,11 @@ func NewContext(ctx context.Context, s Service) context.Context { | ||||
| 	return context.WithValue(ctx, serviceKey{}, s) | ||||
| } | ||||
|  | ||||
| // NewFunction returns a new Function for a one time executing Service | ||||
| func NewFunction(opts ...Option) Function { | ||||
| 	return newFunction(opts...) | ||||
| } | ||||
|  | ||||
| // NewPublisher returns a new Publisher | ||||
| func NewPublisher(topic string, c client.Client) Publisher { | ||||
| 	if c == nil { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user