Merge pull request #172 from micro/func

Function
This commit is contained in:
Asim Aslam 2017-06-01 12:50:58 +01:00 committed by GitHub
commit 097395c5cb
4 changed files with 198 additions and 2 deletions

View File

@ -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
View 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
View 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()
}

View File

@ -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 {