Aaltomainen sininen teknologinen tuulahdus mustalla taustalla kuvittamassa artikkelia eri ohjelmointikielillä testaamista.

Kuinka nopeasti eri ohjelmointikielet vastaavat HTTP-kutsuihin?

Eri ohjelmointikielillä testaaminen: Kokeilimme ja vertailimme, kuinka nopeasti eri ohjelmointikielet vastaavat http-kutsuihin. Katso yllättävät tulokset!

Kellottaen pintaa

”Kellottaen pintaa” ilmaisu tulee vanhasta työnsuunnittelun termistä kellottaa, johon liittyy työn pilkkominen osiin ja niiden keston mittaaminen kellon avulla. Tähän liittyy myös vähemmän tykätty työrooli eli niin sanottu ”kellokalle”. Tässä tapauksessa vaan yksittäisten suoritusten kestoajan mittaaminen.

Eri ohjelmointikielillä testaaminen

Kokeillaan eri teknologioilla eli tässä tapauksessa lähinnä eri ohjelmointikielillä määrittelyn mukaisen web – palvelin toteutuksen suorituskykyä. Määrittely koskee http-kutsuihin vastaamista, mutta koodipohja tarjoaa kielestä riippuen mahdollisuuden laajentaa toteutusta esimerkiksi tietokantahakuihin. Testaus tapahtuu samalla koneella, missä serveriä ajetaan. Toteutukset on pyritty tekemään yhden kooditiedoston avulla, joiden lisäksi tulevat käytettävät kirjastot. Ensin on testattu kielen mukana tai muuten helposti tulevaa http-palvelinta sitten on jatkettu vastaavia toteutuksia erillisen http palvelimen alle. Jos ratkaisu ei helposti tukenut moniajoa niin se jätettiin erillisen http-palvelimen asiaksi, samoin id – kenttää ei väkisin synkronoitu, jos toteutus tapahtui moniajona. Eroja kielten välille olisi varmaan tullut lisää jos prosessointitarvetta ohjelmakoodissa olisi lisätty. FastCGI puolelle lähdettäessä on useita palvelinvaihtoehtoja ja eri kielten toimivuus niissä vaihtelee ja näistä tulevia yhdistelmiä olisikin sitten useita, tässä niiden käsittelyä on vasta aloitettu.

Yhteenveto kokeilujen perusteella

Koereq/s
Nodejs24616
Python5028
Java2277
Go254373
C549791
C-Apache-FastCGI56206
Go-Apache-FastCGI26907
 

Kuormitustestausohjelmista

Http-servereiden kuormitustestauksesta voisi sanoa sen verran, että kannattaa ajaa testiohjelmaa samalla koneella kuin testattavaa http-serveriäkin, ettei tule testanneeksi enimmäkseen nettiyhteyttä. Testiohjelman tulisi olla tehokas, että se itse ei muodostu pullonkaulaksi ja kevyt, että pääosa cpu:sta jäisi testattavalle http-serverille. ”Keep alive” ja ”ipv6” -yhteyksiä olisi hyvä myös tukea. Kokeilin testiohjelmia httperf, ab ja weighttp.

* httperf, tulee käyttöjärjestelmä jakelun mukana, yksisäikeinen kätevä ohjelma

* ab eli ”Apache HTTP server benchmarking tool”, mukana Apache httpd:n työkaluissa, yksisäikeinen kätevä ohjelma

* weighttp, liittyy Lighttpd web serveriin kts: https://fi.wikipedia.org/wiki/Lighttpd, haettavissa tuolta: https://github.com/lighttpd/weighttp.git ja käännettävissä helposti, monisäikeinen kevyt ohjelma, puuttuu tuki post-kutsuille.

Tavallaan se testausohjelma, joka antaa parhaat lukemat http-serverille on itse kevyin.

Toki eri testiohjelmia voi käyttää myös yhdessä.

Erillinen http-palvelin

Erillistä http-palvelinta olisi hyvä käyttää sen tuomien lisämahdollisuuksien, mm. tietoturvan vuoksi. Proxy- eli edustapalvelinratkaisu päälle laittaminen olisi houkutteleva, mutta se johtaisi helposti monoliitin rakentamiseen proxyn taakse ja taustapalvelimien portit olisi konfiguroitava. Mutta, jos tekisi näistä pienistä palveluista FastCGI sovelluksia, niin ne voisi erikseen vaan kääntää ja kopioida varsinaisen http-palvelimen FastCGI – hakemistoon, josta ne olisivat heti käytettävissä ja kutsuttavissa omalla url:llaan. FastCGI palveluiden hyvä puoli CGI – palveluihin nähden on että jokaista http-kutsua varten ei tarvitse käynnistää uutta prosessia, vaan valmis prosessi odottaa holdissa kuluttamatta cpu:ta uutta kutsua esimerkiksi c-ssä: while(FCGI_Accept() >= 0){… jossa luuppi jatkuu kun FCGI_Accept() herää.

Kuormituksen kasvaessa Http-palvelimen FastCGI osaa käynnistää palveluista lisää prosesseja ja kuormituksen pienentyessä vähentää. Ja jos on pelkoa että pieni FastCGI palvelun koodi vuotaa resursseja, niin voidaan asettaa kutsumäärä, jonka jälkeen se sammutetaan ja käynnistetään uudelleen joka tapauksessa.

Apache

Esimerkiksi Apachen http-palvelimeen FastCGI palvelun konfigurointi oletus tiedostoon:

/etc/httpd/conf/httpd.conf, kun apache-mod_fcgid on asennettu, käy lisäämällä oikeille paikoilleen seuraavat:

<IfModule alias_module>

ScriptAlias /fcgi-bin/ ”/var/www/fcgi-bin/”

</IfModule>

<Location /var/www/fcgi-bin>

SetHandler fcgid-script

Options +ExecCGI

Order allow,deny

Allow from all

</Location>

Lisää konfigurointi ohjeita: https://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html

Määritelmä: https://fastcgi-archives.github.io/FastCGI_Specification.html

Wiki ja lisää palvelimia ja kieliä: https://en.wikipedia.org/wiki/FastCGI

Määrittely

Haaveena on, että määrittelyt olisi niin hyviä, että kaikki voisivat koodata sillä kielellä kuin haluavat, kunhan palvelut toimisivat sovitussa ympäristössä ja vastaavat riittävän monta kertaa sekunnissa, reagoivat oikein oikeisiin ja vääriin kutsuihin ja mitä muuta on määritelty. Määrittely olisi hyvä sisältää ajettavat testit. Mikäli toteuttaja vaihtuu ja määrittelyyn tulee myöhemmin muutos, uuden toteuttajan ei olisi pakko katsoa edeltäjän koodia ollenkaan vaan koodata uuden määrittelyn mukainen palvelu. Siksi palveluiden tulisi olla pieniä.

Vaikka vain kokeiltaisiin eri teknologioiden tehokkuutta, olisi hyvä määritellä testiesimerkki, jonka avulla kokeilu suoritetaan.

Tehtävänä on toteuttaa palvelu,

1) joka vastataan http GET kutsuun,

2) jossa on ”who” parametri, joka on pystyttävä poimimaan kutsusta ja palauttamaan yhdistettynä vastaukseen ”quaerit”.

3) Lisäksi halutaan id, jonka palvelun tulee generoida yhdestä tai useammasta juoksevasta sarjasta.

4) Vastaukseen lisätään pieni kevyt vakio kuorma ”message”:”Dolorem ipsum” ja vastauksen tulee olla json-muotoa.

5) Vastaukseen tulee lisätä oikein laskettu http header ”Content-Length”

6) ja vakio header: Content-Type: application/json; charset=utf-8

7) url:n alkuosa voidaan valita toteutuksen mukaan

8) palvelun on toimittava myös ”http Keep Alive”-tilassa vaikka ”Keep-Alive” headeria ei tarvitse palauttaa

Palvelun on vastattava seuraavien esimerkkien mukaisiin testeihin:

a) Kuormitettavuus

weighttp -n 1000000 -c 100 -t 8 -k http://localhost/koeurl?who=Cicero

finished in 37 sec, 493 millisec and 951 microsec, 26670 req/s, 7427 kbyte/s
requests: 1000000 total, 1000000 started, 1000000 done, 1000000 succeeded, 0 failed, 0 errored
status codes: 1000000 2xx, 0 3xx, 0 4xx, 0 5xx
traffic: 285185850 bytes total, 225566777 bytes http, 59619073 bytes data

b) Headereiden tarkistus ja vastauksen muoto

curl -i -X GET http://localhost/koeurl?who=Cicero

HTTP/1.1 200 OK
Date: Sun, 19 Mar 2023 21:17:31 GMT
Server: Apache/2.4.56 (Mageia) mod_fcgid/2.3.9
Content-Length: 60
Content-Type: application/json; charset=utf-8

{”message”:”Dolorem ipsum”, ”id”:57089, ”quaerit”:”Cicero”}

Testit

Nodejs

Testit

weighttp -n 1000000 -c 100 -t 8 -k http://localhost:8000/nodeans?who=Cicero
finished in 40 sec, 623 millisec and 983 microsec, 24616 req/s, 6319 kbyte/s

requests: 1000000 total, 1000000 started, 1000000 done, 1000000 succeeded, 0 failed, 0 errored

status codes: 1000000 2xx, 0 3xx, 0 4xx, 0 5xx

curl -i -X GET http://localhost:8000/nodeans?who=Cicero

HTTP/1.1 200 OK

Content-Type: application/json; charset=utf-8

Content-Length: 62

Date: Mon, 13 Mar 2023 19:53:53 GMT

Connection: keep-alive

Keep-Alive: timeout=5

{”message”:”Dolorem ipsum”, ”id”:1000000, ”quaerit”:”Cicero”}

Koodi

// aja: node http.js


const http = require("http")

const url = require('url')

 

const host = 'localhost'

const port = 8000

let id = 0

 

const requestListener = function (req, res) {

    // console.log("req: " + JSON.stringify(url.parse(req.url, true).query))

    let who = url.parse(req.url, true).query.who

 

    let body = '{"message":"Dolorem ipsum", "id":'+ id++ +', "quaerit":"'+ who +'"}\n'

    res.setHeader("Content-Type", "application/json; charset=utf-8")

    res.setHeader("Content-Length", body.length)

    res.writeHead(200)

    res.end(body)

}

 

const server = http.createServer(requestListener)

server.listen(port, host, () => {

    console.log(`Server is running on http://${host}:${port}`)

})

Python

Testit

weighttp -n 100000 -c 100 -t 8 -k http://localhost:8000/pyuvans?who=Cicero
finished in 19 sec, 885 millisec and 523 microsec, 5028 req/s, 981 kbyte/s
requests: 100000 total, 100000 started, 100000 done, 100000 succeeded, 0 failed, 0 errored
status codes: 100000 2xx, 0 3xx, 0 4xx, 0 5xx

curl -i -s -X GET http://localhost:8000/pyuvans?who=Cicero
HTTP/1.1 200 OK
date: Thu, 23 Mar 2023 10:56:40 GMT
server: uvicorn
Content-Type: application/json; charset=utf-8
Content-Length: 56

{”message”:”Dolorem ipsum”, ”id”:0, ”quaerit”:”Cicero”}

Koodi

# kts. https://www.uvicorn.org/

# toki jos haluaa hepottaa koodia kts. https://fastapi.tiangolo.com/

 

import uvicorn

from urllib import parse

 

id = 0;

 

async def app(scope, receive, send):

    global id

    paramss = scope['query_string'].decode("utf-8")

    parmd = parse.parse_qs(paramss)

    who = parmd['who'][0]

    body = b'{"message":"Dolorem ipsum", "id":'+ bytes(str(id),'UTF-8') + b', "quaerit":"'+ bytes(who,'UTF-8') +b'"}\n'

 

    assert scope['type'] == 'http'

 

    await send({

        'type': 'http.response.start',

        'status': 200,

        'headers': [

            [b'Content-Type', b'application/json; charset=utf-8'],

            [b'Content-Length', bytes(str(len(body)),'UTF-8')],

        ],

    })

    await send({

        'type': 'http.response.body',

        'body': body

    })

    id += 1

 

if __name__ == "__main__":

    uvicorn.run("pyunians:app", port=8000, log_level="error")

Lisäksi kirjaston lataus

# cat requirements.txt

###  virtual envin luonti, joka asentaa myös pip-komennon

# python3 -m venv venv –system-site-packages

# kts. https://www.uvicorn.org/

###  virtual envin käyttöön otto

#   source venv/bin/activate

#   pip install -r requirements.txt

### ajaminen

# uvicorn pyunians:app

# tai

# python pyunians.py

### Tästä alkaa varsinainen virtual env requirements jota pip lukee

uvicorn

Java

Javallakin onnistuu määrittelyn mukainen web-serveri yhdellä lähdekooditiedostolla, tosin sitä olisi luullut nopeammaksi

Testit

weighttp -n 100000 -c 100 -t 8 -k http://localhost:8000/echo?who=Cicero
finished in 43 sec, 901 millisec and 334 microsec, 2277 req/s, 406 kbyte/s

requests: 100000 total, 100000 started, 100000 done, 100000 succeeded, 0 failed, 0 errored

status codes: 100000 2xx, 0 3xx, 0 4xx, 0 5xx

curl -i -s -X GET http://localhost:8000/echo?who=Cicero

HTTP/1.1 200 OK

Date: Mon, 13 Mar 2023 19:37:54 GMT

Content-type: application/json; charset=utf-8

Content-length: 61

{”message”:”Dolorem ipsum”, ”id”:100000, ”quaerit”:”Cicero”}

Koodi

// ajo: java Httpkoe.java

 

import java.io.IOException;

import java.io.OutputStream;

import java.net.InetSocketAddress;

import java.util.Collections;

import java.util.List;

import java.util.Map;

import java.util.Set;

import java.util.stream.Collectors;

import java.util.stream.Stream;

 

import com.sun.net.httpserver.Headers;

import com.sun.net.httpserver.HttpExchange;

import com.sun.net.httpserver.HttpHandler;

import com.sun.net.httpserver.HttpServer;

 

 

public class Httpkoe {

 

    int id = 0;

 

    public static Map<String, String> getParamMap(String query) {

        if (query == null || query.isEmpty()) return Collections.emptyMap();

        return Stream.of(query.split("&"))

                .filter(s -> !s.isEmpty())

                .map(kv -> kv.split("=", 2))

                .collect(Collectors.toMap(x -> x[0], x -> x[1]));

    }

 

    class Handler implements HttpHandler {

        public void handle(HttpExchange xchg) throws IOException {

            Map params = getParamMap(xchg.getRequestURI().getQuery());

            String form = "{\"message\":\"Dolorem ipsum\", \"id\":%d, \"quaerit\":\"%s\"}\n";

            xchg.getResponseHeaders().add("Content-Type", "application/json; charset=utf-8");

            String response = String.format(form, id, params.get("who"));

            xchg.sendResponseHeaders(200, response.length());

            OutputStream os = xchg.getResponseBody();

            os.write(response.toString().getBytes());

            os.close();

            id++;

        }

    }

 

    void aloita(HttpServer server) {

        server.createContext("/echo", new Handler());

        server.start();

    }

 

    public static void main(String[] args) throws IOException {

        HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);

        Httpkoe httpkoe = new Httpkoe();

        httpkoe.aloita(server);

    }

}

Go

Go (golang) :lla määrittelyn mukainen juttu tehtiin jo neljännes miljoonaa kertaa sekunnissa

Testit

weighttp -n 10000000 -c 100 -t 8 -k http://localhost:8000/gohttp?who=Cicero

finished in 39 sec, 312 millisec and 300 microsec, 254373 req/s, 45927 kbyte/s

requests: 10000000 total, 10000000 started, 10000000 done, 10000000 succeeded, 0 failed, 0 errored

status codes: 10000000 2xx, 0 3xx, 0 4xx, 0 5xx

curl -i -s -X GET http://localhost:8000/gohttp?who=Cicero

HTTP/1.1 200 OK

Content-Type: application/json; charset=utf-8

Date: Tue, 14 Mar 2023 19:53:35 GMT

Content-Length: 62

{”message”:”Dolorem ipsum”, ”id”:9807936, ”quaerit”:”Cicero”}

Koodi

// kts https://gobyexample.com/http-servers

// aja: go run gohttpd.go

// tai

// build: go build gohttpd.go

// aja: ./gohttpd

 

package main

 

import (

        "fmt"

        "net/http"

)

 

var id = 0

 

func hello(w http.ResponseWriter, req *http.Request) {

        who := req.URL.Query().Get("who")

        w.Header().Set("Content-Type", "application/json; charset=utf-8")

        fmt.Fprintf(w, "{\"message\":\"Dolorem ipsum\", \"id\":%d, \"quaerit\":\"%s\"}\n", id, who)

        id++

}

 

func main() {

        http.HandleFunc("/gohttp", hello)

        http.ListenAndServe(":8000", nil)

}

C

C:lläkin saa tehtyä määrittelyn mukaisen jutun, ei ihan niin siistillä koodilla kuin go:ssa, mutta yhdellä kooditiedostolla kuitenkin ja päästäänkin sitten jo puolen miljoonan paremmalle puolelle suorituksissa sekunnissa. Mietityttämään jäi tuliko varattu muisti aina vapautettua oikein.

Testit

weighttp -n 10000000 -c 100 -t 8 -k http://localhost:8000/chttp?who=Cicero

finished in 18 sec, 188 millisec and 715 microsec, 549791 req/s, 112153 kbyte/s

requests: 10000000 total, 10000000 started, 10000000 done, 10000000 succeeded, 0 failed, 0 errored

status codes: 10000000 2xx, 0 3xx, 0 4xx, 0 5xx

curl -i -s http://localhost:8000/chttp?who=Cicero

HTTP/1.1 200 OK

Connection: Keep-Alive

Content-Length: 62

Content-Type: application/json; charset=utf-8

Date: Wed, 15 Mar 2023 19:55:04 GMT

{”message”:”Dolorem ipsum”, ”id”:9909580, ”quaerit”:”Cicero”}

Koodi

#define _GNU_SOURCE

#include <microhttpd.h>

#include <stdlib.h>

#include <string.h>

#include <stdio.h>

 

// kts. https://www.gnu.org/software/libmicrohttpd/

// ja https://www.gnu.org/software/libmicrohttpd/tutorial.html

//

// käännös: gcc -O3 -lmicrohttpd microhttpdkoe.c -o microhttpdkoe

// ajo: ./microhttpdkoe 8000

 

#define PAGE "<html><head><title>libmicrohttpd demo</title>"    \

    "</head><body>libmicrohttpd demo</body></html>\n"

 

int id = 0;

 

enum MHD_Result print_out_key (void *cls, enum MHD_ValueKind kind,

                               const char *key, const char *value)

{

    printf ("%s: %s\n", key, value);

    return MHD_YES;

}

 

static enum MHD_Result

ahc_echo(void * cls,

         struct MHD_Connection * connection,

         const char * url,

         const char * method,

         const char * version,

         const char * upload_data,

         size_t * upload_data_size,

         void ** ptr) {

    static int dummy;

    const char * page = cls;

    struct MHD_Response * response;

    int ret;

 

    if (0 != strcmp(method, "GET"))

        return MHD_NO; /* unexpected method */

    if (&dummy != *ptr)

    {

        /* The first time only the headers are valid,

           do not respond in the first round... */

        *ptr = &dummy;

        return MHD_YES;

    }

    if (0 != *upload_data_size)

        return MHD_NO; /* upload data in a GET!? */

    *ptr = NULL; /* clear context pointer */

 

 

    //MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, &print_out_key, NULL); // debugia varten

    //printf("? %s\n", MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "who"));

    const char * who = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "who");

    const char * form = "{\"message\":\"Dolorem ipsum\", \"id\":%d, \"quaerit\":\"%s\"}\n";  

    char * vastaus;

    if (-1 == asprintf (&vastaus, form, id++, who))

    {

        /* oho */

        return MHD_NO;

    }

    //printf("pit: %d str: %s\n", strlen(vastaus), vastaus);

 

    response = MHD_create_response_from_buffer (strlen(vastaus),

                                                (void*) vastaus,

                                                MHD_RESPMEM_PERSISTENT);

    MHD_add_response_header (response, "Content-Type", "application/json; charset=utf-8");

    ret = MHD_queue_response(connection,

                             MHD_HTTP_OK,

                             response);

    MHD_destroy_response(response);

    //free(vastaus);

    return ret;

}

 

int main(int argc,

         char ** argv) {

    struct MHD_Daemon * d;

    if (argc != 2) {

        printf("%s PORT\n",

               argv[0]);

        return 1;

    }

    d = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION,

                         atoi(argv[1]),

                         NULL,

                         NULL,

                         &ahc_echo,

                         PAGE,

                         MHD_OPTION_END);

    if (d == NULL)

        return 1;

    (void) getc (stdin);

    MHD_stop_daemon(d);

    return 0;

}

C-Apache-FastCGI

Määrittelyn mukainen toiminto c:llä toteutettuna Apachen fcgi palveluna antaa ihan hyvät vauhdit, Apache hidastaa, mutta tarjoaa mahdollisuuden lisätä kaikkea kivaa kuten filtterit ja staattiset tiedosto yms.

Testaus

weighttp -n 1000000 -c 100 -t 8 -k http://localhost/fcgi-bin/ceefcgi.fcgi?who=Cicero

finished in 17 sec, 791 millisec and 527 microsec, 56206 req/s, 15659 kbyte/s

requests: 1000000 total, 1000000 started, 1000000 done, 1000000 succeeded, 0 failed, 0 errored

status codes: 1000000 2xx, 0 3xx, 0 4xx, 0 5xx

curl -i -X GET http://localhost/fcgi-bin/ceefcgi.fcgi?who=Caius

HTTP/1.1 200 OK

Date: Sun, 19 Mar 2023 20:58:57 GMT

Server: Apache/2.4.56 (Mageia) mod_fcgid/2.3.9

Content-Length: 59

Content-Type: application/json; charset=utf-8

{”message”:”Dolorem ipsum”, ”id”:17862, ”quaerit”:”Caius”}

Koodi

#define _GNU_SOURCE

#include "fcgi_stdio.h"

#include <stdlib.h>

#include <string.h>

 

// esimerkki fcgi-ohjelma

// käännös gcc ceefcgi.c -o ceefcgi.fcgi -lfcgi -O3 -Wall -Wextra -pedantic -std=c11

int main(void)

{

    int id = 0;

    while(FCGI_Accept() >= 0){

        char *query_string = getenv("QUERY_STRING");

        char *who = strstr(query_string, "who=" );

        if(!who) who = "";

        else who = who + 4;

        char * vastaus;

        const char * form = "{\"message\":\"Dolorem ipsum\", \"id\":%d, \"quaerit\":\"%s\"}\n";

        int contentlen = asprintf(&vastaus, form, id++, who);

 

        printf("Content-Type: application/json; charset=utf-8\r\n"

               "Content-Length: %d\r\n"

               "\r\n"

               "%s",

               contentlen, vastaus);

        free(vastaus);

    }

    return 0;

}

Go-Apache-FastCGI

Sitten saman määrittelyn mukainen toiminto go:llä toteutettuna Apachen fcgi palveluna on sekin vauhdikas, eikä tarvitse miettiä onko kaikki free(…) kutsut paikoillaan kuten c:ssä.

Testi

weighttp -n 1000000 -c 100 -t 8 -k http://localhost/fcgi-bin/gosfcgi.fcgi?who=Cicero

finished in 37 sec, 164 millisec and 515 microsec, 26907 req/s, 7493 kbyte/s

requests: 1000000 total, 1000000 started, 1000000 done, 1000000 succeeded, 0 failed, 0 errored

status codes: 1000000 2xx, 0 3xx, 0 4xx, 0 5xx

curl -i -X GET http://localhost/fcgi-bin/gosfcgi.fcgi?who=Cicero

HTTP/1.1 200 OK

Date: Fri, 17 Mar 2023 21:32:16 GMT

Server: Apache/2.4.55 (Mageia) mod_fcgid/2.3.9

Transfer-Encoding: chunked

Content-Type: application/json; charset=utf-8

{”message”:”Dolorem ipsum”, ”id”:12353, ”quaerit”:”Cicero”}

Koodi

Tuossa go:n fcgi.Serve:ssä ollaan holdissa ja se vastaa c:n while(FCGI_Accept() >= 0){ :äää

// käännös:

//     go build gosfcgi.go

// asennus:

//     sudo cp gosfcgi /var/www/fcgi-bin/gosfcgi.fcgi

package main

 

import (

        "fmt"

        "log"

        "strconv"

        "net/http"

        "net/http/fcgi"

)

 

var id = 0

 

func homeView(w http.ResponseWriter, req *http.Request) {

        kuka := req.URL.Query().Get("who")

        vastaus := fmt.Sprintf("{\"message\":\"Dolorem ipsum\", \"id\":%d, \"quaerit\":\"%s\"}\n", id, kuka)

        w.Header().Set("Content-Type", "application/json; charset=utf-8")

        w.Header().Set("Content-Length", strconv.Itoa(len(vastaus)))

        fmt.Fprintf(w, vastaus)

        id++

}

 

func main() {

        err := fcgi.Serve(nil, http.HandlerFunc(homeView)) // nil tässä tarkoitta stedio:n käyttöä

        if err != nil {

                log.Fatal(err)

        }

}

Muita uutisia