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. DeepSeek+Zotero

    技术背景 在DeepSeek系列文章中,我们已经分别介绍过关于DeepSeek在Ubuntu Linux平台和Windows平台上的本地部署方案,以及Ollama.ChatBox和AnythingLL ...

  2. [CF1981E] Turtle and Intersected Segments 题解

    好题好题. 难点在建图,因为图的边数将会决定最小生成树的时间复杂度.我们肯定希望能够只建 \(O(n)\) 级别的边,这样时间复杂度就可以做到 \(O(n\log n)\). 观察到当 \(i,j,k ...

  3. 库卡机器人KR500维修保养

    随着现代工业自动化,KUKA库卡机器人以其卓越的性能.灵活的操作和高效的产出而备受赞誉.然而,为了确保机器人的持续稳定运行和延长使用寿命,应联系子锐机器人维修对库卡机器人保养至关重要. 一.库卡机器人 ...

  4. Processing多窗口程序范例(一)

    Processing学习到一定程度必定会关注源码,关注扩展功能,其中窗口创建是值得关注的技术点(实现多窗口).下面就以一个简单范例来展开讨论. 范例代码 主程序先上: package syf.demo ...

  5. 玩转摄像头之MT9V034(最新打样,展示下,欢迎观摩,哈哈)低照度 红外透视应用

    分辨率:752*480  低照度 效果超好先上图 图像处理.物联网.fpga.stm32研究 我的店铺:ccjt.taobao.com 

  6. CTF-CRYPTO-ECC(1)

    CTF-CRYPTO-ECC(1) 椭圆加密 1.简介 椭圆曲线密码学(Elliptic curve cryptography),简称 ECC,和RSA.ElGamel 算法等类似,是一种公开秘钥加密 ...

  7. PPT-产品页图片并茂

    一.好的文案原则 图片并茂 -重点突出 二.操作 抠图 拷贝图片到ppt页面->选中图片->双击删除背景->标记要删除的区域 背景替换 1.复制图片->粘贴->置于底层 ...

  8. Java 线程安全的集合

    Vector ArrayList 的线程安全版本,对所有的修改方法都进行了 synchronized 同步处理.适用于多线程环境下对数据一致性要求高,且读写操作相对比较均衡,不需要很高并发性能的场景. ...

  9. Trae 开发工具与使用技巧

    大家好,我是 V 哥. 前不久,字节重磅推出 AI 原生 IDE Trae,有了这款工具,程序员的开发效率得到了大大的提升,如何你是程序员,还没有使用起来,那 V 哥建议你即刻起马上安装上,谁用谁知道 ...

  10. Django实战项目-学习任务系统-任务管理

    接着上期代码框架,开发第3个功能,任务管理,再增加一个学习任务表,用来记录发布的学习任务的标题和内容,预计完成天数,奖励积分和任务状态等信息. 第一步:编写第三个功能-任务管理 1,编辑模型文件: . ...