49从零开始用Rust编写nginx,我竟然在同一个端口上绑定了多少IP
wmproxy
wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 负载均衡, 静态文件服务器,websocket代理,四层TCP/UDP转发,内网穿透等,会将实现过程分享出来,感兴趣的可以一起造个轮子
项目地址
国内: https://gitee.com/tickbh/wmproxy
github: https://github.com/tickbh/wmproxy
设计目标
快速的设置多IP绑定,及IP端口段的支持,方便快速的自定义能力。
IP解析示例
以下是常见的IP解析示例情况,本地ip为192.168.0.100示例:
正常IP解析
127.0.0.1:8869解析成 ipv4 127.0.0.1 端口 8869,只接受本地来的连接信息0.0.0.0:8869解析成 ipv4 0.0.0.0 端口 8869,可接受所有来自ipv4的连接信息
以
:开头的地址,且不包含-:8869解析成 ipv4 127.0.0.1 端口 8869 及 ipv4 192.168.0.100 端口 8869
包含
-的地址:8869-:8871解析成 ipv4 127.0.0.1 端口 8869 - 8871 三个端口地址 及 ipv4 192.168.0.100 端口 8869 - 8871 三个端口地址,总共6个端口地址127.0.0.1:8869-:8871解析成 ipv4 127.0.0.1 端口 8869 - 8871 三个端口地址 总共3个端口地址127.0.0.1:8869-192.168.0.100:8871解析成 ipv4 127.0.0.1 端口 8869 - 8871 三个端口地址 总共3个端口地址,忽略后面的地址,只接受端口号
手动多个地址,可以空格或者
,做间隔127.0.0.1:8869 127.0.0.1:8899 192.168.0.100:8899就相应的解析成三个端口地址
定义类
由于解析出来的地址可能是多个或者个单个,这里用数组来进行表示
#[derive(Debug, Clone)]
pub struct WrapVecAddr(pub Vec<SocketAddr>);
通常序列化会用到FromStr将字符串转化成类
反序列化都会用到Display将类转化成字符串
所以在这里,我们将实现FromStr及Display
impl FromStr for WrapVecAddr {
type Err = AddrParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
// 范围的如:8080-:8090, 表示11端口
if s.contains("-") {
let vals = s
.split(&['-'])
.filter(|s| !s.is_empty())
.collect::<Vec<&str>>();
let start = parse_socker_addr(vals[0])?;
if vals.len() != 2 {
return Ok(WrapVecAddr(start));
} else {
let end = parse_socker_addr(vals[1])?;
let mut results = vec![];
for port in start[0].port()..=end[1].port() {
for idx in &start {
let mut addr = idx.clone();
addr.set_port(port);
results.push(addr);
}
}
return Ok(WrapVecAddr(results));
}
} else {
let vals = s
.split(&[',', ' '])
.filter(|s| !s.is_empty())
.collect::<Vec<&str>>();
let mut results = vec![];
for s in vals {
results.extend(parse_socker_addr(s)?);
}
Ok(WrapVecAddr(results))
}
}
}
impl Display for WrapVecAddr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut values = vec![];
for a in &self.0 {
values.push(format!("{}", a));
}
f.write_str(&values.join(","))
}
}
这样子后我们将配置加上就可以自动实现序列化及反序列化了
#[serde_as]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
#[serde_as(as = "DisplayFromStr")]
pub bind_addr: WrapVecAddr,
// ...
}
获取本机地址
通过库local-ip-address获取本地IP地址,再根据缺省IP时添加本地IP地址访问,因为缺省时有可能是需要本地内网进行访问。所以需要补上本地网卡地址。所以我们在解析以:时做了特殊处理:
fn parse_socker_addr(s: &str) -> Result<Vec<SocketAddr>, AddrParseError> {
if s.starts_with(":") {
let port = s.trim_start_matches(':');
let mut results = vec![];
if let Ok(port) = port.parse::<u16>() {
if let Ok(v) = local_ip() {
results.push(SocketAddr::new(v, port));
}
if let Ok(v) = local_ipv6() {
results.push(SocketAddr::new(v, port));
}
results.push(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port));
} else {
results.push(format!("127.0.0.1{s}").parse::<SocketAddr>()?);
}
Ok(results)
} else {
let addr = s.parse::<SocketAddr>()?;
Ok(vec![addr])
}
}
参数获取
以下举例
file-server的参数
#[derive(Debug, Clone, Bpaf)]
#[allow(dead_code)]
struct FileServerConfig {
/// 静态文件根目录路径
#[bpaf(short, long, fallback(String::new()))]
pub(crate) root: String,
#[bpaf(
short,
long,
fallback(WrapVecAddr(vec![SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8869)])),
display_fallback
)]
/// 监听地址
pub(crate) listen: WrapVecAddr,
#[bpaf(long)]
/// 监听地址
pub(crate) listen_ssl: Option<WrapVecAddr>,
/// ...
}
如此我们就可以轻松的用SSL监听及普通的监听添加多个端口的支持。
wmproxy file-server --listen :8869-8871
此时我们可以同时监听3个端口均支持文件服务器。此时我们就可以轻松控制多个端口地址。
绑定多个地址
以下是负载均衡中的绑定示例
for v in &value.bind_addr.0 {
if bind_addr_set.contains(&v) {
continue;
}
bind_addr_set.insert(v);
let url = format!("http://{}", v);
log::info!("HTTP服务:{},提供http处理及转发功能。", Style::new().blink().green().apply_to(url));
let listener = Helper::bind(v).await?;
listeners.push(listener);
tlss.push(false);
}
for v in &value.bind_ssl.0 {
if bind_addr_set.contains(&v) {
continue;
}
bind_addr_set.insert(v);
if !is_ssl {
return Err(crate::ProxyError::Extension("配置SSL端口但未配置证书"));
}
let url = format!("https://{}", v);
log::info!("HTTPs服务:{},提供https处理及转发功能。", Style::new().blink().green().apply_to(url));
let listener = Helper::bind(v).await?;
listeners.push(listener);
tlss.push(is_ssl);
}
支持ssl绑定及非ssl绑定同一个location。
其中链接信息使用了console,输出了绿色的,可以点击的url链接。可以方便在启动的时候进行点击。

图中圈圈位置是可以点击跳转成url,方便本地开发环境的时候测试使用。
总结
通过FromStr及Display的重定义,我们可以支持更强大的自定义的序列化操作,系统绑定端口既认端口号也认绑定IP,所以我们可以对同个端口进行多次绑定。
点击 [关注],[在看],[点赞] 是对作者最大的支持
49从零开始用Rust编写nginx,我竟然在同一个端口上绑定了多少IP的更多相关文章
- bloom-server 基于 rust 编写的 rest api cache 中间件
bloom-server 基于 rust 编写的 rest api cache 中间件,他位于lb 与api worker 之间,使用redis 作为缓存内容存储, 我们需要做的就是配置proxy,同 ...
- centos LNMP第一部分环境搭建 LAMP LNMP安装先后顺序 php安装 安装nginx 编写nginx启动脚本 懒汉模式 mv /usr/php/{p.conf.default,p.conf} php运行方式SAPI介绍 第二十三节课
centos LNMP第一部分环境搭建 LAMP安装先后顺序 LNMP安装先后顺序 php安装 安装nginx 编写nginx启动脚本 懒汉模式 mv /usr/local/php/{ ...
- Linux实战教学笔记37:企业级Nginx Web服务优化实战(上)
一,Nginx基本安全优化 1.1 调整参数隐藏Nginx软件版本号信息 一般来说,软件的漏洞都和版本有关,这个很像汽车的缺陷,同一批次的要有问题就都有问题,别的批次可能就都是好的.因此,我们应尽量隐 ...
- 基于Nginx dyups模块的站点动态上下线并实现简单服务治理
简介 今天主要讨论一下,对于分布式服务,站点如何平滑的上下线问题. 分布式服务 在分布式服务下,我们会用nginx做负载均衡, 业务站点访问某服务站点的时候, 统一走nginx, 然后nginx根据一 ...
- Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差
Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差别 Nginx连接fastcgi的方式有2种:unix domain socket和TCP,Uni ...
- 使用nginx反向代理实现多端口映射(未解决)
问题: 想实现访问在同一个主机上实现多个域名访问, 如用 blog.xxx.com访问博客(使用8000端口), app.xxx.com访问其他应用(使用8080端口): 不同的服务用URL区分,不用 ...
- 使用python找出nginx访问日志中访问次数最多的10个ip排序生成网页
使用python找出nginx访问日志中访问次数最多的10个ip排序生成网页 方法1:linux下使用awk命令 # cat access1.log | awk '{print $1" &q ...
- nginx 反向代理时丢失端口的解决方案
今天,配置nginx反向代理时遇到一个问题,当设置nginx监听80端口时转发请求没有问题.但一旦设置为监听其他端口,就一直跳转不正常:如,访问欢迎页面时应该是重定向到登录页面,在这个重定向的 ...
- nginx配置基于域名、端口、IP的虚拟主机
1.基于域名的虚拟主机: 绝大多数企业对外提供服务的网站使用的都是基于域名的主机,通过不同的域名区分不同的虚拟主机. 首先我们进入安装nginxd的目录下:/application/nginx-1.6 ...
- nginx——绑定 Nginx 进程到不同的 CPU 上
为什么要绑定 Nginx 进程到不同的 CPU 上 :默认情况下,Nginx 的多个进程有可能跑在某一个 CPU 或 CPU 的某一核上,导致 Nginx 进程使用硬件的资源不均,因此绑定 Nginx ...
随机推荐
- 基于python+django的求职招聘网站-网上招聘管理系统设计与实现
该系统是基于python+django的求职招聘网站.网上招聘管理系统.网上人才招聘系统.毕业生求职招聘系统.大学生求职招聘系统.校园招聘系统.企业招聘系统.系统适合场景:大学生.课程作业.毕业设计. ...
- Prime Time - 介绍
Prime Time是对timing进行分析 Prime Time使用的是STA方法进行分析 工具会有更新,但是核心内容是不变的 Prime Time(intro to STA) 没有PT工具的时候, ...
- Oracle数据库如何解决创建用户名开头必须要C##问题?
1.问题 我们在创建用户,概要文件等时,由于使用的是容器数据库,其文件名必须以C##开头. 我们在学习过程中暂时不需要对齐进行区分,所以如何修改这个设定呢? 2.解决 参考链接如何解决创建用户名开头必 ...
- JVM内存用量的再学习
JVM内存用量的再学习 背景 最近解决一个SQLServer的问题耗时很久. 最终找到了一个看似合理的问题解释. 但是感觉不能只是总结于数据库方面 因为为了解决这个问题增加了很多监控措施. 所以想就这 ...
- 银河麒麟安装多版本gcc的方式方法
银河麒麟安装多版本gcc的方式方法 背景 最近想升级一下gcc 但是发现自己编译的话非常麻烦 记得之前CentOS7的时候有一个scl的处理 发现CentOS8 已经没有scl的仓库了 简单验证了一下 ...
- [转帖]金仓数据库KingbaseES数据目录结构
KingbaseES数据库结构 [kingbase@postgresV8]$ tree -LP2data/ . ├── data │ ├── base # 存储用户创建的数据库文件及隶属于用户数据库的 ...
- [转帖]《Linux性能优化实战》笔记(23)—— 内核线程 CPU 利用率过高,perf 与 火焰图
在排查网络问题时,我们还经常碰到的一个问题,就是内核线程的 CPU 使用率很高.比如,在高并发的场景中,内核线程 ksoftirqd 的 CPU 使用率通常就会比较高.回顾一下前面学过的 CPU 和网 ...
- [转帖]py_innodb_page_info.py工具使用
目录 一.Linux安装Python3 1. 解压包 2. 安装环境 3. 生成编译脚本 4. 检查python3.10的编译器 5. 建立Python3和pip3的软链 6. 添加到PATH 7. ...
- 【构造,树】【Loj】Loj6669 Nauuo and Binary Tree
2023.7.3 Problem Link 交互库有一棵 \(n\) 个点的二叉树,你每次可以询问两个点之间的距离,猜出这棵二叉树.\(n\le 3000\),询问次数上限 \(30000\). 首先 ...
- 01uni-app的创建运行在不同端上的配置 以及tarBar的配置
uni-app的创建### 01==>创建uni-app的项目非常简单.不需要注意什么注意点哈!! 创建项目的时候 可以参考官网 https://uniapp.dcloud.io/quickst ...