gwan version: 3.12.26
servlet type: C and Perl
problem:
gwan internal cache make request not re-read the script
test:
create 'log' dir :
[bash]# mkdir -p /dev/shm/random-c
[bash]# chmod 777 /dev/shm/random-c
create /path/to/gwan/0.0.0.0_8080/#0.0.0.0/csp/random.c
// ============================================================================
// C servlet sample for the G-WAN Web Application Server (http://trustleap.ch/)
// ----------------------------------------------------------------------------
// hello.c: just used with Lighty's Weighttp to benchmark a minimalist servlet
// ============================================================================
// imported functions:
// get_reply(): get a pointer on the 'reply' dynamic buffer from the server
// xbuf_cat(): like strcat(), but it works in the specified dynamic buffer
// ----------------------------------------------------------------------------
#include <sys/time.h>
#include "gwan.h" // G-WAN exported functions
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//------------------
void init_random(){
struct /*sys/time.h->*/timeval res;
/*sys/time.h->*/gettimeofday(&res,NULL);
/*stdlib.h->*/srand( (unsigned int)/*stdlib.h->*/time(NULL) + res.tv_usec);
}
//------------------
char *get_rnd_char(int num){
char *char_list = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
int char_list_len = 62;
char *ret = (char *)/*stdlib.h->*/malloc((num * sizeof(char)) + 1);
int i,r;
for(i=0;i<num;i++){
r=(int) (/*stdlib.h->*/rand() % char_list_len);
ret[i] = char_list[r==char_list_len ? r-1 : r];
}
ret[num] = '\0';
return ret;
}
//------------------
int main(int argc, char *argv[])
{
char *rnd_out; //-- random data for browser output and file input
char *rnd_file; //-- random file
char *rnd_path; //-- for speed let's make on ramdisk /dev/shm/random-c/
char *t;
FILE *F;
int num_char=10;
int arg_cnt=1;
if(argc>0){
//-- why nobody love C ? one of the reason is these kind parsing thing
while ((t = /*string.h->*/strtok(argv[0], "=")) != NULL) {
argv[0] = NULL;
if(arg_cnt == 2){
num_char = /*stdlib.h->*/atoi(t);
}
arg_cnt++;
}
}else{
//-- get random number betwen 1 to 1000
num_char = (rand() % 1000)+1;
}
init_random();
//-- create random data
rnd_out = get_rnd_char(num_char);
//-- creating "log" path
//-- why nobody love C ? more reason
rnd_file = get_rnd_char(20);
// "/dev/shm/random-c/xxxxxxxxxxxxxxxxxxxx" -> 38 chars + 1 for \0
rnd_path = (char *)/*stdlib.h->*/malloc((38 * sizeof(char)) + 1);
rnd_path[0] = '\0';
/*string.h->*/strcat(rnd_path,"/dev/shm/random-c/");
/*string.h->*/strcat(rnd_path,rnd_file);
//-- save to file
F = /*stdio.h->*/fopen(rnd_path,"w");
/*stdio.h->*/fprintf(F,"%s",rnd_out);
/*stdio.h->*/fclose(F);
//-- send output to browser
/*gwan.h->*/xbuf_cat(get_reply(argv), rnd_out);
//-- cleanup memory
//-- why nobody love C ? MAIN reason: no easy way of memory management
/*stdlib.h->*/free(rnd_file);
/*stdlib.h->*/free(rnd_out);
/*stdlib.h->*/free(rnd_path);
return 200; // return an HTTP code (200:'OK')
}
// ============================================================================
// End of Source Code
// ============================================================================
run on browser:
http://localhost:8080/?random.c
then you should have one 20char random file at /dev/shm/random-c/
here the 'problem', run:
ab -n 1000 'http://localhost:8080/?random.c'
my ubuntu have output:
Finished 1000 requests
Server Software: G-WAN
Server Hostname: localhost
Server Port: 8080
Document Path: /?random.c
Document Length: 440 bytes
Concurrency Level: 1
Time taken for tests: 0.368 seconds
Complete requests: 1000
Failed requests: 361
(Connect: 0, Receive: 0, Length: 361, Exceptions: 0)
Write errors: 0
Total transferred: 556492 bytes
HTML transferred: 286575 bytes
Requests per second: 2718.73 [#/sec] (mean)
Time per request: 0.368 [ms] (mean)
Time per request: 0.368 [ms] (mean, across all concurrent requests)
Transfer rate: 1477.49 [Kbytes/sec] received
try:
[bash]# ls /dev/shm/random-c/
the directory only list 4 or 5 random files, which expected was 1000files
tested on random.c and perl's version random.pl
so the back to beginning question, how to disable GWAN internal cache, I try to read gwan user guide for set something in handler, but found nothing (or I miss something in that guide ).
thanks for GWAN team for this great product.
any answer welcome .. thanks
I think that the feature you are talking about is micro caching. To disable it, the URI needs to be unique on each request within 200 ms. (Like adding random number on URI)
The G-WAN FAQ state:
"To spare the need for a frontend cache server (and to let G-WAN be used as a caching reverse-proxy) G-WAN supports micro-caching, a RESTful feature. When a given URI is invoked at high concurrencies and when generating the payload take a lot of time, then G-WAN will automatically cache a page for 200 milliseconds (the average latency on the Internet) to make sure that the cache is up-to-date: within 200 ms, consecutive requests provide the expected result. To prevent micro-caching from being triggered, use a changing query parameter (per user session id, random, counter, etc.) for concurrent requests."
Note that for v4.10+ caching is disabled by default, look at the gwan/init.c file.
Related
Using libzmq 4.2.5 on centos 7. Getting very high latency when messages are sent from DEALER to ROUTER and even from ROUTER to DEALER. So I wrote a simple client-server program using tcp and sent messages between them just for comparison. Tcp appears to be fast.
Sending single byte from DEALER to ROUTER, zmq takes 900 microseconds.
Sending single byte from client to server, tcp takes 150 microseconds.
What am I doing wrong. I thought zmq will be at least as fast as tcp. Is there any tuning I can do to make zmq faster?
Update
router.cpp
#include <zmq.hpp>
struct data
{
char one[21];
unsigned long two;
};
data * pdata;
std::size_t counter=0;
int main()
{
zmq::context_t context(1);
zmq::socket_t Device(context,ZMQ_ROUTER);
int iHighWaterMark=0;
Device.setsockopt(ZMQ_SNDHWM,&iHighWaterMark,sizeof(int));
Device.setsockopt(ZMQ_RCVHWM,&iHighWaterMark,sizeof(int));
Device.bind("tcp://0.0.0.0:5555");
pdata=new data[10000];
struct timespec ts_dtime;
unsigned long sec;
zmq::message_t message;
zmq::pollitem_t arrPollItems[]={{Device, 0, ZMQ_POLLIN, 0},{NULL,
0, ZMQ_POLLIN, 0}};
while(counter < 10000)
{
try
{
int iAssert = zmq::poll(arrPollItems, 1, -1);
if (iAssert <= 0)
{
if (-1 == iAssert)
{
printf("zmq_poll failed errno: %d error:%s", errno,
zmq_strerror(errno));
}
continue;
}
if (arrPollItems[0].revents == ZMQ_POLLIN)
{
while(true)
{
if(! Device.recv(&message,ZMQ_DONTWAIT))
break;
Device.recv(&message);
strncpy(pdata[counter].one,
(char*)message.data(),message.size());
clock_gettime(CLOCK_REALTIME, &ts_dtime);
pdata[counter].two = (ts_dtime.tv_sec*1e9)+
ts_dtime.tv_nsec;
++counter;
}
}
}
catch(...)
{
}
}
for(int i=0;i<counter;++i)
printf("%d %s %lu\n",i+1,pdata[i].one,pdata[i].two);
return 0;
}
dealer.cpp
#include <zmq.hpp>
#include<unistd.h>
int main()
{
zmq::context_t context(1);
zmq::socket_t Device(context,ZMQ_DEALER);
int iHighWaterMark=0;
Device.setsockopt(ZMQ_SNDHWM,&iHighWaterMark,sizeof(int));
Device.setsockopt(ZMQ_RCVHWM,&iHighWaterMark,sizeof(int));
Device.setsockopt(ZMQ_IDENTITY,"TEST",4);
Device.connect("tcp://0.0.0.0:5555");
usleep(100000);
struct timespec ts_dtime;
unsigned long sec;
for(std::size_t i=0;i<10000;++i)
{
clock_gettime(CLOCK_REALTIME, &ts_dtime);
sec=(ts_dtime.tv_sec*1e9)+ ts_dtime.tv_nsec;
zmq::message_t message(21);
sprintf((char *)message.data(),"%lu",sec);
Device.send(message);
usleep(500);
}
return 0;
}
update 2:
router.cpp
#include <zmq.hpp>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
const char *bind_to;
int roundtrip_count;
size_t message_size;
int rc;
int i;
if (argc != 4) {
printf ("usage: local_lat <bind-to> <message-size> "
"<roundtrip-count>\n");
return 1;
}
bind_to = argv[1];
message_size = atoi (argv[2]);
roundtrip_count = atoi (argv[3]);
zmq::context_t ctx(1);
zmq::socket_t s(ctx,ZMQ_ROUTER);
zmq::message_t msg,id;
int iHighWaterMark=0;
s.setsockopt(ZMQ_SNDHWM , &iHighWaterMark,
sizeof (int));
s.setsockopt(ZMQ_RCVHWM , &iHighWaterMark,
sizeof (int));
s.bind( bind_to);
struct timespec ts_dtime;
unsigned long sec;
for (i = 0; i != roundtrip_count; i++) {
rc =s.recv(&id);
if (rc < 0) {
printf ("error in zmq_recvmsg: %s\n", zmq_strerror (errno));
return -1;
}
rc = s.recv(&msg, 0);
if (rc < 0) {
printf ("error in zmq_recvmsg: %s\n", zmq_strerror (errno));
return -1;
}
clock_gettime(CLOCK_REALTIME, &ts_dtime);
sec=((ts_dtime.tv_sec*1e9)+ ts_dtime.tv_nsec);
printf("%.*s %lu\n",20,(char *)msg.data(),sec);
}
s.close();
return 0;
}
dealer.cpp
#include <zmq.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main (int argc, char *argv[])
{
const char *connect_to;
int roundtrip_count;
size_t message_size;
int rc;
int i;
void *watch;
unsigned long elapsed;
double latency;
if (argc != 4) {
printf ("usage: remote_lat <connect-to> <message-size> "
"<roundtrip-count>\n");
return 1;
}
connect_to = argv[1];
message_size = atoi (argv[2]);
roundtrip_count = atoi (argv[3]);
zmq::context_t ctx(1);
zmq::socket_t s(ctx,ZMQ_DEALER);
struct timespec ts_dtime;
unsigned long sec;
int iHighWaterMark=0;
s.setsockopt(ZMQ_SNDHWM , &iHighWaterMark,
sizeof (int));
s.setsockopt(ZMQ_RCVHWM , &iHighWaterMark,
sizeof (int));
s.connect(connect_to);
for (i = 0; i != roundtrip_count; i++) {
zmq::message_t msg(message_size+20);
clock_gettime(CLOCK_REALTIME, &ts_dtime);
sec=(ts_dtime.tv_sec*1e9)+ ts_dtime.tv_nsec;
sprintf((char *)msg.data(),"%lu",sec);
rc = s.send(msg);
if (rc < 0) {
printf ("error in zmq_sendmsg: %s\n", zmq_strerror (errno));
return -1;
}
sleep(1);
}
s.close();
return 0;
}
output :
1562125527489432576 1562125527489773568
1562125528489582848 1562125528489961472
1562125529489740032 1562125529490124032
1562125530489944832 1562125530490288896
1562125531490101760 1562125531490439424
1562125532490261248 1562125532490631680
1562125533490422272 1562125533490798080
1562125534490555648 1562125534490980096
1562125535490745856 1562125535491161856
1562125536490894848 1562125536491245824
1562125537491039232 1562125537491416320
1562125538491229184 1562125538491601152
1562125539491375872 1562125539491764736
1562125540491517184 1562125540491908352
1562125541491657984 1562125541492027392
1562125542491816704 1562125542492193536
1562125543491963136 1562125543492338944
1562125544492103680 1562125544492564992
1562125545492248832 1562125545492675328
1562125546492397312 1562125546492783616
1562125547492543744 1562125547492926720
1562125564495211008 1562125564495629824
1562125565495372032 1562125565495783168
1562125566495515904 1562125566495924224
1562125567495660800 1562125567496006144
1562125568495806464 1562125568496160000
1562125569495896064 1562125569496235520
1562125570496080128 1562125570496547584
1562125571496235008 1562125571496666624
1562125572496391424 1562125572496803584
1562125573496532224 1562125573496935680
1562125574496652800 1562125574497053952
1562125575496843776 1562125575497277184
1562125576496997120 1562125576497417216
1562125577497182208 1562125577497726976
1562125578497336832 1562125578497726464
1562125579497549312 1562125579497928704
1562125580497696512 1562125580498115328
1562125581497847808 1562125581498198528
1562125582497998336 1562125582498340096
1562125583498140160 1562125583498622464
1562125584498295296 1562125584498680832
1562125585498445312 1562125585498842624
1562125586498627328 1562125586499025920
All are in the range for 350-450us
Q1: What am I doing wrong?I thought zmq will be at least as fast as tcp.
Code-wise, nothing.
Performance-wise, the ZeroMQ is fantastic plus has so many features that tcp does not and will not provide right out of the box:
Test-setup "Sending single byte..." seems to step right into the left edge of the high-performance / low-latency messaging service:
Lets first understand the Latency and where did it come from:
The observed resulting latency figures are product of the overall sum of the resources-usage ( resources allocations + resources pools management operations + data manipulations ) and processing-efforts ( all we try to do with the data, here including times, that our task had to spend in a waiting queue, due to the system-scheduler planned multi-tasking workunits scheduling, that are not from our testing workload, but the operating system has to schedule and execute, according to the fair-scheduling-policy and actual process-priority settings ) and communications channels transport-delays ( comms E2E transport latency )
Lets next understand what do we try to compare with:
A difference between a Transmission Control Protocol ( raw tcp ) and a ZeroMQ zmq framework of smart Scalable Formal Communication Archetypes with a rich set of high-level, distributed behaviours, is about a few galaxies big.
ZeroMQ was designed as rather a Signalling and Messaging infrastructure using some of these feature-rich set of behaviours that match together - often depicted by some human-alike behaviour-archetype:
One PUSH-es, any number of joined counterparties PULL
One REQ-ests, someone from a group on the other end of the phone REP-lies
One, even potentially a one from some larger group of agents, PUB-lishes, any amount of already subscribed subscribers receive such a SUB-scribed message.
For details, one may kindly read a brief overview about the main conceptual differences in [ ZeroMQ hierarchy in less than a five seconds ] Section.
This is nothing a TCP-protocol will ever provide on its own.
This is a comfort one likes to pay for by some negligible amount of latency. Negligible? Yes, negligible once compared to the many man*years of ultimate software craftsmanship anyone would have to pay for designing another at least similarly smart messaging framework to compete with ZeroMQ.
Q2: Is there any tuning I can do to make zmq faster?
Maybe yes, maybe not.
Update:- try avoiding Identity management ( tcp has no such thing either, so measured RTT-s are the lesser comparable or meaningful )
- try avoiding the blocking manner of the HWM-configurations ( tcp has no such thing either )
- may try to measure the same over a non-tcp-protocol ( a PAIR/PAIR Formal Scalable Communication Archetype, best over the least complex protocol data-pumps as inproc:// is or ipc:// in case your SandBox test bed needs to still keep distributed non-local copies etc. ) ZeroMQ context-instance's internal overheads spent on the .send() resp. .receive() methods
- may try to allow for slight increase in performance by using more threads available for the Context instance
( other performance demasking tricks depend on the nature of real-world usage - as a robustness to dropped messages, feasibility to use a conflated mode of operations, better buffer-alignment with O/S, zero-copy tricks - all being of some interest here, yet have to let and keep the smart ZeroMQ infrastructure of distributed behaviours operational, which is by far more complex task to execute, than a trivial serial sequence of otherwise blind and isolated tcp-socket byte-level operations - so, comparing times is possible, but comparing individual draconic dragster-class car ( well, better a vehicle, not even a car ) with something like a globally operating infrastructure of distributed behaviour ( like Taxify or Uber, named here just to make a use of a trivialised (dis-)similarity of approximately same scale of magnitude ) leaves the numbers reporting about phenomena, that do not provide the similar comfort, scalability of use-cases, almost linear performance scaling and robustness of the real-world use )
- may add more scheduling determinism with making the Context-instance's respective IoTHREADs-hard-wired onto respective CPU-core(s), so that the overall I/O-performance never gets evicted from CPU-schedule and remains deterministically mapped / pre-locked on even exclusively administratively dedicated CPU-core(s) - depends on a level of need and administrative policies if trying to do this ultimate performance hack
For any performance related tweaking, one will need to post an MCVE + a fully described benchmark test suite. The ZeroMQ Latency test results report shows:
Conclusion
In a controlled environment RDTSC instruction can be used to measure
time rapidly. This allows us to measure latency/density for individual
messages instead of computing averages for the whole test.
We've used this approach to get performance figures of ØMQ lightweight
messaging kernel (version 0.1) and we've got following results:
-- In low-volume case the latency is almost the same as the latency of the underlying transport (TCP): 50 microseconds.
-- The average jitter of latency is minimal: 0.225 microsecond.
-- The throughput on sender side is 4.8 million messages a second.
-- The density on sender side is mostly about 0.140 microsecond, however, with occasional peaks the mean density is 0.208 microsecond.
-- The throughput on receiver side is 2.7 million messages a second.
-- The density on receiver side is mostly about 0.3 microsecond. Approximately each 100 messages new batch is received causing density
to grow to 3-6 microseconds. The mean density is 0.367 microsecond.
If in an ultimate need for latency shaving, one may try nanomsg, the ZeroMQ's younger sister originated by Martin SUSTRIK, the co-father of ZeroMQ ( now maintained afaik by someone else )
I want to securely send data to my InfluxDB over the Internet using a NodeMCU MCU and a self signed cert.
I found this library that seems to accomplish exactly this but get compile errors, more below -> https://medium.com/#teebr/iot-with-an-esp32-influxdb-and-grafana-54abc9575fb2
This library seems to only use HTTP -> Am i mistaken?
https://www.arduinolibraries.info/libraries/esp8266-influxdb
Using the example from the 1st library above from TEEBR, i get this error compiling - Any suggestions on how to fix? Will this run on my NodeMCU?
Thanks
C:\Users\Jason\Documents\Arduino\libraries\Influx-Arduino-master\InfluxArduino.cpp:1:24: fatal error: HTTPClient.h: No such file or directory
#include
^
compilation terminated.
exit status 1
Error compiling for board NodeMCU 1.0 (ESP-12E Module).
My code
//https://medium.com/#teebr/iot-with-an-esp32-influxdb-and-grafana-54abc9575fb2
#include <WiFi.h>
#include "InfluxArduino.hpp"
#include "InfluxCert.hpp"
InfluxArduino influx;
//connection/ database stuff that needs configuring
char WIFI_NAME[] = "ssid";
const char WIFI_PASS[] = "password!";
const char INFLUX_DATABASE[] = "db_name";
const char INFLUX_IP[] = "10.10.101.101";
const char INFLUX_USER[] = "db_name"; //username if authorization is enabled.
const char INFLUX_PASS[] = "Password"; //password for if authorization is enabled.
const char INFLUX_MEASUREMENT[] = "FromESP8266"; //measurement name for the database. (in practice, you can use several, this example just uses the one)
unsigned long DELAY_TIME_US = 5 * 1000 * 1000; //how frequently to send data, in microseconds
unsigned long count = 0; //a variable that we gradually increase in the loop
void setup()
{
Serial.begin(115200);
WiFi.begin(WIFI_NAME, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected!");
influx.configure(INFLUX_DATABASE,INFLUX_IP); //third argument (port number) defaults to 8086
influx.authorize(INFLUX_USER,INFLUX_PASS); //if you have set the Influxdb .conf variable auth-enabled to true, uncomment this
influx.addCertificate(ROOT_CERT); //uncomment if you have generated a CA cert and copied it into InfluxCert.hpp
Serial.print("Using HTTPS: ");
Serial.println(influx.isSecure()); //will be true if you've added the InfluxCert.hpp file.
}
void loop()
{
unsigned long startTime = micros(); //used for timing when to send data next.
//update our field variables
float dummy = ((float)random(0, 1000)) / 1000.0;
count++;
//write our variables.
char tags[32];
char fields[32];
sprintf(tags,"new_tag=Yes"); //write a tag called new_tag
sprintf(fields,"count=%d,random_var=%0.3f",count,dummy); //write two fields: count and random_var
bool writeSuccessful = influx.write(INFLUX_MEASUREMENT,tags,fields);
if(!writeSuccessful)
{
Serial.print("error: ");
Serial.println(influx.getResponse());
}
while ((micros() - startTime) < DELAY_TIME_US)
{
//wait until it's time for next reading. Consider using a low power mode if this will be a while.
}
}
Long time reader, first time poster - Thanks for all the help in the past!
I have two programs communicating via named pipes (on a Mac), but the buffer size of named pipes is too small. Program 1 writes 50K bytes to pipe 1 before reading pipe 2. Named pipes are 8K (on my system) so program 1 blocks until the data is consumed. Program 2 reads 20K bytes from pipe 1 and then writes 20K bytes to pipe2. Pipe2 can't hold 20K so program 2 now blocks. It will only be released when program 1 does its reads. But program 1 is blocked waiting for program 2. deadlock
I thought I could fix the problem by creating a gasket program that reads stdin non-blocking and writes stdout non-blocking, temporarily storing the data in a large buffer. I tested the program using cat data | ./gasket 0 | ./gasket 1 > out, expecting out to be a copy of data. However, while the first invocation of gasket works as expected, the read in the second program returns 0 before all the data is consumed and never returns anything other than 0 in follow on calls.
I tried the code below both on a MAC and Linux. Both behave the same. I've added logging so that I can see that the fread from the second invocation of gasket starts getting no data even though it has not read all the data written by the first invocation.
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#define BUFFER_SIZE 100000
char buffer[BUFFER_SIZE];
int elements=0;
int main(int argc, char **argv)
{
int total_read=0, total_write=0;
FILE *logfile=fopen(argv[1],"w");
int flags = fcntl(fileno(stdin), F_GETFL, 0);
fcntl(fileno(stdin), F_SETFL, flags | O_NONBLOCK);
flags = fcntl(fileno(stdout), F_GETFL, 0);
fcntl(fileno(stdout), F_SETFL, flags | O_NONBLOCK);
while (1) {
int num_read=0;
if (elements < (BUFFER_SIZE-1024)) { // space in buffer
num_read = fread(&buffer[elements], sizeof(char), 1024, stdin);
elements += num_read;
total_read += num_read;
fprintf(logfile,"read %d (%d) elements \n",num_read, total_read); fflush(logfile);
}
if (elements > 0) { // something in buffer that we can write
int num_written = fwrite(&buffer[0],sizeof(char),elements, stdout); fflush(stdout);
total_write += num_written;
fprintf(logfile,"wrote %d (%d) elements \n",num_written, total_write); fflush(logfile);
if (num_written > 0) { // copy data to top of buffer
for (int i=0; i<(elements-num_written); i++) {
buffer[i] = buffer[i+num_written];
}
elements -= num_written;
}
}
}
}
I guess I could make the gasket multi-threaded and use blocking reads in one thread and blocking writes in the other, but I would like to understand why non-blocking IO seems to break for me.
Thanks!
My general solution to any IPC project is to make the client and server non-blocking I/O. To do so requires queuing data both on writing and reading, to handle cases where the OS can't read/write, or can only read/write a portion of your message.
The code below will probably seem like EXTREME overkill, but if you get it working, you can use it the rest of your career, whether for named pipes, sockets, network, you name it.
In pseudo-code:
typedef struct {
const char* pcData, * pcToFree; // pcData may no longer point to malloc'd region
int iToSend;
} DataToSend_T;
queue of DataToSend_T qdts;
// Caller will use malloc() to allocate storage, and create the message in
// that buffer. MyWrite() will free it now, or WritableCB() will free it
// later. Either way, the app must NOT free it, and must not even refer to
// it again.
MyWrite( const char* pcData, int iToSend ) {
iSent = 0;
// Normally the OS will tell select() if the socket is writable, but if were hugely
// compute-bound, then it won't have a chance to. So let's call WritableCB() to
// send anything in our queue that is now sendable. We have to send the data in
// order, of course, so can't send the new data until the entire queue is done.
WritableCB();
if ( qdts has no entries ) {
iSent = write( pcData, iToSend );
// TODO: check error
// Did we send it all? We're done.
if ( iSent == iToSend ) {
free( pcData );
return;
}
}
// OK, either 1) we had stuff queued already meaning we can't send, or 2)
// we tried to send but couldn't send it all.
add to queue qdts the DataToSend ( pcData + iSent, pcData, iToSend - iSent );
}
WritableCB() {
while ( qdts has entries ) {
DataToSend_T* pdts = qdts head;
int iSent = write( pdts->cData, pdts->iToSend );
// TODO: check error
if ( iSent == pdts->iToSend ) {
free( pdts->pcToFree );
pop the front node off qdts
else {
pdts->pcData += iSent;
pdts->iToSend -= iSent;
return;
}
}
}
// Off-subject but I like a TINY buffer as an original value, that will always
// exercise the "buffer growth" code for almost all usage, so we're sure it works.
// If the initial buffer size is like 1M, and almost never grows, then the grow code
// may be buggy and we won't know until there's a crash years later.
int iBufSize = 1, iEnd = 0; iEnd is the first byte NOT in a message
char* pcBuf = malloc( iBufSize );
ReadableCB() {
// Keep reading the socket until there's no more data. Grow buffer if necessary.
while (1) {
int iRead = read( pcBuf + iEnd, iBufSize - iEnd);
// TODO: check error
iEnd += iRead;
// If we read less than we had space for, then read returned because this is
// all the available data, not because the buffer was too small.
if ( iRead < iBufSize - iEnd )
break;
// Otherwise, double the buffer and try reading some more.
iBufSize *= 2;
pcBuf = realloc( pcBuf, iBufSize );
}
iStart = 0;
while (1) {
if ( pcBuf[ iStart ] until iEnd-1 is less than a message ) {
// If our partial message isn't at the front of the buffer move it there.
if ( iStart ) {
memmove( pcBuf, pcBuf + iStart, iEnd - iStart );
iEnd -= iStart;
}
return;
}
// process a message, and advance iStart by the size of that message.
}
}
main() {
// Do your initial processing, and call MyWrite() to send and/or queue data.
while (1) {
select() // see man page
if ( the file handle is readable )
ReadableCB();
if ( the file handle is writable )
WritableCB();
if ( the file handle is in error )
// handle it;
if ( application is finished )
exit( EXIT_SUCCESS );
}
}
In order to design our API/messages, I've made some preliminary tests with our data:
Protobuf V3 Message:
message TcpGraphes {
uint32 flowId = 1;
repeated uint64 curTcpWinSizeUl = 2; // max 3600 elements
repeated uint64 curTcpWinSizeDl = 3; // max 3600 elements
repeated uint64 retransUl = 4; // max 3600 elements
repeated uint64 retransDl = 5; // max 3600 elements
repeated uint32 rtt = 6; // max 3600 elements
}
Message build as multipart message in order to add the filter functionality for the client
Tested with 10 python clients: 5 running on the same PC (localhost), 5 running on an external PC.
Protocol used was TCP. About 200 messages were sent every second.
Results:
Local client are working: they get every messages
Remote clients are missing some messages (throughput seems to be limited by the server to 1Mbit/s per client)
Server code (C++):
// zeroMQ init
zmq_ctx = zmq_ctx_new();
zmq_pub_sock = zmq_socket(zmq_ctx, ZMQ_PUB);
zmq_bind(zmq_pub_sock, "tcp://*:5559");
every second, about 200 messages are sent in a loop:
std::string serStrg;
tcpG.SerializeToString(&serStrg);
// first part identifier: [flowId]tcpAnalysis.TcpGraphes
std::stringstream id;
id << It->second->first << tcpG.GetTypeName();
zmq_send(zmq_pub_sock, id.str().c_str(), id.str().length(), ZMQ_SNDMORE);
zmq_send(zmq_pub_sock, serStrg.c_str(), serStrg.length(), 0);
Client code (python):
ctx = zmq.Context()
sub = ctx.socket(zmq.SUB)
sub.setsockopt(zmq.SUBSCRIBE, '')
sub.connect('tcp://x.x.x.x:5559')
print ("Waiting for data...")
while True:
message = sub.recv() # first part (filter part, eg:"134tcpAnalysis.TcpGraphes")
print ("Got some data:",message)
message = sub.recv() # second part (protobuf bin)
We have looked at the PCAP and the server don't use the full bandwidth available, I can add some new subscribers, remove some existing ones, every remote subscriber gets "only" 1Mbit/s.
I've tested an Iperf3 TCP connection between the two PCs and I reach 60Mbit/s.
The PC who runs the python clients has about 30% CPU last.
I've minimized the console where the clients are running in order to avoid the printout but it has no effect.
Is it a normal behavior for the TCP transport layer (PUB/SUB pattern) ? Does it means I should use the EPGM protocol ?
Config:
windows xp for the server
windows 7 for the python remote clients
zmq version 4.0.4 used
A performance motivated interest ?
Ok, let's first use the resources a bit more adequately :
// //////////////////////////////////////////////////////
// zeroMQ init
// //////////////////////////////////////////////////////
zmq_ctx = zmq_ctx_new();
int aRetCODE = zmq_ctx_set( zmq_ctx, ZMQ_IO_THREADS, 10 );
assert( 0 == aRetCODE );
zmq_pub_sock = zmq_socket( zmq_ctx, ZMQ_PUB );
aRetCODE = zmq_setsockopt( zmq_pub_sock, ZMQ_AFFINITY, 1023 );
// ^^^^
// ||||
// (:::::::::::)-------++++
// >>> print ( "[{0: >16b}]".format( 2**10 - 1 ) ).replace( " ", "." )
// [......1111111111]
// ||||||||||
// |||||||||+---- IO-thread 0
// ||||||||+----- IO-thread 1
// |......+------ IO-thread 2
// :: : :
// |+------------ IO-thread 8
// +------------- IO-thread 9
//
// API-defined AFFINITY-mapping
Non-windows platforms with a more recent API can touch also scheduler details and tweak O/S-side priorities even better.
Networking ?
Ok, let's first use the resources a bit more adequately :
aRetCODE = zmq_setsockopt( zmq_pub_sock, ZMQ_TOS, <_a_HIGH_PRIORITY_ToS#_> );
Converting the whole infrastructure into epgm:// ?
Well, if one wishes to experiment and gets warranted resources for doing that E2E.
I am testing G-WAN server and I'd like using rewrite rules.
With apache the rule is :
RewriteRule ^(.+)-(.+)-(.+)-1.jpg$ imagesproduitnew/$3/$2.jpg [L]
I am trying to do it by handlers JPG, but I have lot of difficulties.
Has anybody already done something like that ?
My handlers is called url_wr.c in the path /0.0.0.0_80/#0.0.0.0/handlers
Here is the script
int init(char *argv[], int argc);
int main(int argc, char *argv[])
{
const long state = (long)argv[0];
if(state == HDL_AFTER_READ)
{
xbuf_t *read_xbuf = (xbuf_t*)get_env(argv, READ_XBUF);
xbuf_replfrto(read_xbuf, read_xbuf->ptr, read_xbuf->ptr + 16, "/blog", "/?blog");
}
return 255; // execute next connection step
}
int clean(char *argv[], int argc);
In gwan.log, it is not writen loaded url_wr.c
If I put printf in each function, it doesn't work.
The servlet bloc.c works well.
I also tried tu put the code in handlers/main.c and in the root of gwan directory.
I have only a error.log file for the site which says just error404 without any details of the handlers.
Thanks by advance for your support
You must use a G-WAN connection handler, either to use:
a plain-rewrite: one example is given at the end of the developers page,
OR,
a regex library (libc provides regex calls) if you target a more general rewrite scheme. Here is an example in C and the explanations are there, courtesy of "Regular Expressions in C" from the "Linux Gazette".
This could also be made rom a servlet, but then you would have to trigger a redirection (unless the resource was explicitely placed into a cache). If this is acceptable, then v3.10+ will let you do it in C#, PHP, Python, etc.
UPDATE following the code published in the question:
Your init() call is empty so main() is never called. You should do this instead:
// ----------------------------------------------------------------------------
// init() will initialize your data structures, load your files, etc.
// ----------------------------------------------------------------------------
// init() should return -1 if failure (to allocate memory for example)
int init(int argc, char *argv[])
{
// define which handler states we want to be notified in main():
// enum HANDLER_ACT {
// HDL_INIT = 0,
// HDL_AFTER_ACCEPT, // just after accept (only client IP address setup)
// HDL_AFTER_READ, // each time a read was done until HTTP request OK
// HDL_BEFORE_PARSE, // HTTP verb/URI validated but HTTP headers are not
// HDL_AFTER_PARSE, // HTTP headers validated, ready to build reply
// HDL_BEFORE_WRITE, // after a reply was built, but before it is sent
// HDL_HTTP_ERRORS, // when G-WAN is going to reply with an HTTP error
// HDL_CLEANUP };
//
u32 *states = (u32*)get_env(argv, US_HANDLER_STATES);
*states = 1 << HDL_AFTER_READ; // we assume "GET /hello" sent in one shot
puts("init()");
return 0;
}
Also, make sure that connection handlers are named main.c. In contrast, content handlers carry the name of the targeted file extension (gif.c, html.c, etc).