初识axum | 青训营笔记

573 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

尝试用axum写后端

Dependencies

axum hyper tokio
Note: 目前还没有引入tower

Setup

想先手搓整个流程而不用macro,顺便学习下框架,于是首先把依赖的default features全禁了,不够再慢慢加feature。

Steps

one

先照着axum的文档抄一下,但是把#[tokio::main]撅了。
于是马上就发现要开启tokio的rt-multi-thread以及往hyper加server和tcp。
但是为什么还是没有Server::bind?理论上有了tcp应该就有了。
把http2加到hyper里面,编译成功。
这文档指定有点问题。

main.rs

use std::net::Ipv6Addr;

use axum::{
    routing::{get, IntoMakeService},
    Router,
};

use hyper::Server;

fn main() {
    tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .unwrap()
        .block_on(async {
            Server::bind(&(Ipv6Addr::UNSPECIFIED, 14514).into())
                .serve(build_router())
                .await
                .unwrap()
        })
}

fn build_router() -> IntoMakeService<Router> {
    Router::new()
        .route("/", get(|| async { "Hello, World!" }))
        .into_make_service()
}

先run起来看看。

127.0.0.1 已拒绝连接。
ERR_CONNECTION_REFUSED


看来::不包括0.0.0.0
[::1]试试

[::1]  发送了无效的响应。
ERR_INVALID_HTTP_RESPONSE

?
在浏览器的DevTool里面看到协议是http1.1
没用http2么?
去掉http2换上http1,终于成功显示出了Hello, World!
后来经过群友提醒才记起来http2要绑定ssl用。

two

思考怎么让Server同时监听ipv6和ipv4。
没有在搜索引擎找到答案,于是直接去axum的仓库的issue里面找。
最后终于找到了居然是在discussion里面
还要自己手动合并两个AddrIncoming。hyper不太行。

combind_incoming.rs

use std::{
    pin::Pin,
    task::{Context, Poll},
};

use hyper::server::{accept::Accept, conn::AddrIncoming};

pub struct CombinedIncoming {
    pub a: AddrIncoming,
    pub b: AddrIncoming,
}

impl Accept for CombinedIncoming {
    type Conn = <AddrIncoming as Accept>::Conn;
    type Error = <AddrIncoming as Accept>::Error;

    fn poll_accept(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
        if let Poll::Ready(value) = Pin::new(&mut self.a).poll_accept(cx) {
            return Poll::Ready(value);
        }

        if let Poll::Ready(value) = Pin::new(&mut self.b).poll_accept(cx) {
            return Poll::Ready(value);
        }

        Poll::Pending
    }
}

main.rs

mod combind_incoming;

use std::net::{Ipv4Addr, Ipv6Addr};

use axum::{
    routing::{get, IntoMakeService},
    Router,
};
use combind_incoming::CombinedIncoming;
use hyper::{server::conn::AddrIncoming, Server};

fn main() {
    tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .unwrap()
        .block_on(async {
            Server::builder(CombinedIncoming {
                a: AddrIncoming::bind(&(Ipv6Addr::UNSPECIFIED, 14514).into()).unwrap(),
                b: AddrIncoming::bind(&(Ipv4Addr::UNSPECIFIED, 14514).into()).unwrap(),
            })
            .serve(build_router())
            .await
            .unwrap()
        })
}

fn build_router() -> IntoMakeService<Router> {
    Router::new()
        .route("/", get(|| async { "Hello, World!" }))
        .into_make_service()
}

测试一下。

Hello, World!

good

Summary

搞个Hello World都这么搞,我觉得接下来的开发之路会有点折磨。
不过也可能是我用了手动挡的缘故。

License

This work is licensed under CC BY-SA 4.0