踩坑记录:无需部署,如何通过公网IP访问本地服务

前言

最近在做一个物联网相关的需求,大致要求是用户使用指纹识别开门后,除了将设备属性上报到华为云IoT平台外,还需要在华为云配置数据转发,将开门日志转发到后端服务并存到数据库,用于向用户端展示开门日志。

这其实是一个挺朴实无华的需求的,只需要在华为云上把数据转发配置为自己服务器的地址就行了。问题就在于:我们只有一个已经无法容纳其它较大量级服务的腾讯云服务器,也就是说,我们无法将服务部署上云。

问题

好了,现在的问题就在于:项目在本地进行开发,如何让华为云将数据转发到本地的Web服务?

这里我想到了三个解决方法:

  1. 硬头皮直接将项目部署到云服务器。(下下下策)
  2. 通过内网穿透技术,让公网IP映射到本地服务。
  3. 在服务器部署一个轻量的Python脚本(大的跑不动,小脚本还是能跑的),用于转发我们的Http请求,到时候我只需要在本机和服务器之间开一个端口映射即可。

第一种方案并不可行,项目采用SpringBoot进行开发,引入了一些中间件,部署很繁琐,就算使用docker-compose,每次都要重新打jar包和重新构建镜像也挺烦人的,本地改完代码即可运行它不香吗?

第二个策略------内网穿透,看着很香,但我没试过(于是踩了很多坑)

第三个策略:下下策,实现方案太不优雅了,相当于又引入一个服务。

内网穿透踩坑

研究来研究去,只剩下内网穿透这条路可以走了,那还多说什么,来试试吧,试试就逝世,在这个过程中我踩了挺多坑的,下面我将把这些坑总结一下,方便有和我一样需求的小伙伴少走些弯路。

PS:本文旨在让一些和我一样没用过内网穿透的小伙伴少走些弯路,大佬们看个乐子就好了。当然如果有更好的解决方案也可以教教小弟。

踩坑1 :采用ngrok进行内网穿透,并采用其配置的免费域名或者随机生成的域名。

  • 为何采用此方案:免费,不用自己买服务器和域名。
  • 问题
    • 第一次访问需要携带特殊的请求头,否则是一个ngrok自己的页面。
    • 后面不知道为何只有本机能访问。
    • 国外服务器,速度极慢,华为云上连不通。
  • 后续:后面想通过在服务器和本地都跑一个ngrok,但看到ngrok的仓库上一次commit还是在八年前,emmm,想想还是算了。

踩坑2:采用CloudFlare的tunnel做内网穿透

  • 教程:参考各类博客,大致只需要将自己的域名的DNS改成Cloudflare那边的,在平台开通tunnel后在本地跑一个cloudflare的守护进程即可。
  • 问题
    • 华为云上一直报出URL格式错误,原因暂时未知,卡了很久。
    • 带宽太小,加上服务器在国外,华为云那边的连通性测试都无法通过,10次连通性测试大概只有1次能跑通

踩坑3:让用户直接请求我服务器的公网IP,服务器用Nginx反向代理到CloudFlare那边的域名

  • 为啥会这么做:正如我上面所说,因为刚开始我发现只用内网穿透的域名,在华为云那边配置的时候一直会出现callback url error,就是URL的格式错误,我很不能理解,以为加一层代理应该就没问题了。
  • 问题
    • 事实证明这样做也无法解决URL格式错误的问题。
    • 其本质上也没有解决带宽太小的问题。
    • 反向代理后,Host如果没经过特殊修改,还会报出Direct IP access not allowed的错误。脑瓜子更疼了。

最终方案 :在云服务器和本地都跑一个frp,通过frp的HTTP代理就可以实现自定义域名访问内网的Web服务。 不过还是踩了个小坑:在配置frp的服务端的时候,要将http监听的端口放到bindPort之上,否则会报new proxy [iot-server] type [http] error: type [http] not supported when vhost http port is not set,原因暂时未知。

下文我将分享,使用frp实现的自定义域名访问内网服务的过程。

frp实践

什么是frp

frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议,且支持 P2P 通信。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。

frp 仓库的更新很活跃,截至写作本文,最近一次commit是在16hour前,这也是为什么我采用frp而不是ngrok。仓库地址:frp/README_zh.md at dev · fatedier/frp (github.com)

实战操作

下面给出用frp实现内网穿透的步骤。实话说官方文档写得很详细了,大家也可以看看官方文档通过自定义域名访问内网的 Web 服务 | frp (gofrp.org)(不过似乎在HTTP代理配置那里有个小坑)

下载

首先,打开frp官网,找到上面GitHub的Release地址,根据系统下载不同的版本。例如我本地下的是Windows amd64,服务器下的是Linux amd64,下载后将Linux版的传到服务器并解压。

frp 主要由两个组件组成:客户端(frpc) 和 服务端(frps)。通常情况下,服务端部署在具有公网 IP 地址的机器上,而客户端部署在需要穿透的内网服务所在的机器上。release下载的压缩包上同时含有frpc和frps,我们只需要使用其中一个即可(对于本地我们只需要frpc,对于服务器我们使用frps)

配置

根据具体需求,具体请看官网通过 SSH 访问内网机器 | frp (gofrp.org),下面我给出暴露本地服务的配置。

  • 客户端的配置文件是:frpc.toml,toml的格式要求很严格,按照官网的要求写。下面是我的配置:
toml 复制代码
serverAddr = "服务器公网IP"
serverPort = 7000

[[proxies]]
name = "iot-server"
type = "http"
localPort = 9090
customDomains = ["自定义域名,也可以直接用公网IP"]
  • 服务端的配置文件是:frps.toml,我的配置如下
toml 复制代码
vhostHTTPPort = 9090
bindPort = 7000

注意,这里有个坑,如果不把vhostHTTPPort写到bindPort前面 ,会报出下面的错误:new proxy [iot-server] type [http] error: type [http] not supported when vhost http port is not set

通过上述的配置,我就能通过访问公网ip:9090来访问我本地在9090端口跑的SpringBoot应用啦。

启动

  • 客户端:./frpc -c ./frpc.toml
  • 服务端:./frps -c ./frps.toml

最佳实践:使用systemd管理

在 Linux 系统下,使用 systemd 可以方便地控制 frps 服务端的启动、停止、配置后台运行以及开机自启动。

  • 安装systemd
bash 复制代码
yum install systemd
  • 创建frps.service文件
bash 复制代码
sudo vim /etc/systemd/system/frps.service
  • 写入内容
ini 复制代码
[Unit]
# 服务名称,可自定义
Description = frp server
After = network.target syslog.target
Wants = network.target

[Service]
Type = simple
# 启动frps的命令,需修改为您的frps的安装路径
ExecStart = /path/to/frps -c /path/to/frps.toml

[Install]
WantedBy = multi-user.target
  • 使用systemd命令管理frps服务
bash 复制代码
# 启动frp
sudo systemctl start frps
# 停止frp
sudo systemctl stop frps
# 重启frp
sudo systemctl restart frps
# 查看frp状态
sudo systemctl status frps
  • 设置开机自启动
bash 复制代码
sudo systemctl enable frps

这样,我们就成功实现了通过自定义域名/公网IP来访问本地服务的需求。由于内网服务缺乏公网 IP 地址,因此无法直接被非局域网内的用户访问。用户通过访问服务端的 frps,frp 负责根据请求的端口或其他信息将请求路由到相应的内网机器,从而实现通信。

总结

对于今后有从华为云IoT平台将数据转发到第三方HTTP应用服务器需求的UU,考虑到开发和测试的方便,目前的最佳实践是:(前提是您的项目不需要部署上线,只作为内部测试/比赛演示)

  • 在各大云产商那里购买一个低配的服务器,从而拿到一个公网IP
  • 在本地和服务器上都跑一个frp
  • 利用frp配置 HTTP 类型的代理,让你可以通过自定义域名访问内网的 Web 服务
相关推荐
浪裡遊3 分钟前
跨域问题(Cross-Origin Problem)
linux·前端·vue.js·后端·https·sprint
声声codeGrandMaster11 分钟前
django之优化分页功能(利用参数共存及封装来实现)
数据库·后端·python·django
呼Lu噜1 小时前
WPF-遵循MVVM框架创建图表的显示【保姆级】
前端·后端·wpf
bing_1581 小时前
为什么选择 Spring Boot? 它是如何简化单个微服务的创建、配置和部署的?
spring boot·后端·微服务
学c真好玩1 小时前
Django创建的应用目录详细解释以及如何操作数据库自动创建表
后端·python·django
Asthenia04121 小时前
GenericObjectPool——重用你的对象
后端
Piper蛋窝1 小时前
Go 1.18 相比 Go 1.17 有哪些值得注意的改动?
后端
excel1 小时前
招幕技术人员
前端·javascript·后端
盖世英雄酱581362 小时前
什么是MCP
后端·程序员