📜 ⬆️ ⬇️

Proxygen - HTTP framework for C ++ from Facebook

Proxygen is a collection of libraries for using HTTP in C ++, including, among other things, a very easy to use HTTP server. In addition to the classic HTTP / 1.1 framework, Proxygen supports SPDY / 3 and SPDY / 3.1 . HTTP / 2 will also be fully supported soon.

Proxygen was not intended as a replacement for Apache or nginx - these projects are focused on creating sufficiently flexible and configurable web servers, which allow, through fine tuning, to achieve maximum performance. The task of Proxygen is to work quite well on the default settings, giving the programmer an easy-to-use web server and web client that easily integrates into existing projects. We want to help people build web services in C ++ with less cost and we believe that Proxygen is a great framework for this. You can read the documentation on it and connect to the development on Github .


Prehistory


Proxygen started as a project to create a custom high-performance reverse proxy with load balancing about four years ago. We planned that Proxygen will become a library for generating proxy servers (you probably already guessed it from the very name Proxygen). But since then, he has seriously evolved. We realize that there are already a decent number of programs that solve such problems (Apache, nginx, HAProxy, Varnish, etc), however, we decided to go our own way.
')
Why did we create our own HTTP stack?

Integration

Being able to quickly and easily integrate into your existing Facebook infrastructure was critical. For example, the ability to administer our HTTP infrastructure with tools like Thrift makes it easy to integrate with existing systems. The ability to easily monitor and measure Proxygen performance using systems such as ODS (our internal monitoring tool) allows you to quickly respond to new data and modify the product. Creating our own HTTP stack allowed us to more closely interact with the systems and components we need.

Reuse code

We wanted to create a foundation for building network components for all our projects. Currently, more than a dozen of our internal systems are built using Proxygen, including parts of systems such as Haystack , HHVM , our HTTP traffic balancers, some parts of our mobile infrastructure. Proxygen is a platform where we can, for example, work on support for the HTTP / 2 protocol and as soon as it is fully ready - get its support in all our products.

Scalability

We honestly tried to take existing products and scale them across our entire infrastructure. With some it even worked, some options worked for a long time. But at some point, the product used was no longer able to keep up with the growth of our capacities.

Functional

Some number of features at the time of writing Proxygen was absent in other similar projects (and in some is not yet). Some of these features are very useful for us: SPDY, WebSockets, HTTP / 1.1 (keep-alive), TLS-false start, some features of load distribution. Building our own HTTP stack unleashed our hands in terms of implementing this functionality.

Initially launched in 2011 by several of our engineers, who were striving to make the use of the HTTP protocol more efficient, Proxygen was developed by a team of 3-4 main developers and a dozen internal contributors. Milestones of the project:



There are a number of important points in the development, but we think that the code will tell this story better than us.

At the moment we have several years of experience using Proxygen. The library has already processed trillions of HTTP (S) and SPDY requests. We believe that we have already reached the stage when this project is not ashamed to share with the community.

Architecture


The core of the HTTP layer is divided into four abstractions: session, codec, transaction, and handler. A session is created for each connection. Each session has a codec that is responsible for serializing and deserializing frames into HTTP messages. A session is responsible for sending each message from the codec to a specific transaction. The library user is responsible for writing handlers for messages coming into the transaction. This design allows us to support new multiplexing protocols such as SPDY and HTTP / 2.

image

Proxygen actively uses the capabilities of the latest C ++ standard and depends on Thrift and Folly . We used move semantics to avoid the cost of copying large objects like body buffers and request and response headers. In addition, using non-blocking I / O and linux epoll under the hood, we created a server that is efficient both in terms of memory usage and CPU time.

HTTP server


The example of the server that we included in the release is a good starting point if you want to start with a simple, out-of-the-box, asynchronous backbone of the server.

EchoServer.cpp
/* * Copyright (c) 2014, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ #include "EchoHandler.h" #include "EchoStats.h" #include "proxygen/httpserver/HTTPServer.h" #include "proxygen/httpserver/RequestHandlerFactory.h" #include <folly/Portability.h> #include <folly/Memory.h> #include <folly/io/async/EventBaseManager.h> #include <unistd.h> using namespace EchoService; using namespace proxygen; using folly::EventBase; using folly::EventBaseManager; using folly::SocketAddress; using Protocol = HTTPServer::Protocol; DEFINE_int32(http_port, 11000, "Port to listen on with HTTP protocol"); DEFINE_int32(spdy_port, 11001, "Port to listen on with SPDY protocol"); DEFINE_int32(thrift_port, 10000, "Port to listen on for thrift"); DEFINE_string(ip, "localhost", "IP/Hostname to bind to"); DEFINE_int32(threads, 0, "Number of threads to listen on. Numbers <= 0 " "will use the number of cores on this machine."); class EchoHandlerFactory : public RequestHandlerFactory { public: void onServerStart() noexcept override { stats_.reset(new EchoStats); } void onServerStop() noexcept override { stats_.reset(); } RequestHandler* onRequest(RequestHandler*, HTTPMessage*) noexcept override { return new EchoHandler(stats_.get()); } private: folly::ThreadLocalPtr<EchoStats> stats_; }; int main(int argc, char* argv[]) { gflags::ParseCommandLineFlags(&argc, &argv, true); google::InitGoogleLogging(argv[0]); google::InstallFailureSignalHandler(); std::vector<HTTPServer::IPConfig> IPs = { {SocketAddress(FLAGS_ip, FLAGS_http_port, true), Protocol::HTTP}, {SocketAddress(FLAGS_ip, FLAGS_spdy_port, true), Protocol::SPDY}, }; if (FLAGS_threads <= 0) { FLAGS_threads = sysconf(_SC_NPROCESSORS_ONLN); CHECK(FLAGS_threads > 0); } HTTPServerOptions options; options.threads = static_cast<size_t>(FLAGS_threads); options.idleTimeout = std::chrono::milliseconds(60000); options.shutdownOn = {SIGINT, SIGTERM}; options.handlerFactories = RequestHandlerChain() .addThen<EchoHandlerFactory>() .build(); HTTPServer server(std::move(options)); server.bind(IPs); // Start HTTPServer mainloop in a separate thread std::thread t([&] () { server.start(); }); t.join(); return 0; } 


EchoHandler.cpp
 /* * Copyright (c) 2014, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ #include "EchoHandler.h" #include "EchoStats.h" #include "proxygen/httpserver/RequestHandler.h" #include "proxygen/httpserver/ResponseBuilder.h" using namespace proxygen; namespace EchoService { EchoHandler::EchoHandler(EchoStats* stats): stats_(stats) { } void EchoHandler::onRequest(std::unique_ptr<HTTPMessage> headers) noexcept { stats_->recordRequest(); } void EchoHandler::onBody(std::unique_ptr<folly::IOBuf> body) noexcept { if (body_) { body_->prependChain(std::move(body)); } else { body_ = std::move(body); } } void EchoHandler::onEOM() noexcept { ResponseBuilder(downstream_) .status(200, "OK") .header("Request-Number", folly::to<std::string>(stats_->getRequestCount())) .body(std::move(body_)) .sendWithEOM(); } void EchoHandler::onUpgrade(UpgradeProtocol protocol) noexcept { // handler doesn't support upgrades } void EchoHandler::requestComplete() noexcept { delete this; } void EchoHandler::onError(ProxygenError err) noexcept { delete this; } } 


We conducted a benchmark of our echo server on a computer with 32 Intel® Xeon® CPU E5-2670 @ 2.60GHz logical cores and 16 GiB memory, varying the number of worker threads from one to eight. We ran the client on the same machine in order to avoid network delays and this is what we got:

# Client Settings:
# For each server workflow, 2 clients
# 400 simultaneously open connections
# 100 connection requests
# 60 seconds testing
# The results indicate the average value of the results of 10 tests
# simple GET, 245 bytes of request headers, 600 bytes of response (without saving to disk)
# SPDY / 3.1 allows up to 10 concurrent connection requests

image

Although the echo server itself is a rather primitive thing compared to a real web server, this benchmark still shows how efficiently Proxygen works with SPDY and HTTP / 2. The Proxygen HTTP server is easy to use and immediately works quite productively, although we focused more on ease of use than on the maximum possible speed. For example, the filter model in the server gives you the opportunity to process some common data blocks using the algorithms defined for them, moreover, in such a way that each individual code block of the algorithm is easily testable to a unit. On the other hand, the need for memory allocation associated with this filter model is not ideal for high-performance applications.

Influence


Proxygen allows us to quickly implement the necessary functionality, release it in production and immediately get the result. For example, we were interested in evaluating the HPACK request header compression format, but unfortunately we didn’t have any HTTP / 2 clients or servers, and indeed the HTTP / 2 specification itself is still in development. Proxygen allowed us to implement HPACK, try using it on top of SPDY and roll out the release simultaneously to our servers and mobile clients. The ability to quickly experiment with real traffic in the HPACK format gave us the opportunity to understand its real performance and evaluate the benefits of its use.

Open source code


The Proxygen codebase is in a state of active development and will continue to evolve. If you like the HTTP protocol, high-performance network code, modern C ++, we will be happy to work with you! Please feel free to send pull requests to Github .

We are actively involved in the development of open source projects and are always looking for the opportunity to share our code with the community. The network infrastructure development team has already laid out Thrift and Proxygen in open source - two important network components of Facebook. We hope that they will find their use in other projects.

Thanks to all the engineers who contributed to the development of this project: Ajit Banerjee, David Gadling, Claudiu Gheorghe, Rajat Goel, Peter Griess, Martin Lau, Adam Lazur, Noam Lerner, Xu Ning, Brian Pane, Praveen Kumar Woo xie.

Source: https://habr.com/ru/post/243181/


All Articles