1624636740

# How to Host a Valheim Dedicated Server with Docker on a Rock Pi X

Valheim is all the rage these days, surpassing even Dota 2 with 500K peak players on February 21st, 2020 and crossing its 6 million sales mark just a few days back. As much as I’d like to proclaim myself as a non-conformist, I’m ashamed to say that I too, succumbed to Valheim’s mysterious allure.

I picked up this game just over 3 weeks ago and the first thing I did, given my obsession with self-hosting, was to find the most cost-efficient way to host a dedicated server for it.

Disclaimer: I’m neither affiliated with nor sponsored by Radxa/Allnet, the manufacturer/distributor of the Rock Pi X. I bought the Rock Pi X with my own money and I’m writing purely to share my experience of running a dedicated server on it.

## What is Valheim?

In case you’ve been living under a rock, Valheim is currently the hottest survival sandbox game on the market developed by the Swedish developer, Iron Gate Studios, comprising of a team of just 5 people. Valheim’s peak players statistics on Steam at one point was second only to Counter Strike: Global Offensive (CS:GO), which is very impressive given that it’s only in Early Access and has only been released for slightly over a month then.

#valheim #docker #rock-pi-x

1665369120

## CellularAutomata.jl: Cellular Automata Simulation toolkit for Julia

Cellular Automata

A cellular automaton is a collection of "colored" cells on a grid of specified shape that evolves through a number of discrete time steps according to a set of rules based on the states of neighboring cells. The rules are then applied iteratively for as many time steps as desired.

mathworld.wolfram.com/CellularAutomaton

## Elementary CA

To generate an elementary cellular automaton, use

``````ca = CellularAutomaton(rule, init, gen)
``````

where `rule` is the Wolfram code (integer), `init` is a vector containing the initial starting condition and `gen` is the number of generations to be computed. For a single starting cell in the middle just omit the `init` vector.

To generate 15 generations of elementary cellular automaton of rule 90 use

``````using CellularAutomata

ca90 = CellularAutomaton(90, 16)
``````
``````                            #
# #
#   #
# # # #
#       #
# #     # #
#   #   #   #
# # # # # # # #
#               #
# #             # #
#   #           #   #
# # # #         # # # #
#       #       #       #
# #     # #     # #     # #
#   #   #   #   #   #   #   #
# # # # # # # # # # # # # # # #
``````

## Totalistic CA

For a more complex cellular automaton you can change the number of states `k` the cell can be and the radius `r` of neighbors that can influence the states. If `k` is changed to be larger than 2, a totalistic CA is computed where only the average value of all neighbors count. This can be done like this

``````ca = CellularAutomaton(993, 15, k=3)
``````
``````                        X
XXX
X# #X
X     X
XXX   XXX
X# #X X# #X
X     #     X
XXX   ###   XXX
X# #X # X # X# #X
X      # X #      X
XXX    ## X ##    XXX
X# #X  #   X   #  X# #X
X     X### XXX ###X     X
XXX   X XX  # #  XX X   XXX
X# #X XX###X## ##X###XX X# #X
``````

## 2 dimensional CAs

Two dimensional cellular automaton (like Conway's Game of Life) can be created by

``````ca = CA2d(B, S, init, gen)
``````

where `B` and `S` are vectors that have the numbers of neighboring cells that define when cell is born or survives, `init` (matrix) is the initial starting condition and `gen` is the number of generations the CA is to be computed.

Game of life is then run for 9 generations for e.g. a turbine pattern by typing

``````ca = CA2d([3], [2, 3], init, 9)
``````

1st step

``````   ###### ##
###### ##
##
##     ##
##     ##
##     ##
##
## ######
## ######

``````

2nd

``````    ####
#    # ##
#    #   #
##    #
##    #  #
#  #   #  #
#  #    ##
#    ##
#   #    #
## #    #
####

``````

3rd

``````     ##
####
# ## ## #
##  #
##  ##  ###
#### #  ###
#  #   #  #
###  # ####
###  ##  ##
#  ##
# ## ## #
####
##

``````

4th

``````    #  #
#
##
# ##      #
#  #   #
#   # ###
#           #
### #   #
#   #  #
#      ## #
##
#
#  #

``````

5th

``````        ##
#
###  ##
### #   #
#    # ##
# #
## #    #
#   # ###
##  ###
#
##
``````

6th

``````        ##
#
# #  ##
# # ###  #
#  ######
## ##
######  #
#  ### # #
##  # #
#
##

``````

7th

``````     #  # #
## # ###
#      #
##     #

#     ##
#      #
### # ##
# #  #

``````

8th

``````    ## ## #
##  ## ##
#
##
##     ##
##
#
## ##  ##
# ## ##

``````

9th

``````   ###### ##
###### ##
##
##     ##
##     ##
##     ##
##
## ######
## ######

``````

## Running Tests

To run tests, execute the following command from the root folder of the repository:

``julia tests/run_tests.jl``

Author: Natj
Source Code: https://github.com/natj/CellularAutomata.jl

1617686160

## Linux Dedicated Server Hosting - Cloudminister

Cloudminister is a company that provides managed linux dedicated servers hosting in India. The Linux Dedicated servers allow you to fully manage all hosting solutions which directly improves the performance of your business. The servers we offer work with the best hardware and software and enhances network speed. This is the reason why people choose Cloudminister for Linux vps server. The company has a huge list of plans as per your business needs and you can choose any plan according to the requirement of your project.

### Cloudminister Dedicated Linux Server hosting in India feature:

• Enterprise SSD HDDs
• DDR4 ECC RAM Blazing
• fast performance
• Free Control Panel

### Complete Control of Dedicated Server Hosting:

• WordPress Toolkit
• Platform compatibility
• Powerful servers
• User-friendly interface
• Developer pack
• Plesk mobile manager
• Security

#linux dedicated server #linux dedicated server hosting #dedicated server hosting in india #cheap dedicated server india #dedicated server price in india

1624636740

## How to Host a Valheim Dedicated Server with Docker on a Rock Pi X

Valheim is all the rage these days, surpassing even Dota 2 with 500K peak players on February 21st, 2020 and crossing its 6 million sales mark just a few days back. As much as I’d like to proclaim myself as a non-conformist, I’m ashamed to say that I too, succumbed to Valheim’s mysterious allure.

I picked up this game just over 3 weeks ago and the first thing I did, given my obsession with self-hosting, was to find the most cost-efficient way to host a dedicated server for it.

Disclaimer: I’m neither affiliated with nor sponsored by Radxa/Allnet, the manufacturer/distributor of the Rock Pi X. I bought the Rock Pi X with my own money and I’m writing purely to share my experience of running a dedicated server on it.

## What is Valheim?

In case you’ve been living under a rock, Valheim is currently the hottest survival sandbox game on the market developed by the Swedish developer, Iron Gate Studios, comprising of a team of just 5 people. Valheim’s peak players statistics on Steam at one point was second only to Counter Strike: Global Offensive (CS:GO), which is very impressive given that it’s only in Early Access and has only been released for slightly over a month then.

#valheim #docker #rock-pi-x

1623044495

## Linux Dedicated Server - Cloudminister Technologies

Cloudminister is a company that provides managed Linux Dedicated Servers in India. The Linux Dedicated servers allow you to fully manage all hosting solutions which directly improves the performance of your business. The servers we offer work with the best hardware and software and enhances network speed. This is the reason why people choose Cloudminister for Linux dedicated server Hosting.

Cloudminister Dedicated Linux Server hosting in India feature:

• Enterprise SSD HDDs
• DDR4 ECC RAM
• Blazing fast performance
• Free Control Panel

#linux dedicated server hosting #dedicated server hosting in india #dedicated server price in india #cheap dedicated server india

1648667160

## LaravelS: Glue for using Swoole in Laravel Or Lumen

🚀 LaravelS is `an out-of-the-box adapter` between Swoole and Laravel/Lumen.

Please `Watch` this repository to get the latest updates.

`````` _                               _  _____
| |                             | |/ ____|
| |     __ _ _ __ __ ___   _____| | (___
| |    / _` | '__/ _` \ \ / / _ \ |\___ \
| |___| (_| | | | (_| |\ V /  __/ |____) |
|______\__,_|_|  \__,_| \_/ \___|_|_____/ ``````

## Features

Built-in Http/WebSocket server

Multi-port mixed protocol

Custom process

Memory resident

Asynchronous event listening

Millisecond cron job

Common Components

Support Laravel/Lumen both, good compatibility

Simple & Out of the box

## Benchmark

Which is the fastest web framework?

TechEmpower Framework Benchmarks

## Install

1.Require package via Composer(packagist).

``````composer require "hhxsv5/laravel-s:~3.7.0" -vvv
# Make sure that your composer.lock file is under the VCS``````

2.Register service provider(pick one of two).

`Laravel`: in `config/app.php` file, `Laravel 5.5+ supports package discovery automatically, you should skip this step`

``````'providers' => [
//...
Hhxsv5\LaravelS\Illuminate\LaravelSServiceProvider::class,
],``````

`Lumen`: in `bootstrap/app.php` file

``\$app->register(Hhxsv5\LaravelS\Illuminate\LaravelSServiceProvider::class);``

3.Publish configuration and binaries.

After upgrading LaravelS, you need to republish; click here to see the change notes of each version.

``````php artisan laravels publish
# Configuration: config/laravels.php
# Binary: bin/laravels bin/fswatch bin/inotify``````

4.Change `config/laravels.php`: listen_ip, listen_port, refer Settings.

5.Performance tuning

Number of Workers: LaravelS uses Swoole's `Synchronous IO` mode, the larger the `worker_num` setting, the better the concurrency performance, but it will cause more memory usage and process switching overhead. If one request takes 100ms, in order to provide 1000QPS concurrency, at least 100 Worker processes need to be configured. The calculation method is: worker_num = 1000QPS/(1s/1ms) = 100, so incremental pressure testing is needed to calculate the best` worker_num`.

## Run

`Please read the notices carefully before running`, Important notices(IMPORTANT).

• Commands: `php bin/laravels {start|stop|restart|reload|info|help}`.
• Boot options for the commands `start` and `restart`.
• `Runtime` files: `start` will automatically execute `php artisan laravels config` and generate these files, developers generally don't need to pay attention to them, it's recommended to add them to `.gitignore`.

## Deploy

It is recommended to supervise the main process through Supervisord, the premise is without option `-d` and to set `swoole.daemonize` to `false`.

``````[program:laravel-s-test]
directory=/var/www/laravel-s-test
command=/usr/local/bin/php bin/laravels start -i
numprocs=1
autostart=true
autorestart=true
startretries=3
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
``````

## Cooperate with Nginx (Recommended)

Demo.

``````gzip on;
gzip_min_length 1024;
gzip_comp_level 2;
gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml application/x-httpd-php image/jpeg image/gif image/png font/ttf font/otf image/svg+xml;
gzip_vary on;
gzip_disable "msie6";
upstream swoole {
# Connect IP:Port
server 127.0.0.1:5200 weight=5 max_fails=3 fail_timeout=30s;
# Connect UnixSocket Stream file, tips: put the socket file in the /dev/shm directory to get better performance
#server unix:/yourpath/laravel-s-test/storage/laravels.sock weight=5 max_fails=3 fail_timeout=30s;
#server 192.168.1.1:5200 weight=3 max_fails=3 fail_timeout=30s;
#server 192.168.1.2:5200 backup;
keepalive 16;
}
server {
listen 80;
# Don't forget to bind the host
server_name laravels.com;
root /yourpath/laravel-s-test/public;
access_log /yourpath/log/nginx/\$server_name.access.log  main;
autoindex off;
index index.html index.htm;
# Nginx handles the static resources(recommend enabling gzip), LaravelS handles the dynamic resource.
location / {
try_files \$uri @laravels;
}
# Response 404 directly when request the PHP file, to avoid exposing public/*.php
#location ~* \.php\$ {
#    return 404;
#}
location @laravels {
# proxy_connect_timeout 60s;
# proxy_send_timeout 60s;
proxy_http_version 1.1;
# "swoole" is the upstream
proxy_pass http://swoole;
}
}``````

## Cooperate with Apache

``````LoadModule proxy_module /yourpath/modules/mod_proxy.so

<IfModule deflate_module>
SetOutputFilter DEFLATE
DeflateCompressionLevel 2
AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml application/x-httpd-php image/jpeg image/gif image/png font/ttf font/otf image/svg+xml
</IfModule>

<VirtualHost *:80>
# Don't forget to bind the host
ServerName www.laravels.com

DocumentRoot /yourpath/laravel-s-test/public;
DirectoryIndex index.html index.htm
<Directory "/">
AllowOverride None
Require all granted
</Directory>

ProxyRequests Off
ProxyPreserveHost On
<Proxy balancer://laravels>
ProxySet lbmethod=byrequests
</Proxy>
#ProxyPass / balancer://laravels/
#ProxyPassReverse / balancer://laravels/

# Apache handles the static resources, LaravelS handles the dynamic resource.
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)\$ balancer://laravels%{REQUEST_URI} [P,L]

ErrorLog \${APACHE_LOG_DIR}/www.laravels.com.error.log
CustomLog \${APACHE_LOG_DIR}/www.laravels.com.access.log combined
</VirtualHost>``````

## Enable WebSocket server

The Listening address of WebSocket Sever is the same as Http Server.

1.Create WebSocket Handler class, and implement interface `WebSocketHandlerInterface`.The instant is automatically instantiated when start, you do not need to manually create it.

``````namespace App\Services;
use Hhxsv5\LaravelS\Swoole\WebSocketHandlerInterface;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;
/**
* @see https://www.swoole.co.uk/docs/modules/swoole-websocket-server
*/
class WebSocketService implements WebSocketHandlerInterface
{
// Declare constructor without parameters
public function __construct()
{
}
// public function onHandShake(Request \$request, Response \$response)
// {
// Custom handshake: https://www.swoole.co.uk/docs/modules/swoole-websocket-server-on-handshake
// The onOpen event will be triggered automatically after a successful handshake
// }
public function onOpen(Server \$server, Request \$request)
{
// Before the onOpen event is triggered, the HTTP request to establish the WebSocket has passed the Laravel route,
// so Laravel's Request, Auth information are readable, Session is readable and writable, but only in the onOpen event.
// \Log::info('New WebSocket connection', [\$request->fd, request()->all(), session()->getId(), session('xxx'), session(['yyy' => time()])]);
// The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually.
\$server->push(\$request->fd, 'Welcome to LaravelS');
}
public function onMessage(Server \$server, Frame \$frame)
{
// \Log::info('Received message', [\$frame->fd, \$frame->data, \$frame->opcode, \$frame->finish]);
// The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually.
\$server->push(\$frame->fd, date('Y-m-d H:i:s'));
}
public function onClose(Server \$server, \$fd, \$reactorId)
{
// The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually.
}
}``````

2.Modify `config/laravels.php`.

``````// ...
'websocket'      => [
'enable'  => true, // Note: set enable to true
'handler' => \App\Services\WebSocketService::class,
],
'swoole'         => [
//...
// Must set dispatch_mode in (2, 4, 5), see https://www.swoole.co.uk/docs/modules/swoole-server/configuration
'dispatch_mode' => 2,
//...
],
// ...``````

3.Use `SwooleTable` to bind FD & UserId, optional, Swoole Table Demo. Also you can use the other global storage services, like Redis/Memcached/MySQL, but be careful that FD will be possible conflicting between multiple `Swoole Servers`.

4.Cooperate with Nginx (Recommended)

Refer WebSocket Proxy

``````map \$http_upgrade \$connection_upgrade {
''      close;
}
upstream swoole {
# Connect IP:Port
server 127.0.0.1:5200 weight=5 max_fails=3 fail_timeout=30s;
# Connect UnixSocket Stream file, tips: put the socket file in the /dev/shm directory to get better performance
#server unix:/yourpath/laravel-s-test/storage/laravels.sock weight=5 max_fails=3 fail_timeout=30s;
#server 192.168.1.1:5200 weight=3 max_fails=3 fail_timeout=30s;
#server 192.168.1.2:5200 backup;
keepalive 16;
}
server {
listen 80;
# Don't forget to bind the host
server_name laravels.com;
root /yourpath/laravel-s-test/public;
access_log /yourpath/log/nginx/\$server_name.access.log  main;
autoindex off;
index index.html index.htm;
# Nginx handles the static resources(recommend enabling gzip), LaravelS handles the dynamic resource.
location / {
try_files \$uri @laravels;
}
# Response 404 directly when request the PHP file, to avoid exposing public/*.php
#location ~* \.php\$ {
#    return 404;
#}
# Http and WebSocket are concomitant, Nginx identifies them by "location"
# !!! The location of WebSocket is "/ws"
# Javascript: var ws = new WebSocket("ws://laravels.com/ws");
location =/ws {
# proxy_connect_timeout 60s;
# proxy_send_timeout 60s;
# proxy_read_timeout: Nginx will close the connection if the proxied server does not send data to Nginx in 60 seconds; At the same time, this close behavior is also affected by heartbeat setting of Swoole.
proxy_http_version 1.1;
proxy_pass http://swoole;
}
location @laravels {
# proxy_connect_timeout 60s;
# proxy_send_timeout 60s;
proxy_http_version 1.1;
proxy_pass http://swoole;
}
}``````

5.Heartbeat setting

Heartbeat setting of Swoole

``````// config/laravels.php
'swoole' => [
//...
// All connections are traversed every 60 seconds. If a connection does not send any data to the server within 600 seconds, the connection will be forced to close.
'heartbeat_idle_time'      => 600,
'heartbeat_check_interval' => 60,
//...
],``````

``````# Nginx will close the connection if the proxied server does not send data to Nginx in 60 seconds

6.Push data in controller

``````namespace App\Http\Controllers;
class TestController extends Controller
{
public function push()
{
\$fd = 1; // Find fd by userId from a map [userId=>fd].
/**@var \Swoole\WebSocket\Server \$swoole */
\$swoole = app('swoole');
\$success = \$swoole->push(\$fd, 'Push data to fd#1 in Controller');
var_dump(\$success);
}
}``````

## Listen events

### System events

Usually, you can reset/destroy some `global/static` variables, or change the current `Request/Response` object.

`laravels.received_request` After LaravelS parsed `Swoole\Http\Request` to `Illuminate\Http\Request`, before Laravel's Kernel handles this request.

``````// Edit file `app/Providers/EventServiceProvider.php`, add the following code into method `boot`
// If no variable \$events, you can also call Facade \Event::listen().
\$events->listen('laravels.received_request', function (\Illuminate\Http\Request \$req, \$app) {
\$req->query->set('get_key', 'hhxsv5');// Change query of request
\$req->request->set('post_key', 'hhxsv5'); // Change post of request
});``````

`laravels.generated_response` After Laravel's Kernel handled the request, before LaravelS parses `Illuminate\Http\Response` to `Swoole\Http\Response`.

``````// Edit file `app/Providers/EventServiceProvider.php`, add the following code into method `boot`
// If no variable \$events, you can also call Facade \Event::listen().
\$events->listen('laravels.generated_response', function (\Illuminate\Http\Request \$req, \Symfony\Component\HttpFoundation\Response \$rsp, \$app) {
});``````

### Customized asynchronous events

This feature depends on `AsyncTask` of `Swoole`, your need to set `swoole.task_worker_num` in `config/laravels.php` firstly. The performance of asynchronous event processing is influenced by number of Swoole task process, you need to set task_worker_num appropriately.

1.Create event class.

``````use Hhxsv5\LaravelS\Swoole\Task\Event;
class TestEvent extends Event
{
protected \$listeners = [
// Listener list
TestListener1::class,
// TestListener2::class,
];
private \$data;
public function __construct(\$data)
{
\$this->data = \$data;
}
public function getData()
{
return \$this->data;
}
}``````

2.Create listener class.

``````use Hhxsv5\LaravelS\Swoole\Task\Task;
class TestListener1 extends Listener
{
/**
* @var TestEvent
*/
protected \$event;

public function handle()
{
\Log::info(__CLASS__ . ':handle start', [\$this->event->getData()]);
sleep(2);// Simulate the slow codes
// Deliver task in CronJob, but NOT support callback finish() of task.
// Note: Modify task_ipc_mode to 1 or 2 in config/laravels.php, see https://www.swoole.co.uk/docs/modules/swoole-server/configuration
var_dump(\$ret);
// The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually.
}
}``````

3.Fire event.

``````// Create instance of event and fire it, "fire" is asynchronous.
\$event = new TestEvent('event data');
// \$event->delay(10); // Delay 10 seconds to fire event
// \$event->setTries(3); // When an error occurs, try 3 times in total
\$success = Event::fire(\$event);
var_dump(\$success);// Return true if sucess, otherwise false``````

This feature depends on `AsyncTask` of `Swoole`, your need to set `swoole.task_worker_num` in `config/laravels.php` firstly. The performance of task processing is influenced by number of Swoole task process, you need to set task_worker_num appropriately.

``````use Hhxsv5\LaravelS\Swoole\Task\Task;
{
private \$data;
private \$result;
public function __construct(\$data)
{
\$this->data = \$data;
}
public function handle()
{
\Log::info(__CLASS__ . ':handle start', [\$this->data]);
sleep(2);// Simulate the slow codes
// The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually.
\$this->result = 'the result of ' . \$this->data;
}
// Optional, finish event, the logic of after task handling, run in worker process, CAN deliver task
public function finish()
{
\Log::info(__CLASS__ . ':finish start', [\$this->result]);
}
}``````

``````// Create instance of TestTask and deliver it, "deliver" is asynchronous.
// \$task->setTries(3); // When an error occurs, try 3 times in total
var_dump(\$ret);// Return true if sucess, otherwise false``````

## Millisecond cron job

Wrapper cron job base on Swoole's Millisecond Timer, replace `Linux` `Crontab`.

1.Create cron job class.

``````namespace App\Jobs\Timer;
use Swoole\Coroutine;
use Hhxsv5\LaravelS\Swoole\Timer\CronJob;
class TestCronJob extends CronJob
{
protected \$i = 0;
// !!! The `interval` and `isImmediate` of cron job can be configured in two ways(pick one of two): one is to overload the corresponding method, and the other is to pass parameters when registering cron job.
// --- Override the corresponding method to return the configuration: begin
public function interval()
{
return 1000;// Run every 1000ms
}
public function isImmediate()
{
return false;// Whether to trigger `run` immediately after setting up
}
// --- Override the corresponding method to return the configuration: end
public function run()
{
\Log::info(__METHOD__, ['start', \$this->i, microtime(true)]);
// do something
// sleep(1); // Swoole < 2.1
Coroutine::sleep(1); // Swoole>=2.1 Coroutine will be automatically created for run().
\$this->i++;
\Log::info(__METHOD__, ['end', \$this->i, microtime(true)]);

if (\$this->i >= 10) { // Run 10 times only
\Log::info(__METHOD__, ['stop', \$this->i, microtime(true)]);
\$this->stop(); // Stop this cron job, but it will run again after restart/reload.
// Deliver task in CronJob, but NOT support callback finish() of task.
// Note: Modify task_ipc_mode to 1 or 2 in config/laravels.php, see https://www.swoole.co.uk/docs/modules/swoole-server/configuration
var_dump(\$ret);
}
// The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually.
}
}``````

2.Register cron job.

``````// Register cron jobs in file "config/laravels.php"
[
// ...
'timer'          => [
'enable' => true, // Enable Timer
'jobs'   => [ // The list of cron job
// Enable LaravelScheduleJob to run `php artisan schedule:run` every 1 minute, replace Linux Crontab
// \Hhxsv5\LaravelS\Illuminate\LaravelScheduleJob::class,
// Two ways to configure parameters:
// [\App\Jobs\Timer\TestCronJob::class, [1000, true]], // Pass in parameters when registering
\App\Jobs\Timer\TestCronJob::class, // Override the corresponding method to return the configuration
],
// Enable the global lock to ensure that only one instance starts the timer when deploying multiple instances. This feature depends on Redis, please see https://laravel.com/docs/7.x/redis
'global_lock'     => false,
'global_lock_key' => config('app.name', 'Laravel'),
],
// ...
];``````

3.Note: it will launch multiple timers when build the server cluster, so you need to make sure that launch one timer only to avoid running repetitive task.

4.LaravelS `v3.4.0` starts to support the hot restart [Reload] `Timer` process. After LaravelS receives the `SIGUSR1` signal, it waits for `max_wait_time`(default 5) seconds to end the process, then the `Manager` process will pull up the `Timer` process again.

5.If you only need to use `minute-level` scheduled tasks, it is recommended to enable `Hhxsv5\LaravelS\Illuminate\LaravelScheduleJob` instead of Linux Crontab, so that you can follow the coding habits of Laravel task scheduling and configure `Kernel`.

``````// app/Console/Kernel.php
protected function schedule(Schedule \$schedule)
{
// runInBackground() will start a new child process to execute the task. This is asynchronous and will not affect the execution timing of other tasks.
\$schedule->command(TestCommand::class)->runInBackground()->everyMinute();
}``````

## Automatically reload after modifying code

Via `inotify`, support Linux only.

1.Install inotify extension.

2.Turn on the switch in Settings.

3.Notice: Modify the file only in `Linux` to receive the file change events. It's recommended to use the latest Docker. Vagrant Solution.

Via `fswatch`, support OS X/Linux/Windows.

1.Install fswatch.

2.Run command in your project root directory.

``````# Watch current directory
./bin/fswatch
# Watch app directory
./bin/fswatch ./app``````

Via `inotifywait`, support Linux.

1.Install inotify-tools.

2.Run command in your project root directory.

``````# Watch current directory
./bin/inotify
# Watch app directory
./bin/inotify ./app``````

When the above methods does not work, the ultimate solution: set `max_request=1,worker_num=1`, so that `Worker` process will restart after processing a request. The performance of this method is very poor, `so only development environment use`.

## Get the instance of `SwooleServer` in your project

``````/**
* \$swoole is the instance of `Swoole\WebSocket\Server` if enable WebSocket server, otherwise `Swoole\Http\Server`
* @var \Swoole\WebSocket\Server|\Swoole\Http\Server \$swoole
*/
\$swoole = app('swoole');
var_dump(\$swoole->stats());
\$swoole->push(\$fd, 'Push WebSocket message');``````

## Use `SwooleTable`

1.Define Table, support multiple.

All defined tables will be created before Swoole starting.

``````// in file "config/laravels.php"
[
// ...
'swoole_tables'  => [
// Scene：bind UserId & FD in WebSocket
'ws' => [// The Key is table name, will add suffix "Table" to avoid naming conflicts. Here defined a table named "wsTable"
'size'   => 102400,// The max size
'column' => [// Define the columns
['name' => 'value', 'type' => \Swoole\Table::TYPE_INT, 'size' => 8],
],
],
//...Define the other tables
],
// ...
];``````

2.Access `Table`: all table instances will be bound on `SwooleServer`, access by `app('swoole')->xxxTable`.

``````namespace App\Services;
use Hhxsv5\LaravelS\Swoole\WebSocketHandlerInterface;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;
class WebSocketService implements WebSocketHandlerInterface
{
/**@var \Swoole\Table \$wsTable */
private \$wsTable;
public function __construct()
{
\$this->wsTable = app('swoole')->wsTable;
}
// Scene：bind UserId & FD in WebSocket
public function onOpen(Server \$server, Request \$request)
{
// var_dump(app('swoole') === \$server);// The same instance
/**
* Get the currently logged in user
* This feature requires that the path to establish a WebSocket connection go through middleware such as Authenticate.
* E.g:
* Browser side: var ws = new WebSocket("ws://127.0.0.1:5200/ws");
* Then the /ws route in Laravel needs to add the middleware like Authenticate.
* Route::get('/ws', function () {
*     // Respond any content with status code 200
*     return 'websocket';
* })->middleware(['auth']);
*/
// \$user = Auth::user();
// \$userId = \$user ? \$user->id : 0; // 0 means a guest user who is not logged in
\$userId = mt_rand(1000, 10000);
// if (!\$userId) {
//     // Disconnect the connections of unlogged users
//     \$server->disconnect(\$request->fd);
//     return;
// }
\$this->wsTable->set('uid:' . \$userId, ['value' => \$request->fd]);// Bind map uid to fd
\$this->wsTable->set('fd:' . \$request->fd, ['value' => \$userId]);// Bind map fd to uid
\$server->push(\$request->fd, "Welcome to LaravelS #{\$request->fd}");
}
public function onMessage(Server \$server, Frame \$frame)
{
foreach (\$this->wsTable as \$key => \$row) {
if (strpos(\$key, 'uid:') === 0 && \$server->isEstablished(\$row['value'])) {
\$content = sprintf('Broadcast: new message "%s" from #%d', \$frame->data, \$frame->fd);
\$server->push(\$row['value'], \$content);
}
}
}
public function onClose(Server \$server, \$fd, \$reactorId)
{
\$uid = \$this->wsTable->get('fd:' . \$fd);
if (\$uid !== false) {
\$this->wsTable->del('uid:' . \$uid['value']); // Unbind uid map
}
\$this->wsTable->del('fd:' . \$fd);// Unbind fd map
\$server->push(\$fd, "Goodbye #{\$fd}");
}
}``````

## Multi-port mixed protocol

To make our main server support more protocols not just Http and WebSocket, we bring the feature `multi-port mixed protocol` of Swoole in LaravelS and name it `Socket`. Now, you can build `TCP/UDP` applications easily on top of Laravel.

Create `Socket` handler class, and extend `Hhxsv5\LaravelS\Swoole\Socket\{TcpSocket|UdpSocket|Http|WebSocket}`.

``````namespace App\Sockets;
use Hhxsv5\LaravelS\Swoole\Socket\TcpSocket;
use Swoole\Server;
class TestTcpSocket extends TcpSocket
{
public function onConnect(Server \$server, \$fd, \$reactorId)
{
\Log::info('New TCP connection', [\$fd]);
\$server->send(\$fd, 'Welcome to LaravelS.');
}
public function onReceive(Server \$server, \$fd, \$reactorId, \$data)
{
\$server->send(\$fd, 'LaravelS: ' . \$data);
if (\$data === "quit\r\n") {
\$server->send(\$fd, 'LaravelS: bye' . PHP_EOL);
\$server->close(\$fd);
}
}
public function onClose(Server \$server, \$fd, \$reactorId)
{
\Log::info('Close TCP connection', [\$fd]);
\$server->send(\$fd, 'Goodbye');
}
}``````

These `Socket` connections share the same worker processes with your `HTTP`/`WebSocket` connections. So it won't be a problem at all if you want to deliver tasks, use `SwooleTable`, even Laravel components such as DB, Eloquent and so on. At the same time, you can access `Swoole\Server\Port` object directly by member property `swoolePort`.

``````public function onReceive(Server \$server, \$fd, \$reactorId, \$data)
{
\$port = \$this->swoolePort; // Get the `Swoole\Server\Port` object
}``````
``````namespace App\Http\Controllers;
class TestController extends Controller
{
public function test()
{
/**@var \Swoole\Http\Server|\Swoole\WebSocket\Server \$swoole */
\$swoole = app('swoole');
// \$swoole->ports: Traverse all Port objects, https://www.swoole.co.uk/docs/modules/swoole-server/multiple-ports
\$port = \$swoole->ports[0]; // Get the `Swoole\Server\Port` object, \$port[0] is the port of the main server
foreach (\$port->connections as \$fd) { // Traverse all connections
// \$swoole->send(\$fd, 'Send tcp message');
// if(\$swoole->isEstablished(\$fd)) {
//     \$swoole->push(\$fd, 'Send websocket message');
// }
}
}
}``````

Register Sockets.

``````// Edit `config/laravels.php`
//...
'sockets' => [
[
'host'     => '127.0.0.1',
'port'     => 5291,
'type'     => SWOOLE_SOCK_TCP,// Socket type: SWOOLE_SOCK_TCP/SWOOLE_SOCK_TCP6/SWOOLE_SOCK_UDP/SWOOLE_SOCK_UDP6/SWOOLE_UNIX_DGRAM/SWOOLE_UNIX_STREAM
'open_eof_check' => true,
'package_eof'    => "\r\n",
],
'handler'  => \App\Sockets\TestTcpSocket::class,
'enable'   => true, // whether to enable, default true
],
],``````

About the heartbeat configuration, it can only be set on the `main server` and cannot be configured on `Socket`, but the `Socket` inherits the heartbeat configuration of the `main server`.

For TCP socket, `onConnect` and `onClose` events will be blocked when `dispatch_mode` of Swoole is `1/3`, so if you want to unblock these two events please set `dispatch_mode` to `2/4/5`.

``````'swoole' => [
//...
'dispatch_mode' => 2,
//...
];``````

Test.

TCP: `telnet 127.0.0.1 5291`

UDP: [Linux] `echo "Hello LaravelS" > /dev/udp/127.0.0.1/5292`

Register example of other protocols.

• UDP
• Http
• WebSocket: The main server must `turn on WebSocket`, that is, set `websocket.enable` to `true`.

## Coroutine

Swoole Coroutine

Warning: The order of code execution in the coroutine is out of order. The data of the request level should be isolated by the coroutine ID. However, there are many singleton and static attributes in Laravel/Lumen, the data between different requests will affect each other, it's `Unsafe`. For example, the database connection is a singleton, the same database connection shares the same PDO resource. This is fine in the synchronous blocking mode, but it does not work in the asynchronous coroutine mode. Each query needs to create different connections and maintain IO state of different connections, which requires a connection pool.

`DO NOT` enable the coroutine, only the custom process can use the coroutine.

## Custom process

Support developers to create special work processes for monitoring, reporting, or other special tasks. Refer addProcess.

Create Proccess class, implements CustomProcessInterface.

``````namespace App\Processes;
use Hhxsv5\LaravelS\Swoole\Process\CustomProcessInterface;
use Swoole\Coroutine;
use Swoole\Http\Server;
use Swoole\Process;
class TestProcess implements CustomProcessInterface
{
/**
*/
private static \$quit = false;

public static function callback(Server \$swoole, Process \$process)
{
// The callback method cannot exit. Once exited, Manager process will automatically create the process
while (!self::\$quit) {
\Log::info('Test process: running');
// sleep(1); // Swoole < 2.1
Coroutine::sleep(1); // Swoole>=2.1: Coroutine & Runtime will be automatically enabled for callback().
// Deliver task in custom process, but NOT support callback finish() of task.
// Note: Modify task_ipc_mode to 1 or 2 in config/laravels.php, see https://www.swoole.co.uk/docs/modules/swoole-server/configuration
var_dump(\$ret);
// The upper layer will catch the exception thrown in the callback and record it in the Swoole log, and then this process will exit. The Manager process will re-create the process after 3 seconds, so developers need to try/catch to catch the exception by themselves to avoid frequent process creation.
// throw new \Exception('an exception');
}
}
// Requirements: LaravelS >= v3.4.0 & callback() must be async non-blocking program.
public static function onReload(Server \$swoole, Process \$process)
{
// Stop the process...
// Then end process
self::\$quit = true;
// \$process->exit(0); // Force exit process
}
// Requirements: LaravelS >= v3.7.4 & callback() must be async non-blocking program.
public static function onStop(Server \$swoole, Process \$process)
{
// Stop the process...
// Then end process
\Log::info('Test process: stopping');
self::\$quit = true;
// \$process->exit(0); // Force exit process
}
}``````

Register TestProcess.

``````// Edit `config/laravels.php`
// ...
'processes' => [
'test' => [ // Key name is process name
'class'    => \App\Processes\TestProcess::class,
'redirect' => false, // Whether redirect stdin/stdout, true or false
'pipe'     => 0,     // The type of pipeline, 0: no pipeline 1: SOCK_STREAM 2: SOCK_DGRAM
'enable'   => true,  // Whether to enable, default true
//'num'    => 3   // To create multiple processes of this class, default is 1
//'queue'    => [ // Enable message queue as inter-process communication, configure empty array means use default parameters
//    'msg_key'  => 0,    // The key of the message queue. Default: ftok(__FILE__, 1).
//    'mode'     => 2,    // Communication mode, default is 2, which means contention mode
//    'capacity' => 8192, // The length of a single message, is limited by the operating system kernel parameters. The default is 8192, and the maximum is 65536
//],
//'restart_interval' => 5, // After the process exits abnormally, how many seconds to wait before restarting the process, default 5 seconds
],
],``````

Note: The callback() cannot quit. If quit, the Manager process will re-create the process.

Example: Write data to a custom process.

``````// config/laravels.php
'processes' => [
'test' => [
'class'    => \App\Processes\TestProcess::class,
'redirect' => false,
'pipe'     => 1,
],
],``````
``````// app/Processes/TestProcess.php
public static function callback(Server \$swoole, Process \$process)
{
\$process->write('TestProcess: ' . \$data);
}
}``````
``````// app/Http/Controllers/TestController.php
public function testProcessWrite()
{
/**@var \Swoole\Process \$process */
\$process = app('swoole')->customProcesses['test'];
\$process->write('TestController: write data' . time());
}``````

## Common components

### Apollo

`LaravelS` will pull the `Apollo` configuration and write it to the `.env` file when starting. At the same time, `LaravelS` will start the custom process `apollo` to monitor the configuration and automatically `reload` when the configuration changes.

Enable Apollo: add `--enable-apollo` and Apollo parameters to the startup parameters.

``php bin/laravels start --enable-apollo --apollo-server=http://127.0.0.1:8080 --apollo-app-id=LARAVEL-S-TEST``

``````// Edit `config/laravels.php`
'processes' => Hhxsv5\LaravelS\Components\Apollo\Process::getDefinition(),``````
``````// When there are other custom process configurations
'processes' => [
'test' => [
'class'    => \App\Processes\TestProcess::class,
'redirect' => false,
'pipe'     => 1,
],
// ...
] + Hhxsv5\LaravelS\Components\Apollo\Process::getDefinition(),``````

List of available parameters.

### Prometheus

Support Prometheus monitoring and alarm, Grafana visually view monitoring metrics. Please refer to Docker Compose for the environment construction of Prometheus and Grafana.

Require extension APCu >= 5.0.0, please install it by `pecl install apcu`.

Copy the configuration file `prometheus.php` to the `config` directory of your project. Modify the configuration as appropriate.

``````# Execute commands in the project root directory
cp vendor/hhxsv5/laravel-s/config/prometheus.php config/``````

If your project is `Lumen`, you also need to manually load the configuration `\$app->configure('prometheus');` in `bootstrap/app.php`.

Configure `global` middleware: `Hhxsv5\LaravelS\Components\Prometheus\RequestMiddleware::class`. In order to count the request time consumption as accurately as possible, `RequestMiddleware` must be the `first` global middleware, which needs to be placed in front of other middleware.

Register ServiceProvider: `Hhxsv5\LaravelS\Components\Prometheus\ServiceProvider::class`.

Configure the CollectorProcess in `config/laravels.php` to collect the metrics of Swoole Worker/Task/Timer processes regularly.

``'processes' => Hhxsv5\LaravelS\Components\Prometheus\CollectorProcess::getDefinition(),``

Create the route to output metrics.

``````use Hhxsv5\LaravelS\Components\Prometheus\Exporter;

Route::get('/actuator/prometheus', function () {
\$result = app(Exporter::class)->render();
return response(\$result, 200, ['Content-Type' => Exporter::REDNER_MIME_TYPE]);
});``````

Complete the configuration of Prometheus and start it.

``````global:
scrape_interval: 5s
scrape_timeout: 5s
evaluation_interval: 30s
scrape_configs:
- job_name: laravel-s-test
honor_timestamps: true
metrics_path: /actuator/prometheus
scheme: http
follow_redirects: true
static_configs:
- targets:
- 127.0.0.1:5200 # The ip and port of the monitored service
# Dynamically discovered using one of the supported service-discovery mechanisms
# https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config
# - job_name: laravels-eureka
#   honor_timestamps: true
#   scrape_interval: 5s
#   metrics_path: /actuator/prometheus
#   scheme: http
#   follow_redirects: true
# eureka_sd_configs:
# - server: http://127.0.0.1:8080/eureka
#   follow_redirects: true
#   refresh_interval: 5s``````

Start Grafana, then import panel json.

## Other features

### Configure Swoole events

Supported events:

1.Create an event class to implement the corresponding interface.

``````namespace App\Events;
use Hhxsv5\LaravelS\Swoole\Events\ServerStartInterface;
use Swoole\Atomic;
use Swoole\Http\Server;
class ServerStartEvent implements ServerStartInterface
{
public function __construct()
{
}
public function handle(Server \$server)
{
// Initialize a global counter (available across processes)
\$server->atomicCount = new Atomic(2233);

// Invoked in controller: app('swoole')->atomicCount->get();
}
}``````
``````namespace App\Events;
use Hhxsv5\LaravelS\Swoole\Events\WorkerStartInterface;
use Swoole\Http\Server;
class WorkerStartEvent implements WorkerStartInterface
{
public function __construct()
{
}
public function handle(Server \$server, \$workerId)
{
// Initialize a database connection pool
// DatabaseConnectionPool::init();
}
}``````

2.Configuration.

``````// Edit `config/laravels.php`
'event_handlers' => [
'ServerStart' => [\App\Events\ServerStartEvent::class], // Trigger events in array order
'WorkerStart' => [\App\Events\WorkerStartEvent::class],
],``````

### Serverless

#### Alibaba Cloud Function Compute

1.Modify `bootstrap/app.php` and set the storage directory. Because the project directory is read-only, the `/tmp` directory can only be read and written.

``\$app->useStoragePath(env('APP_STORAGE_PATH', '/tmp/storage'));``

2.Create a shell script `laravels_bootstrap` and grant `executable permission`.

``````#!/usr/bin/env bash
set +e

# Create storage-related directories
mkdir -p /tmp/storage/app/public
mkdir -p /tmp/storage/framework/cache
mkdir -p /tmp/storage/framework/sessions
mkdir -p /tmp/storage/framework/testing
mkdir -p /tmp/storage/framework/views
mkdir -p /tmp/storage/logs

# Set the environment variable APP_STORAGE_PATH, please make sure it's the same as APP_STORAGE_PATH in .env
export APP_STORAGE_PATH=/tmp/storage

# Start LaravelS
php bin/laravels start``````

3.Configure `template.xml`.

``````ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
laravel-s-demo:
Type: 'Aliyun::Serverless::Service'
Properties:
Description: 'LaravelS Demo for Serverless'
fc-laravel-s:
Type: 'Aliyun::Serverless::Function'
Properties:
Handler: laravels.handler
Runtime: custom
MemorySize: 512
Timeout: 30
CodeUri: ./
InstanceConcurrency: 10
EnvironmentVariables:
BOOTSTRAP_FILE: laravels_bootstrap
``````

## Important notices

### Singleton Issue

Under FPM mode, singleton instances will be instantiated and recycled in every request, request start=>instantiate instance=>request end=>recycled instance.

Under Swoole Server, All singleton instances will be held in memory, different lifetime from FPM, request start=>instantiate instance=>request end=>do not recycle singleton instance. So need developer to maintain status of singleton instances in every request.

Common solutions:

Write a `XxxCleaner` class to clean up the singleton object state. This class implements the interface `Hhxsv5\LaravelS\Illuminate\Cleaners\CleanerInterface` and then registers it in `cleaners` of `laravels.php`.

`Reset` status of singleton instances by `Middleware`.

Re-register `ServiceProvider`, add `XxxServiceProvider` into `register_providers` of file `laravels.php`. So that reinitialize singleton instances in every request Refer.

### Known issues

Known issues: a package of known issues and solutions.

### Debugging method

Logging; if you want to output to the console, you can use `stderr`, Log::channel('stderr')->debug('debug message').

Laravel Dump Server(Laravel 5.7 has been integrated by default).

Read request by `Illuminate\Http\Request` Object, \$_ENV is readable, \$_SERVER is partially readable, `CANNOT USE` \$_GET/\$_POST/\$_FILES/\$_COOKIE/\$_REQUEST/\$_SESSION/\$GLOBALS.

``````public function form(\Illuminate\Http\Request \$request)
{
\$name = \$request->input('name');
\$all = \$request->all();
\$photo = \$request->file('photo');
// Call getContent() to get the raw POST body, instead of file_get_contents('php://input')
\$rawContent = \$request->getContent();
//...
}``````

### Output response

Respond by `Illuminate\Http\Response` Object, compatible with echo/vardump()/print_r()，`CANNOT USE` functions dd()/exit()/die()/header()/setcookie()/http_response_code().

``````public function json()
{
}``````

### Persistent connection

`Singleton connection` will be resident in memory, it is recommended to turn on `persistent connection` for better performance.

1. Database connection, it `will` reconnect automatically `immediately` after disconnect.
``````// config/database.php
'connections' => [
'my_conn' => [
'driver'    => 'mysql',
'host'      => env('DB_MY_CONN_HOST', 'localhost'),
'port'      => env('DB_MY_CONN_PORT', 3306),
'database'  => env('DB_MY_CONN_DATABASE', 'forge'),
'charset'   => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix'    => '',
'strict'    => false,
'options'   => [
// Enable persistent connection
\PDO::ATTR_PERSISTENT => true,
],
],
],``````
1. Redis connection, it `won't` reconnect automatically `immediately` after disconnect, and will throw an exception about lost connection, reconnect next time. You need to make sure that `SELECT DB` correctly before operating Redis every time.
``````// config/database.php
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'), // It is recommended to use phpredis for better performance.
'default' => [
'host'       => env('REDIS_HOST', 'localhost'),
'port'       => env('REDIS_PORT', 6379),
'database'   => 0,
'persistent' => true, // Enable persistent connection
],
],``````

Avoid using global variables. If necessary, please clean or reset them manually.

Infinitely appending element into `static`/`global` variable will lead to OOM(Out of Memory).

``````class Test
{
public static \$array = [];
public static \$string = '';
}

// Controller
public function test(Request \$req)
{
// Out of Memory
Test::\$array[] = \$req->input('param1');
Test::\$string .= \$req->input('param2');
}``````

Memory leak detection method

Modify `config/laravels.php`: `worker_num=1, max_request=1000000`, remember to change it back after test;

Add routing `/debug-memory-leak` without `route middleware` to observe the memory changes of the `Worker` process;

Start `LaravelS` and request `/debug-memory-leak` until `diff_mem` is less than or equal to zero; if `diff_mem` is always greater than zero, it means that there may be a memory leak in `Global Middleware` or `Laravel Framework`;

After completing `Step 3`, `alternately` request the business routes and `/debug-memory-leak` (It is recommended to use `ab`/`wrk` to make a large number of requests for business routes), the initial increase in memory is normal. After a large number of requests for the business routes, if `diff_mem` is always greater than zero and `curr_mem` continues to increase, there is a high probability of memory leak; If `curr_mem` always changes within a certain range and does not continue to increase, there is a low probability of memory leak.

If you still can't solve it, max_request is the last guarantee.

Pressure test