Skip to content

Commit e4e0459

Browse files
committed
Merge branch '0.x'
2 parents 67ea599 + b3398e3 commit e4e0459

27 files changed

+559
-33
lines changed

.env.dev.dist

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@
1010

1111
COMPOSE_PROJECT_NAME=reactphp_foundation
1212

13+
TIMEZONE=Europe/Moscow
14+
SOURCE_PATH=.
15+
DEPLOYMENT_PATH=/var/www/foundation
16+
17+
# php
18+
PHP_VERSION=7.4
19+
1320
# app
1421
APP_ENV=dev
1522
APP_SERVER_PORT=6636
23+
# prebuilt image for stack
24+
# see Dockerfile-stack from the 'docker' directory
25+
APP_STACK_IMAGE_NAME=
26+
APP_STACK_IMAGE_VERSION=
27+
28+
# haproxy
29+
HAPROXY_VERSION=2.2
30+
HAPROXY_PORT_EXPOSE=6637

.github/images/haproxy_stats.png

232 KB
Loading
126 KB
Loading

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
.idea/
22

33
.env
4+
docker-compose.yml
5+
docker-compose.stack.yml
46

57
vendor/
68

CHANGELOG.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010

1111
- No changes yet.
1212

13+
## [0.2.0] - 2020-07-20
14+
### Added
15+
16+
- Shutdown service that gracefully terminates the event loop
17+
after corresponding signals are received (using `ext-pcntl`).
18+
- Docker-compose configuration for local development.
19+
- Docker stack configuration for [Swarm mode](https://docs.docker.com/engine/swarm/key-concepts),
20+
installation guide in README.md.
21+
- [HAProxy](https://www.haproxy.com) configuration to balance requests
22+
between multiple application replicas within docker network using built-in DNS service.
23+
1324
## [0.1.0] - 2020-06-22
1425
### Added
1526

@@ -18,8 +29,8 @@ with `.env` and `parameters.yml` support.
1829
- Main `Application` class that starts
1930
the async socket server and the event loop.
2031
- `ServerInterface` and the `HandlerInterface`, example handler implementation.
21-
- Non-blocking logger (based on [wyrihaximus/react-psr-3-stdio](https://github.com/WyriHaximus/reactphp-psr-3-stdio))
32+
- Non-blocking logger (based on [wyrihaximus/react-psr-3-stdio](https://github.com/WyriHaximus/reactphp-psr-3-stdio)).
2233

23-
[Unreleased]: https://github.com/itnelo/reactphp-foundation/compare/0.1.0...0.x
34+
[Unreleased]: https://github.com/itnelo/reactphp-foundation/compare/0.2.0...0.x
2435
[0.2.0]: https://github.com/itnelo/reactphp-foundation/compare/0.1.0..0.2.0
2536
[0.1.0]: https://github.com/itnelo/reactphp-foundation/releases/tag/0.1.0

README.md

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,129 @@ This skeleton for self-sufficient, asynchronous microservice contains:
1818
- `symfony/config`
1919
- `symfony/yaml`
2020

21-
It follows strong SOLID design and fully PSR-compatible,
22-
with PHP 7.4+ features in mind
23-
(starting with typed properties).
21+
It follows strong SOLID design and fully PSR-compatible, built
22+
with PHP 7.4+ features in mind (starting with typed properties).
2423

2524
It is also relatively lightweight and takes benefits
2625
from both [Symfony](https://github.com/symfony/symfony) components
2726
and [ReactPHP](https://github.com/reactphp/reactphp)
2827
without raising up a heavy artillery setup.
2928

29+
## Installation
30+
31+
### Docker Swarm
32+
33+
This setup provides a basic service scalability using [Swarm mode](https://docs.docker.com/engine/swarm/key-concepts).
34+
For testing purposes, let's assume we have the following servers:
35+
36+
```
37+
192.169.56.1 # our pc, manager node; haproxy
38+
192.169.56.10 # vm, worker node; app instance
39+
192.169.56.20 # vm, worker node; app instance
40+
192.169.56.30 # vm, worker node; app instance
41+
```
42+
43+
**Pre-requirements**. Ensure all necessary ports are accessible on each machine
44+
or it may cause implicit problems during communication between nodes.
45+
A common symptom is a successful `nslookup` for all replicas within overlay,
46+
but a failed `ping` of some of them. Here is a quick list:
47+
48+
```
49+
$ sudo ufw status
50+
Status: active
51+
52+
To Action From
53+
-- ------ ----
54+
22/tcp ALLOW 192.169.56.0/24
55+
2376/tcp ALLOW 192.169.56.0/24
56+
2377/tcp ALLOW 192.169.56.0/24
57+
4789/udp ALLOW 192.169.56.0/24
58+
7946/tcp ALLOW 192.169.56.0/24
59+
7946/udp ALLOW 192.169.56.0/24
60+
```
61+
62+
**Limitations**. This setup assumes you are using a single haproxy instance,
63+
on the fixed node in the cluster and only that node will have its ports published:
64+
65+
![how it works schema](https://github.com/itnelo/reactphp-foundation/blob/master/.github/images/how_it_works_schema.png)
66+
67+
**Step 1**. Create a manager node (for haproxy with exposed ports):
68+
69+
```
70+
# our pc
71+
$ docker swarm init --advertise-addr 192.169.56.1
72+
```
73+
74+
And a few worker nodes:
75+
76+
```
77+
# vm
78+
$ docker swarm join --token JOIN_TOKEN --advertise-addr 192.169.56.10 192.169.56.1
79+
```
80+
81+
where `JOIN_TOKEN` is a parameter obtained by `docker swarm join-token worker` on the manager node.
82+
Repeat this action for all other worker servers in your cluster
83+
using their own advertise addresses.
84+
85+
**Step 2**. Assign geography labels to be able to evenly distribute
86+
containers between all available servers:
87+
88+
```
89+
# our pc
90+
$ docker node update --label-add provider_location_machine=do.fra1.d1 HOSTNAME
91+
```
92+
93+
where `HOSTNAME` is a server identifier, see `docker node ls` on the manager node.
94+
95+
**Step 3**. Clone the repository and adjust environment variables:
96+
97+
```
98+
# our pc
99+
$ git clone git@github.com:itnelo/reactphp-foundation.git my-service && cd "$_"
100+
$ cp .env.dev.dist .env
101+
```
102+
103+
Fill `APP_STACK_IMAGE_NAME` and `APP_STACK_IMAGE_VERSION` with your image metadata
104+
from the desired registry.
105+
106+
**Step 4**. Apply stack configuration:
107+
108+
```
109+
# our pc
110+
$ set -a; . .env; set +a && envsubst < docker-compose.stack.yml.dist > docker-compose.stack.yml
111+
```
112+
113+
You should also adjust placement constraints
114+
(according to **Step 2**) to ensure Swarm scheduler is able to assign tasks
115+
to the configured nodes. Check `haproxy.stack.cfg` from the `docker` directory
116+
if you have changed some ports or just use a custom haproxy image as well.
117+
118+
**Step 5**. Deploy services:
119+
120+
```
121+
# our pc
122+
$ docker stack deploy --orchestrator swarm --compose-file docker-compose.stack.yml my-service
123+
```
124+
125+
By accessing `192.169.56.1:6637/stats` (if you stick to the default configuration;
126+
use your manager node IP) a rendered page with backend statistics should be available:
127+
128+
![haproxy stats](https://github.com/itnelo/reactphp-foundation/blob/master/.github/images/haproxy_stats.png)
129+
130+
To rebalance app replicas between nodes, after one is added or removed, use:
131+
132+
```
133+
$ docker service update --force my-service_app
134+
```
135+
30136
## See also
31137

32138
- [driftphp/driftphp](https://github.com/driftphp/driftphp)
33139
If you are looking for a deeper Symfony integration, with Kernel adaptation
34140
to async environment.
141+
- [thesecretlivesofdata.com/raft](http://thesecretlivesofdata.com/raft/)
142+
A helpful visualization to understand how the distributed consensus algorithm,
143+
used by Docker Swarm, works.
35144

36145
## Changelog
37146

bin/app

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@ $container = new ContainerBuilder($parameterBag);
4545
// loading service definitions.
4646
$definitionLoader = new YamlFileLoader($container, new FileLocator());
4747
$definitionFilePaths = [
48-
__DIR__ . '/../config/request_handlers.yml',
49-
__DIR__ . '/../config/loggers.yml',
50-
__DIR__ . '/../config/async/streams.yml',
51-
__DIR__ . '/../config/async/server.yml',
52-
__DIR__ . '/../config/async/event_loop.yml',
48+
__DIR__ . '/../config/services/request_handlers.yml',
49+
__DIR__ . '/../config/services/loggers.yml',
50+
__DIR__ . '/../config/services/shutdown.yml',
51+
__DIR__ . '/../config/services/async/streams.yml',
52+
__DIR__ . '/../config/services/async/server.yml',
53+
__DIR__ . '/../config/services/async/event_loop.yml',
5354
__DIR__ . '/../config/services.yml',
5455
];
5556

@@ -58,7 +59,7 @@ foreach ($definitionFilePaths as $definitionFilePath) {
5859
}
5960

6061
// we are starting an async web server with some concurrent code
61-
// so we don't need any caching/preloading logic for this case.
62+
// so we don't need any container caching logic for this case.
6263
$container->compile(true);
6364

6465
$application = new Application($container);

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"minimum-stability": "stable",
2626
"require": {
2727
"php": ">=7.4",
28+
"ext-pcntl": ">=7.4",
2829
"psr/container": "^1.0",
2930
"psr/http-message": "^1.0",
3031
"psr/log": "^1.1",

composer.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/parameters.yml.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ parameters:
55
app.locale: en
66

77
# Socket server configuration
8+
app.server.host: 'tcp://0.0.0.0'
89
app.server.port: '%env(int:APP_SERVER_PORT)%'

0 commit comments

Comments
 (0)