行业知识
如何内网nginx正向代理外网,并实现高德地图
项目背景
近期遇到一个内网实现高德地图的需求,初步分析之后,需要解决两个问题,1、nginx正向代理访问高德地图相关域名;2、创建高德地图实例,并实现相关功能
一、先说nginx这块,一台网关服务器A,放静态资源包,一台代理服务器B,访问外网,因为是内网应用,使用的都是离线包,包如下
- nginx-1.22.1.tar.gz 提取码 x7qv
- ngx_http_proxy_connect_module-master.zip 提取码 exyq
- ngx_http_substitutions_filter_module-master.zip 提取码 sit9
- openssl-1.1.1w.tar.gz 提取码 d9em
1、网关服务器A,nginx搭建:
#root 权限创建用户nginxuseradd nginxpasswd nginx#进入nginx用户目录,新建upload目录,上传nginx包和sub_filter包cd /home/nginxmkdir -p uploadrz -ry#解压nginxcd /home/nginx/uploadtar -zxvf nginx-1.22.1.tar.gz#解压sub_filter模块包unzip ngx_http_substitutions_filter_module-master.zipmv ngx_http_substitutions_filter_module-master ngx_http_substitutions_filter_module#编译 sub_filtercd nginx-1.22.1./configure --with-http_sub_module --add-module=/home/nginx/upload/ngx_http_substitutions_filter_module#安装 默认安装在/usr/local/nginxmake && make install#执行后再给nginx赋权chown -R nginx:nginx /usr/local/nginx /home/nginx/upload#修改nginx配置文件 启动nginx
A服务器nginx配置如下:
worker_processes auto;error_log logs/error.log error;pid logs/nginx.pid;worker_rlimit_nofile 16384;events {use epoll;worker_connections 16384;}http {includemime.types;default_typeapplication/octet-stream;sendfileon;keepalive_timeout65;#gzip on;upstream backend {server "代理服务器B的ip"}server {listen8080;server_namelocalhost;location / {root html;index index.html index.htm;}#代理获取jsapi文件并修改文件内容location /mapsUrl/ {proxy_set_header Accept-Encoding "";set $proxyip "服务器A的ip:8080";#注意这里的proxy_pass 我用动态的有时候reload时不生效,可以直接用静态的proxy_passhttp://代理服务器ip:8080/maps;proxy_passhttp://服务器A的ip:8080/maps;sub_filter_types *;sub_filter_once off;sub_filter 'https' 'http';sub_filter 'webapi.amap.com' '$proxyip/webapi';sub_filter 'restapi.amap.com' '$proxyip/restapi';sub_filter 'vdata.amap.com' '$proxyip/vdata';sub_filter 'a.amap.com' '$proxyip/aUrl';sub_filter 'vdata0{1,2,3,4}.amap.com' '$proxyip/vdata0{1,2,3,4}';sub_filter '{vdata,vdata01,vdata02,vdata03,vdata04}.amap.com' '$proxyip/{vdata,vdata01,vdata02,vdata03,vdata04}';sub_filter 'wprd0{1,2,3,4}.is.autonavi.com' '$proxyip/wprd0{1,2,3,4}';sub_filter 'webrd0{1,2,3,4}.is.autonavi.com' '$proxyip/webrd0{1,2,3,4}';}location /maps { proxy_pass http://$backend; } location ~* ^/webapi(.*) { proxy_pass http://$backend; } location ~* ^/restapi(.*) { proxy_pass http://$backend; } location /vdata { proxy_pass http://$backend; } location ~* ^/aUrl(.*) { proxy_pass http://$backend; } location ~* ^/wprd0(\d*)/(.*) { proxy_pass http://$backend; } location ~* ^/webrd0(\d*)/(.*) { proxy_pass http://$backend; } location ~* ^/vdata0(\d*)/(.*) { proxy_pass http://$backend; } error_page 500 502 503 504 /50x.html; location = /50x.html {root html;}}} 2、代理服务器B,nginx搭建:
#root 权限创建用户nginxuseradd nginxpasswd nginx#进入nginx用户目录,新建upload目录,上传nginx包和正向代理模块包、ssl包cd /home/nginxmkdir -p uploadrz -ry#解压nginxcd /home/nginx/uploadtar -zxvf nginx-1.22.1.tar.gz#解压openssl-1.1.1w.tar.gz#解压正向代理模块包unzip ngx_http_proxy_connect_module-master.zipmv ngx_http_proxy_connect_module-master ngx_http_proxy_connect_module#查看 正向代理模块包proxy_connect_rewrite_102101.patch位置(因为nginx版本问题,对代理模块的版本有要求)cd nginx-1.22.1ll ../ngx_http_proxy_connect_module/patch#导入模块patch -p1B代理服务器nginx配置如下:
worker_processes auto;error_log logs/error.log error;pid logs/nginx.pid;worker_rlimit_nofile 16384;events {use epoll;worker_connections 16384;}http {includemime.types;default_typeapplication/octet-stream;sendfileon;keepalive_timeout65;#gzip on;proxy_connect_timeout5;proxy_read_timeout60;proxy_send_timeout5;proxy_buffer_size16k;proxy_buffers464k;proxy_busy_buffers_size128k;proxy_temp_file_write_size128k;server {listen80;server_namelocalhost;location / {root html;index index.html index.htm;}location /maps { resolver 114.114.114.114; set $backend "webapi.amap.com"; proxy_pass https://$backend; } location ~* ^/aUrl(.*) { resolver 114.114.114.114; set $backend "a.amap.com"; proxy_pass https://$backend$is_args$args; } location ~* ^/restapi(.*) { resolver 114.114.114.114; set $backend "restapi.amap.com"; proxy_pass https://$backend$is_args$args; } location ~* ^/webapi(.*) { resolver 114.114.114.114; set $backend "webapi.amap.com"; proxy_pass https://$backend$is_args$args; } location ~* ^/vdata0(\d*)/(.*) { resolver 114.114.114.114; set $backend "amap.com"; proxy_pass https://vdata0.$backend/$is_args$args; } location ~* ^/vdata(.*) { resolver 114.114.114.114; set $backend "vdata.amap.com"; proxy_pass https://$backend$is_args$args; } location ~* ^/wprd0(\d*)/(.*) { resolver 114.114.114.114; set $backend "is.autonavi.com"; proxy_pass https://wprd0.$backend/$is_args$args; } location ~* ^/webrd0(\d*)/(.*) { resolver 114.114.114.114; set $backend "is.autonavi.com"; proxy_pass https://webrd0.$backend/$is_args$args; } }}到此nginx安装完成
二、配置代理服务器的dns
我们网络部门给开的dns地址是114.114.114.114,需要在代理服务器配置一下
vim /etc/resolv.conf#添加dns 114.114.114.114三、重启nginx,验证代理是否成功
这里需要注意下,对应的外网域名可能会变化,我目前遇到的域名有以下几个:webapi.amap.com、restapi.amap.com、vdata.amap.com、a.amap.com、vdata0{1,2,3,4}.amap.com、webrd0{1,2,3,4}.is.autonavi.com、wprd0{1,2,3,4}.is.autonavi.com
具体需要开通哪些域名,根据项目需求。
在代理服务器B,进行以下操作,进行验证:
#检验配置是否正确./nginx -t#重启nginx./nginx -s reload#验证是否可以访问域名webapi.amap.comcurl --proxy 127.0.0.1:80 http://webapi.amap.com/maps至此正向代理服务搭建完成。
四、参考官网JSAPI集成高德地图
官网地址如下:
https://lbs.amap.com/api/javascript-api-v2/guide/abc/jscode
集成地图这块比较简单,按照官网的步骤就可以实现,这里只是大概说下实现步骤和遇到的问题:
第一步:
按 NPM 方式安装使用 Loader ,如果是内网环境,现在外网安装,再把包拷贝到内网使用
npm i @amap/amap-jsapi-loader --save第二步:
在项目中新建 MapContainer.vue 文件,用作地图组件,在 MapContainer.vue 地图组件中创建 p 标签作为地图容器 ,并设置地图容器的 id 属性为 container。
第三步:
设置地图容器样式
#container{ padding:0px; margin: 0px; width: 100%; height: 800px; }第四步:
在 MapContainer.vue文件中初始化地图
import AMapLoader from "@amap/amap-jsapi-loader";window._AMapSecurityConfig = { securityJsCode: "「你申请的安全密钥」",};export default { name: "map-view", mounted() { this.initAMap(); }, unmounted() { this.map?.destroy(); }, methods: { initAMap() { AMapLoader.load({ key: "", // 申请好的Web端开发者Key,首次调用 load 时必填 version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15 plugins: [], // 需要使用的的插件列表,如比例尺'AMap.Scale'等 }) .then((AMap) => { this.map = new AMap.Map("container", { // 设置地图容器id viewMode: "3D", // 是否为3D地图模式 zoom: 11, // 初始化地图级别 center: [116.397428, 39.90923], // 初始化地图中心点位置 }); }) .catch((e) => { console.log(e); }); }, },};#container { width: 100%; height: 800px;}注意:因为我的主项目对define方法有定义,和JSAPI代码有冲突,导致JSAPI被请求后,无法正常初始化,所以在执行AMapLoader.load之前对window.define做了重新赋值,如下:
window.defineInit= window.definewindow.define= null#TODO 执行AMapLoader.load,执行完成之后,重新赋值definewindow.define= window.defineInitwindow.defineInit= null因为是loader方式,所以在调用之前,要全局拦截js,转发请求到代理服务器
function hookScript() {const $url = '网关服务器A的IP:8080'const maps = 'https://webapi.amap.com/maps'const webapi = 'https://webapi.amap.com'const property = Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype, 'src')const nativeSet = property.setfunction customiseSrcSet(url){if(url.toString().search(maps) !== -1){url = $url + '/mapsUrl' + url.split(maps)[1]} else if (url.toString().search(webapi) !== -1){url = $url + '/webapi' + url.split(webapi)[1]}nativeSet.call(this, url)}Object.defineProperty(HTMLScriptElement.propertype, 'src', {set: customiseSrcSet}}hookScript()至此高德地图集成完成。
总结:
整个过程遇到一下几个问题:
- 加载JSAPI后,无法调起后续请求,也无任何报错,因为内网开发,一开始以为网络问题,后来网络开通后,还是无法调起,经过源码打断点发现,内部JS 因window.define已定义,导致初始化终止,通过重置window.define方法解决JSAPI不完全调用问题;
- JSAPI会内部调用其他域名的请求,对于这个问题,一开始使用Object.getOwnPropertyDescriptor在原型上去全局拦截script、image、xhr等,进而重写url,但是项目里对fetch请求方式做了定义,无法很好的拦截jsapi发出的fetch请求,最后除了“webapi.amap.com”,其他的域名通过sub_filter进行全文替换,把域名替换成代理服务的地址;
- nginx的dns缓存问题,经常服务启动后,长时间不访问,代理就报错502、504等连接超时问题,后通过proxy_pass动态域名解析解决该问题。
上一篇:高防服务器哪里好?