agoo 2.1.3 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of agoo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +6 -2
- data/ext/agoo/con.c +0 -1
- data/ext/agoo/log.c +60 -12
- data/ext/agoo/log.h +3 -0
- data/ext/agoo/page.c +0 -1
- data/ext/agoo/request.c +5 -1
- data/ext/agoo/server.c +121 -33
- data/ext/agoo/server.h +5 -0
- data/lib/agoo/version.rb +1 -1
- data/lib/rack/handler/agoo.rb +13 -2
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2aebba91a863e1bdbaa53c1d31560728968f7d2e27c27af21691bf8d502f4afb
|
4
|
+
data.tar.gz: e0b3efd1eaa9dbe2629738c3b8ecdc0e61e208ae94f54598a182791c23d4873c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82944a0b58ea473b2b4228c3d59901b29fdae19ef3ad1711a84980d7d2028418abaeb9f05b93d4d7f8649e0ceeb3bebaaaa5d448ac48dcbc647cdeeaf494a169
|
7
|
+
data.tar.gz: 84894610f09ea3a7c089cc11e4edc3479bf641dda306f7e9dbf89fc88e4c0c8018d0d84a8e7df98c6296215f9c4a26cacc6b3c593e63b869308177f820411cee
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
### 2.2.0 - 2018-05-30
|
4
|
+
|
5
|
+
- Clustering now supported with forked workers making Agoo even faster.
|
6
|
+
|
7
|
+
- Changed addition header keys to be all uppercase and also replace `-` with `_` to match Rack unit tests instead of the spec.
|
8
|
+
|
3
9
|
### 2.1.3 - 2018-05-16
|
4
10
|
|
5
11
|
- Optimized `rackup` to look for files in the root directory before calling rackup as the default. The root is now set to `./public` instead of `.`. The `:rmux` (rack multiplex) turns that off.
|
data/README.md
CHANGED
@@ -52,13 +52,17 @@ use of rack compatible gems such as Hanami and Rails. Agoo also supports WebSock
|
|
52
52
|
|
53
53
|
## News
|
54
54
|
|
55
|
+
- Clustered Agoo is ready. For slower application and a machine with multiple
|
56
|
+
cores a significant improvement is performance is realized. The application
|
57
|
+
must be stateless in that no data is shared between workers.
|
58
|
+
|
55
59
|
- WebSocket and SSE are supported and a PR has been submitted to updated the
|
56
60
|
Rack spec. Go over to the [proposed Rack
|
57
61
|
extension](https://github.com/rack/rack/pull/1272) and give it a look and a
|
58
62
|
thumbs-up or heart if you like it.
|
59
63
|
|
60
|
-
- Agoo now serves Rails static assets more than
|
61
|
-
default Puma. Thats right, [
|
64
|
+
- Agoo now serves Rails static assets more than 8000 times faster than the
|
65
|
+
default Puma. Thats right, [8000 times faster](misc/rails.md).
|
62
66
|
|
63
67
|
## Releases
|
64
68
|
|
data/ext/agoo/con.c
CHANGED
data/ext/agoo/log.c
CHANGED
@@ -25,6 +25,10 @@ static const char log_name[] = "agoo.log";
|
|
25
25
|
static const char log_prefix[] = "agoo.log.";
|
26
26
|
static const char log_format[] = "%s/agoo.log.%d";
|
27
27
|
|
28
|
+
static const char log_pid_name[] = "agoo.log_%d";
|
29
|
+
static const char log_pid_prefix[] = "agoo.log_%d.";
|
30
|
+
static const char log_pid_format[] = "%s/agoo.log_%d.%d";
|
31
|
+
|
28
32
|
static struct _Color colors[] = {
|
29
33
|
{ .name = "black", .ansi = "\033[30;1m" },
|
30
34
|
{ .name = "red", .ansi = "\033[31;1m" },
|
@@ -174,19 +178,30 @@ remove_old_logs() {
|
|
174
178
|
char *end;
|
175
179
|
char path[1024];
|
176
180
|
DIR *dir = opendir(the_log.dir);
|
177
|
-
|
181
|
+
char prefix[32];
|
182
|
+
int psize;
|
183
|
+
char name[32];
|
184
|
+
|
185
|
+
if (the_log.with_pid) {
|
186
|
+
psize = sprintf(prefix, log_pid_prefix, getpid());
|
187
|
+
sprintf(name, log_pid_name, getpid());
|
188
|
+
} else {
|
189
|
+
memcpy(prefix, log_prefix, sizeof(log_prefix));
|
190
|
+
psize = (int)sizeof(log_prefix) - 1;
|
191
|
+
memcpy(name, log_name, sizeof(log_name));
|
192
|
+
}
|
178
193
|
while (NULL != (de = readdir(dir))) {
|
179
194
|
if ('.' == *de->d_name || '\0' == *de->d_name) {
|
180
195
|
continue;
|
181
196
|
}
|
182
|
-
if (0 != strncmp(
|
197
|
+
if (0 != strncmp(prefix, de->d_name, psize)) {
|
183
198
|
continue;
|
184
199
|
}
|
185
200
|
// Don't remove the primary log file.
|
186
|
-
if (0 == strcmp(
|
201
|
+
if (0 == strcmp(name, de->d_name)) {
|
187
202
|
continue;
|
188
203
|
}
|
189
|
-
seq = strtol(de->d_name +
|
204
|
+
seq = strtol(de->d_name + psize, &end, 10);
|
190
205
|
if (the_log.max_files < seq) {
|
191
206
|
snprintf(path, sizeof(path), "%s/%s", the_log.dir, de->d_name);
|
192
207
|
remove(path);
|
@@ -204,13 +219,26 @@ log_rotate() {
|
|
204
219
|
fclose(the_log.file);
|
205
220
|
the_log.file = NULL;
|
206
221
|
}
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
222
|
+
if (the_log.with_pid) {
|
223
|
+
char name[32];
|
224
|
+
|
225
|
+
sprintf(name, log_pid_name, getpid());
|
226
|
+
for (int seq = the_log.max_files; 0 < seq; seq--) {
|
227
|
+
snprintf(to, sizeof(to), log_pid_format, the_log.dir, getpid(), seq + 1);
|
228
|
+
snprintf(from, sizeof(from), log_pid_format, the_log.dir, getpid(), seq);
|
229
|
+
rename(from, to);
|
230
|
+
}
|
231
|
+
snprintf(to, sizeof(to), log_pid_format, the_log.dir, getpid(), 1);
|
232
|
+
snprintf(from, sizeof(from), "%s/%s", the_log.dir, name);
|
233
|
+
} else {
|
234
|
+
for (int seq = the_log.max_files; 0 < seq; seq--) {
|
235
|
+
snprintf(to, sizeof(to), log_format, the_log.dir, seq + 1);
|
236
|
+
snprintf(from, sizeof(from), log_format, the_log.dir, seq);
|
237
|
+
rename(from, to);
|
238
|
+
}
|
239
|
+
snprintf(to, sizeof(to), log_format, the_log.dir, 1);
|
240
|
+
snprintf(from, sizeof(from), "%s/%s", the_log.dir, log_name);
|
211
241
|
}
|
212
|
-
snprintf(to, sizeof(to), log_format, the_log.dir, 1);
|
213
|
-
snprintf(from, sizeof(from), "%s/%s", the_log.dir, log_name);
|
214
242
|
rename(from, to);
|
215
243
|
|
216
244
|
the_log.file = fopen(from, "w");
|
@@ -272,8 +300,11 @@ static void
|
|
272
300
|
open_log_file() {
|
273
301
|
char path[1024];
|
274
302
|
|
275
|
-
|
276
|
-
|
303
|
+
if (the_log.with_pid) {
|
304
|
+
snprintf(path, sizeof(path), "%s/%s_%d", the_log.dir, log_name, getpid());
|
305
|
+
} else {
|
306
|
+
snprintf(path, sizeof(path), "%s/%s", the_log.dir, log_name);
|
307
|
+
}
|
277
308
|
the_log.file = fopen(path, "a");
|
278
309
|
if (NULL == the_log.file) {
|
279
310
|
rb_raise(rb_eIOError, "Failed to create '%s'.", path);
|
@@ -867,6 +898,7 @@ log_init(VALUE mod) {
|
|
867
898
|
the_log.console = true;
|
868
899
|
the_log.classic = true;
|
869
900
|
the_log.colorize = true;
|
901
|
+
the_log.with_pid = false;
|
870
902
|
the_log.zone = (int)(timegm(tm) - t);
|
871
903
|
the_log.day_start = 0;
|
872
904
|
the_log.day_end = 0;
|
@@ -897,5 +929,21 @@ log_init(VALUE mod) {
|
|
897
929
|
log_cat_reg(&eval_cat, "eval", INFO, BLUE, false);
|
898
930
|
log_cat_reg(&push_cat, "push", INFO, DARK_CYAN, false);
|
899
931
|
|
932
|
+
log_start(false);
|
933
|
+
}
|
934
|
+
|
935
|
+
void
|
936
|
+
log_start(bool with_pid) {
|
937
|
+
if (NULL != the_log.file) {
|
938
|
+
fclose(the_log.file);
|
939
|
+
the_log.file = NULL;
|
940
|
+
}
|
941
|
+
the_log.with_pid = with_pid;
|
942
|
+
if (with_pid && '\0' != *the_log.dir) {
|
943
|
+
if (0 != mkdir(the_log.dir, 0770) && EEXIST != errno) {
|
944
|
+
rb_raise(rb_eIOError, "Failed to create '%s'.", the_log.dir);
|
945
|
+
}
|
946
|
+
open_log_file();
|
947
|
+
}
|
900
948
|
pthread_create(&the_log.thread, NULL, loop, log);
|
901
949
|
}
|
data/ext/agoo/log.h
CHANGED
@@ -75,6 +75,7 @@ struct _Log {
|
|
75
75
|
bool console; // if true print log message to stdout
|
76
76
|
bool classic; // classic in stdout
|
77
77
|
bool colorize; // color in stdout
|
78
|
+
bool with_pid;
|
78
79
|
int zone; // timezone offset from GMT in seconds
|
79
80
|
int64_t day_start;
|
80
81
|
int64_t day_end;
|
@@ -116,4 +117,6 @@ extern LogCat log_cat_find(const char *label);
|
|
116
117
|
extern void log_cat(LogCat cat, const char *fmt, ...);
|
117
118
|
extern void log_catv(LogCat cat, const char *fmt, va_list ap);
|
118
119
|
|
120
|
+
extern void log_start(bool with_pid);
|
121
|
+
|
119
122
|
#endif /* __AGOO_LOG_H__ */
|
data/ext/agoo/page.c
CHANGED
data/ext/agoo/request.c
CHANGED
@@ -398,7 +398,11 @@ add_header_value(VALUE hh, const char *key, int klen, const char *val, int vlen)
|
|
398
398
|
rb_hash_aset(hh, rb_str_new(hkey, klen + 5), sval);
|
399
399
|
// Contrary to the Rack spec, Rails expects all upper case keys so add those as well.
|
400
400
|
for (k = hkey + 5; '\0' != *k; k++) {
|
401
|
-
|
401
|
+
if ('-' == *k) {
|
402
|
+
*k = '_';
|
403
|
+
} else {
|
404
|
+
*k = toupper(*k);
|
405
|
+
}
|
402
406
|
}
|
403
407
|
rb_hash_aset(hh, rb_str_new(hkey, klen + 5), sval);
|
404
408
|
}
|
data/ext/agoo/server.c
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
#include <netdb.h>
|
7
7
|
#include <netinet/tcp.h>
|
8
8
|
#include <poll.h>
|
9
|
+
#include <signal.h>
|
9
10
|
#include <stdarg.h>
|
10
11
|
#include <stdio.h>
|
11
12
|
#include <stdlib.h>
|
@@ -13,6 +14,8 @@
|
|
13
14
|
#include <sys/select.h>
|
14
15
|
#include <sys/socket.h>
|
15
16
|
#include <sys/time.h>
|
17
|
+
#include <sys/types.h>
|
18
|
+
#include <sys/wait.h>
|
16
19
|
#include <unistd.h>
|
17
20
|
#include <stdatomic.h>
|
18
21
|
|
@@ -81,17 +84,19 @@ server_mark(void *ptr) {
|
|
81
84
|
void
|
82
85
|
server_shutdown() {
|
83
86
|
if (the_server.inited) {
|
84
|
-
log_cat(&info_cat, "Agoo shutting down.");
|
87
|
+
log_cat(&info_cat, "Agoo with pid %d shutting down.", getpid());
|
85
88
|
the_server.inited = false;
|
86
89
|
if (the_server.active) {
|
90
|
+
double giveup = dtime() + 1.0;
|
91
|
+
|
87
92
|
the_server.active = false;
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
93
|
+
pthread_detach(the_server.listen_thread);
|
94
|
+
pthread_detach(the_server.con_thread);
|
95
|
+
while (0 < atomic_load(&the_server.running)) {
|
96
|
+
dsleep(0.1);
|
97
|
+
if (giveup < dtime()) {
|
98
|
+
break;
|
99
|
+
}
|
95
100
|
}
|
96
101
|
sub_cleanup(&the_server.sub_cache);
|
97
102
|
// The preferred method to of waiting for the ruby threads would
|
@@ -123,6 +128,42 @@ server_shutdown() {
|
|
123
128
|
queue_cleanup(&the_server.eval_queue);
|
124
129
|
pages_cleanup(&the_server.pages);
|
125
130
|
http_cleanup();
|
131
|
+
|
132
|
+
if (1 < the_server.worker_cnt && getpid() == *the_server.worker_pids) {
|
133
|
+
int i;
|
134
|
+
int status;
|
135
|
+
int exit_cnt = 1;
|
136
|
+
int j;
|
137
|
+
|
138
|
+
for (i = 1; i < the_server.worker_cnt; i++) {
|
139
|
+
kill(the_server.worker_pids[i], SIGKILL);
|
140
|
+
}
|
141
|
+
for (j = 0; j < 20; j++) {
|
142
|
+
for (i = 1; i < the_server.worker_cnt; i++) {
|
143
|
+
if (0 == the_server.worker_pids[i]) {
|
144
|
+
continue;
|
145
|
+
}
|
146
|
+
if (0 < waitpid(the_server.worker_pids[i], &status, WNOHANG)) {
|
147
|
+
if (WIFEXITED(status)) {
|
148
|
+
//printf("exited, status=%d for %d\n", the_server.worker_pids[i], WEXITSTATUS(status));
|
149
|
+
the_server.worker_pids[i] = 0;
|
150
|
+
exit_cnt++;
|
151
|
+
} else if (WIFSIGNALED(status)) {
|
152
|
+
//printf("*** killed by signal %d for %d\n", the_server.worker_pids[i], WTERMSIG(status));
|
153
|
+
the_server.worker_pids[i] = 0;
|
154
|
+
exit_cnt++;
|
155
|
+
}
|
156
|
+
}
|
157
|
+
}
|
158
|
+
if (the_server.worker_cnt <= exit_cnt) {
|
159
|
+
break;
|
160
|
+
}
|
161
|
+
dsleep(0.2);
|
162
|
+
}
|
163
|
+
if (exit_cnt < the_server.worker_cnt) {
|
164
|
+
printf("*-*-* Some workers did not exit.\n");
|
165
|
+
}
|
166
|
+
}
|
126
167
|
}
|
127
168
|
}
|
128
169
|
|
@@ -131,7 +172,9 @@ configure(Err err, int port, const char *root, VALUE options) {
|
|
131
172
|
the_server.port = port;
|
132
173
|
the_server.pages.root = strdup(root);
|
133
174
|
the_server.thread_cnt = 0;
|
175
|
+
the_server.worker_cnt = 1;
|
134
176
|
the_server.running = 0;
|
177
|
+
the_server.fd = 0;
|
135
178
|
the_server.listen_thread = 0;
|
136
179
|
the_server.con_thread = 0;
|
137
180
|
the_server.max_push_pending = 32;
|
@@ -148,6 +191,15 @@ configure(Err err, int port, const char *root, VALUE options) {
|
|
148
191
|
rb_raise(rb_eArgError, "thread_count must be between 1 and 1000.");
|
149
192
|
}
|
150
193
|
}
|
194
|
+
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("worker_count"))))) {
|
195
|
+
int wc = FIX2INT(v);
|
196
|
+
|
197
|
+
if (1 <= wc || wc < MAX_WORKERS) {
|
198
|
+
the_server.worker_cnt = wc;
|
199
|
+
} else {
|
200
|
+
rb_raise(rb_eArgError, "thread_count must be between 1 and %d.", MAX_WORKERS);
|
201
|
+
}
|
202
|
+
}
|
151
203
|
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("max_push_pending"))))) {
|
152
204
|
int tc = FIX2INT(v);
|
153
205
|
|
@@ -214,6 +266,8 @@ configure(Err err, int port, const char *root, VALUE options) {
|
|
214
266
|
* - *:pedantic* [_true_|_false_] if true response header and status codes are checked and an exception raised if they violate the rack spec at https://github.com/rack/rack/blob/master/SPEC, https://tools.ietf.org/html/rfc3875#section-4.1.18, or https://tools.ietf.org/html/rfc7230.
|
215
267
|
*
|
216
268
|
* - *:thread_count* [_Integer_] number of ruby worker threads. Defaults to one. If zero then the _start_ function will not return but instead will proess using the thread that called _start_. Usually the default is best unless the workers are making IO calls.
|
269
|
+
*
|
270
|
+
* - *:worker_count* [_Integer_] number of workers to fork. Defaults to one which is not to fork.
|
217
271
|
*/
|
218
272
|
static VALUE
|
219
273
|
rserver_init(int argc, VALUE *argv, VALUE self) {
|
@@ -250,40 +304,46 @@ rserver_init(int argc, VALUE *argv, VALUE self) {
|
|
250
304
|
return Qnil;
|
251
305
|
}
|
252
306
|
|
253
|
-
static void
|
254
|
-
|
307
|
+
static void
|
308
|
+
setup_listen() {
|
255
309
|
struct sockaddr_in addr;
|
256
310
|
int optval = 1;
|
257
|
-
struct pollfd pa[1];
|
258
|
-
struct pollfd *fp = pa;
|
259
|
-
struct _Err err = ERR_INIT;
|
260
|
-
struct sockaddr_in client_addr;
|
261
|
-
int client_sock;
|
262
|
-
socklen_t alen = 0;
|
263
|
-
Con con;
|
264
|
-
int i;
|
265
|
-
uint64_t cnt = 0;
|
266
311
|
|
267
|
-
if (0 >= (
|
312
|
+
if (0 >= (the_server.fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
|
268
313
|
log_cat(&error_cat, "Server failed to open server socket. %s.", strerror(errno));
|
269
314
|
atomic_fetch_sub(&the_server.running, 1);
|
270
|
-
|
315
|
+
rb_raise(rb_eIOError, "Server failed to open server socket. %s.", strerror(errno));
|
271
316
|
}
|
272
317
|
#ifdef OSX_OS
|
273
|
-
setsockopt(
|
318
|
+
setsockopt(the_server.fd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval));
|
274
319
|
#endif
|
275
320
|
memset(&addr, 0, sizeof(addr));
|
276
321
|
addr.sin_family = AF_INET;
|
277
322
|
addr.sin_addr.s_addr = INADDR_ANY;
|
278
323
|
addr.sin_port = htons(the_server.port);
|
279
|
-
setsockopt(
|
280
|
-
setsockopt(
|
281
|
-
if (0 > bind(
|
324
|
+
setsockopt(the_server.fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
|
325
|
+
setsockopt(the_server.fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
|
326
|
+
if (0 > bind(the_server.fd, (struct sockaddr*)&addr, sizeof(addr))) {
|
282
327
|
log_cat(&error_cat, "Server failed to bind server socket. %s.", strerror(errno));
|
283
328
|
atomic_fetch_sub(&the_server.running, 1);
|
284
|
-
|
329
|
+
rb_raise(rb_eIOError, "Server failed to bind server socket. %s.", strerror(errno));
|
285
330
|
}
|
286
|
-
listen(
|
331
|
+
listen(the_server.fd, 1000);
|
332
|
+
}
|
333
|
+
|
334
|
+
static void*
|
335
|
+
listen_loop(void *x) {
|
336
|
+
int optval = 1;
|
337
|
+
struct pollfd pa[1];
|
338
|
+
struct _Err err = ERR_INIT;
|
339
|
+
struct sockaddr_in client_addr;
|
340
|
+
int client_sock;
|
341
|
+
socklen_t alen = 0;
|
342
|
+
Con con;
|
343
|
+
int i;
|
344
|
+
uint64_t cnt = 0;
|
345
|
+
|
346
|
+
pa->fd = the_server.fd;
|
287
347
|
pa->events = POLLIN;
|
288
348
|
pa->revents = 0;
|
289
349
|
|
@@ -303,9 +363,9 @@ listen_loop(void *x) {
|
|
303
363
|
}
|
304
364
|
if (0 != (pa->revents & POLLIN)) {
|
305
365
|
if (0 > (client_sock = accept(pa->fd, (struct sockaddr*)&client_addr, &alen))) {
|
306
|
-
log_cat(&error_cat, "Server accept connection failed. %s.", strerror(errno));
|
366
|
+
log_cat(&error_cat, "Server with pid %d accept connection failed. %s.", getpid(), strerror(errno));
|
307
367
|
} else if (NULL == (con = con_create(&err, client_sock, ++cnt))) {
|
308
|
-
log_cat(&error_cat, "Server accept connection failed. %s.", err.msg);
|
368
|
+
log_cat(&error_cat, "Server with pid %d accept connection failed. %s.", getpid(), err.msg);
|
309
369
|
close(client_sock);
|
310
370
|
cnt--;
|
311
371
|
err_clear(&err);
|
@@ -316,15 +376,16 @@ listen_loop(void *x) {
|
|
316
376
|
fcntl(client_sock, F_SETFL, O_NONBLOCK);
|
317
377
|
setsockopt(client_sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
|
318
378
|
setsockopt(client_sock, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
|
319
|
-
log_cat(&con_cat, "Server accepted connection %llu on port %d [%d]",
|
379
|
+
log_cat(&con_cat, "Server with pid %d accepted connection %llu on port %d [%d]",
|
380
|
+
getpid(), (unsigned long long)cnt, the_server.port, con->sock);
|
320
381
|
queue_push(&the_server.con_queue, (void*)con);
|
321
382
|
}
|
322
383
|
}
|
323
384
|
if (0 != (pa->revents & (POLLERR | POLLHUP | POLLNVAL))) {
|
324
385
|
if (0 != (pa->revents & (POLLHUP | POLLNVAL))) {
|
325
|
-
log_cat(&error_cat, "Agoo server socket on port %d closed.", the_server.port);
|
386
|
+
log_cat(&error_cat, "Agoo server with pid %d socket on port %d closed.", getpid(), the_server.port);
|
326
387
|
} else {
|
327
|
-
log_cat(&error_cat, "Agoo server socket on port %d error.", the_server.port);
|
388
|
+
log_cat(&error_cat, "Agoo server with pid %d socket on port %d error.", getpid(), the_server.port);
|
328
389
|
}
|
329
390
|
the_server.active = false;
|
330
391
|
}
|
@@ -727,9 +788,36 @@ server_start(VALUE self) {
|
|
727
788
|
VALUE *vp;
|
728
789
|
int i;
|
729
790
|
double giveup;
|
791
|
+
int pid;
|
730
792
|
|
793
|
+
*the_server.worker_pids = getpid();
|
794
|
+
setup_listen();
|
731
795
|
the_server.active = true;
|
732
796
|
|
797
|
+
for (i = 1; i < the_server.worker_cnt; i++) {
|
798
|
+
#if 0
|
799
|
+
pid = fork();
|
800
|
+
#else
|
801
|
+
{
|
802
|
+
VALUE rpid = rb_funcall(rb_cObject, rb_intern("fork"), 0);
|
803
|
+
|
804
|
+
if (Qnil == rpid) {
|
805
|
+
pid = 0;
|
806
|
+
} else {
|
807
|
+
pid = NUM2INT(rpid);
|
808
|
+
}
|
809
|
+
}
|
810
|
+
#endif
|
811
|
+
if (0 > pid) { // error, use single process
|
812
|
+
log_cat(&error_cat, "Failed to fork. %s.", strerror(errno));
|
813
|
+
break;
|
814
|
+
} else if (0 == pid) {
|
815
|
+
log_start(true);
|
816
|
+
break;
|
817
|
+
} else {
|
818
|
+
the_server.worker_pids[i] = pid;
|
819
|
+
}
|
820
|
+
}
|
733
821
|
pthread_create(&the_server.listen_thread, NULL, listen_loop, NULL);
|
734
822
|
pthread_create(&the_server.con_thread, NULL, con_loop, NULL);
|
735
823
|
|
@@ -744,7 +832,7 @@ server_start(VALUE self) {
|
|
744
832
|
VALUE agoo = rb_const_get_at(rb_cObject, rb_intern("Agoo"));
|
745
833
|
VALUE v = rb_const_get_at(agoo, rb_intern("VERSION"));
|
746
834
|
|
747
|
-
log_cat(&info_cat, "Agoo %s listening on port %d.", StringValuePtr(v), the_server.port);
|
835
|
+
log_cat(&info_cat, "Agoo %s with pid %d is listening on port %d.", StringValuePtr(v), getpid(), the_server.port);
|
748
836
|
}
|
749
837
|
if (0 >= the_server.thread_cnt) {
|
750
838
|
Req req;
|
data/ext/agoo/server.h
CHANGED
@@ -16,12 +16,16 @@
|
|
16
16
|
#include "sub.h"
|
17
17
|
#include "upgraded.h"
|
18
18
|
|
19
|
+
#define MAX_WORKERS 32
|
20
|
+
|
19
21
|
typedef struct _Server {
|
20
22
|
volatile bool inited;
|
21
23
|
volatile bool active;
|
22
24
|
int thread_cnt;
|
25
|
+
int worker_cnt;
|
23
26
|
int max_push_pending;
|
24
27
|
int port;
|
28
|
+
int fd;
|
25
29
|
bool pedantic;
|
26
30
|
bool root_first;
|
27
31
|
atomic_int running;
|
@@ -40,6 +44,7 @@ typedef struct _Server {
|
|
40
44
|
Hook hook404;
|
41
45
|
struct _Queue eval_queue;
|
42
46
|
|
47
|
+
int worker_pids[MAX_WORKERS];
|
43
48
|
VALUE *eval_threads; // Qnil terminated
|
44
49
|
} *Server;
|
45
50
|
|
data/lib/agoo/version.rb
CHANGED
data/lib/rack/handler/agoo.rb
CHANGED
@@ -10,12 +10,19 @@ module Rack
|
|
10
10
|
# The Rack::Handler::Agoo module is a handler for common rack config.rb files.
|
11
11
|
module Agoo
|
12
12
|
|
13
|
-
# Run the server. Options are the same as for Agoo::Server plus a :port
|
14
|
-
# and :
|
13
|
+
# Run the server. Options are the same as for Agoo::Server plus a :port,
|
14
|
+
# :root, :rmux, and :wc option.
|
15
|
+
#
|
16
|
+
# - *:port [_Integer_] port to listen on
|
17
|
+
# - *:root [_String_] root or public directory for static assets
|
18
|
+
# - *:rmux [_true_|_false_] if true look in the root directory first before calling Ruby hooks
|
19
|
+
# - *:wc* [_Integer_] number of workers to fork. Defaults to one which is not to fork.
|
20
|
+
# - */path=MyHandler* path and class name to handle requests on that path
|
15
21
|
def self.run(handler, options={})
|
16
22
|
port = 9292
|
17
23
|
root = './public'
|
18
24
|
root_set = false
|
25
|
+
worker_count = 1;
|
19
26
|
default_handler = nil
|
20
27
|
not_found_handler = nil
|
21
28
|
path_map = {}
|
@@ -30,6 +37,9 @@ module Rack
|
|
30
37
|
root = v
|
31
38
|
root_set = true
|
32
39
|
options.delete(k)
|
40
|
+
elsif :wc == k
|
41
|
+
worker_count = v.to_i
|
42
|
+
options.delete(k)
|
33
43
|
elsif :rmux == k
|
34
44
|
options[:root_first] = false
|
35
45
|
elsif k.nil?
|
@@ -44,6 +54,7 @@ module Rack
|
|
44
54
|
end
|
45
55
|
}
|
46
56
|
options[:thread_count] = 0
|
57
|
+
options[:worker_count] = worker_count
|
47
58
|
::Agoo::Server.init(port, root, options)
|
48
59
|
path_map.each { |path,handler|
|
49
60
|
::Agoo::Server.handle(nil, path, handler)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: agoo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Ohler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-05-
|
11
|
+
date: 2018-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oj
|
@@ -135,12 +135,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
135
|
requirements:
|
136
136
|
- Linux or macOS
|
137
137
|
rubyforge_project: agoo
|
138
|
-
rubygems_version: 2.7.
|
138
|
+
rubygems_version: 2.7.6
|
139
139
|
signing_key:
|
140
140
|
specification_version: 4
|
141
141
|
summary: An HTTP server
|
142
142
|
test_files:
|
143
|
-
- test/base_handler_test.rb
|
144
|
-
- test/log_test.rb
|
145
143
|
- test/rack_handler_test.rb
|
144
|
+
- test/base_handler_test.rb
|
146
145
|
- test/static_test.rb
|
146
|
+
- test/log_test.rb
|