golang roadrunner中文文档(四)app服务器

2021年6月8日13:18:46

 

golang roadrunner中文文档(一)基础介绍

golang roadrunner中文文档(二)PHP Workers

golang roadrunner中文文档(三)HTTPS 和 HTTP/2

golang roadrunner中文文档(四)app服务器

golang roadrunner中文文档(五)集成到其他服务 docker

服务器命令

RoadRunner 应用程序可以通过从 PHP 应用程序的根调用一个简单的命令来启动。

$ rr serve 

您还可以使用自定义位置的配置启动 RoadRunner:

$ rr serve -c ./app/.rr.yaml

重新加载所有 RoadRunner 服务:

$ rr reset

您可以将此命令附加为 IDE 中的文件观察器。

仅重置特定插件:

$ rr reset http

运行 golang pprof 服务器(调试模式):

$ rr serve -d -c .rr.yaml

以交互模式查看所有活动工作人员的状态。

$ rr workers -i
Workers of [http]:
+---------+-----------+---------+---------+-----------------+
|   PID   |  STATUS   |  EXECS  | MEMORY  |     CREATED     |
+---------+-----------+---------+---------+-----------------+
|    9440 | ready     |  42,320 | 31 MB   | 22 days ago     |
|    9447 | ready     |  42,329 | 31 MB   | 22 days ago     |
|    9454 | ready     |  42,306 | 31 MB   | 22 days ago     |
|    9461 | ready     |  42,316 | 31 MB   | 22 days ago     |
+---------+-----------+---------+---------+-----------------+

日志记录

RoadRunner 提供了单独控制每个插件的日志的能力。

全局配置

要全局配置日志记录,请使用logsconfig 部分:

logs:
  mode: production
  output: stderr

使用开发模式。它启用开发模式(使 DPanicLevel 日志恐慌),使用控制台编码器,写入标准错误,并禁用采样。堆栈跟踪会自动包含在 WarnLevel 及更高级别的日志中。

logs:
  mode: development

输出到单独的文件:

logs:
  mode: production
  output: file.log

要使用控制台友好输出:

logs:
  encoding: console # default value

要抑制特定日志级别下的消息:

logs:
  encoding: console # default value
  level: info

频道

此外,您可以使用channels部分单独配置每个插件日志消息

logs:
  encoding: console # default value
  level: info
  channels:
    server.mode: none # disable server logging. Also `off` can be used.
    http:
      mode: production
      output: http.log

概括:

  1. 级别:panicerrorwarninfodebug默认值:debug
  2. 编码:consolejson默认值:console
  3. 模式:productiondevelopmentraw默认值:development
  4. 输出:file.logstderrstdout默认stderr.
  5. 错误输出:err_file.logstderrstdout默认stderr.

随意注册您自己的ZapLogger扩展。

 

自动重载

RoadRunner 能够自动检测 PHP 文件更改并重新加载连接的服务。这种方法使您无需max_jobs: 1手动或手动重置服务器即可开发应用程序

配置

要为 http 服务启用重新加载:

reload:
  # sync interval
  interval: 1s
  # global patterns to sync
  patterns: [ ".php" ]
  # list of included for sync services
  services:
    http:
      # recursive search for file patterns to add
      recursive: true
      # ignored folders
      ignore: [ "vendor" ]
      # service specific file pattens to sync
      patterns: [ ".php", ".go", ".md" ]
      # directories to sync. If recursive is set to true,
      # recursive sync will be applied only to the directories in `dirs` section
      dirs: [ "." ]

性能

reload组件会影响应用服务器的性能。确保仅在开发模式下使用它。将来我们计划重写此插件以使用通知事件的本机操作系统功能。

 

生产用途

在生产环境中运行 RoadRunner 时,必须承认多个提示和建议。

状态和记忆

状态和内存不在不同的工作实例之间共享而是为单个工作实例共享由于单个工作人员通常处理多个请求,因此您应该小心:

  • 确保关闭所有描述符(尤其是在出现致命异常的情况下)。
  • [可选]gc_collect_cycles如果您想保持低内存,请考虑在每次执行后调用(这会稍微减慢您的应用程序的速度)。
  • 观察内存泄漏 - 您必须对使用的组件更加挑剔。如果出现内存泄漏,Worker 将被重新启动,但通过正确设计应用程序来完全避免这个问题应该不难。
  • 避免状态污染(即内存中的全局变量或用户数据缓存)。
  • 数据库连接和任何管道/套接字都是潜在的故障点。处理它的简单方法是在每次迭代后关闭所有连接。请注意,它不是性能最高的解决方案。

配置

  • 确保不要在 RPC 服务中监听 0.0.0.0(除非在 Docker 中)。
  • 使用管道连接到一个工人以获得更高的性能(Unix 套接字只是慢一点)。
  • 将您的泳池时间调整为您喜欢的值。
  • workers = 系统中的 CPU 线程数,除非您的应用程序受 IO 限制,否则请试探性地选择该数量。
  • max_jobs如果您在一段时间内遇到任何应用程序稳定性问题,请考虑为您的员工使用
  • RoadRunner 使用 Keep-Alive 连接的性能提高 40%。
  • 将内存限制设置为至少 10-20% 以下max_memory_usage
  • 由于 RoadRunner 工作人员从 cli 运行,您需要通过opcache.enable_cli=1.
  • 在云环境中运行 rr 时,请确保使用健康检查端点
  • 使用user配置中的选项从基于 Linux 的系统上的特定用户启动工作进程。

在 Linux 上将 RR 服务器作为守护进程运行

在 RR 存储库中,您可以找到 rr.server systemd 单元文件。该文件的结构如下:

[Unit]
Description=High-performance PHP application server

[Service]
Type=simple
ExecStart=/usr/local/bin/roadrunner serve -c <path/to/.rr.yaml>
Restart=always
RestartSec=30

[Install]
WantedBy=default.target 

用户应该做的唯一一件事就是ExecStart用您自己的选项更新选项。为此,请设置正确的roadrunner二进制路径、所需标志和 .rr.yaml 文件的路径。通常,此类用户单元文件位于.config/systemd/user/对于 RR,它可能是.config/systemd/user/rr.service要启用它,请使用以下命令:systemctl enable --user rr.servicesystemctl start rr.service就是这样。现在 roadrunner 应该在你的服务器上作为守护进程运行。

此外,您可以在此处找到有关 systemd 单元文件的更多信息:链接

 

应用指标

RoadRunner 服务器包括一个基于Prometheus的嵌入式指标服务器

启用指标

要启用指标添加metrics部分到您的配置:

metrics:
  address: localhost:2112

完成后,您可以使用http://localhost:2112/metricsurl访问 Prometheus 指标

确保安装指标扩展:

$ composer require spiral/roadrunner-metrics

应用指标

您还可以使用到服务器的 RPC 连接发布特定于应用程序的指标。首先,您必须在配置文件中注册一个指标:

metrics:
  address: localhost:2112
  collect:
    app_metric_counter:
      type: counter
      help: "Application counter."

从应用程序发送指标:

$metrics = new RoadRunner\Metrics\Metrics(
    Goridge\RPC\RPC::create(RoadRunner\Environment::fromGlobals()->getRPCAddress())
);

$metrics->add('app_metric_counter', 1);

支持的类型:仪表、计数器、汇总、直方图。

标记指标

您可以使用标记(标签)指标对值进行分组:

metrics:
  address: localhost:2112
  collect:
    app_type_duration:
      type: histogram
      help: "Application counter."
      labels: ["type"]

您应该在推送指标时为标签指定值:

$metrics = new RoadRunner\Metrics\Metrics(
    Goridge\RPC\RPC::create(RoadRunner\Environment::fromGlobals()->getRPCAddress())
);

$metrics->add('app_type_duration', 0.5, ['some-type']);

声明指标

您可以从 PHP 应用程序本身声明指标:

$metrics->declare(
    'test',
    RoadRunner\Metrics\Collector::counter()->withHelp('Test counter')
);


健康端点

RoadRunner 服务器包含一个健康检查端点,用于返回工作人员的健康状况。

启用健康

要启用运行状况检查端点,请status在您的配置中添加一个部分:

status:
  address: localhost:2114

要访问修复检查,请使用以下 URL:

http://localhost:2114/health?plugin=http

您可以使用健康检查检查一个或多个插件。目前,仅支持 HTTP。

启用后,运行状况检查端点将响应以下内容:

  • HTTP 200如果至少有一名工作人员准备好处理请求。
  • HTTP 500如果没有工作人员准备好为请求提供服务。

用例

运行状况检查端点可用于以下用途:

 

搭建服务器

RoadRunner 使用 Endure 来管理依赖项,这允许您为每个单独的项目调整和扩展应用程序功能。

安装 Golang

要构建应用服务器,您需要安装Golang 1.16+

创建 main.go

main.go文件复制项目的根目录中。

package main

import (
    "log"

    endure "github.com/spiral/endure/pkg/container"
    // plugins
    "github.com/spiral/roadrunner-binary/v2/cli"
    httpPlugin "github.com/spiral/roadrunner/v2/plugins/http"
    "github.com/spiral/roadrunner/v2/plugins/informer"
    "github.com/spiral/roadrunner/v2/plugins/kv/boltdb"
    "github.com/spiral/roadrunner/v2/plugins/kv/memcached"
    "github.com/spiral/roadrunner/v2/plugins/kv/memory"
    "github.com/spiral/roadrunner/v2/plugins/logger"
    "github.com/spiral/roadrunner/v2/plugins/metrics"
    "github.com/spiral/roadrunner/v2/plugins/redis"
    "github.com/spiral/roadrunner/v2/plugins/reload"
    "github.com/spiral/roadrunner/v2/plugins/resetter"
    "github.com/spiral/roadrunner/v2/plugins/rpc"
    "github.com/spiral/roadrunner/v2/plugins/server"
    "github.com/temporalio/roadrunner-temporal/activity"
    temporalClient "github.com/temporalio/roadrunner-temporal/client"
    "github.com/temporalio/roadrunner-temporal/workflow"
)

func main() {
    var err error
    cli.Container, err = endure.NewContainer(nil, endure.SetLogLevel(endure.ErrorLevel), endure.RetryOnFail(false))
    if err != nil {
        log.Fatal(err)
    }

    err = cli.Container.RegisterAll(
        // logger plugin
        &logger.ZapLogger{},
        // metrics plugin
        &metrics.Plugin{},
        // http server plugin
        &httpPlugin.Plugin{},
        // reload plugin
        &reload.Plugin{},
        // informer plugin (./rr workers, ./rr workers -i)
        &informer.Plugin{},
        // resetter plugin (./rr reset)
        &resetter.Plugin{},
        // rpc plugin (workers, reset)
        &rpc.Plugin{},
        // server plugin (NewWorker, NewWorkerPool)
        &server.Plugin{},

        // temporal plugins
        &temporalClient.Plugin{},
        &activity.Plugin{},
        &workflow.Plugin{},
    )
    if err != nil {
        log.Fatal(err)
    }

    cli.Execute()
}

您现在无需构建go run main.go serve.

了解如何创建http 中间件以拦截 HTTP 流。

RPC 集成

您可以使用共享 RPC 总线从 PHP 工作人员连接到 RoadRunner 服务器。为此,您必须创建一个RPC的实例,并将其配置为使用.rr文件中指定的地址

要求

要在 RPC 模式下从 PHP 应用程序连接到 RoadRunner,您需要:

  • ext-sockets
  • ext-json

配置

要更改默认的 RPC 端口 (localhost:6001),请使用:

rpc:
  listen: tcp://127.0.0.1:6001
$rpc = Goridge\RPC\RPC::create(RoadRunner\Environment::fromGlobals()->getRPCAddress());

您可以立即使用此 RPC 调用嵌入式 RPC 服务,例如 HTTP:

var_dump($rpc->call('informer.Workers', 'http'));

您可以在本节中阅读如何创建自己的服务和 RPC 方法

 

编写插件

RoadRunner 使用 Endure 容器来管理依赖项。这种方法类似于具有自动方法注入的 PHP 容器实现。您可以创建自己的插件、事件侦听器、中间件等。

要定义您的插件,请使用公共Init方法创建一个带有错误返回值的结构(您可以spiral/errors用作error包):

package custom

const PluginName = "custom"

type Plugin struct{}

func (s *Plugin) Init() error {
    return nil
}

您可以通过创建自定义版本的main.go文件并构建它来注册您的插件

依赖关系

您可以通过在您的Init方法中请求依赖项来访问其他 RoadRunner 插件

package custom

import (
    "github.com/spiral/roadrunner/v2/plugins/http"
    "github.com/spiral/roadrunner/v2/plugins/rpc"
)

type Service struct {
}

func (s *Service) Init(r *rpc.Plugin, rr *http.Plugin) error {
    return nil
}

确保请求依赖项作为指针。

配置

在大多数情况下,您的服务需要一组配置值。RoadRunner 可以使用config插件(通过接口)自动填充和验证您的配置结构

配置示例:

custom:
  address: tcp://localhost:8888

插入:

package custom

import (
    "github.com/spiral/roadrunner/v2/plugins/config"
    "github.com/spiral/roadrunner/v2/plugins/http"
    "github.com/spiral/roadrunner/v2/plugins/rpc"

    "github.com/spiral/errors"
)

const PluginName = "custom"

type Config struct{
    Address string `mapstructure:"address"`
}

type Plugin struct {
    cfg *Config
}

// You can also initialize some defaults values for config keys
func (cfg *Config) InitDefaults() {
    if cfg.Address == "" {
        cfg.Address = "tcp://localhost:8088"
    }
}

func (s *Plugin) Init(r *rpc.Plugin, h *http.Plugin, cfg config.Configurer) error {
    const op = errors.Op("custom_plugin_init") // error operation name
    if !cfg.Has(PluginName) {
        return errors.E(op, errors.Disabled)
    }

    // unmarshall 
    err := cfg.UnmarshalKey(PluginName, &s.cfg)
    if err != nil {
        // Error will stop execution
        return errors.E(op, err)
    }

    // Init defaults
    s.cfg.InitDefaults()

    return nil
}

errors.Disabled是一种特殊的错误,它指示 Endure 禁用此插件和此根的所有依赖项。如果至少插件保持活动状态,RR2 将在此错误类型后继续工作。

服务

在您的结构中创建ServeStop方法,让 RoadRunner 启动和停止您的服务。

type Plugin struct {}

func (s *Plugin) Serve() chan error {
    const op = errors.Op("custom_plugin_serve")
    errCh := make(chan error, 1)

    err := s.DoSomeWork()
    err != nil {
        errCh <- errors.E(op, err)
        return errCh
    }

    return nil
}

func (s *Plugin) Stop() error {
    return s.stopServing()
}

func (s *Plugin) DoSomeWork() error {
    return nil
}

Serve方法是线程安全的。它运行在由Endure容器管理的单独 goroutine中。一个注意事项是,您应该在调用Stop容器时取消阻止它否则,超时后服务将被杀死(可在 Endure 中设置)。

在运行时收集依赖项

RR2 提供了一种通过Collects接口在运行时收集依赖项的方法这对于中间件或具有附加功能的扩展插件非常有用,而无需更改它。
让我们创建一个 HTTP 中间件:

步骤(以实际http插件和Middleware界面为例):

  1. 声明一个必需的接口

    // Middleware interface
    type Middleware interface {
    Middleware(f http.Handler) http.HandlerFunc
    }
  2. 实现方法,它应该有一个参数名称(endure.Named接口)和Middleware(步骤 1)。

// Collects collecting http middlewares
func (s *Plugin) AddMiddleware(name endure.Named, m Middleware) {
    s.mdwr[name.Name()] = m
}
  1. Collects为所需结构实现持久接口并返回在步骤 2 方法上实现的。
// Collects collecting http middlewares
func (s *Plugin) Collects() []interface{} {
    return []interface{}{
        s.AddMiddleware,
    }
}

Endure 将自动检查注册的结构是否实现了该AddMiddleware方法的所有参数(如果参数是结构,则会找到一个结构)。在我们的例子中,一个结构应该实现endure.Named接口(它返回插件的用户友好名称)和Middleware接口。

RPC 方法

您还可以使用 EndureCollects接口为 PHP 工作人员公开一组 RPC 方法Endure 将自动获取结构并在PluginName名称下公开 RPC 方法

用 RPC 方法扩展你的插件,插件根本不会改变。唯一要做的一件事是使用 RPC 方法创建一个文件(我们称之为rpc.go),并在此处公开插件的所有 RPC 方法,而无需更改插件本身:
基于informer插件的示例

我假设我们创建了一个文件rpc.go下一步是创建一个结构:

  1. 创建一个结构:(记录器是可选的)
package custom

type rpc struct {
    srv *Plugin
    log logger.Logger
}
  1. 创建一个要公开的方法:
func (s *rpc) Hello(input string, output *string) error {
    *output = input
    return nil
}
  1. 使用Collects接口向 Endure 公开 RPC 服务:
// CollectTarget resettable service.
func (p *Plugin) CollectTarget(name endure.Named, r Informer) error {
    p.registry[name.Name()] = r
    return nil
}

// Collects declares services to be collected.
func (p *Plugin) Collects() []interface{} {
    return []interface{}{
        p.CollectTarget,
    }
}

// Name of the service.
func (p *Plugin) Name() string {
    return PluginName
}

// RPCService returns associated rpc service.
func (p *Plugin) RPC() interface{} {
    return &rpc{srv: p, log: p.log}
}

让我们来看看这些方法:

  1. CollectTarget:告诉 Endure,我们要收集所有实现endure.NamedInformer接口的插件
  2. Collects: 忍接口实现。
  3. Name:endure.Named返回用户友好插件名称的接口实现。
  4. RPC: RPC plugin 收集所有实现RPC接口和endure.NamedRPC 接口不接受任何参数,但返回接口(插件)。

要使用RPC 实例在 PHP 中使用它

var_dump($rpc->call('custom.Hello', 'world'));

 

服务插件

服务插件是在 RR 中引入的v2.0.5

主要能力

  1. 执行 PHP 代码、二进制文件、bash/powershell 脚本。
  2. 指定时间后重新启动。
  3. 控制特定命令的执行时间。
  4. 提供统计到Informer约插件%CPUPID和使用RSS memory

配置

service:
  some_service_1:
    command: "php tests/plugins/service/test_files/loop.php"
    process_num: 10
    exec_timeout: 0
    remain_after_exit: true
    restart_sec: 1

  some_service_2:
    command: "tests/plugins/service/test_files/test_binary"
    process_num: 1
    remain_after_exit: true
    restart_delay: 1s
    exec_timeout: 0

描述:

  1. 服务插件支持任意数量的嵌套命令。

  2. command- 要执行的命令。这里对命令没有限制。这里可能是二进制文件、PHP 文件、脚本等。

  3. process_num - 默认值:1,命令触发的进程数。

  4. exec_timeout - 默认值:0(无限制),允许进程运行的最大时间。

  5. remain_after_exit- 默认值:假。退出后保留进程。例如,如果你需要每 10 秒重启一次进程 exec_timeout应该是 10s,并且remain_after_exit应该设置为 true。注意:如果您从外部终止该进程并且如果remain_after_exit为 true,则该进程将重新启动。

  6. restart_sec- 默认值:30 秒。进程停止和重新启动之间的延迟。

 

posted on 2021-06-08 13:20  zh7314  阅读(508)  评论(0编辑  收藏  举报