Learning from LinYaCool WebServer (imitating muduo) (http)

linyacool / WebServer

How to say it, I saw a server that imitates moduo, but the previous mini-muduo is an echo server, this is an http server, and the ideas are the same. On the concurrency model, this server uses Reactor + thread pool, and each connection is Thread is also a Reactor. This is the first time I have seen the implementation of this model. It has advantages over mini-muduo and it uses smart pointers to avoid memory leaks. This does increase the difficulty of programming, but its guarantee Memory leakage is indeed a place that programmers need to pay special attention to.

There are roughly these classes of
Server (entry class, which contains Accptor to handle connection events)

Channel (for encapsulation of file descriptors)

Epoll (For epoll packaging, add and delete Channel)

EventLoop (encapsulation of the while loop, in which an Epoll collects events there)

EventLoopThread (EventLoop thread class, is a child thread to use EventLoop to monitor the connection socket)

EventLoopThreadPool (contains a specified number of EventLoopThread to share all connection sockets)

HTTPData (connection class, read data in it, process data, distribute data)

Logging (asynchronous log)

Process: (The symbols are not exactly the same)

mainLoop()
myHttpServer()->eventLoopThreadPool_(),accpetChannel()->myHttpServer.start()->eventLoopThreadPool_.start()-> eventLoopThread(),eventLoopThread.startLoop()->thread.start()-> subEventLoop()->subEventLoop.loop()->mainloop.addToPoller(acceptChannel)
mainLoop.loop()

Corresponding instructions:

  • Initialize the main loop mainloop
  • Initialize Server
  • First initialize eventLoopThreadPool, then initialize the listening socket and put it into accpetChannel
  • Then start Server.start
  • Among them, 4 eventLoopThread are initialized in eventLoopThreadPool
  • And start the thread to call the thread function they registered, that is, start a thread unique EventLoop subReactor
  • Each subEventLoop call loop is the poll (epoll_wait) called by the Epoll class. At this time, there are no client sockets on these epolls.
  • At this time, the work of the child thread is completed, return to Server::start in the parent thread, and let mainLoop listen to acceptChannel
  • Then call mainloop.loop to wait for the client to connect.

Process:
mainLoop.loop returns ->accpetChannel.handleEvents->myHttpServer.handNewConn->accept to get conn_fd->HttpData req_info(conn_fd)->Channel connChannel(conn_fd)->subloop.queueInLoop asynchronous execution req_info.newEvent, subloop.addToPoller( connChannel,2000)

Corresponding instructions:

  • At this time, the client connects and will return in mainLoop.loop,
  • Through the callback handNewConn to accept, bind the obtained conn_fd to HttpData and Channel
  • Use EventLoopThreadPool to obtain subEventLoop in rotation, and use queueInLoop in subEventLoop to add task function HttpData::newEvent to subEventLoop
  • Which will add conn_Channel to the red-black tree monitored by subLoop
  • A 2-second timer will also be bound to the conn_Channel. This timer is managed by the global timerManage. The client does not send a message within 2s, and the timer will be deleted when it expires (but will be delayed in EventLoop). Let the count of the smart pointer of conn_Channel -1 in the destructor function to "clear" the timeout client.

When the customer message comes
Process:
subLoop.loop returns -> EPOLLIN?->req_info.handleRead() EPOLLOUT?->req_info.handleWrite()
req_info.handleRead()->read,parseURI,parseHeaders,analysisRequest ,req_info.handleWrite( )->write

req_info.handleConn -> View and modify the Epoll status of the connection class -> subEventloop.updatePoller(connChennel)/subEventloop.runInLoop(HttpData::handleClose) asynchronously execute req_info.handleClose->subLoop.removeFromPoller(connChennel)

Corresponding instructions:

  • When the client message comes, subLoop.loop returns, Channel will perform read and write according to the returned event, read the input in handleRead, and process the http request, handleWrite will send the data that needs to be sent back, and then will view it in handleConn And modify the Epoll status of the connection class, and choose to update the Epoll event of connChennel or close the connection asynchronously according to the current status.

One request completed

The design of the Log class is also relatively ingenious. It uses a thread to send the information that needs to be written to the log. The user interface is very easy Log<<"xxx". See the source code for details.

experience:

void EventLoop::queueInLoop(Functor&& cb) {
    
    
  {
    
    
    MutexLockGuard lock(mutex_);
    pendingFunctors_.emplace_back(std::move(cb));
    
  }

  if (!isInLoopThread() || callingPendingFunctors_) wakeup();
}
  1. (Correct the error in my previous blog about queueInLoop in mini-muduo) ``queueInLoop executes tasks asynchronously, and the judgment in queueInLoop does the role of callingPendingFunctors_: Take out all tasks and execute them in the interval of callingPendingFunctors_=true in EventLoop::loop. In queueInLoop, first judge if the thread of the sending task is not the thread created by EventLoop, then if the thread of EventLoop has taken the task from pendingFunctors_ in vector.swap mode (to avoid iterator failure), at this time callingPendingFunctors_ is true, Then EventLoop.weakup can allow epoll to execute the next round of tasks. If the thread called by queueInLoop is the thread created by EventLoop, then the same! isInLoopThread() is false, callingPendingFunctors_ is also false, and wakeup is not called asynchronously. After that, wait until the eventloop wakes up in the next round or executes doPendingFunctors, to ensure that the task added this time is in the task vector.
  2. runInLoop also executes tasks asynchronously, and the function of queueInLoop is more like adding tasks to task Vector asynchronously.
  3. The usage of thread local storage _thread
  4. Realize the function of waking up epoll through linux new api eventfd
  5. Transform process-oriented code into object-oriented code through packaging into classes
  6. Each 域名:域值option in the head of http is stored as the key and value of the map for easy analysis
  7. Using the application layer buffer, the read data is appended to the end of the buffer, and the shortcomings of a fixed-length array are solved by dynamic expansion of string.
  8. , Understand the function of the mainReactor+subReactor+threadpool mode, (this threadpool is not used for calculation, but loop for subReactor)
  9. Use the function getopt to facilitate the parsing of command-line parameters, which is simply the gospel of writing ls, cp and other commands
  10. By setting the callback function by setReadHandler(bind(&Server::handNewConn, this)), you can execute the function of the object and ensure that the interface of setReadHandler is unified. It should be noted that the this pointer of the incoming object will make the binding of this function not Security (this was mentioned in the previous chapters of muduo)
  11. In ET mode, each read or output needs to be read or write until EAGAIN

Bug: Port 80 of the server bind is already occupied and needs to be bound. There is a problem that the carriage return cannot be displayed on the web page

Disadvantages: utf-8 is not supported, POST is omitted, and the timer is poorly designed

Guess you like

Origin blog.csdn.net/adlatereturn/article/details/108502385