nginx—fastcgi—Writing Hello World in FCGI with C++—in CentOS7

http://chriswu.me/blog/writing-hello-world-in-fcgi-with-c-plus-plus/

March 23, 2012

Prerequisites

This post will assume some familiarity with C++. The setup instructions also assume the server to deploy to is Ubuntu. This tutorial can be applied to other operating systems but I do not provide instructions on installing libraries on other operating systems.

Why FCGI?

FCGI (FastCGI) is a protocol in which web applications can talk to a web server to serve web requests. Long story short FCGI was developed to solve the scalability shortcomings that CGI. Typical CGI applications will fork a new process to deal with the request. While this is convenient, it is terribly inefficient due to the overhead of creating and terminating a process for each web request. The idea with FCGI is that you can spawn a static number of processes to handle web requests. This allows for the overhead of creating and terminating processes to be eliminated as resources for request handling can be reused within one process handling multiple requests.

FCGI easily allows for our application to accept web requests by interacting with stdio. There are alternatives such as implementing your own http server using tools like boost. Simple interface and robust are two of the key points of using fcgi for developing web applications in C++.

There are more reasons to use fcgi but you can check out the official site and the wikipedia page for more info.

Installation

We will need to install the libfcgi++ library, a web server (nignx in our case) and spawn-fcgi to run the fcgi app. We’ll also install curl to help test later on.

sudo apt-get install libfcgi-dev  sudo apt-get install spawn-fcgi  sudo apt-get install nginx  sudo apt-get install curl

The Code

Most of this code is basically a copy paste re-interpretation of the example echo-cpp.cpp from fastcgi.com


点击(此处)折叠或打开

  1. #include <iostream>
  2. #include "fcgio.h"

  3. using namespace std;

  4. int main(void) {
  5.     // Backup the stdio streambufs
  6.     streambuf * cin_streambuf = cin.rdbuf();
  7.     streambuf * cout_streambuf = cout.rdbuf();
  8.     streambuf * cerr_streambuf = cerr.rdbuf();

  9.     FCGX_Request request;

  10.     FCGX_Init();
  11.     FCGX_InitRequest(&request, 0, 0);

  12.     while (FCGX_Accept_r(&request) == 0) {
  13.         fcgi_streambuf cin_fcgi_streambuf(request.in);
  14.         fcgi_streambuf cout_fcgi_streambuf(request.out);
  15.         fcgi_streambuf cerr_fcgi_streambuf(request.err);

  16.         cin.rdbuf(&cin_fcgi_streambuf);
  17.         cout.rdbuf(&cout_fcgi_streambuf);
  18.         cerr.rdbuf(&cerr_fcgi_streambuf);

  19.         cout << "Content-type: text/html\r\n"
  20.              << "\r\n"
  21.              << "<html>\n"
  22.              << " <head>\n"
  23.              << " <title>Hello, World!</title>\n"
  24.              << " </head>\n"
  25.              << " <body>\n"
  26.              << " <h1>Hello, World!</h1>\n"
  27.              << " </body>\n"
  28.              << "</html>\n";

  29.         // Note: the fcgi_streambuf destructor will auto flush
  30.     }

  31.     // restore stdio streambufs
  32.     cin.rdbuf(cin_streambuf);
  33.     cout.rdbuf(cout_streambuf);
  34.     cerr.rdbuf(cerr_streambuf);

  35.     return 0;
  36. }

Breaking code dump down we see that the de-facto interface to stdio (cout/cin/cerr) are hijacked to serve the purposes of request handling. It’s possible to retrieve the iostreams back since we save the stdio stream buffers.

We then initialize the FCGX library with FCGX_Init() and initialize the request object that we share across requests for the lifetime of this process.

We then have a blocking loop accepting fcgi requests with our call to FCGX_Accept_r. The _r version calls the multi-thread safe version of the function, although this is not necessary in our single threaded program. This will cleanup the old request that was passed into FCXG_Accept_r and initialize the new request when it comes in.

We construct the fcgi stream buffers inside the loop, using RAII pattern to ensure the buffers are flushed at the end of the request processing (end of the loop)

Nginx Config

Next up we’ll setup nginx to listen on port 80 for http requests and forward those along the the fcgi process which will listen on port 8000.

The key directive is fastcgi_pass 127.0.0.1:8000 indicates that nginx should forward the fcgi request to port 8000 at localhost. You can replace the address with an upstream directive if you want to want to load balance across many processes. The rest of the fastcgi_param directives are optional and just set appropriate environment variables which get forwarded to the fcgi application.


点击(此处)折叠或打开

  1. events {
  2.   worker_connections 1024;
  3. }

  4. http {
  5.   server {
  6.     listen 80;
  7.     server_name localhost;

  8.     location / {
  9.       fastcgi_pass 127.0.0.1:8000;

  10.       fastcgi_param GATEWAY_INTERFACE CGI/1.1;
  11.       fastcgi_param SERVER_SOFTWARE nginx;
  12.       fastcgi_param QUERY_STRING $query_string;
  13.       fastcgi_param REQUEST_METHOD $request_method;
  14.       fastcgi_param CONTENT_TYPE $content_type;
  15.       fastcgi_param CONTENT_LENGTH $content_length;
  16.       fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  17.       fastcgi_param SCRIPT_NAME $fastcgi_script_name;
  18.       fastcgi_param REQUEST_URI $request_uri;
  19.       fastcgi_param DOCUMENT_URI $document_uri;
  20.       fastcgi_param DOCUMENT_ROOT $document_root;
  21.       fastcgi_param SERVER_PROTOCOL $server_protocol;
  22.       fastcgi_param REMOTE_ADDR $remote_addr;
  23.       fastcgi_param REMOTE_PORT $remote_port;
  24.       fastcgi_param SERVER_ADDR $server_addr;
  25.       fastcgi_param SERVER_PORT $server_port;
  26.       fastcgi_param SERVER_NAME $server_name;
  27.     }
  28.   }
  29. }

Running the code

The system now comprises of three parts. One will be the executable fcgi c++ app. In order to run this we need spawn-fcgi and to listen to a port. Nginx will then forward web requests to this port, translating the http proctocol into the fast cgi protocol.


点击(此处)折叠或打开

  1. # run nginx using the provided configuration
  2. sudo nginx -c < path to nginx.conf>

  3. # compile hello_world
  4. g++ main_v1.cpp -lfcgi++ -lfcgi -o hello_world

  5. # spawn the fcgi app on port 8000 with no fork
  6. spawn-fcgi -p 8000 -n hello_world
Then just head to the browser and enter http://localhost/ into the location bar and voilà! Hello, World! See the next part in this tutorial to see how to incorporate the request uri and the request content into this simple app.





<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(207) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值