summaryrefslogtreecommitdiff
path: root/docs/library
diff options
context:
space:
mode:
authorValery Piashchynski <[email protected]>2021-09-10 15:21:06 +0300
committerValery Piashchynski <[email protected]>2021-09-10 15:21:06 +0300
commit8b70fb48b2b0a9451d9b82a17ac2f4cd8a1f561e (patch)
tree852cef5775c326f62dac96e8b1f80ef9b75962c2 /docs/library
parent183d0ac682b57f285c9193492e50310046422184 (diff)
Add docs folder
Signed-off-by: Valery Piashchynski <[email protected]>
Diffstat (limited to 'docs/library')
-rw-r--r--docs/library/aws-lambda.md262
-rw-r--r--docs/library/event-listeners.md65
-rw-r--r--docs/library/standalone-usage.md48
3 files changed, 375 insertions, 0 deletions
diff --git a/docs/library/aws-lambda.md b/docs/library/aws-lambda.md
new file mode 100644
index 00000000..3fb0a5c0
--- /dev/null
+++ b/docs/library/aws-lambda.md
@@ -0,0 +1,262 @@
+# AWS Lambda
+RoadRunner can run PHP as AWS Lambda function.
+
+### Installation
+Prior to the function deployment, you must compile or download PHP binary files to run your application. There are multiple projects available for such goal:
+
+- https://github.com/araines/serverless-php
+- https://github.com/stechstudio/php-lambda (includes pre-built versions of PHP binaries)
+
+Place PHP binaries in a `bin/` folder of your project.
+
+### PHP Worker
+PHP worker does not require any specific configuration to run inside Lambda function. We can use default snippet with internal counter to demonstrate how workers are being reused:
+
+```php
+<?php
+/**
+ * @var Goridge\RelayInterface $relay
+ */
+use Spiral\Goridge;
+use Spiral\RoadRunner;
+
+ini_set('display_errors', 'stderr');
+require __DIR__ . "/vendor/autoload.php";
+
+$worker = new RoadRunner\Worker(new Goridge\StreamRelay(STDIN, STDOUT));
+$psr7 = new RoadRunner\Http\PSR7Worker(
+ $worker,
+ new \Nyholm\Psr7\Factory\Psr17Factory(),
+ new \Nyholm\Psr7\Factory\Psr17Factory(),
+ new \Nyholm\Psr7\Factory\Psr17Factory()
+);
+
+while ($req = $psr7->waitRequest()) {
+ try {
+ $resp = new \Nyholm\Psr7\Response();
+ $resp->getBody()->write(str_repeat("hello world", 1000));
+
+ $psr7->respond($resp);
+ } catch (\Throwable $e) {
+ $psr7->getWorker()->error((string)$e);
+ }
+
+```
+
+Name this file `handler.php` and put it into the root of your project. Make sure to run `composer require spiral/roadrunner`.
+
+### Application
+We can create a simple application to demonstrate how it works:
+1. You need 3 files, main.go with the `Endure` container:
+
+```golang
+import (
+ _ "embed"
+ "log"
+ "os"
+ "os/signal"
+ "sync"
+ "syscall"
+ "time"
+
+ endure "github.com/spiral/endure/pkg/container"
+ "github.com/spiral/roadrunner/v2/plugins/config"
+ "github.com/spiral/roadrunner/v2/plugins/logger"
+ "github.com/spiral/roadrunner/v2/plugins/server"
+)
+
+//go:embed .rr.yaml
+var rrYaml []byte
+
+func main() {
+ _ = os.Setenv("PATH", os.Getenv("PATH")+":"+os.Getenv("LAMBDA_TASK_ROOT"))
+ _ = os.Setenv("LD_LIBRARY_PATH", "./lib:/lib64:/usr/lib64")
+
+ cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.ErrorLevel))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ cfg := &config.Viper{}
+ cfg.CommonConfig = &config.General{GracefulTimeout: time.Second * 30}
+ cfg.ReadInCfg = rrYaml
+ cfg.Type = "yaml"
+
+ // only 4 plugins needed here
+ // 1. Server which should provide pool to us
+ // 2. Our mini plugin, which expose this pool for us
+ // 3. Logger
+ // 4. Configurer
+ err = cont.RegisterAll(
+ cfg,
+ &logger.ZapLogger{},
+ &Plugin{},
+ &server.Plugin{},
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ err = cont.Init()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ ch, err := cont.Serve()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ sig := make(chan os.Signal, 1)
+ signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
+
+ wg := &sync.WaitGroup{}
+ wg.Add(1)
+
+ go func() {
+ defer wg.Done()
+ for {
+ select {
+ case e := <-ch:
+ log.Println(e.Error.Error())
+ case <-sig:
+ err = cont.Stop()
+ if err != nil {
+ log.Println(err)
+ }
+ return
+ }
+ }
+ }()
+
+ wg.Wait()
+}
+```
+
+2. And `Plugin` for the RR:
+```golang
+package main
+
+import (
+ "context"
+ "sync"
+ "unsafe"
+
+ "github.com/aws/aws-lambda-go/lambda"
+ "github.com/spiral/roadrunner/v2/pkg/payload"
+ "github.com/spiral/roadrunner/v2/pkg/pool"
+ "github.com/spiral/roadrunner/v2/plugins/logger"
+ "github.com/spiral/roadrunner/v2/plugins/server"
+)
+
+type Plugin struct {
+ sync.Mutex
+ log logger.Logger
+ srv server.Server
+ wrkPool pool.Pool
+}
+
+func (p *Plugin) Init(srv server.Server, log logger.Logger) error {
+ var err error
+ p.srv = srv
+ p.log = log
+ return err
+}
+
+func (p *Plugin) Serve() chan error {
+ errCh := make(chan error, 1)
+ p.Lock()
+ defer p.Unlock()
+ var err error
+
+ p.wrkPool, err = p.srv.NewWorkerPool(context.Background(), pool.Config{
+ Debug: false,
+ NumWorkers: 1,
+ MaxJobs: 0,
+ AllocateTimeout: 0,
+ DestroyTimeout: 0,
+ Supervisor: &pool.SupervisorConfig{
+ WatchTick: 0,
+ TTL: 0,
+ IdleTTL: 0,
+ ExecTTL: 0,
+ MaxWorkerMemory: 0,
+ },
+ }, nil, nil)
+
+ go func() {
+ // register handler
+ lambda.Start(p.handler())
+ }()
+
+ if err != nil {
+ errCh <- err
+ }
+ return errCh
+}
+
+func (p *Plugin) Stop() error {
+ p.Lock()
+ defer p.Unlock()
+
+ if p.wrkPool != nil {
+ p.wrkPool.Destroy(context.Background())
+ }
+ return nil
+}
+
+func (p *Plugin) handler() func(pld string) (string, error) {
+ return func(pld string) (string, error) {
+ data := fastConvert(pld)
+ // execute on worker pool
+ if p.wrkPool == nil {
+ // or any error
+ return "", nil
+ }
+ exec, err := p.wrkPool.Exec(payload.Payload{
+ Context: nil,
+ Body: data,
+ })
+ if err != nil {
+ return "", err
+ }
+ return exec.String(), nil
+ }
+}
+
+// reinterpret_cast conversion cast from string to []byte
+// unsafe
+func fastConvert(d string) []byte {
+ return *(*[]byte)(unsafe.Pointer(&d))
+}
+```
+3. Config file, which can be embedded into the binary with `embed` import:
+```yaml
+server:
+ command: "php handler.php"
+ user: ""
+ group: ""
+ env:
+ - SOME_KEY: "SOME_VALUE"
+ - SOME_KEY2: "SOME_VALUE2"
+ relay: pipes
+ relay_timeout: 60s
+```
+Here you can use full advantage of the RR2, you can include any plugin here and configure it with the embedded config (within reasonable limits).
+
+To build and package your lambda function run:
+
+```
+$ GOOS=linux GOARCH=amd64 go build -o main main.go
+$ zip main.zip * -r
+```
+
+You can now upload and invoke your handler using simple string event.
+
+## Notes
+There are multiple notes you have to acknowledge.
+
+- start with 1 worker per lambda function in order to control your memory usage.
+- make sure to include env variables listed in the code to properly resolve the location of PHP binary and it's dependencies.
+- avoid database connections without concurrency limit
+- avoid database connections
diff --git a/docs/library/event-listeners.md b/docs/library/event-listeners.md
new file mode 100644
index 00000000..612d12bd
--- /dev/null
+++ b/docs/library/event-listeners.md
@@ -0,0 +1,65 @@
+# Event Listeners
+RoadRunner server exposes the way to handle internal events using custom event listeners, we can demonstrate how to display console message each time HTTP server responds:
+
+```golang
+func main() {
+ rr.Logger.Formatter = &logrus.TextFormatter{ForceColors: true}
+
+ rr.Container.Register(env.ID, env.NewService(map[string]string{"rr": rr.Version}))
+
+ rr.Container.Register(rpc.ID, &rpc.Service{})
+ rr.Container.Register(http.ID, &http.Service{})
+ rr.Container.Register(static.ID, &static.Service{})
+
+ // add event listener to http server
+ svc, _ := Container.Get(http.ID)
+ svc.(*http.Service).AddListener(myListener)
+
+ // you can register additional commands using cmd.CLI
+ rr.Execute()
+}
+```
+
+Where `myListener` is:
+
+```golang
+import (
+ "github.com/sirupsen/logrus"
+ rrhttp "github.com/spiral/roadrunner/service/http"
+)
+
+func myListener(event int, ctx interface{}) {
+ switch event {
+ case rrhttp.EventResponse:
+ e := ctx.(*rrhttp.ResponseEvent)
+ logrus.Info(
+ "%s %v %s %s",
+ e.Request.RemoteAddr,
+ e.Response.Status,
+ e.Request.Method,
+ e.Request.URI,
+ )
+ case rrhttp.EventError:
+ e := ctx.(*rrhttp.ErrorEvent)
+
+ if _, ok := e.Error.(roadrunner.JobError); ok {
+ logrus.Info(
+ "%v %s %s",
+ 500,
+ e.Request.Method,
+ e.Request.URI,
+ )
+ } else {
+ logrus.Info(
+ "%v %s %s %s",
+ 500,
+ e.Request.Method,
+ e.Request.URI,
+ e.Error,
+ )
+ }
+ }
+}
+```
+
+You can find a list of available events [here](https://godoc.org/github.com/spiral/roadrunner).
diff --git a/docs/library/standalone-usage.md b/docs/library/standalone-usage.md
new file mode 100644
index 00000000..04209c95
--- /dev/null
+++ b/docs/library/standalone-usage.md
@@ -0,0 +1,48 @@
+# Standalone Usage
+You can use RoadRunner library as the component, without bringing the whole application into your project.
+
+In order to create PHP worker pool on Golang:
+
+```golang
+srv := roadrunner.NewServer(
+ &roadrunner.ServerConfig{
+ Command: "php client.php echo pipes",
+ Relay: "pipes",
+ Pool: &roadrunner.Config{
+ NumWorkers: int64(runtime.NumCPU()),
+ AllocateTimeout: time.Second,
+ DestroyTimeout: time.Second,
+ },
+ })
+defer srv.Stop()
+
+srv.Start()
+
+res, err := srv.Exec(&roadrunner.Payload{Body: []byte("hello")})
+```
+
+Worker (echo) structure would look like:
+
+```php
+<?php
+/**
+ * @var Goridge\RelayInterface $relay
+ */
+use Spiral\Goridge;
+use Spiral\RoadRunner;
+
+ini_set('display_errors', 'stderr');
+require 'vendor/autoload.php';
+
+$rr = new RoadRunner\Worker(new Spiral\Goridge\StreamRelay(STDIN, STDOUT));
+
+while ($body = $rr->receive($context)) {
+ try {
+ $rr->send((string)$body, (string)$context);
+ } catch (\Throwable $e) {
+ $rr->error((string)$e);
+ }
+}
+```
+
+Make sure to run `go get github.com/spiral/roadrunner` and `composer require spiral/roadrunner` to load necessary dependencies.