这是我参与「第五届青训营 」伴学笔记创作活动的第 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