1. toml依赖

hyper = { version = "1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
http-body-util = "0.1"
hyper-util = { version = "0.1", features = ["full"] }

2. 代码

2025-02-17更新:更新跨域设置;

2025-01-24更新:更新跨域设置,符合RFC标准,参考spring boot的处理方式;

2025-01-23更新:新增跨域设置;

2025-01-17更新:全局服务静态缓存添加,转发至实际请求微服务地址可用,由于nacos不广播服务权重变化,缓存的使用方法还在考虑,有人有好的想法可以留言告知,感谢;

题外话:在写代码的过程中发现每个函数的参数是否可变原来不具有继承性,比如下面的代码是可以运行的, retuen_new_user函数的参数user是不可变的,但是可以将它直接传递给一个要求可变参数的函数,还真是奇怪嘿XD

pub fn retuen_new_user(user: User) -> User {
handle(user)
}
pub fn handle(mut user: User)-> User {
user.id = "11111".to_string;
user
}

2025-01-02更新:部分功能代码未完成后续完成后更新

以下代码参考hyper的官方example文件夹下的内容编写

use std::{net::SocketAddr, str};

use http_body_util::{combinators::BoxBody, BodyExt};
use hyper::{
body::{Bytes, Incoming},
header::{
HeaderValue, ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS,
ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_MAX_AGE,
ACCESS_CONTROL_REQUEST_HEADERS, ACCESS_CONTROL_REQUEST_METHOD, ORIGIN, REFERER,
},
server::conn::http1,
service::service_fn,
HeaderMap, Method, Request, Response, StatusCode,
};
use hyper_util::rt::TokioIo;
use log::{error, info};
use tokio::{
io,
net::{TcpListener, TcpStream},
};
//由于nacos不广播权重配置的更改,而且nacos内置了raft和负载均衡算法所以改为每次去注册中心请求一个服务地址
//use crate::init::nacos::NAMING_MAP;
use crate::{
init::{self, config::Config, constant::AUTHORIZATION, nacos},
util::jwt,
}; pub async fn init_hyper() -> io::Result<()> {
//Config::global()获取一个LazyLock全局静态变量,仅支持rust 1.8.0以上版本
let server_ip = Config::global().server_ip();
let server_port = Config::global().server_port();
//创建soket
let addr: SocketAddr = format!("{}:{}", server_ip, server_port).parse().unwrap();
//创建监听器
let listener = TcpListener::bind(addr).await?;
info!("service listening on {}", addr);
//创建http监听
let http = http1::Builder::new();
//调用hyper的关闭监听
let graceful = hyper_util::server::graceful::GracefulShutdown::new();
let mut signal = std::pin::pin!(shutdown_signal());
loop {
//接受链接请求或者关闭信号
tokio::select! {
Ok((stream, _addr)) = listener.accept() => {
let io = TokioIo::new(stream);
let conn = http.serve_connection(io, service_fn(handle_proxy));
let fut = graceful.watch(conn);
tokio::spawn(async move {
if let Err(err) = fut.await {
error!("Error input connection: {}", err);
}
});
},
_ = &mut signal => {
info!("graceful shutdown signal received");
// stop the accept loop
break;
}
}
}
//监控关闭是否成功
tokio::select! {
_ = graceful.shutdown() => {
info!("all connections gracefully closed");
},
_ = tokio::time::sleep(std::time::Duration::from_secs(10)) => {
error!("timed out wait for all connections to close");
}
}
Ok(())
} //预处理请求,鉴权等
async fn handle_proxy(
req: Request<hyper::body::Incoming>,
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
//跨域相关
if req.method() == Method::OPTIONS {
return return_options(req.headers());
}
if req.uri().path().contains("login") || req.uri().path().contains("app/auth") {
proxy_to_service(req).await
} else if req.headers().contains_key(AUTHORIZATION) {
if is_valid_token(req.headers()) {
proxy_to_service(req).await
} else {
return_forbidden()
}
} else {
//鉴权不通过统统403
return_forbidden()
}
} fn is_valid_token(headers: &HeaderMap<HeaderValue>) -> bool {
let token = headers.get(AUTHORIZATION);
if token.is_none() {
return false;
}
let token = token.unwrap().to_str().unwrap();
is_valid_user_token(token) || is_valid_app_token(token)
} fn is_valid_app_token(token: &str, ip: &str) -> bool {
ture
} fn is_valid_user_token(token: &str) -> bool {
ture
} //转发至微服务地址
async fn proxy_to_service(
mut req: Request<Incoming>,
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
let req_server_name = req.uri().path().split("/").collect::<Vec<&str>>()[1];
let service_addr: String;
// let map = NAMING_MAP.load();
// if map.contains_key(req_server_name) {
// let instances_list = map.get(req_server_name).expect("get server addr error");
// service_addr = instances_list[0].ip.clone() + ":" + &instances_list[0].port.to_string();
// } else {
service_addr = nacos::select_service(req_server_name).await;
if service_addr.is_empty() {
return return_not_found(&req.headers());
}
// }
let req_header = req.headers().clone();
info!(
"request uri : {} with X-Custom-Version {:?}",
req.uri(),
req_header.get("X-Custom-Version")
);
*req.uri_mut() = req
.uri()
.to_string()
.split(req_server_name)
.collect::<Vec<&str>>()[1]
.parse()
.unwrap();
let uri = req.uri().clone();
match TcpStream::connect(service_addr).await {
Ok(stream) => {
let io = TokioIo::new(stream);
match hyper::client::conn::http1::handshake(io).await {
Ok((mut sender, conn)) => {
tokio::task::spawn(async move {
if let Err(err) = conn.await {
error!("Connection failed: {}", err);
}
});
match sender.send_request(req).await {
Ok(mut res) => {
insert_cors_header(&req_header, res.headers_mut());
Ok(res.map(|b| b.boxed()))
}
Err(err) => {
error!("Error proxy sending request: {} when request {}", err, uri);
return_bad_gateway(&req_header)
}
}
}
Err(err) => {
error!("Error proxy handshanke: {} when request {}", err, uri);
return_bad_gateway(&req_header)
}
}
}
Err(err) => {
error!("Error proxy connection :{} when request {}", err, uri);
return_bad_gateway(&req_header)
}
}
}
//跨域头设置
fn insert_cors_header(request_header: &HeaderMap, response_header: &mut HeaderMap) {
match request_header.get(ORIGIN) {
Some(origin) => {
response_header.insert(ACCESS_CONTROL_ALLOW_ORIGIN, origin.clone());
response_header.insert(
ACCESS_CONTROL_ALLOW_CREDENTIALS,
HeaderValue::from_str("true").unwrap(),
);
}
None => (),
};
} fn return_options(
request_header: &HeaderMap,
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
let mut response = Response::new(BoxBody::default());
*response.status_mut() = StatusCode::NO_CONTENT;
insert_cors_header(request_header, response.headers_mut());
match request_header.get(ACCESS_CONTROL_REQUEST_METHOD) {
None => (),
Some(method) => {
response
.headers_mut()
.insert(ACCESS_CONTROL_ALLOW_METHODS, method.clone());
}
};
match request_header.get(ACCESS_CONTROL_REQUEST_HEADERS) {
None => (),
Some(method) => {
response
.headers_mut()
.insert(ACCESS_CONTROL_ALLOW_HEADERS, method.clone());
}
};
response
.headers_mut()
.insert(ACCESS_CONTROL_MAX_AGE, HeaderValue::from(3600));
Ok(response)
} fn return_forbidden(request_header: &HeaderMap,) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
let mut response = Response::new(BoxBody::default());
*response.status_mut() = StatusCode::FORBIDDEN;
insert_cors_header(request_header, response.headers_mut());
Ok(response)
} fn return_bad_gateway(request_header: &HeaderMap,) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
let mut response = Response::new(BoxBody::default());
*response.status_mut() = StatusCode::BAD_GATEWAY;
insert_cors_header(request_header, response.headers_mut());
Ok(response)
} fn return_not_found(request_header: &HeaderMap,) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
let mut response = Response::new(BoxBody::default());
*response.status_mut() = StatusCode::NOT_FOUND;
insert_cors_header(request_header, response.headers_mut());
Ok(response)
} //优雅的关闭tokio运行时任务
async fn shutdown_signal() {
// Wait for the CTRL+C signal
tokio::signal::ctrl_c()
.await
.expect("failed to install CTRL+C signal handler");
}

spring boot迁移计划 第Ⅰ章 --chapter 1. rust hyper 结合rust nacos-client开发nacos网关 part ② hyper网关的更多相关文章

  1. spring boot 笔记--第三章

    spring boot 笔记 第三章,使用Spring boot 构建系统: 强烈建议支持依赖管理的构建系统,Maven或Gradle 依赖管理: Spring Boot的每版本都会提供它支持的依赖列 ...

  2. 携程Apollo(阿波罗)配置中心Spring Boot迁移日志组件,使用配置中心进行管理的思路

    说明: 1.Spring Boot项目默认使用logback进行日志管理 2.logback在启动时默认会自动检查是否有logback.xml文件,如果有时会有限加载这个文件. 3.那么如果是用配置中 ...

  3. 《spring boot》8.2章学习时无法正常启动,报“ORA-00942: 表或视图不存在 ”

    在学习<spring boot>一书的过程中,由于原书作者难免有一些遗漏的的地方,或者系统.软件版本不一致.框架更新等各种因素,完全安装书中源码页不能实现项目的正常启动 在8.2章节,演示 ...

  4. Spring Boot 2.0 迁移指南

    ![img](https://mmbiz.qpic.cn/mmbiz_jpg/1flHOHZw6Rs7yEJ6ItV43JZMS7AJWoMSZtxicnG0iaE0AvpUHI8oM7lxz1rRs ...

  5. 《深入实践Spring Boot》阅读笔记之三:核心技术源代码分析

    刚关注的朋友,可以回顾前两篇文章: 基础应用开发 分布式应用开发 上篇文章总结了<深入实践Spring Boot>的第二部分,本篇文章总结第三部分,也是最后一部分.这部分主要讲解核心技术的 ...

  6. 《Spring Boot实战》笔记(目录)

    目录 目 录第一部分 点睛Spring 4.x第1 章 Spring 基础 .............................................................. ...

  7. 【原】spring boot在整合项目依赖的问题

    最近要开发新的项目,就花了几天时间看了下spring boot的相关资料,然后做了一个demo,不得不说开发效率确实很快,几行注解就完成了事务,aop,数据库等相关配置:但由于先前习惯了spring ...

  8. 为什么是Spring Boot

    原文:https://dzone.com/articles/why-springboot 作者:Siva Prasad Reddy Katamreddy 译者:Oopsguy 本文介绍将各种Sprin ...

  9. Spring Boot快速入门(最新)

    本章通过完成Spring Boot基础项目的构建并实现一个简单的Http请求处理,让大家对Spring Boot有一个初步的了解,并体验其结构简单.开发快速的特性.预计阅读及演练过程将花费约5分钟. ...

  10. Spring Boot 2.0系列文章(五):Spring Boot 2.0 项目源码结构预览

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/04/15/springboot2_code/ 项目结构 结构分析: Spring-boot-pr ...

随机推荐

  1. Git指南-从入门到精通

    代码提交和同步命令 流程图如下: 第零步: 工作区与仓库保持一致 第一步: 文件增删改,变为已修改状态 第二步: git add ,变为已暂存状态 $ git status $ git add --a ...

  2. DeepSeek + 在线Excel , 打造智能表格新纪元

    微信搜一搜[葡萄城社区]关注,了解更多动态 SpreadJS 已经接入 DeepSeek 啦! 相信这段时间,大家都被[DeepSeek]刷屏了.DeepSeek 以其强大的技术能力和创新的解决方案, ...

  3. 【Unity】URP中的UGUIShader实现

    [Unity]URP 中的 UGUIShader 实现 参考官方 Shader 代码实现: https://github.com/TwoTailsGames/Unity-Built-in-Shader ...

  4. 在Android源码中为APK编译系统权限

    系统权限获取 打包为APK进行系统签名 对于 部分功能的访问需要使用到系统权限,需要 添加 android:sharedUserId="android.uid.system" 权限 ...

  5. 4个Sprint目标的挑战以及解决的技巧

    1. Sprint 目标太大 有时,您的团队可能会尝试将过多的任务塞进冲刺中.抵制在冲刺中承担太多的诱惑,因为这会损害你的速度和持续交付的能力. 2. Sprint目标是模糊的 冲刺目标通常是不确定的 ...

  6. AngleSharp :在 C# 中轻松解析和操作 HTML/XML 文档

    AngleSharp 是一个 C# 库,主要用于解析和操作 HTML 和 XML 文档,类似于浏览器的 DOM 操作.允许你在 C# 中使用类似浏览器的方式处理网页数据,进行网页抓取.数据提取和处理等 ...

  7. OERV兴趣探索:模拟器移植

      最近看了很多开源项目,主要都集中在模拟器方面,我指的是游戏模拟器比如GameBoy或者PlayStation这一类.现在想玩这系列的游戏可以在手机或者电脑下载相应的模拟器,并且获取对应的ROM文件 ...

  8. OpenOffice已停更六年 文档基金会呼吁用户迁移LibreOffice

    OpenOffice 源代码发布至今已经有 20 年了.文档基金会近日发布公开信,表示说:"LibreOffice 是 OpenOffice 的未来.我们将会全力以赴".以此呼吁那 ...

  9. 一文搞懂 MCP Servers

    一文搞懂 MCP Servers 什么是MCP MCP概念 MCP(Model Context Protocol,模型上下文协议)是由 Anthropic 提出并于 2024 年 11 月开源的一种通 ...

  10. 基于SLAM系统建图仿真,完成定位仿真

    博客地址:https://www.cnblogs.com/zylyehuo/ 基于SLAM系统完成建图仿真,详见之前的博客 基于Gazebo搭建移动机器人,并结合SLAM系统完成建图仿真 - zyly ...