mirror of
https://github.com/actix/actix-web.git
synced 2025-01-03 05:48:45 +00:00
implement worker availability system
This commit is contained in:
parent
8298da0f4a
commit
d4808acee1
4 changed files with 115 additions and 110 deletions
|
@ -26,6 +26,7 @@ extern crate tokio_timer;
|
||||||
extern crate tower_service;
|
extern crate tower_service;
|
||||||
extern crate trust_dns_resolver;
|
extern crate trust_dns_resolver;
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate actix;
|
extern crate actix;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
use std::sync::{
|
|
||||||
atomic::{AtomicUsize, Ordering},
|
|
||||||
Arc,
|
|
||||||
};
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{fmt, io, mem, net};
|
use std::{fmt, io, mem, net};
|
||||||
|
|
||||||
|
@ -18,7 +14,7 @@ use actix::{
|
||||||
|
|
||||||
use super::accept::{AcceptLoop, AcceptNotify, Command};
|
use super::accept::{AcceptLoop, AcceptNotify, Command};
|
||||||
use super::server_service::{self, ServerNewService, ServerServiceFactory};
|
use super::server_service::{self, ServerNewService, ServerServiceFactory};
|
||||||
use super::worker::{Conn, StopWorker, Worker, WorkerClient};
|
use super::worker::{Conn, StopWorker, Worker, WorkerAvailability, WorkerClient};
|
||||||
use super::NewService;
|
use super::NewService;
|
||||||
use super::{PauseServer, ResumeServer, StopServer, Token};
|
use super::{PauseServer, ResumeServer, StopServer, Token};
|
||||||
|
|
||||||
|
@ -258,14 +254,14 @@ impl Server {
|
||||||
|
|
||||||
fn start_worker(&self, idx: usize, notify: AcceptNotify) -> (Addr<Worker>, WorkerClient) {
|
fn start_worker(&self, idx: usize, notify: AcceptNotify) -> (Addr<Worker>, WorkerClient) {
|
||||||
let (tx, rx) = unbounded::<Conn>();
|
let (tx, rx) = unbounded::<Conn>();
|
||||||
let conns = Connections::new(notify, 0, 0);
|
let avail = WorkerAvailability::new(notify);
|
||||||
let worker = WorkerClient::new(idx, tx, conns.clone());
|
let worker = WorkerClient::new(idx, tx, avail.clone());
|
||||||
let services: Vec<Box<ServerServiceFactory + Send>> =
|
let services: Vec<Box<ServerServiceFactory + Send>> =
|
||||||
self.services.iter().map(|v| v.clone_factory()).collect();
|
self.services.iter().map(|v| v.clone_factory()).collect();
|
||||||
|
|
||||||
let addr = Arbiter::start(move |ctx: &mut Context<_>| {
|
let addr = Arbiter::start(move |ctx: &mut Context<_>| {
|
||||||
ctx.add_message_stream(rx);
|
ctx.add_message_stream(rx);
|
||||||
Worker::new(ctx, services)
|
Worker::new(ctx, services, avail)
|
||||||
});
|
});
|
||||||
|
|
||||||
(addr, worker)
|
(addr, worker)
|
||||||
|
@ -413,68 +409,6 @@ impl StreamHandler<ServerCommand, ()> for Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
|
||||||
/// Contains information about connection.
|
|
||||||
pub struct Connections(Arc<ConnectionsInner>);
|
|
||||||
|
|
||||||
impl Connections {
|
|
||||||
fn new(notify: AcceptNotify, maxconn: usize, maxconnrate: usize) -> Self {
|
|
||||||
let maxconn_low = if maxconn > 10 { maxconn - 10 } else { 0 };
|
|
||||||
let maxconnrate_low = if maxconnrate > 10 {
|
|
||||||
maxconnrate - 10
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
Connections(Arc::new(ConnectionsInner {
|
|
||||||
notify,
|
|
||||||
maxconn,
|
|
||||||
maxconnrate,
|
|
||||||
maxconn_low,
|
|
||||||
maxconnrate_low,
|
|
||||||
conn: AtomicUsize::new(0),
|
|
||||||
connrate: AtomicUsize::new(0),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn available(&self) -> bool {
|
|
||||||
self.0.available()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct ConnectionsInner {
|
|
||||||
notify: AcceptNotify,
|
|
||||||
conn: AtomicUsize,
|
|
||||||
connrate: AtomicUsize,
|
|
||||||
maxconn: usize,
|
|
||||||
maxconnrate: usize,
|
|
||||||
maxconn_low: usize,
|
|
||||||
maxconnrate_low: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConnectionsInner {
|
|
||||||
fn available(&self) -> bool {
|
|
||||||
if self.maxconnrate <= self.connrate.load(Ordering::Relaxed) {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
self.maxconn > self.conn.load(Ordering::Relaxed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn notify_maxconn(&self, maxconn: usize) {
|
|
||||||
if maxconn > self.maxconn_low && maxconn <= self.maxconn {
|
|
||||||
self.notify.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn notify_maxconnrate(&self, connrate: usize) {
|
|
||||||
if connrate > self.maxconnrate_low && connrate <= self.maxconnrate {
|
|
||||||
self.notify.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bind_addr<S: net::ToSocketAddrs>(addr: S) -> io::Result<Vec<net::TcpListener>> {
|
fn bind_addr<S: net::ToSocketAddrs>(addr: S) -> io::Result<Vec<net::TcpListener>> {
|
||||||
let mut err = None;
|
let mut err = None;
|
||||||
let mut succ = false;
|
let mut succ = false;
|
||||||
|
|
|
@ -3,16 +3,23 @@ use std::rc::Rc;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::{fmt, net};
|
use std::{fmt, net};
|
||||||
|
|
||||||
|
use futures::future::{err, ok};
|
||||||
use futures::task::AtomicTask;
|
use futures::task::AtomicTask;
|
||||||
use futures::{future, Async, Future, Poll};
|
use futures::{Async, Future, Poll};
|
||||||
use tokio_reactor::Handle;
|
use tokio_reactor::Handle;
|
||||||
use tokio_tcp::TcpStream;
|
use tokio_tcp::TcpStream;
|
||||||
|
|
||||||
use super::{NewService, Service};
|
use super::{NewService, Service};
|
||||||
|
|
||||||
|
pub enum ServerMessage {
|
||||||
|
Connect(net::TcpStream),
|
||||||
|
Shutdown,
|
||||||
|
ForceShutdown,
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) type BoxedServerService = Box<
|
pub(crate) type BoxedServerService = Box<
|
||||||
Service<
|
Service<
|
||||||
Request = net::TcpStream,
|
Request = ServerMessage,
|
||||||
Response = (),
|
Response = (),
|
||||||
Error = (),
|
Error = (),
|
||||||
Future = Box<Future<Item = (), Error = ()>>,
|
Future = Box<Future<Item = (), Error = ()>>,
|
||||||
|
@ -59,7 +66,7 @@ where
|
||||||
T::Future: 'static,
|
T::Future: 'static,
|
||||||
T::Error: fmt::Display + 'static,
|
T::Error: fmt::Display + 'static,
|
||||||
{
|
{
|
||||||
type Request = net::TcpStream;
|
type Request = ServerMessage;
|
||||||
type Response = ();
|
type Response = ();
|
||||||
type Error = ();
|
type Error = ();
|
||||||
type Future = Box<Future<Item = (), Error = ()>>;
|
type Future = Box<Future<Item = (), Error = ()>>;
|
||||||
|
@ -72,22 +79,27 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, stream: net::TcpStream) -> Self::Future {
|
fn call(&mut self, req: ServerMessage) -> Self::Future {
|
||||||
let stream = TcpStream::from_std(stream, &Handle::default()).map_err(|e| {
|
match req {
|
||||||
error!("Can not convert to an async tcp stream: {}", e);
|
ServerMessage::Connect(stream) => {
|
||||||
});
|
let stream = TcpStream::from_std(stream, &Handle::default()).map_err(|e| {
|
||||||
|
error!("Can not convert to an async tcp stream: {}", e);
|
||||||
|
});
|
||||||
|
|
||||||
if let Ok(stream) = stream {
|
if let Ok(stream) = stream {
|
||||||
let guard = self.counter.get();
|
let guard = self.counter.get();
|
||||||
|
|
||||||
Box::new(
|
Box::new(
|
||||||
self.service
|
self.service
|
||||||
.call(stream)
|
.call(stream)
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
.map(move |_| drop(guard)),
|
.map(move |_| drop(guard)),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Box::new(future::err(()))
|
Box::new(err(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Box::new(ok(())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,14 +145,10 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create(&self) -> Box<Future<Item = BoxedServerService, Error = ()>> {
|
fn create(&self) -> Box<Future<Item = BoxedServerService, Error = ()>> {
|
||||||
Box::new(
|
Box::new((self.inner)().new_service().map(move |inner| {
|
||||||
(self.inner)()
|
let service: BoxedServerService = Box::new(ServerService::new(inner));
|
||||||
.new_service()
|
service
|
||||||
.map(move |inner| {
|
}))
|
||||||
let service: BoxedServerService = Box::new(ServerService::new(inner));
|
|
||||||
service
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
use std::{net, time};
|
use std::{net, time};
|
||||||
|
|
||||||
use futures::sync::mpsc::{SendError, UnboundedSender};
|
use futures::sync::mpsc::{SendError, UnboundedSender};
|
||||||
use futures::sync::oneshot;
|
use futures::sync::oneshot;
|
||||||
use futures::{future, Future};
|
use futures::{future, Async, Future, Poll};
|
||||||
|
|
||||||
use actix::msgs::StopArbiter;
|
use actix::msgs::StopArbiter;
|
||||||
use actix::{
|
use actix::{
|
||||||
|
@ -10,8 +12,9 @@ use actix::{
|
||||||
Response, WrapFuture,
|
Response, WrapFuture,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::server_service::{self, BoxedServerService, ServerServiceFactory};
|
use super::accept::AcceptNotify;
|
||||||
use super::{server::Connections, Token};
|
use super::server_service::{self, BoxedServerService, ServerMessage, ServerServiceFactory};
|
||||||
|
use super::Token;
|
||||||
|
|
||||||
#[derive(Message)]
|
#[derive(Message)]
|
||||||
pub(crate) struct Conn {
|
pub(crate) struct Conn {
|
||||||
|
@ -25,12 +28,12 @@ pub(crate) struct Conn {
|
||||||
pub(crate) struct WorkerClient {
|
pub(crate) struct WorkerClient {
|
||||||
pub idx: usize,
|
pub idx: usize,
|
||||||
tx: UnboundedSender<Conn>,
|
tx: UnboundedSender<Conn>,
|
||||||
conns: Connections,
|
avail: WorkerAvailability,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkerClient {
|
impl WorkerClient {
|
||||||
pub fn new(idx: usize, tx: UnboundedSender<Conn>, conns: Connections) -> Self {
|
pub fn new(idx: usize, tx: UnboundedSender<Conn>, avail: WorkerAvailability) -> Self {
|
||||||
WorkerClient { idx, tx, conns }
|
WorkerClient { idx, tx, avail }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send(&self, msg: Conn) -> Result<(), SendError<Conn>> {
|
pub fn send(&self, msg: Conn) -> Result<(), SendError<Conn>> {
|
||||||
|
@ -38,7 +41,33 @@ impl WorkerClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn available(&self) -> bool {
|
pub fn available(&self) -> bool {
|
||||||
self.conns.available()
|
self.avail.available()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct WorkerAvailability {
|
||||||
|
notify: AcceptNotify,
|
||||||
|
available: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorkerAvailability {
|
||||||
|
pub fn new(notify: AcceptNotify) -> Self {
|
||||||
|
WorkerAvailability {
|
||||||
|
notify,
|
||||||
|
available: Arc::new(AtomicBool::new(false)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn available(&self) -> bool {
|
||||||
|
self.available.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&self, val: bool) {
|
||||||
|
let old = self.available.swap(val, Ordering::Release);
|
||||||
|
if !old && val {
|
||||||
|
self.notify.notify()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,9 +86,8 @@ impl Message for StopWorker {
|
||||||
/// Worker accepts Socket objects via unbounded channel and start requests
|
/// Worker accepts Socket objects via unbounded channel and start requests
|
||||||
/// processing.
|
/// processing.
|
||||||
pub(crate) struct Worker {
|
pub(crate) struct Worker {
|
||||||
// conns: Connections,
|
|
||||||
services: Vec<BoxedServerService>,
|
services: Vec<BoxedServerService>,
|
||||||
// counters: Vec<Arc<AtomicUsize>>,
|
availability: WorkerAvailability,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actor for Worker {
|
impl Actor for Worker {
|
||||||
|
@ -69,10 +97,11 @@ impl Actor for Worker {
|
||||||
impl Worker {
|
impl Worker {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
ctx: &mut Context<Self>, services: Vec<Box<ServerServiceFactory + Send>>,
|
ctx: &mut Context<Self>, services: Vec<Box<ServerServiceFactory + Send>>,
|
||||||
|
availability: WorkerAvailability,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let wrk = Worker {
|
let wrk = Worker {
|
||||||
|
availability,
|
||||||
services: Vec::new(),
|
services: Vec::new(),
|
||||||
// counters: services.iter().map(|i| i.counter()).collect(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.wait(
|
ctx.wait(
|
||||||
|
@ -82,8 +111,10 @@ impl Worker {
|
||||||
error!("Can not start worker: {:?}", e);
|
error!("Can not start worker: {:?}", e);
|
||||||
Arbiter::current().do_send(StopArbiter(0));
|
Arbiter::current().do_send(StopArbiter(0));
|
||||||
ctx.stop();
|
ctx.stop();
|
||||||
}).and_then(|services, act, _| {
|
}).and_then(|services, act, ctx| {
|
||||||
act.services.extend(services);
|
act.services.extend(services);
|
||||||
|
act.availability.set(true);
|
||||||
|
ctx.spawn(CheckReadiness(true));
|
||||||
fut::ok(())
|
fut::ok(())
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -91,12 +122,20 @@ impl Worker {
|
||||||
wrk
|
wrk
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shutdown(&self, _force: bool) {
|
fn shutdown(&mut self, force: bool) {
|
||||||
// self.services.iter().for_each(|h| h.shutdown(force));
|
if force {
|
||||||
|
self.services.iter_mut().for_each(|h| {
|
||||||
|
h.call(ServerMessage::ForceShutdown);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.services.iter_mut().for_each(|h| {
|
||||||
|
h.call(ServerMessage::Shutdown);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shutdown_timeout(
|
fn shutdown_timeout(
|
||||||
&self, ctx: &mut Context<Worker>, tx: oneshot::Sender<bool>, dur: time::Duration,
|
&mut self, ctx: &mut Context<Worker>, tx: oneshot::Sender<bool>, dur: time::Duration,
|
||||||
) {
|
) {
|
||||||
// sleep for 1 second and then check again
|
// sleep for 1 second and then check again
|
||||||
ctx.run_later(time::Duration::new(1, 0), move |slf, ctx| {
|
ctx.run_later(time::Duration::new(1, 0), move |slf, ctx| {
|
||||||
|
@ -120,7 +159,7 @@ impl Handler<Conn> for Worker {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, msg: Conn, _: &mut Context<Self>) {
|
fn handle(&mut self, msg: Conn, _: &mut Context<Self>) {
|
||||||
Arbiter::spawn(self.services[msg.handler.0].call(msg.io))
|
Arbiter::spawn(self.services[msg.handler.0].call(ServerMessage::Connect(msg.io)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,3 +190,26 @@ impl Handler<StopWorker> for Worker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CheckReadiness(bool);
|
||||||
|
|
||||||
|
impl ActorFuture for CheckReadiness {
|
||||||
|
type Item = ();
|
||||||
|
type Error = ();
|
||||||
|
type Actor = Worker;
|
||||||
|
|
||||||
|
fn poll(&mut self, act: &mut Worker, _: &mut Context<Worker>) -> Poll<(), ()> {
|
||||||
|
let mut val = true;
|
||||||
|
for service in &mut act.services {
|
||||||
|
if let Ok(Async::NotReady) = service.poll_ready() {
|
||||||
|
val = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.0 != val {
|
||||||
|
self.0 = val;
|
||||||
|
act.availability.set(val);
|
||||||
|
}
|
||||||
|
Ok(Async::NotReady)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue