openresty动态扩容支持持久化(完整版) - CSDN博客

1,746 阅读29分钟
原文链接: blog.csdn.net

实现功能:

1,支持http查询lua_shared
2,支持redisCluster动态路由
3,支持业务日志收集(大字段不考虑)
4,支持日志切割(linux-shell)
5,支持upstream动态扩容并持久化

6,支持nginx应用与配置分离运行(Linux-shell)

7,支持Jenkins一键自动部署.zip(windows-cmd)

8,支持prometheus指标监控

用到的模块(升级openresty很多都自带):

1,LuaJIT-2.0.4.tar.gz
2,luafilesystem-v_1_6_3.tar.gz
3,lua-nginx-module-0.10.8.zip
4,lua-resty-redis-cluster-master.zip
5,lua-resty-redis-master.zip
6,nginx-clojure-0.4.5.tar.gz(nginx调用java时用)
7,ngx_http_dyups_module-0.2.9.zip
8,redis2-nginx-module-0.14.zip

整体思路:

1,请求通过F5路由到下面的4台nginx,nginx通过url拦截解析request header,request body 获取到对应的路由key
(apiSign,其实是业务method拼接)通过调用redisCluster返回对应的upstreamName路由,顺便收集全量的请求响应信息(包括多次请求,会涉及到nginx执行顺序)json
2,对收集到的日志在做处理,切割,通过logstash入kafka(也可以直接对接kafka,但可能因为kafka故障丢失数据)
3,动态扩容是临时加减服务器(server应用)以及对upstream的增删改查操作,通过ngx_http_dyups_module模块完成,支持rest,我的实现思路是:将upstream抽成独立文件include加载,nginx启动初始化时,读一份upstream lua_shared做备份(upstreamName,value),http请求操作upstream时,则拦截request_body,通过ngx.location.capture方式操作nginx内存,如果res返回成功,则将request_body处理,写入到lua_shared,并更新upstream File(这里有个坑,ngx_http_dyups_module模块,sprintf做的不是太友好,不完全,没有显示全部的upstream信息,如果显示全部的信息,则可以每次调用http直接操作nginx内存,然后调用ngx.location.capture detail
方法拿到全量的upstream list 直接写入upstream File)
4,Jenkins自动部署则因为这边是一份代码区分N个环境,则需要创建多个job,通用win-cmd命令(Jenkins是win版,汗,如果linux版更容易)


上传代码

nginx.conf

#运行用户和组,缺省为nobody,若改为别的用户和组,则需要先创建用户和组
#user wls81 wls; 

#开启进程数,一般与CPU核数等同
worker_processes  4;

#设置进程到cpu(四cpu:0001 0010 0100 1000)
#worker_cpu_affinity 0001 0010 0100 1000;

#每个进程最大打开文件数
worker_rlimit_nofile 8000;

#进程号保存文件
#pid        /wls/apache/applogs/ng_sbtps-opf-dmzweb-nginx/nginx.pid;
#error_log  logs/error.log;
pid        logs/nginx.pid;

#设置错误日志
#error_log  /wls/apache/applogs/ng_sbtps-opf-dmzweb-nginx/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;


events
{
    #运行模式设置[ kqueue | rtsig | epoll | /dev/poll | select | poll ];
    #使用epoll(linux2.6的高性能方式)
    use epoll;
	
    #每个进程最大连接数(最大连接=连接数x进程数)
    worker_connections  8000;
}

http
{

    #文件扩展名与文件类型映射表
    #include       mime.types;

    #默认文件类型
    #default_type  text/html;
    default_type  application/octet-stream;
    
    #服务器名称相关设置
    server_names_hash_max_size    256;
    server_names_hash_bucket_size 512;

    #默认编码
    charset UTF-8;

    #开启高效文件传输模式,直接从系统级别传输(Linux 2.4以上支持,纯文件服务器才能打开)
    sendfile   off;

    #网络TCP_NOPUSH和TCP_NODELAY参数设置
    #tcp_nopush on;
    tcp_nodelay on;

    #设置保留链接超时时间为75秒 设置header超时时间为20秒
    keepalive_timeout 75 20;

    #打开gzip压缩
    gzip  on;

    #最小压缩文件大小
    gzip_min_length  1K;

    #压缩缓冲区
    gzip_buffers     4 8k;

    #压缩类型
    gzip_types       text/* text/css application/javascript application/x-javascript application/xml;

    #压缩级别 1-9 1最快 9最慢
    gzip_comp_level  9;

    #压缩通过代理的所有文件
    gzip_proxied     any;

    #vary header支持
    gzip_vary        on;

    #压缩版本(默认1.1,前端为squid2.5使用1.0)
    gzip_http_version 1.1;
    

    #输出缓冲区
    output_buffers   4  32k;

    #输出拆包大小
    postpone_output  1460;

    #接收header的缓冲区大小
    client_header_buffer_size 128k;
    large_client_header_buffers 4 256k;

    #客户端发送header超时
    client_header_timeout  3m;

    #客户端发送内容超时
    client_body_timeout    3m;

    #发送到客户端超时
    send_timeout           3m;



    #捕捉代理端的http错误
    #proxy_intercept_errors  on;



    #日志文件格式
    log_format main '$remote_addr $http_x_forwarded_for $remote_user $time_iso8601 $status '
                           '$server_protocol $request_id $comp_sign $request_method $reSetReqUri $uri $http_referer $gzip_ratio '
                           '"$http_user_agent" '
                           '$body_bytes_sent $bytes_sent $request_length "$upstream_addr" "$upstream_header_time" "$upstream_response_time" $request_time';   
	log_format  requestBody  '$remote_addr - $remote_user [$time_local] "$request" '
                      '"$status" $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" logTraceId:$request_id $comp_sign $request_method $apiSign req_body:"$request_body" resp_body:"$resp_body" resp_map:"$resp_map"'
                      '$upstream_addr $bytes_sent $request_length "$upstream_response_time" "$request_time"';	
	lua_need_request_body on; 
    #日志文件(不记录)
    #access_log  /dev/null;
    #access_log   logs/access.log main;
	#error_log  /wls/appsystems/nginx/openresty-1.11.2.4/nginx/logs/error.log error;




    #默认主机配置
    #include default_host.conf;
    
    

    #包含其它虚拟主机配置;
    include servers/*.com;
    include servers/*.net;
    include servers/*.org;
    include servers/*.cn;
}
sbtps-opf-ngx-sf-nginx.com
include /wls/appsystems/nginx/servers/conf/servers/respCodeMap.map;
#api映射配置
lua_shared_dict apiMappingShared 50m;
lua_shared_dict healthStatus 1m;
lua_shared_dict redisSwitchShared 256k;
lua_shared_dict prometheus_metrics 10M;
client_body_buffer_size 100m;
client_max_body_size 100m;


lua_shared_dict upstreamShared 64m; # 定义upstream共享内存空间


#init_worker_by_lua_file conf/scripts/luas/init.lua;
init_by_lua_file /wls/appsystems/nginx/servers/conf/scripts/luas/init.lua;
#获取访问客户的真实IP
map $http_x_forwarded_for  $clientRealIp {
		""	$remote_addr;
		~^(?P[0-9\.]+),?.*$	$firstAddr;
}

#限制客户端的访问频次,这里限制每秒20次上限
limit_req_zone $clientRealIp zone=SF-WEB-AUTHAPP-LIMIT:10m rate=100r/s;
#配置负载均衡服务器(采用IP Hash算法,相同客户IP会转发到相同服务器)


server
{
    #设置监听端口
    listen 37775 default;
	
    #设置服务器域名(IP访问和多域名访问可不设置)
    #server_name _*;
    #server_name  www.test.com;
	

    #开启shtml支持
    #ssi on;
    #ssi_silent_errors on;
    #ssi_types text/shtml;
	

    #设置主访问日志
    #access_log /wls/appsystems/nginx/openresty-1.11.2.4/nginx/logs/access.log main;
	#access_log /wls/appsystems/nginx/openresty-1.11.2.4/nginx/logs/requestBody.log  requestBody;
	error_log  logs/error.log error;
	access_log logs/access.log  main;
	error_log logs/request.log  notice;
	
	##定义$networkSign的值,区别内网外网
	set $networkSign "OPENAPI_NGINX_ROUTE_NODE_LAN";
	##定义$request_trace_id的值,在1.11.x之前,我们可以使用类似的方式声明,只要能确保  
	##其值出现重复的可能性尽可能的小即可。  
	set $request_trace_id trace-id-$pid-$connection-$bytes_sent-$msec;  
	set $comp_sign "";
	set $reSetReqUri $request_uri;
	set $resp_body "";
	set $resp_map "";
	set $apiSign "";
	
    #access_log  /dev/null;
    #fastcgi_intercept_errors on;
    #error_page  404 403  =    /404.html;
    #error_page  500 502 503 504  =  /50x.html;
	include /wls/appsystems/nginx/servers/conf/servers/server-http-error-page.err;
   #设置转发到跟投APP后端服务器的URL(正则表达式)
    #location ~ (^/pa/interface/jk/(cgi-bin|servlet|chart)/|\.jsp$)
   #{
       #proxy_pass http://backendServer;
       #include proxy.conf;
   #}

    #设置转发到后端服务器的URL(正则表达式)
    #location ~ (^/pa/newstock/hq/(cgi-bin|servlet|chart)/|\.jsp$)
   #{
       #proxy_pass http://backendServer;
       #include proxy.conf;
   #}
	

    #设置监控nginx状态URL
    location /__nginxstatus
    {
       stub_status on;
       access_log off;
    }

	location ^~ /reloadApiMapping {
		default_type	"text/plain";
        content_by_lua '
			ngx.log(ngx.ERR,"this is reloadApiMapping configuration !")
			require("apiMapping");
			apiMapping.reloadApiMapping();
		';
    }
	location ^~ /setRedisSwitch {
		default_type	"text/plain";
		content_by_lua '
			ngx.log(ngx.ERR,"this is setRedisSwitch configuration !")
			require("apiMapping");
			apiMapping.setRedisSwitch();
		';
	}
	location ^~ /getLuaShared {
		default_type	"text/plain";
		content_by_lua '
			ngx.log(ngx.ERR,"this is getLuaShared configuration !")
			require("apiMapping");
			apiMapping.getLuaShared();
		';
	}
	location /metrics {
		access_log off;
		content_by_lua '
			--ngx.log(ngx.ERR,"this is metrics configuration !")
			metric_connections:set(ngx.var.connections_reading, {"active"})
			metric_connections:set(ngx.var.connections_reading, {"reading"})
			metric_connections:set(ngx.var.connections_waiting, {"waiting"})
			metric_connections:set(ngx.var.connections_writing, {"writing"})					
			prometheus:collect()
		';
	}
	location /favicon.ico {
		log_not_found off;
		access_log off;
	}
	location ^~ /detail {
		dyups_interface; # 这个指令表示这边是接口站点
		lua_need_request_body on;
	}
	location ^~ /upstream {
		dyups_interface; # 这个指令表示这边是接口站点
		lua_need_request_body on;
	}
	location ^~ /upstreamMain {
		dyups_interface; # 这个指令表示这边是接口站点
		lua_need_request_body on;
		content_by_lua '
			--ngx.log(ngx.ERR,"this is nginx-upstreamMain operation !")
			require("upstreamdynamic");
			upstreamdynamic.run();
		';
	}
	
	location ^~ /beat/checkSet {
		default_type	"text/plain";
        content_by_lua '
			local reqUri = ngx.var.uri;	
			local status=string.sub(reqUri,16,-1);
			local healthStatus = ngx.shared.healthStatus;
			healthStatus:set("status", status);
			local data = {};
			data["status"]=status;
			ngx.say(cjson.encode(data));
		';
    }
	
	location ^~ /beat/check {
		default_type	"text/plain";
		access_log off;
        content_by_lua '
			local healthStatus = ngx.shared.healthStatus;
			local status = healthStatus:get("status");
			if status == nil or status=="" then
				status ="UP"
			end
			
			local data = {};
			data["status"]=status;
			ngx.say(cjson.encode(data));
		';
    }
	
    #设定根目录(若全部请求转发到后端服务器则不需要设置) 
    location /
    {
		#web根目录,根据实际情况调整
		
		#limit_req zone=SF-WEB-AUTHAPP-LIMIT nodelay;
		#proxy_pass http://SF-WEB-AUTHAPP/;
		proxy_set_header Host $host;
		proxy_set_header            X-real-ip $remote_addr;
		proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header logTraceId $request_id;
		set $comp_sign AUTH2;
		set $reSetReqUri $request_uri; 
		set $redisResultJson "";
		lua_need_request_body on;
		#set $resp_map "";
		rewrite_by_lua  'apiMapping.getCompSign()';
		body_filter_by_lua '
			--ngx.log(ngx.ERR,"this is nginx-request configuration !")
			require("request");
			request.run();
		';
		proxy_set_header X-Request-ID $request_id;
		proxy_pass         http://$comp_sign/AuthApp$reSetReqUri;
		log_by_lua '
			--ngx.log(ngx.ERR,"this is nginx-logout configuration !")
			require("logout");
			logout.run();
		';
    }
}
server-http-error-page.err
error_page 404 = /httpStatusRewrite?apiRespCodeParam=-10201;
error_page 403 = /httpStatusRewrite?apiRespCodeParam=-5;
error_page 503 = /httpStatusRewrite?apiRespCodeParam=-6;
error_page 500 502 504  = /httpStatusRewrite?apiRespCodeParam=-1;

location /httpStatusRewrite{
	default_type	"text/plain";
	set $apiRespCode  $arg_apiRespCodeParam;
	if ( $apiRespMsg = '' ) {
		return 200 '{"resCode":"-1","resMsg":"请求失败"}';
	}
	return 200 '{"resCode":"$apiRespCode","resMsg":"$apiRespMsg"}';
}
respCodeMap.map
map $apiRespCode  $apiRespMsg {	
	"0" "SUCCESS。";
	"-10000" "参数不可为空。";
	"-1" "请求失败。";
	"-5" "禁止访问。";
	"-6" "访问频率过快,请稍后再试。";
	"-10201" "未定义路径。";
	"-1006" "参数格式异常。";
	"-1013" "目前仅支持POST请求方式。";
}
upstream.com
upstream SBTPS-OPF-AIO-WEB-SF-AUTH-APP-MACS
{   
    server 10.17.144.176:8080;
    server 10.17.144.177:8080;
    keepalive 1024;
}

upstream SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS
{   
    server 10.17.145.86:30074;
    server 10.17.145.87:30074;
	server 10.17.145.94:30074;
    server 10.17.145.95:30074;
    keepalive 1024;
}

upstream SBTPS-OPF-AIO-WEB-SF-AUTH-APP-YDKH
{   
    server 10.17.145.88:30074;
    server 10.17.145.89:30074;
    keepalive 1024;
}

upstream SBTPS-OPF-AIO-WEB-SF-AUTH-APP-TDX
{   
    server 10.17.145.94:30074;
    server 10.17.145.95:30074;
    keepalive 1024;
}

upstream SBTPS-OPF-AIO-WEB-SF-AUTH-APP-KH-IDVERIFY
{   
    server 10.17.145.96:30074;
    server 10.17.145.97:30074;
    keepalive 1024;
}

upstream SBTPS-OPF-AIO-WEB-WEBII-AUTH-APP
{   
    server 10.17.167.133:8080;
    server 10.17.167.134:8080;
    keepalive 1024;
}

upstream SBTPS-OPF-AIO-WEB-DMZPTR-AUTH-APP
{   
    server 10.17.145.94:30074;
    server 10.17.145.95:30074;
    keepalive 1024;
}
init.lua
function init()
	cjson = require "cjson";
	lfs = require "lfs"
	local __FILE__ = debug.getinfo(1,'S').source:sub(2)
	--获取当前执行脚本的文件名以及所在路径
	--SCRIPT_ROOT_PATH:/wls/appsystems/nginx20170714/openresty-1.11.2.4/nginx/conf/scripts/luas/
	SCRIPT_ROOT_PATH = dirname(__FILE__);
	--upstreamFile = SCRIPT_ROOT_PATH.."../../servers/upstream.com";
	initLuaModule()
	
	local LATENCY_BUCKETS = {0.05, 0.2, 0.5, 1, 5, 10}
	local BYTESIZE_BUCKETS = {1, 3, 5, 10, 15 , 20}
	prometheus = require("prometheus").init("prometheus_metrics")
	--counter metrics for host+upstream
	metric_requests = prometheus:counter("openapi_nginx_http_requests_total", "Number of HTTP requests", {"host","upstream", "status"})
	--metric_ssorequests = prometheus:counter("nginx_http_sso_requests_total", "Number of HTTP sso requests", {"status"})
	--gauge metrics
	metric_connections = prometheus:gauge("openapi_nginx_http_connections", "Number of HTTP connections", {"state"})
	--histogram metrics for upstream
	metric_latency = prometheus:histogram("openapi_nginx_http_request_duration_seconds", "HTTP request latency", {"upstream"}, LATENCY_BUCKETS)
	--metric_ssolatency = prometheus:histogram("nginx_http_sso_duration_seconds", "HTTP sso request latency",{}, LATENCY_BUCKETS)
	metric_bytes = prometheus:histogram("openapi_nginx_http_request_bytes_sent", "HTTP responses size", {"upstream"}, BYTESIZE_BUCKETS)

	apiMapping = require "apiMapping"
	apiMapping.init()
	upstreamdynamic = require "upstreamdynamic"
	
	upstreamdynamic.init()
	redisClusterConfig()
end 

function dirname(str)
    if str:match(".-/.-") then
        local name = string.gsub(str, "(.*/)(.+)", "%1")
        return name
    elseif str:match(".-\\.-") then
        local name = string.gsub(str, "(.*\\)(.+)", "%1")
        return name
    else
        return ''
    end
end
--/wls/apache/appsystems/ng_sis-stock-frontend-restapi/wwwroot/lua/project_init.lua
--- 当前文件名
--local __FILE__ = debug.getinfo(1,'S').source:sub(2)
--/wls/apache/appsystems/ng_sis-stock-frontend-restapi/wwwroot/lua/

--lua_package_path    "$prefix/?.lua;;";
--local script_root = dirname(__FILE__);
function initLuaModule(rootPath)
	--local rootPath=SCRIPT_ROOT_PATH..'scripts/luas/';
	local rootPath=SCRIPT_ROOT_PATH;
	getpathes(rootPath, nil);
end

function getpathes(rootpath, pathes)
    pathes = pathes or {}
	local attr = lfs.attributes(rootpath)
    if attr and attr.mode == 'directory' then		
		package.path = string.format("%s?.lua;%s", rootpath, package.path)
		for entry in lfs.dir(rootpath) do
			if entry ~= '.' and entry ~= '..' then
				getpathes(rootpath  ..  entry.. '/', pathes)
			end
		end              
	end

    return pathes
end

function redisClusterConfig()
	local config = {
					name="openapi",
					serv_list = {
							{ip="10.25.174.28", port = 6001},
						    {ip="10.25.174.28", port = 6002},
							{ip="10.25.174.28", port = 6003},
							{ip="10.25.174.28", port = 7001},
							{ip="10.25.174.28", port = 7002},
							{ip="10.25.174.28", port = 7003},
							{ip="10.25.174.28", port = 6001},
							{ip="10.25.174.28", port = 6002},
						},
						keepalive_timeout = 200,
						keepalove_cons = 20
					}
	ngx.shared.redisClusterConfig = config;
	--networkSign = "OPENAPI_NGINX_ROUTE_NODE_LAN";--内网
	--networkSign = "OPENAPI_NGINX_ROUTE_NODE_WAN";--外网
end
init();	
apiMapping.lua
apiMapping={};

apiMapping.mappingJsonFile=SCRIPT_ROOT_PATH.."modules/apimapping/apiMapping.json";

function apiMapping.init()
	--local cjson = require "cjson";
	local apiMappingShared = ngx.shared.apiMappingShared;
	local redisSwitchShared = ngx.shared.redisSwitchShared;
	local file = io.open(apiMapping.mappingJsonFile, "r");
	local ret,pcallBack=pcall(parseJson,file:read("*all"));
	file:close();
	if ret then
		apiMappingShared:flush_all();
		for name, value in pairs(pcallBack) do
			--ngx.log(ngx.NOTICE,"name:"..name.."||".."value:"..cjson.encode(value));
			apiMappingShared:set(name, cjson.encode(value));
		end
	else
		ngx.log(ngx.ERR,"init method,JSON PARSE apiMapping.json error.");
	end
	redisSwitchShared:set("redisSwitch","true");

end

function getRequestArgs()
	local args = ngx.req.get_uri_args()
	local requestArgs = {};
	for key, value in pairs(args) do
		if type(value) == "table" then
			--ngx.log(ngx.ERR,"key:"..key.."value:"..value)
			--ngx.say(key, ": ", table.concat(value, ", "))
			requestArgs[key] = table.concat(value, ", ")
		else  
			requestArgs[key] = value
			--ngx.log(ngx.ERR,"key:"..key.."value:"..value)
			--ngx.say(key, ": ", value)  
		end  
	end
	return requestArgs;
end

function apiMapping.getLuaShared()
	local request_method = ngx.var.request_method;
	local url = ngx.var.uri;
	local args =  strSplit("/",url);
	local requestArgs = getRequestArgs();
	--ngx.log(ngx.ERR,"requestArgs:"..cjson.encode(requestArgs))
	local apiMappingShared = ngx.shared.apiMappingShared;
	local redisSwitchShared = ngx.shared.redisSwitchShared;
	local upstreamShared = ngx.shared.upstreamShared;
	
	if request_method ~= "GET" then
		ngx.say("此getLuaShared接口仅支持GET请求方式。")
		return
	end
	if #args>=4 then	
		ngx.say("getLuaShared接口查询格式不对!")
		return
	end
	--ngx.log(ngx.ERR,"args[3]:"..args[3])
	local tableTemp={};
	if args[3] == "redisSwitchShared" then
		if next(requestArgs) ~= nil then
			for key, value in pairs(requestArgs) do
				if key == "key" and value == "flag" then
					local v = redisSwitchShared:get("redisSwitch");
					tableTemp[value] = v;
				end
			end
		end
	end
	
	if args[3] == "apiMappingShared" then
		if next(requestArgs) ~= nil then
			for key, value in pairs(requestArgs) do
				if key == "key" and value == "all" then
					local keys = apiMappingShared:get_keys(1024);
					--ngx.log(ngx.ERR,"keys"..cjson.encode(keys));
					for index, key in pairs(keys) do
						local v = apiMappingShared:get(key);
						tableTemp[key]=v
					end
				else
					local v = apiMappingShared:get(value);
					tableTemp[value]=v;
					--ngx.say("查询apiMappingShared的key:"..value.."||".."value:"..cjson.encode(v))
				end
			end
		end
	end
	if args[3] == "upstreamShared" then
		if next(requestArgs) ~= nil then
			for key, value in pairs(requestArgs) do
				if key == "key" and value == "all" then
					local keys = upstreamShared:get_keys(1024);
					--ngx.log(ngx.ERR,"keys"..cjson.encode(keys));
					for index, key in pairs(keys) do
						local v = upstreamShared:get(key);
						tableTemp[key]=v
					end
				else
					local v = upstreamShared:get(value);
					tableTemp[value]=v;
					--ngx.say("查询apiMappingShared的key:"..value.."||".."value:"..cjson.encode(v))
				end
			end
		end
	end
	if next(tableTemp) ~= nil then
		ngx.say(cjson.encode(tableTemp));
		--ngx.log(ngx.ERR,"tableTemp:"..cjson.encode(tableTemp));
	else
		ngx.say("查询信息不存在~!");
	end
end

function parseJson(jsonString)
	if jsonString then
		--local cjson = require "cjson"
		--parse json
		local data= cjson.decode(jsonString);
		--ngx.say(data["appId"])
		return data;
	else
		return nil;
	end
end

function httpPostParamReaderByReloadApiMapping()--专门为apiMapping.reloadApiMapping()所用
	-- read post data
	--server中使用lua_need_request_body on; 或者在location lua代码块中使用 ngx.req.read_body(),日志中能获取$request_body
	ngx.req.read_body()
	local data = ngx.req.get_body_data()
	local postData = nil;
	local ret,backData=pcall(parseJson,data);
	if ret then
		postData = backData;
	else
		ngx.log(ngx.ERR,"reloadApiMapping JSON PARSE apiMapping post json data error.");
	end
	return postData;
end

function copyfile(source,destination)
	sourcefile = io.open(source,"r")
	destinationfile = io.open(destination,"w")
	for line in sourcefile:lines() do
		destinationfile:write(line)
	end
	sourcefile:close()
	destinationfile:close()
end

function printCaptureResult(code)
	local responseBody =ngx.location.capture("/httpStatusRewrite?apiRespCodeParam="..code);
	ngx.say(responseBody['body'])
	ngx.exit(ngx.HTTP_OK)
	--ngx.say(msg)
end

function routeLocation(apiSign)
	--local responseBody =ngx.location.capture("/springboot/"..apiSign);	
	local responseBody =ngx.location.capture("/springboot?cacheKey="..apiSign);
	--ngx.log(ngx.ERR,"this is routeLocation:"..apiSign);
	--ngx.log(ngx.ERR,"the responseBody.status is :"..responseBody.status);
	--ngx.log(ngx.ERR,"the responseBody.body is :"..responseBody.body);
	return responseBody.body
end

function getRedisClusterCompSign(apiSign)
	local redis_cluster = require "resty.rediscluster";
	local red = redis_cluster:new(ngx.shared.redisClusterConfig);
	--local KEY_NAME = "OPENAPI_NGINX_ROUTE_NODE_LAN";--内网
	--local KEY_NAME = "OPENAPI_NGINX_ROUTE_NODE_WAN";--外网
	--local res = red:init_pipeline()
	--ngx.log(ngx.ERR,"config.redis_keepalive_timeout is :"..red.config.keepalive_timeout)
	--ngx.log(ngx.ERR,"apiSign is :"..apiSign)
	--ngx.log(ngx.ERR,"KEY_NAME is :"..KEY_NAME)
	--red:set("forward_sbtpsboa_queryIPOsSecuInfo", "SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS")
	--red:set(apiSign, "SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS")
	--local results, err = red:get("forward_sbtpsboa_queryIPOsSecuInfo")
	--local results, err = red:get(apiSign)
	local networkSign = ngx.var.networkSign;
	--ngx.log(ngx.ERR,"networkSign is :"..networkSign)
	local results, err = red:hget(ngx.var.networkSign,apiSign)--networkSign在init.lua中定义
	--red:close()
	--local results, err = red:commit_pipeline()
	--local cjson = require "cjson"
	--ngx.log(ngx.ERR,"result is :"..cjson.encode(results))
	if not results then
        ngx.log(ngx.ERR,"failed to get apiSign: "..err)
        return
    end
	if type(results) == "userdata" then--redis get nil
        return nil
	end
	--ngx.log(ngx.ERR,"results is :"..results);
	return results
	
end

function apiMapping.setRedisSwitch()
	local redisSwitchShared = ngx.shared.redisSwitchShared;
	local requestArgs = getRequestArgs();
	--ngx.log(ngx.ERR,"requestArgs:"..cjson.encode(requestArgs))
	if next(requestArgs) ~= nil then
		for key, value in pairs(requestArgs) do
			if key == "flag" then
				redisSwitchShared:set("redisSwitch",value);
				ngx.say("设置查询redis开关:"..key.." = "..value)
			end
		end
	end
end

function apiMapping.reloadApiMapping()
	--local postBody = httpPostParamReader();
	local postBody = httpPostParamReaderByReloadApiMapping();
	local apiMappingShared = ngx.shared.apiMappingShared;
	if postBody then
		--apiMappingShared:flush_all();
		--ngx.log(ngx.NOTICE,"1:apiMappingShared.get.forward_kess_idVerify :"..apiMappingShared:get("forward_kess_idVerify"))
		local confJson = {};
		for apiSign, compSign in pairs(postBody) do
			--ngx.say(table2str(mapping))
			confJson[apiSign]=compSign;
			apiMappingShared:set(apiSign,cjson.encode(compSign));
			--ngx.log(ngx.NOTICE,"name:"..apiSign.."||".."value:"..cjson.encode(compSign));
			--ngx.log(ngx.NOTICE,"apiMappingShared.get.apiSign:".."||"..apiMappingShared:get("forward_xjb_updateAgmt"));	
		end
		--ngx.log(ngx.NOTICE,"2:apiMappingShared.get.forward_kess_idVerify :"..apiMappingShared:get("forward_kess_idVerify"))
		--ngx.log(ngx.NOTICE,"confJson:"..cjson.encode(confJson));
		local bakFile=apiMapping.mappingJsonFile..".bak";
		copyfile(apiMapping.mappingJsonFile,bakFile);
		local f=io.open(apiMapping.mappingJsonFile,"w+")
		f:write("{\n");
		if next(confJson) ~=nil then
			local idx =1;
			for key, value in pairs(confJson) do
				if idx>1 then
					f:write(",\n");
				end
				f:write("\""..key.."\":"..cjson.encode(value));
				--f:write("\""..key.."\":".."\""..value.."\"");
				f:flush()
				idx = idx+1;
			end
		end
		f:write("\n}");
		f:close();
		printCaptureResult(0);
		--ngx.location.capture("/httpStatusRewrite?apiRespCodeParam=0");
		--ngx.say("success");
	else
		printCaptureResult(-10000)
		--ngx.say("empty request data");
	end
end

function readGetTypeParam(source)
	local nSplitArray = {}
	local head = {}
	local body = {}
	for match in (source):gmatch("(.-)" .. "&" .. "()" ) do
		local equalIdx = string.find(match,"=");
		if equalIdx then
			local paramName = string.sub(match, 1, equalIdx-1) ;
			local paramValue = string.sub(match, equalIdx+1, string.len(match)) ;
			if paramName == "requestParam" then
				paramName = string.match(paramName,"%s*(.-)%s*$");
				if paramValue then
					paramValue = string.match(paramValue,"%s*(.-)%s*$");
				end
				--print("put param :"..paramName.."|"..paramValue)
				body[paramName] = paramValue;
			elseif paramName and  paramName ~= "requestParam" then
				paramName = string.match(paramName,"%s*(.-)%s*$");
				if paramValue then
					paramValue = string.match(paramValue,"%s*(.-)%s*$");
				end
				head[paramName] = paramValue;
			else
				return false,nil;
			end
		end
	end
	nSplitArray["head"] = head;
	nSplitArray["body"] = body;
	return true,nSplitArray;
end

function decodeURI(s)
	if s ~= nil then
		return string.gsub(s, '%%(%x%x)', function(h) return string.char(tonumber(h, 16)) end)
	else
		return nil
	end
end
--URL decode 转换
function escape(s)
	if s ~= nil then
		return string.gsub(s, "([^A-Za-z0-9_])", function(c) return string.format("%%%02x", string.byte(c)) end)
	else
		return nil
	end
end
--16进制转换为字符串
function hex2str(hex)
 local str = "";
 for i = 1, string.len(hex) do
     local charcode = tonumber(string.byte(hex, i, i));
     str = str .. string.format("%02X", charcode);
 end
 return str;
end

function httpPostParamReader()
	-- read post data
	--server中使用lua_need_request_body on; 或者在location lua代码块中使用 ngx.req.read_body(),日志中能获取$request_body
	ngx.req.read_body()
	local data = ngx.req.get_body_data()
	if data == nil then 
		printCaptureResult(-10000)
		--return 
	end
	data  = ngx.unescape_uri(data)
	--ngx.log(ngx.NOTICE,"data:"..data);
	local contentType = ngx.req.get_headers()["content-type"] ;
	local postData = nil;
	local resCode = nil;
	if contentType and string.find(contentType,"application/json") then
		--json reader
		--ngx.log(ngx.INFO,data);
		local ret,backData=pcall(parseJson,data);
		if ret then
			postData = backData;
		else
			printCaptureResult(-1006)
			--ngx.location.capture("/httpStatusRewrite?apiRespCodeParam=-1006");
			--ngx.say("{\"resCode\":\"-1006\",\"resMsg\":\"参数格式异常.\"}");
		end
	else
		-- array reader
		--demo :method=method=placeReservedOrder&openId=MACS&appId=7982088765&format=json&sign=7bb98a054081b48f404b0c5a3786d3d90ae95327&requestParam={"BRANCH":"3089","DIRECTION":"0","DUEDATE":"2017-07-11","F_OP_SRC":"X","HD_ID":"39737FD2-B467-4581-B322-561404F504D5","MAC_ADDR":"","MARKET":"00","ORDER_MODE":"3","PRICE":"0","QTY":"100","SECURITIESNAME":"平安银行","SECU_ACC":"0199171787","SECU_CODE":"000001","SECU_NAME":"","SERVERID":"10.25.175.117","STATE":"","TRD_ID":"0B","TRD_TERMCODE":"13433445656","USER_CODE":"150129599"}&timestamp=2017-07-11 18:54:37

		local code,backData = readGetTypeParam(data);
		if code then
		postData = backData;
		else
			printCaptureResult(-1006)
			--ngx.location.capture("/httpStatusRewrite?apiRespCodeParam=-1006");
			--ngx.say("{\"resCode\":\"-1006\",\"resMsg\":\"参数格式异常.\"}");
		end
	end
	return postData;
end
function endwith(str, substr)
        if str == nil or substr == nil then
            return nil, "the string or the sub-string parameter is nil"
        end
        str_tmp = string.reverse(str)
        substr_tmp = string.reverse(substr)
        if string.find(str_tmp, substr_tmp) ~= 1 then
            return false
        else
            return true
        end
end 

--切割字符串,返回字符串数组
function strSplit(delimeter, str)  
    local find, sub, insert = string.find, string.sub, table.insert  
    local res = {}  
    local start, start_pos, end_pos = 1, 1, 1  
    while true do  
        start_pos, end_pos = find(str, delimeter, start, true)  
        if not start_pos then  
            break  
        end  
        insert(res, sub(str, start, start_pos - 1))  
        start = end_pos + 1    
    end  
    insert(res, sub(str,start))  
    return res  
end
--desensitizeStr 备份,调用在log.lua中
function desensitizeStr(source,desensitizeKeyArray)
	for i=1,#desensitizeKeyArray do
		local regex = "(%A*)"..desensitizeKeyArray[i].."(%D*)".."(%d+)"
		local m = string.match(source, regex)
		if m then
			--source = string.gsub(source,regex,replaceStr(%5))
			source = string.gsub(source,regex,"%1"..desensitizeKeyArray[i].."%2".."***")
			--print("source:"..source)
			return source
			--break
		end
	end
end

function apiMapping.getCompSign()
	local request_method = ngx.var.request_method
	--获取参数的值
	if "GET" == request_method then
		--ngx.header.content_type = "application/json";
		printCaptureResult(-1013)
	end
	local postBody = httpPostParamReader();
	if postBody then
		--ngx.say("ngx.var.uri:"..ngx.var.uri);
		local reqUri = ngx.var.uri;
		if endwith(reqUri,"/") then 
			reqUri = string.sub(reqUri,1,string.len(reqUri)-1)
		end
		local apiSign = nil;
		if(string.find(reqUri, "/AuthApp/")==1) then 
			reqUri = string.sub(reqUri,9);
			ngx.var.reSetReqUri = string.sub(ngx.var.reSetReqUri,9);
		end
		if(string.find(reqUri, "/file/")==1) then 
			apiSign = string.gsub(string.sub(reqUri,7),"/","_") ;
		else 
			local apiSignArr = strSplit("/",reqUri);
			if #apiSignArr > 4 then
				--ngx.log(ngx.ERR,"apiSignArr:"..#apiSignArr.."  if apiSignArr.len >4 then not get request_body’s method");
				apiSign = string.gsub(string.sub(reqUri,10),"/","_") ;
			else
				--ngx.say("apiSign:"..apiSign);
				apiSign = string.gsub(string.sub(reqUri,10),"/","_") ;
				local head = postBody["head"];
				if(head ==nil) then 
					printCaptureResult(-1006)
				end	
				local method = head["method"];
				--ngx.say("method:"..cjson.encode(postBody));
				if method and string.match(method,"%s*(.-)%s*$") ~= "" then
					if not endwith(apiSign,"_"..method) then 
						apiSign = apiSign.."_" .. string.match(method,"%s*(.-)%s*$");
					end			
				end
			end	
			--ngx.log(ngx.ERR,"apiSign:"..apiSign);
			--ngx.say("apiSign:"..apiSign);
			local apiMappingConf = ngx.shared.apiMappingShared
			--local res = apiMappingConf:get(apiSign)
			--local res = routeLocation(apiSign)
			local resTable = {}
			local res = nil;
			local redisResultJson = nil;
			local localApiMappingJson = nil;
			--local localApiMappingJson = apiMappingConf:get("forward_sbtpsboa_queryIPOsOrderInfo");
			--local localApiMappingJsonComSign = cjson.decode(localApiMappingJson)["comSign"];
			--ngx.log(ngx.ERR,"localApiMappingJsonComSign.apiSign is :"..localApiMappingJsonComSign);
			--("forward_sbtpsboa_queryIPOsOrderInfo")
			--redisResultJson = getRedisClusterCompSign(apiSign);--返回一个json串
			local redisSwitch = ngx.shared.redisSwitchShared;
			local redisSwitchFlag =redisSwitch:get("redisSwitch")
			if redisSwitchFlag == "true" then
				local ret,backData = pcall(getRedisClusterCompSign,apiSign);
				if ret then
					redisResultJson = backData;
					--ngx.log(ngx.NOTICE,"redisResultJson is :"..redisResultJson);
				else
					ngx.log(ngx.ERR,"获取redis缓存数据失败!");
				end
			end
			if redisSwitchFlag == "true" and redisResultJson == nil then
				ngx.log(ngx.ERR,"查询缓存失败返回为空 !apiSign :"..apiSign);
			end
			--ngx.log(ngx.NOTICE,"redisResultJson is :"..redisResultJson);
			--redisResultJson = nil;--测试读取本地
			if redisResultJson ~= nil then
				ngx.var.redisResultJson = redisResultJson;
				--ngx.log(ngx.NOTICE,"ngx.var.redisResultJson is :"..ngx.var.redisResultJson);
				if cjson.decode(redisResultJson) ~= nil then
					resTable = cjson.decode(redisResultJson)--返回一个json串转为table
				else
					ngx.log(ngx.ERR,"redis 数据格式错误! ", err)
					return 
				end
			else--redis返回为空,做兜底操作(apiMapping.json)
				--local openId = postBody["openId"];
				--if openId then 
				--	apiSign = openId.."_"..apiSign.."_" .. string.match(openId,"%s*(.-)%s*$");
				--end
				localApiMappingJson = apiMappingConf:get(apiSign)
				--ngx.log(ngx.ERR,"localApiMappingJson is :"..localApiMappingJson);
				if localApiMappingJson == nil then
					ngx.log(ngx.ERR,"undefined api sign is :"..apiSign);
					printCaptureResult(-10201)
				end
				ngx.var.redisResultJson = localApiMappingJson;
				if cjson.decode(localApiMappingJson) ~= nil then--返回一个json串转为table
					local resTableTemp = cjson.decode(localApiMappingJson);
					if type(resTableTemp)=="string" then
						resTable = cjson.decode(resTableTemp)
					end
					if type(resTableTemp)=="table" then
						resTable = resTableTemp
					end
				else
					ngx.log(ngx.ERR,"this localApiMappingJson api sign has not set", err)
					return
				end
			end
			if next(resTable) ~= nil then
				--ngx.log(ngx.INFO,"resTable.comSign is :"..resTable["comSign"])
				res = resTable["comSign"]
			end
			--ngx.log(ngx.ERR,"apiSign is :"..apiSign);
			ngx.var.apiSign = apiSign;
			if res and res ~= nil then
				ngx.var.comp_sign = res;
				--ngx.say("res:"..res);
			else
				ngx.log(ngx.ERR,"undefined api sign is :"..apiSign);
				--ngx.say("res:"..apiSign);
				--ngx.exit(ngx.HTTP_OK)
				printCaptureResult(-10201)
			end	
		end
	end
end

return apiMapping;
logout.lua
logout = {};

function desensitizeStr(source,desensitizeKeyArray)--string,table
	if source == nil or source == "" then
		return nil
	end
	if desensitizeKeyArray == nil or desensitizeKeyArray == "" then
		return source
	end
	for i=1,#desensitizeKeyArray do
		local regex = "(%A*)"..desensitizeKeyArray[i].."(%D*)".."(%d+)"
		local m = string.match(source, regex)
		if m then
			--source = string.gsub(source,regex,replaceStr(%5))
			source = string.gsub(source,regex,"%1"..desensitizeKeyArray[i].."%2".."***")
			--print("source:"..source)
			return source
			--break
		end
	end
	return source--执行到这里表示没有一次做string.gsub(没有一次做正则替换)
end
--ngx.log(ngx.NOTICE,"request_body is :"..ngx.var.request_body)
--local source = "pwd\":\"12333123ad\"}\"apwd\":\"1233sd3123\"pwd:123321,adpwd1233fgfsdgs"

function parseJson(jsonString)
	if jsonString then
		--local cjson = require "cjson"
		--parse json
		local data= cjson.decode(jsonString);
		--ngx.say(data["appId"])
		return data;
	else
		return nil;
	end
end

-- 删除table中的元素  
function removeElementByKey(sourceJson,key) 
	if sourceJson == nil then
		return nil
	end
	if key == nil then
		return sourceJson
	end
	local tempTable = nil;
	local ret,backData=pcall(parseJson,sourceJson);
	if ret then
		tempTable = backData;
	else
		--处理请求和响应信息中的超大字段request_body&resp_body
		--ngx.log(ngx.ERR,"{\"resCode\":\"-1007\",\"resMsg\":\"json转换失败或者数据量过大无法转换成json\"}");
		return "{\"desc\":\"big_request_shortcut\"}"
	end
	if tempTable == nil then
		return nil
	end
    --新建一个临时的table  
    local tmp ={}  
  
    --把每个key做一个下标,保存到临时的table中,转换成{1=a,2=c,3=b}   
    --组成一个有顺序的table,才能在while循环准备时使用#table  
    for i in pairs(tempTable) do  
        table.insert(tmp,i)  
    end  
  
    local newTbl = {}  
    --使用while循环剔除不需要的元素  
    local i = 1  
    while i <= #tmp do  
        local val = tmp [i]  
        if val == key then  
            --如果是需要剔除则remove   
            table.remove(tmp,i)  
         else  
            --如果不是剔除,放入新的tabl中  
            newTbl[val] = tempTable[val]  
            i = i + 1  
         end  
     end  
	return cjson.encode(newTbl)
    --return newTbl  
end  

function replaceJson(jsonString,replaceKey,replaceValue)
	if jsonString == nil or jsonString == "" then
		return "{\"resCode\":\"-499\",\"resMsg\":\"Client Closed Request\"}";
	end
	if replaceKey == nil or replaceValue == nil then
		return jsonString
	end
	local tempTable = nil;
	local ret,backData=pcall(parseJson,jsonString);
	if ret then
		tempTable = backData;
	else
		ngx.log(ngx.ERR,"{\"resCode\":\"-1006\",\"resMsg\":\"参数格式异常。\"}");
	end
	if tempTable == nil then
		return nil
	end
	if tempTable[replaceKey] == nil then
		return jsonString
	end
	--处理请求和响应信息中的超大字段request_body&resp_body
	if ngx.var.apiSign == "forward_soas_uploadIdImg" and replaceKey == "request_body" then
		tempTable[replaceKey] = "{\"desc\":\"big_request_shortcut\"}"
	else
		tempTable[replaceKey] = replaceValue
	end
	return cjson.encode(tempTable)
	--return jsonString
end

function getJsonValueToTable(jsonString,stringKey)--返回table
	if jsonString == nil or jsonString == "" then
		return nil
	end
	if stringKey == nil then
		return jsonString
	end
	local tempTable = nil;
	local ret,backData=pcall(parseJson,jsonString);
	if ret then
		tempTable = backData;
	else
		--printCaptureResult(-1006)
		--ngx.location.capture("/httpStatusRewrite?apiRespCodeParam=-1006");
		ngx.log(ngx.ERR,"{\"resCode\":\"-1006\",\"resMsg\":\"参数格式异常。\"}");
		return nil
	end
		
	if type(tempTable)=="string" then
		local ret,backData=pcall(parseJson,tempTable);
		if ret then
			tempTable = backData;
		else
			ngx.log(ngx.ERR,"{\"resCode\":\"-1006\",\"resMsg\":\"参数格式异常。\"}");
			return nil
		end
	end
	if tempTable == nil then
		return nil
	end
	if tempTable[stringKey] == nil then
		return nil
	end
	if type(tempTable[stringKey]) =="string" then
		local ret,backData=pcall(parseJson,tempTable[stringKey]);
		if ret then
			return backData;
		else
			ngx.log(ngx.ERR,"{\"resCode\":\"-1006\",\"resMsg\":\"参数格式异常。\"}");
			return nil
		end
	end
	if type(tempTable[stringKey]) =="table" then
		return tempTable[stringKey]
	end
end

if(ngx.var.uri ~= "/metrics") then
	local host = ngx.var.host:gsub("^www.", "")
	local upstream_addr = ngx.var.upstream_addr
	if(upstream_addr == nil or upstream_addr == "") then
		upstream_addr = "localhost"
	end
	metric_requests:inc(1, {host, upstream_addr, ngx.var.status})
	metric_latency:observe(ngx.now() - ngx.req.start_time(), {upstream_addr})
	metric_bytes:observe(tonumber(ngx.var.bytes_sent)/1024,{upstream_addr})
end

function logout.run()
	local source = ngx.unescape_uri(ngx.var.request_body)--获取到request_body的字符串
	--local responseSrc = ngx.var.resp_body--获取到resp_body的字符串
	local responseDesc = removeElementByKey(ngx.var.resp_body,"resBody")
	--ngx.log(ngx.NOTICE,"responseDesc is :"..responseDesc)
	--ngx.log(ngx.NOTICE,"redisResultJson is :"..ngx.var.redisResultJson)
	local desensitizeKeyArray = getJsonValueToTable(ngx.var.redisResultJson,"desensitizeKeyArray")--获取到desensitizeKeyArray的table
	source = desensitizeStr(source,desensitizeKeyArray)--替换后的request_body字符串
	--ngx.log(ngx.NOTICE,"source is :"..source)
	--ngx.log(ngx.NOTICE,"before replace resp_map is :"..ngx.var.resp_map)
	ngx.var.resp_map = replaceJson(ngx.var.resp_map,"request_body",source)
	ngx.var.resp_map = replaceJson(ngx.var.resp_map,"resp_body",responseDesc)
	--ngx.log(ngx.NOTICE,"after replace resp_map is :"..ngx.var.resp_map)
	ngx.log(ngx.NOTICE,"resp_map"..ngx.var.resp_map)
end

return logout;
prometheus.lua
-- vim: ts=2:sw=2:sts=2:expandtab
--
-- This module uses a single dictionary shared between Nginx workers to keep
-- all metrics. Each counter is stored as a separate entry in that dictionary,
-- which allows us to increment them using built-in `incr` method.
--
-- Prometheus requires that (a) all samples for a given metric are presented
-- as one uninterrupted group, and (b) buckets of a histogram appear in
-- increasing numerical order. We satisfy that by carefully constructing full
-- metric names (i.e. metric name along with all labels) so that they meet
-- those requirements while being sorted alphabetically. In particular:
--
--  * all labels for a given metric are presented in reproducible order (the one
--    used when labels were declared). "le" label for histogram metrics always
--    goes last;
--  * bucket boundaries (which are exposed as values of the "le" label) are
--    presented as floating point numbers with leading and trailing zeroes.
--    Number of of zeroes is determined for each bucketer automatically based on
--    bucket boundaries;
--  * internally "+Inf" bucket is stored as "Inf" (to make it appear after
--    all numeric buckets), and gets replaced by "+Inf" just before we
--    expose the metrics.
--
-- For example, if you define your bucket boundaries as {0.00005, 10, 1000}
-- then we will keep the following samples for a metric `m1` with label
-- `site` set to `site1`:
--
--   m1_bucket{site="site1",le="0000.00005"}
--   m1_bucket{site="site1",le="0010.00000"}
--   m1_bucket{site="site1",le="1000.00000"}
--   m1_bucket{site="site1",le="Inf"}
--   m1_count{site="site1"}
--   m1_sum{site="site1"}
--
-- "Inf" will be replaced by "+Inf" while publishing metrics.
--
-- You can find the latest version and documentation at
-- https://github.com/knyar/nginx-lua-prometheus
-- Released under MIT license.


-- Default set of latency buckets, 5ms to 10s:
local DEFAULT_BUCKETS = {0.005, 0.01, 0.02, 0.03, 0.05, 0.075, 0.1, 0.2, 0.3,
                         0.4, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 10}

-- Metric is a "parent class" for all metrics.
local Metric = {}
function Metric:new(o)
  o = o or {}
  setmetatable(o, self)
  self.__index = self
  return o
end

-- Checks that the right number of labels values have been passed.
--
-- Args:
--   label_values: an array of label values.
--
-- Returns:
--   an error message or nil
function Metric:check_label_values(label_values)
  if self.label_names == nil and label_values == nil then
    return
  elseif self.label_names == nil and label_values ~= nil then
    return "Expected no labels for " .. self.name .. ", got " ..  #label_values
  elseif label_values == nil and self.label_names ~= nil then
    return "Expected " .. #self.label_names .. " labels for " ..
           self.name .. ", got none"
  elseif #self.label_names ~= #label_values then
    return "Wrong number of labels for " .. self.name .. ". Expected " ..
           #self.label_names .. ", got " .. #label_values
  else
    for i, k in ipairs(self.label_names) do
      if label_values[i] == nil then
        return "Unexpected nil value for label " .. k ..  " of " .. self.name
      end
    end
  end
end

local Counter = Metric:new()
-- Increase a given counter by `value`
--
-- Args:
--   value: (number) a value to add to the counter. Defaults to 1 if skipped.
--   label_values: an array of label values. Can be nil (i.e. not defined) for
--     metrics that have no labels.
function Counter:inc(value, label_values)
  local err = self:check_label_values(label_values)
  if err ~= nil then
    self.prometheus:log_error(err)
    return
  end
  self.prometheus:inc(self.name, self.label_names, label_values, value or 1)
end

local Gauge = Metric:new()
-- Set a given gauge to `value`
--
-- Args:
--   value: (number) a value to set the gauge to. Should be defined.
--   label_values: an array of label values. Can be nil (i.e. not defined) for
--     metrics that have no labels.
function Gauge:set(value, label_values)
  if value == nil then
    self.prometheus:log_error("No value passed for " .. self.name)
    return
  end
  local err = self:check_label_values(label_values)
  if err ~= nil then
    self.prometheus:log_error(err)
    return
  end
  self.prometheus:set(self.name, self.label_names, label_values, value)
end

local Histogram = Metric:new()
-- Record a given value in a histogram.
--
-- Args:
--   value: (number) a value to record. Should be defined.
--   label_values: an array of label values. Can be nil (i.e. not defined) for
--     metrics that have no labels.
function Histogram:observe(value, label_values)
  if value == nil then
    self.prometheus:log_error("No value passed for " .. self.name)
    return
  end
  local err = self:check_label_values(label_values)
  if err ~= nil then
    self.prometheus:log_error(err)
    return
  end
  self.prometheus:histogram_observe(self.name, self.label_names, label_values, value)
end

local Prometheus = {}
Prometheus.__index = Prometheus
Prometheus.initialized = false

-- Generate full metric name that includes all labels.
--
-- Args:
--   name: string
--   label_names: (array) a list of label keys.
--   label_values: (array) a list of label values.
-- Returns:
--   (string) full metric name.
local function full_metric_name(name, label_names, label_values)
  if not label_names then
    return name
  end
  local label_parts = {}
  for idx, key in ipairs(label_names) do
    local label_value = (string.format("%s", label_values[idx])
      :gsub("[^\032-\126]", "")  -- strip non-printable characters
      :gsub("\\", "\\\\")
      :gsub('"', '\\"'))
    table.insert(label_parts, key .. '="' .. label_value .. '"')
  end
  return name .. "{" .. table.concat(label_parts, ",") .. "}"
end

-- Construct bucket format for a list of buckets.
--
-- This receives a list of buckets and returns a sprintf template that should
-- be used for bucket boundaries to make them come in increasing order when
-- sorted alphabetically.
--
-- To re-phrase, this is where we detect how many leading and trailing zeros we
-- need.
--
-- Args:
--   buckets: a list of buckets
--
-- Returns:
--   (string) a sprintf template.
local function construct_bucket_format(buckets)
  local max_order = 1
  local max_precision = 1
  for _, bucket in ipairs(buckets) do
    assert(type(bucket) == "number", "bucket boundaries should be numeric")
    -- floating point number with all trailing zeros removed
    local as_string = string.format("%f", bucket):gsub("0*$", "")
    local dot_idx = as_string:find(".", 1, true)
    max_order = math.max(max_order, dot_idx - 1)
    max_precision = math.max(max_precision, as_string:len() - dot_idx)
  end
  return "%0" .. (max_order + max_precision + 1) .. "." .. max_precision .. "f"
end

-- Extract short metric name from the full one.
--
-- Args:
--   full_name: (string) full metric name that can include labels.
--
-- Returns:
--   (string) short metric name with no labels. For a `*_bucket` metric of
--     histogram the _bucket suffix will be removed.
local function short_metric_name(full_name)
  local labels_start, _ = full_name:find("{")
  if not labels_start then
    -- no labels
    return full_name
  end
  local suffix_idx, _ = full_name:find("_bucket{")
  if suffix_idx and full_name:find("le=") then
    -- this is a histogram metric
    return full_name:sub(1, suffix_idx - 1)
  end
  -- this is not a histogram metric
  return full_name:sub(1, labels_start - 1)
end

-- Makes a shallow copy of a table
local function copy_table(table)
  local new = {}
  if table ~= nil then
    for k, v in ipairs(table) do
      new[k] = v
    end
  end
  return new
end

-- Check metric name and label names for correctness.
--
-- Regular expressions to validate metric and label names are
-- documented in https://prometheus.io/docs/concepts/data_model/
--
-- Args:
--   metric_name: (string) metric name.
--   label_names: label names (array of strings).
--
-- Returns:
--   Either an error string, or nil of no errors were found.
local function check_metric_and_label_names(metric_name, label_names)
  if not metric_name:match("^[a-zA-Z_:][a-zA-Z0-9_:]*$") then
    return "Metric name '" .. metric_name ..
           "' contains invalid characters"
  end
  for _, label_name in ipairs(label_names or {}) do
    if label_name == "le" then
      return "Invalid label name 'le' in " .. metric_name
    end
    if not label_name:match("^[a-zA-Z_][a-zA-Z0-9_]*$") then
      return "Metric '" .. metric_name .. "' label name '" .. label_name ..
             "' contains invalid characters"
    end
  end
end

-- Initialize the module.
--
-- This should be called once from the `init_by_lua` section in nginx
-- configuration.
--
-- Args:
--   dict_name: (string) name of the nginx shared dictionary which will be
--     used to store all metrics
--   prefix: (optional string) if supplied, prefix is added to all
--   metric names on output
--
-- Returns:
--   an object that should be used to register metrics.
function Prometheus.init(dict_name, prefix)
  local self = setmetatable({}, Prometheus)
  self.dict = ngx.shared[dict_name or "prometheus_metrics"]
  self.help = {}
  if prefix then
    self.prefix = prefix
  else
    self.prefix = ''
  end
  self.type = {}
  self.registered = {}
  self.buckets = {}
  self.bucket_format = {}
  self.initialized = true

  self:counter("nginx_metric_errors_total",
    "Number of nginx-lua-prometheus errors")
  self.dict:set("nginx_metric_errors_total", 0)
  return self
end

function Prometheus:log_error(...)
  ngx.log(ngx.ERR, ...)
  self.dict:incr("nginx_metric_errors_total", 1)
end

function Prometheus:log_error_kv(key, value, err)
  self:log_error(
    "Error while setting '", key, "' to '", value, "': '", err, "'")
end

-- Register a counter.
--
-- Args:
--   name: (string) name of the metric. Required.
--   description: (string) description of the metric. Will be used for the HELP
--     comment on the metrics page. Optional.
--   label_names: array of strings, defining a list of metrics. Optional.
--
-- Returns:
--   a Counter object.
function Prometheus:counter(name, description, label_names)
  if not self.initialized then
    ngx.log(ngx.ERR, "Prometheus module has not been initialized")
    return
  end

  local err = check_metric_and_label_names(name, label_names)
  if err ~= nil then
    self:log_error(err)
    return
  end

  if self.registered[name] then
    self:log_error("Duplicate metric " .. name)
    return
  end
  self.registered[name] = true
  self.help[name] = description
  self.type[name] = "counter"

  return Counter:new{name=name, label_names=label_names, prometheus=self}
end

-- Register a gauge.
--
-- Args:
--   name: (string) name of the metric. Required.
--   description: (string) description of the metric. Will be used for the HELP
--     comment on the metrics page. Optional.
--   label_names: array of strings, defining a list of metrics. Optional.
--
-- Returns:
--   a Gauge object.
function Prometheus:gauge(name, description, label_names)
  if not self.initialized then
    ngx.log(ngx.ERR, "Prometheus module has not been initialized")
    return
  end

  local err = check_metric_and_label_names(name, label_names)
  if err ~= nil then
    self:log_error(err)
    return
  end

  if self.registered[name] then
    self:log_error("Duplicate metric " .. name)
    return
  end
  self.registered[name] = true
  self.help[name] = description
  self.type[name] = "gauge"

  return Gauge:new{name=name, label_names=label_names, prometheus=self}
end


-- Register a histogram.
--
-- Args:
--   name: (string) name of the metric. Required.
--   description: (string) description of the metric. Will be used for the HELP
--     comment on the metrics page. Optional.
--   label_names: array of strings, defining a list of metrics. Optional.
--   buckets: array if numbers, defining bucket boundaries. Optional.
--
-- Returns:
--   a Histogram object.
function Prometheus:histogram(name, description, label_names, buckets)
  if not self.initialized then
    ngx.log(ngx.ERR, "Prometheus module has not been initialized")
    return
  end

  local err = check_metric_and_label_names(name, label_names)
  if err ~= nil then
    self:log_error(err)
    return
  end

  for _, suffix in ipairs({"", "_bucket", "_count", "_sum"}) do
    if self.registered[name .. suffix] then
      self:log_error("Duplicate metric " .. name .. suffix)
      return
    end
    self.registered[name .. suffix] = true
  end
  self.help[name] = description
  self.type[name] = "histogram"

  self.buckets[name] = buckets or DEFAULT_BUCKETS
  self.bucket_format[name] = construct_bucket_format(self.buckets[name])

  return Histogram:new{name=name, label_names=label_names, prometheus=self}
end

-- Set a given dictionary key.
-- This overwrites existing values, so it should only be used when initializing
-- metrics or when explicitely overwriting the previous value of a metric.
function Prometheus:set_key(key, value)
  local ok, err = self.dict:safe_set(key, value)
  if not ok then
    self:log_error_kv(key, value, err)
  end
end

-- Increment a given counter by `value`.
--
-- Args:
--   name: (string) short metric name without any labels.
--   label_names: (array) a list of label keys.
--   label_values: (array) a list of label values.
--   value: (number) value to add. Optional, defaults to 1.
function Prometheus:inc(name, label_names, label_values, value)
  local key = full_metric_name(name, label_names, label_values)
  if value == nil then value = 1 end
  if value < 0 then
    self:log_error_kv(key, value, "Value should not be negative")
    return
  end

  local newval, err = self.dict:incr(key, value)
  if newval then
    return
  end
  -- Yes, this looks like a race, so I guess we might under-report some values
  -- when multiple workers simultaneously try to create the same metric.
  -- Hopefully this does not happen too often (shared dictionary does not get
  -- reset during configuation reload).
  if err == "not found" then
    self:set_key(key, value)
    return
  end
  -- Unexpected error
  self:log_error_kv(key, value, err)
end

-- Set the current value of a gauge to `value`
--
-- Args:
--   name: (string) short metric name without any labels.
--   label_names: (array) a list of label keys.
--   label_values: (array) a list of label values.
--   value: (number) the new value for the gauge.
function Prometheus:set(name, label_names, label_values, value)
  local key = full_metric_name(name, label_names, label_values)
  self:set_key(key, value)
end

-- Record a given value into a histogram metric.
--
-- Args:
--   name: (string) short metric name without any labels.
--   label_names: (array) a list of label keys.
--   label_values: (array) a list of label values.
--   value: (number) value to observe.
function Prometheus:histogram_observe(name, label_names, label_values, value)
  self:inc(name .. "_count", label_names, label_values, 1)
  self:inc(name .. "_sum", label_names, label_values, value)

  -- we are going to mutate arrays of label names and values, so create a copy.
  local l_names = copy_table(label_names)
  local l_values = copy_table(label_values)

  -- Last bucket. Note, that the label value is "Inf" rather than "+Inf"
  -- required by Prometheus. This is necessary for this bucket to be the last
  -- one when all metrics are lexicographically sorted. "Inf" will get replaced
  -- by "+Inf" in Prometheus:collect().
  table.insert(l_names, "le")
  table.insert(l_values, "Inf")
  self:inc(name .. "_bucket", l_names, l_values, 1)

  local label_count = #l_names
  for _, bucket in ipairs(self.buckets[name]) do
    if value <= bucket then
      -- last label is now "le"
      l_values[label_count] = self.bucket_format[name]:format(bucket)
      self:inc(name .. "_bucket", l_names, l_values, 1)
    end
  end
end

-- Present all metrics in a text format compatible with Prometheus.
--
-- This function should be used to expose the metrics on a separate HTTP page.
-- It will get the metrics from the dictionary, sort them, and expose them
-- aling with TYPE and HELP comments.
function Prometheus:collect()
  ngx.header.content_type = "text/plain"
  if not self.initialized then
    ngx.log(ngx.ERR, "Prometheus module has not been initialized")
    return
  end

  local keys = self.dict:get_keys(0)
  -- Prometheus server expects buckets of a histogram to appear in increasing
  -- numerical order of their label values.
  table.sort(keys)

  local seen_metrics = {}
  for _, key in ipairs(keys) do
    local value, err = self.dict:get(key)
    if value then
      local short_name = short_metric_name(key)
      if not seen_metrics[short_name] then
        if self.help[short_name] then
          ngx.say("# HELP " .. self.prefix .. short_name .. " " .. self.help[short_name])
        end
        if self.type[short_name] then
          ngx.say("# TYPE " .. self.prefix .. short_name .. " " .. self.type[short_name])
        end
        seen_metrics[short_name] = true
      end
      -- Replace "Inf" with "+Inf" in each metric's last bucket 'le' label.
      ngx.say(self.prefix .. key:gsub('le="Inf"', 'le="+Inf"'), " ", value)
    else
      self:log_error("Error getting '", key, "': ", err)
    end
  end
end

return Prometheus
request.lua
request = {};

function request.run()
	local resp_body = string.sub(ngx.arg[1], 1, 1000)
	ngx.ctx.buffered = (ngx.ctx.buffered or"") .. resp_body
	if ngx.arg[2] then
		ngx.var.resp_body = ngx.ctx.buffered
		
		local map = {}
		map["logTraceId"] = ngx.var.request_id;
		map["remote_addr"] = ngx.var.remote_addr;
		map["remote_user"] = ngx.var.remote_user;

		map["time_local"] = ngx.unescape_uri(ngx.var.time_local);
		map["request_method"] = ngx.var.request_method;
		map["apiSign"] = ngx.var.apiSign;
		map["comp_sign"] = ngx.var.comp_sign;
		map["request"] = ngx.unescape_uri(ngx.var.request);

		map["status"] = ngx.var.status;
		map["body_bytes_sent"] = ngx.var.body_bytes_sent;
		map["http_referer"] = ngx.var.http_referer;
		map["http_user_agent"] = ngx.var.http_user_agent;
		map["http_x_forwarded_for"] = ngx.var.http_x_forwarded_for;

		map["request_body"] = ngx.unescape_uri(ngx.var.request_body);

		map["resp_body"] = ngx.unescape_uri(ngx.var.resp_body);

		map["upstream_addr"] = ngx.var.upstream_addr;
		map["bytes_sent"] = ngx.var.bytes_sent;
		map["request_length"] = ngx.var.request_length;
		map["upstream_response_time"] = ngx.var.upstream_response_time;
		map["request_time"] = ngx.var.request_time;
		ngx.var.resp_map = cjson.encode(map);
	end
end

return request;
upstreamdynamic.lua
upstreamdynamic = {};

function upstreamdynamic.init()
	--upstream = require "ngx.upstream"
	--get_servers = upstream.get_servers
	--get_upstreams = upstream.get_upstreams
	--us = get_upstreams()
	local upstreamShared = ngx.shared.upstreamShared;
	upstreamShared:flush_all();
	upstreamFile = SCRIPT_ROOT_PATH.."../../servers/upstream.com";
	local file = io.open(upstreamFile, "r");
	local data = file:read("*all");
	upstreamToShared(data);
end

function upstreamToShared(data)
	local upstreamShared = ngx.shared.upstreamShared;
	--ngx.log(ngx.ERR,"data:"..data);
	local tableTemp = strSplit("\n", data);
	--ngx.log(ngx.ERR,"tableTemp:"..cjson.encode(tableTemp));
	local value = "";
	local key = "";
	local flag = true;
	for _, temp in ipairs(tableTemp) do
		if flag then
			flag = false;
			if string.find(temp,"upstream") then
				local i,j = string.find(temp,"upstream");
				key = trim(string.sub(temp,j+1, string.len(temp)));
			end
		else
			if string.find(temp,"upstream") then
				local i,j = string.find(temp,"upstream");
				key = trim(string.sub(temp,j+1, string.len(temp)));
			elseif string.find(temp,"{") then
				value= value..trim(temp).."\n";
			elseif string.find(temp,";") then
				value= value..trim(temp).."\n";
			elseif string.find(temp,"}") then
				value= value..trim(temp);
				--ngx.log(ngx.ERR,"key:"..key.."||".."value:"..value);
				upstreamShared:set(key,value);
				--ngx.log(ngx.ERR,"upstreamShared.get."..key..":"..upstreamShared:get(key));
				value = "";
				flag = true;
			else
				value= value..trim(temp);
			end
		end
	end
end


function line_break_format(data)--shard按换行符切割\n
	--ngx.log(ngx.ERR,"data :"..data);
	local dataTemp = nil;
	if endwith(data,"\n") then
		data = string.sub(data,1,string.len(data)-1)
	end
	dataTemp = strSplit("\n",data);
	--ngx.log(ngx.ERR,"dataTemp :"..cjson.encode(dataTemp));
	return dataTemp;
end

function semi_colon_format(data)--request_body按分号切割;
	--ngx.log(ngx.ERR,"data :"..data);
	local dataTemp = nil;
	if endwith(data,";") then
		data = string.sub(data,1,string.len(data)-1)
		dataTemp = strSplit(";",data);
	else
		ngx.say("格式检查错误,必须已分号结尾!")
		return
	end
	--ngx.log(ngx.ERR,"dataTemp :"..cjson.encode(dataTemp));
	return dataTemp;
end

function reconstruction_nginx_upstreamDetails(data)
	--ngx.log(ngx.ERR,"reconstruction_nginx_upstreamDetails.data:"..data);
	local dataTemp = strSplit("\n",data);
	--ngx.log(ngx.ERR,"tableTemp:"..cjson.encode(dataTemp));
	local upstreamStr_ip_hash = "";
	local upstreamStr_keepalive = "";
	local upstreamStr_backup = "";
	local upstreamStr_down = "";
	local upstreamStr = "";
	local upstreamName = "";
	local upstreamBraceL = "";
	local upstreamBraceR = "";
	local upstreamServerTemp = "";
	local flag = true;
	for _, temp in ipairs(dataTemp) do
		--ngx.log(ngx.ERR,"reconstruction_nginx_upstreamDetails.tableTemp.temp:"..temp);
		if flag then
			flag = false;
			if string.find(temp,"upstream") then
				local i,j = string.find(temp,"upstream");
				upstreamName = "upstream ".. trim(string.sub(temp,j+1, string.len(temp))).."\n";
				local upstreamSharedValue = getUpstreamFromShared(trim(string.sub(temp,j+1, string.len(temp))));
				local upstreamSharedTemp = line_break_format(upstreamSharedValue)
				for _, temp in ipairs(upstreamSharedTemp) do--shared里面是按分号结尾,换行
					if string.find(temp,"ip_hash") then
						local i,j = string.find(temp,"ip_hash");
						upstreamStr_ip_hash = upstreamStr_ip_hash.."     "..trim(string.sub(temp,i, string.len(temp))).."\n";
					end
					if string.find(temp,"keepalive") then
						local i,j = string.find(temp,"keepalive");
						upstreamStr_keepalive = upstreamStr_keepalive.."     "..trim(string.sub(temp,i, string.len(temp))).."\n";
					end
				end
			end
		else
			if string.find(temp,"upstream") then
				local i,j = string.find(temp,"upstream");
				upstreamName = "upstream ".. trim(string.sub(temp,j+1, string.len(temp))).."\n";
			elseif string.find(temp,"{") then
				upstreamBraceL = upstreamBraceL..trim(temp).."\n";
			elseif string.find(temp,"}") then
				upstreamBraceR = upstreamBraceR..trim(temp).."\n";
				--ngx.log(ngx.ERR,"reconstruction_nginx_upstreamDetails.upstreamTemp:"..upstreamTemp);
				upstreamStr = upstreamStr..upstreamName..upstreamBraceL..upstreamServerTemp..upstreamStr_ip_hash..upstreamStr_keepalive..upstreamBraceR;
				------重置-----
				upstreamBraceR = "";
				upstreamBraceL = "";
				upstreamServerTemp = "";
				upstreamStr_ip_hash = "";
				upstreamStr_keepalive = "";
				flag = true;
			elseif string.find(temp,"server") then
				if string.find(temp,"backup=1 down=0") then
					local i,j = string.find(temp,"backup=1 down=0");--backup 和down同时只生效一个
					upstreamServerTemp = upstreamServerTemp.."     "..trim(string.sub(temp,1,i-1)).." backup;\n";--手动处理成和文件格式一样				
					--ngx.log(ngx.ERR,"upstreamServerTemp"..upstreamServerTemp)
				end
				if string.find(temp,"backup=0 down=1") then
					local i,j = string.find(temp,"backup=0 down=1");
					upstreamServerTemp = upstreamServerTemp.."     "..trim(string.sub(temp,1,i-1)).." down;\n";
				end
				if string.find(temp,"backup=0 down=0") then
					local i,j = string.find(temp,"backup=0 down=0");
					upstreamServerTemp = upstreamServerTemp.."     "..trim(string.sub(temp,1,i-1))..";\n";
				end
			else
				upstreamServerTemp =  upstreamServerTemp.."";
			end
		end
	end
	return upstreamStr;
end
--切割字符串,返回字符串数组
function strSplit(delimeter, str)  
    local find, sub, insert = string.find, string.sub, table.insert  
    local res = {}  
    local start, start_pos, end_pos = 1, 1, 1  
    while true do  
        start_pos, end_pos = find(str, delimeter, start, true)  
        if not start_pos then  
            break  
        end  
        insert(res, sub(str, start, start_pos - 1))  
        start = end_pos + 1    
    end  
    insert(res, sub(str,start))  
    return res  
end

function trim(s) 
	return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
end  
function parseJson(jsonString)
	if jsonString then
		--local cjson = require "cjson"
		--parse json
		local data= cjson.decode(jsonString);
		--ngx.say(data["appId"])
		return data;
	else
		return nil;
	end
end

function getUpstreamFromShared(upstreamName)
	local upstreamShared = ngx.shared.upstreamShared;
	return upstreamShared:get(upstreamName);
end

function setUpstreamFromShared(upstreamName,upstreamValue)
	local upstreamShared = ngx.shared.upstreamShared;
	upstreamShared:set(upstreamName,upstreamValue);
end

function removeUpstreamFromShared(upstreamName)
	local upstreamShared = ngx.shared.upstreamShared;
	upstreamShared:delete(upstreamName);
end

function sharedBackupToFile()
	local upstreamShared = ngx.shared.upstreamShared;
	local file = io.open(upstreamFile, "w+");
	local keys = upstreamShared:get_keys(100);
	--ngx.log(ngx.ERR,"keys"..cjson.encode(keys));
	for index, key in pairs(keys) do
		local value = upstreamShared:get(key);
		--ngx.log(ngx.ERR,"key:"..key.."||".."value:"..value);
		file:write("upstream "..key.."\n"..value.."\n");
	end
	file:flush();
	file:close();
end

----暂时没有用到
function upstreamDetails()
	--local upstream = require "ngx.upstream"
	--local get_servers = upstream.get_servers
	--local get_upstreams = upstream.get_upstreams
	--local us = get_upstreams()
	--ngx.say("us :"..cjson.encode(us));
	local upstreamStr = "";
    for _, u in ipairs(us) do
		--local peers = upstream.get_primary_peers(u);
		--ngx.log(ngx.ERR,cjson.encode(peers));
		if u == "_dyups_upstream_down_host_" then--暂时没搞明白这个是弄啥子的?
			upstreamStr = upstreamStr.."";
		else
			local upstream = "upstream ".. u.."\n".."{".."\n";
			local srvs, err = get_servers(u)
			if not srvs then
				ngx.say("failed to get servers in upstream ", u)
			else
				--ngx.say("srvs :"..cjson.encode(srvs));
				for _, srv in ipairs(srvs) do
					local first = true
					--ngx.log(ngx.ERR,"srv :"..cjson.encode(srv));
					local upstreamTemp = "";
					local upstreamServer = "";
					local upstreamServer_backup = "";
					local upstreamServer_down = "";
					local upstreamServer_weight = "";
					local upstreamServer_fail_timeout = "";
					local upstreamServer_max_fails = "";
					for k, v in pairs(srv) do
						if first then
							first = false
							local upstreamTemp = "";
							--upstreamTemp = upstreamTemp.."    ";
							upstreamTemp = upstreamTemp.." ";
						else
							--upstreamTemp = upstreamTemp..",";
							upstreamTemp = upstreamTemp.." ";
						end
						if type(v) == "table" then
							--ngx.say("v :"..cjson.encode(v));
							upstreamTemp = upstreamTemp.."{", concat(v, ", "), "}"
						else
							if k == "name" then
								upstreamServer = upstreamServer.."server".."   "..v.."  "--cjson.encode(v)
							elseif k == "addr" then
								upstreamTemp = upstreamTemp.."";
							elseif k == "ip_hash" then
								ngx.log(ngx.ERR,"k:"..k.." || ".."v:"..cjson.encode(v));
								upstreamTemp = upstreamTemp..k..";".."\n";
							elseif k == "keepalive" then
								ngx.log(ngx.ERR,"k:"..k.." || ".."v:"..cjson.encode(v));
								upstreamTemp = upstreamTemp..k.."   "..v..";".."\n";
							elseif k == "down" then
								upstreamServer_down = upstreamServer_down..k.."  "
							elseif k == "backup" then
								upstreamServer_backup = upstreamServer_backup..k.."  "
							elseif k == "weight" then
								upstreamServer_weight = upstreamServer_weight..k.."="..v.."  "
							elseif k == "fail_timeout" then
								upstreamServer_fail_timeout = upstreamServer_fail_timeout..k.."="..v.."  "
							elseif k == "max_fails" then
								upstreamServer_max_fails = upstreamServer_max_fails..k.."="..v.."  "
							else
								upstreamTemp = upstreamTemp..k.."="..v--cjson.encode(v)
							end
						end
					end
					--upstream = upstream..upstreamServer.." "..upstreamTemp;
					upstream = upstream.."    "..upstreamServer..upstreamServer_weight..upstreamServer_fail_timeout..upstreamServer_max_fails..upstreamServer_down..upstreamServer_backup..";".."\n";
				end
				upstream = upstream.."}".."\n"
				upstreamStr = upstreamStr..upstream;
			end
		end
		
    end
	ngx.say(upstreamStr);
	--ngx.log(ngx.ERR,upstreamStr);
	return upstreamStr;
end

--reconstructionType(reqest_body||memory||shared)
--reqest_body---格式是已分号(;)分割
--memory---换行符(\n)分割
--shared---换行符(\n)分割
function reconstruction_nginx_upstream(upstreamName,data,reconstructionType)
	--ngx.log(ngx.ERR,"reconstruction_nginx_upstream.upstreamName :"..upstreamName);
	--ngx.log(ngx.ERR,"reconstruction_nginx_upstream.data :"..data);
	--ngx.log(ngx.ERR,"reconstruction_nginx_upstream.reconstructionType :"..reconstructionType);
	local dataTemp = "";
	--local upstreamStr = "";
	local upstreamStr = "upstream ".. upstreamName.."\n".."{".."\n";
	local upstream_shared_ip_hash = "";
	local upstream_shared_keepalive = "";
	local upstream_shared_backup = "";
	local upstream_shared_down = "";
	local upstreamServerTemp = "";
	local upstreamSharedTemp = nil;
	
	local upstreamSharedValue = getUpstreamFromShared(upstreamName);--获取shared当前upstreamName对应的值
	if upstreamSharedValue ~= nil then
		--ngx.log(ngx.ERR,"reconstruction_nginx_upstream.upstreamName.upstreamSharedValue :"..upstreamSharedValue);
		upstreamSharedTemp = line_break_format(upstreamSharedValue)--切割shared尝试拿到ip_hash...
		for _, temp in ipairs(upstreamSharedTemp) do--shared里面是按分号结尾,换行
			--ngx.say("temp :"..temp);
			if string.find(temp,"ip_hash") then
				local i,j = string.find(temp,"ip_hash");
				upstream_shared_ip_hash = upstream_shared_ip_hash.."     "..trim(string.sub(temp,i, string.len(temp))).."\n";
			end
			if string.find(temp,"keepalive") then
				local i,j = string.find(temp,"keepalive");
				upstream_shared_keepalive = upstream_shared_keepalive.."     "..trim(string.sub(temp,i, string.len(temp))).."\n";
			end
		end
	end
	--------------------------------------------------------
	if reconstructionType == "reqest_body" then--请求body里面有ip_hash/keepalive;
		dataTemp = semi_colon_format(data);
		upstreamStr = "{".."\n";
		local upstreamServerTemp = "";
		local upstream_req_body_ip_hash = "";
		local upstream_req_body_keepalive = "";
		local upstream_req_body_backup = "";
		local upstream_req_body_down = "";
		--ngx.log(ngx.ERR,"dataTemp :"..cjson.encode(dataTemp));
			for _, temp in ipairs(dataTemp) do
				--ngx.say("temp :"..temp);
				--ngx.log(ngx.ERR,"temp :"..temp);
				if string.find(temp,"ip_hash") then
					local i,j = string.find(temp,"ip_hash");
					upstream_req_body_ip_hash = upstream_req_body_ip_hash..trim(string.sub(temp,i, string.len(temp)))..";\n";
				elseif string.find(temp,"keepalive") then
					local i,j = string.find(temp,"keepalive");
					upstream_req_body_keepalive = upstream_req_body_keepalive..trim(string.sub(temp,i, string.len(temp)))..";\n";
				else
					upstreamServerTemp = upstreamServerTemp..trim(temp)..";\n";
					--ngx.log(ngx.ERR,"upstreamServerTemp :"..upstreamServerTemp);
				end
			end
		upstreamStr = upstreamStr..upstreamServerTemp..upstream_req_body_ip_hash..upstream_req_body_keepalive.."}"
		--ngx.say("upstreamServerStr :"..upstreamServerStr);
		--ngx.log(ngx.ERR,"upstreamServerStr :"..upstreamStr);
		return upstreamStr;
	end
	if reconstructionType == "memory" then
		dataTemp = line_break_format(data);
		upstreamStr = "{".."\n";
		for _, temp in ipairs(dataTemp) do
				--ngx.say("temp :"..temp);
				--ngx.log(ngx.ERR,"temp :"..temp);
				if string.find(temp,"ip_hash") then
					local i,j = string.find(temp,"ip_hash");
					upstreamStr_ip_hash = upstreamStr_ip_hash..trim(string.sub(temp,i, string.len(temp)))..";\n";
				elseif string.find(temp,"keepalive") then
					local i,j = string.find(temp,"keepalive");
					upstreamStr_keepalive = upstreamStr_keepalive..trim(string.sub(temp,i, string.len(temp)))..";\n";
				else
					upstreamServerTemp = upstreamServerTemp..trim(temp)..";\n";
					--ngx.log(ngx.ERR,"upstreamServerTemp :"..upstreamServerTemp);
				end
			end
	end
	----------------------------------------------------
	if reconstructionType == "shared" then
		dataTemp = line_break_format(data);--此处data是响应信息
		for _, temp in ipairs(dataTemp) do
		--ngx.log(ngx.ERR,"temp :"..temp);
			if string.find(temp,"server") then
				if string.find(temp,"backup=1 down=0") then
					local i,j = string.find(temp,"backup=1 down=0");--backup 和down同时只生效一个
					upstreamServerTemp = upstreamServerTemp.."     "..trim(string.sub(temp,1,i-1)).." backup;";--手动处理成和文件格式一样
				elseif string.find(temp,"backup=0 down=1") then
					local i,j = string.find(temp,"backup=0 down=1");
					upstreamServerTemp = upstreamServerTemp.."     "..trim(string.sub(temp,1,i-1)).." down;";
				elseif string.find(temp,"backup=0 down=0") then
					local i,j = string.find(temp,"backup=0 down=0");
					upstreamServerTemp = upstreamServerTemp.."     "..trim(string.sub(temp,1,i-1))..";";
				else
					upstreamServerTemp = upstreamServerTemp.."";
				end
				upstreamServerTemp = upstreamServerTemp.." \n";
			end
		end
		--ngx.log(ngx.ERR,"upstreamServerTemp :"..upstreamServerTemp);
		upstreamStr = upstreamStr..upstreamServerTemp;
	end
	--ngx.log(ngx.ERR,"upstream_shared_ip_hash :"..upstream_shared_ip_hash);
	upstreamStr = upstreamStr..upstream_shared_ip_hash..upstream_shared_keepalive.."}".."\n";
	--ngx.log(ngx.ERR,"upstreamStr :"..upstreamStr);
	return upstreamStr;
end

function endwith(str, substr)
        if str == nil or substr == nil then
            return nil, "the string or the sub-string parameter is nil"
        end
        str_tmp = string.reverse(str)
        substr_tmp = string.reverse(substr)
        if string.find(str_tmp, substr_tmp) ~= 1 then
            return false
        else
            return true
        end
end

function upstreamdynamic.run()
	local url = ngx.var.uri;
	local data = ngx.req.get_body_data()
	local request_method = ngx.var.request_method;
	url = string.sub(url,1+1,string.len(url));
	--ngx.log(ngx.ERR,"url :"..url);
	local args =  strSplit("/",url);
	--ngx.log(ngx.ERR,"args:"..cjson.encode(args));
	if	#args<4 then
		if request_method == "GET" then
			if args[2] == "detail" then--查看全部列表信息
				--ngx.log(ngx.ERR,"args[2]:"..args[2]);
				local res = ngx.location.capture("/detail",{method=ngx.HTTP_GET});
				if res.status ~= 200 then
					ngx.say("请求有误,请检查!")
					return
				end
				--ngx.say(res['body']);
				local upstreamValue = reconstruction_nginx_upstreamDetails(res['body']);
				--ngx.log(ngx.ERR,"reconstruction_nginx_upstreamDetails:"..upstreamValue);
				ngx.say(upstreamValue);
			end
			if args[2] == "upstream" then--查看单个upstream信息
				--ngx.log(ngx.ERR,"args[2]&args[3]:"..args[2]..","..args[3]);
				local res = ngx.location.capture("/upstream/"..args[3],{method=ngx.HTTP_GET});
				if res.status ~= 200 then
					ngx.say("请求参数有误,请检查!")
					return
				end
				--ngx.say(res['body']);
				local upstreamValue = reconstruction_nginx_upstream(args[3],res['body'],"shared");
				ngx.say(upstreamValue);
			end
		end
		
		if request_method == "POST" then
			if args[2] == "upstream" then--更新单个upstream信息
				--ngx.log(ngx.ERR,"data :"..data);
				--ngx.log(ngx.ERR,"args[2]&args[3]:"..args[2]..","..args[3]);
				local res = ngx.location.capture("/upstream/"..args[3],{method=ngx.HTTP_POST,body=data});
				if res.status ~= 200 then
					ngx.say("请求参数有误,请检查!")
					return
				end
				ngx.say(res['body']);
				--local upstreamValue = getUpstreamFromShared(args[3]);
				local upstreamValue = reconstruction_nginx_upstream(args[3],data,"reqest_body");
				--ngx.log(ngx.ERR,"POST---------upstreamValue:"..upstreamValue);
				--local upstreamValue = semi_colon_format(data);
				--local upstreamSharedValue =getUpstreamFromShared(args[3]);--不管shared里面有没有,都会写shared,有就更新,没有就新增
				--if upstreamValue ~= nil then
				setUpstreamFromShared(args[3],upstreamValue);--注意table--cjson.encode(upstreamValue));
				--end
				sharedBackupToFile();
			end
		end
		
		if request_method == "DELETE" then
			if args[2] == "upstream" then
				--ngx.log(ngx.ERR,"data :"..data);
				--ngx.log(ngx.ERR,"args[2]&args[3]:"..args[2]..","..args[3]);
				local res = ngx.location.capture("/upstream/"..args[3],{method=ngx.HTTP_DELETE});
				if res.status ~= 200 then
					ngx.say("请求参数有误,请检查!")
					return
				end
				ngx.say(res['body']);
				removeUpstreamFromShared(args[3]);--删除shared中对应的key
				sharedBackupToFile();
			end
		end
	
	else
		ngx.log(ngx.ERR,"error for update upstream");
		ngx.say("error for update upstream");
		return
	end
end

return upstreamdynamic;
apiMapping.json
{
"forward_xjb_updateAgmt":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_account_queryGZTrdacct":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_sbtpsboa_queryIPOsSecuInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS","desensitizeKeyArray":["PWD","USER_PWD","pwd","AUTH_INFO","REQ_AUTH_INFO","REQ_EXT_ACC_PWD","EXT_ACC_PWD"]},
"forward_soas_findByBankText":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_queryUserOccuInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_tsp_queryFundVol":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_historicalRevision":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kbss_signElectronicPact":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forword_kbss_getAccountByID":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_getCustIDCardImg":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_getXjbProgress":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_khpp_insertDataBatch":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_findThreeBank":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_queryCancelAccountStatus":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_submitCustRiskAns":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_logonData":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_qer_openQER":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_batch_collectionTbcBankInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_cyb_cybSignResultCallBack":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_listBoodsBizInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_khpp_loadData":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_openacc_getDZHOpenaccResultInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_common_checkShareholderAccounts":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_qer_submitRish":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_queryAccountInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_submitLicense":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_findQuestionnaire":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_chkCust":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxpService_zpck":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryBankSecurityTrans":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_qer_bankAuth":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_uploadIdImg":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_push_applyPush":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_bindCode":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_qer_pledgeOrRepurchase":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_mainChangeObey":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_qer_genSmsVerifyCode":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_queryRiskMatchLevel":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kh_cybAdd":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_lfexOpenAccount":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_finaAcceptedServiceTime":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_tradingDay":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_push_changePushType":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxpService_queryFunds":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_qer_checkRisks":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_queryRegisterAccount":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_videoInvalid":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kh_IdCardpromotion":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_openacc_submitOpenaccInfoForBatch":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryOrder":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_appropriateInfoSave":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_sbtpsboa_entrustOrderCancel":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS","desensitizeKeyArray":["PWD","USER_PWD","pwd","AUTH_INFO","REQ_AUTH_INFO","REQ_EXT_ACC_PWD","EXT_ACC_PWD"]},
"forward_bps_queryRepurchaseOffer":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_sms_sendSMS":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_qer_getCanPledgeShares":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryNewShareAcctInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_pushThs":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_ggt_isLegalClient":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_account_custAgmtCheck":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_getXJBAgmt":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kh_IdCardAuthentication":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_genSmsVerifyCode":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_openAccount":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_bankSecurityTransOut":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_queryCustAdequacyInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_openacc_submitOpenaccNoMaterialInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_OpenAcc_saveHangUpVideo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_custRiskBatchAnswer":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_queryCustSource":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_aoi_ApproMatching":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_queryAssetAccount":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_config_getIp":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_account_bjhgQualificationCheck":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_sms_saveSms":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_common_queryIPOChosenInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_getTrdCustAgmt":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_querySecuMessage":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_tsp_querySecuAcc":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_khpp_delData":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_cts_queryFunds":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryFundsFlow":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_openacc_getUserQueueNo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_queryCustomInfoByType":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxpService_zpqk":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_ggt_getStockholderCardList":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_saveAcceptedAccountTransferInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_validateQualifiedInvestor":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_dataModification":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_push_queryPushStatus":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxpService_adjustFunds":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_mainChangeObeyAllInOne":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_account_searchStkAcctBizInfoEx":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_unfreeze":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_historicaIncome":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_partnerInformation":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_queryProductPurchase":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_getRapidRedeemBank":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_cams_getUserInfoById":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_ggt_submitSurveyAnswers":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_synCustPayAcctInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_sbtpsboa_placeReservedIPOsOrder":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS","desensitizeKeyArray":["PWD","USER_PWD","pwd","AUTH_INFO","REQ_AUTH_INFO","REQ_EXT_ACC_PWD","EXT_ACC_PWD"]},
"forward_tsp_getUserInfoById":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_khpp_updateData":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_cashRapidRedeem":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_common_IDVerifyHessian":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_checkCashRapidRedeem":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_tsp_querySystemStatus":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_account_searchStkAcctBizInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_qer_getCustInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryShareProfit":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_uploadImage":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_addChannelSMS":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_fundCodeVerification":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_tsp_queryUserBasicInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_OpenAcc_checkChannelAuth":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_searchStkAcctBizInfoEx":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_account_queryTrdacct":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trdacct_findTracctByIdno":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_userOccuInfoMainten":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_fundAccount":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_sbtpsboa_CancelIPOsTodayOrder":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS","desensitizeKeyArray":["PWD","USER_PWD","pwd","AUTH_INFO","REQ_AUTH_INFO","REQ_EXT_ACC_PWD","EXT_ACC_PWD"]},
"forward_otc_cybSignResultCallBack":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kh_IdCardpromotionTract":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_verification":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_bankSecurityTransIn":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_getProtocolList":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_qer_openQERNew":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_liftLossAccount":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_aoi_querySpecialAgreement":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryDeliveryOrder":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_common_searchStkAcctBizInfoToEx":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_batch_runTbcTask":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_queryTrdacctByDates":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_tsp_queryAccounts":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kbss_getAccountByID":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_getAgreementInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_cts_zpqk":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_qtyFile":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_XjbAdd":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_account_queryCustAgreement":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_getAvailableNew":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_common_queryIPOChosenInfoNew":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_findTracctByIdno":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_app_search":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS","desensitizeKeyArray":["PWD","USER_PWD","pwd","AUTH_INFO","REQ_AUTH_INFO","REQ_EXT_ACC_PWD","EXT_ACC_PWD"]},
"forward_trade_queryIPOChosenInfoNew":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_useInformation":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kbss_synClientInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_accountCancellation":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_tsp_queryExtAccByCustCode":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_savaQuestionnaire":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_openacc_antHangUp":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_account_operateStkAcctBizInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_getRetainShares":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_savaCustomer":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_aoi_queryHisRiskTest":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_findCust":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_checkSmsVerifyCode":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_cancelAccount":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_appropriateSet":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_login":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_queryCustCommonInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_verify_verifyByChannel":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_openacc_getOpenaccInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_tsp_queryRiskInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_app_stocksearch":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_getEnumByType":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_queryRiskLevel":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_queryProductProportion":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryMatched":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_synCuacctInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_repurchaseOffer":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_openacc_submitOpenaccInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_pagedQueryMatched":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kh_searchYmtByCardNo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_IPO_queryIPOChosenInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_custCapitalInfoMainten":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_getCustAgmtStatus":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_aoi_queryStockCodeAppro":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_qer_canOpenQER":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_getAvailable":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_fundFile":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_ggt_getSurveyQuestions":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_savaRisk":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryNewShareAssignInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_queryKhppBatchInfoByUser":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_changeCashTreasureStatus":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_common_midDataPersistence":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_account_openQualifiedInvestorRight":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_savaBank":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_qer_getRishQuestions":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_signElectPromise":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_queryHisQuestions":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_imgReuslt":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_custRecordInfoMainten":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_dataCheck":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_setRetainShares":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_InformationInquiry":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_ggt_openGGT":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_queryAccountResult":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_readImg":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_accountActivation":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_querySecuAcct":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_searchYmt":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_productPurchase":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_aoi_batchRiskSurvey":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_tsp_queryShares":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_beneficiaryInfoMainten":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"file_lfex_qtyFile":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_queryBeneficiaryInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_appropriateQuery":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_ymtCancellation":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_qer_getPledgeSharesList":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_uploadImg":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_savaCust":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_aoi_MessageRevealSN":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kh_listOfStkTrdAcct":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_signCashRapidRedeem":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_ocr_readImgText":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_securitiesAccount":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_sbtpsboa_queryIPOsOrderInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS","desensitizeKeyArray":["PWD","USER_PWD","pwd","AUTH_INFO","REQ_AUTH_INFO","REQ_EXT_ACC_PWD","EXT_ACC_PWD"]},
"forward_cams_listCuacct":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_sbtpsboa_modifyIPOsOrderInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_queryAppropriateSurveyRating":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_relationshipConfirmation":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_getProtocolByID":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_preMainChangeObey":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_ymtApply":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_signElectContract":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_accountResult":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_getCustBaseInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_sbtpsboa_queryOrderInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS","desensitizeKeyArray":["PWD","USER_PWD","pwd","AUTH_INFO","REQ_AUTH_INFO","REQ_EXT_ACC_PWD","EXT_ACC_PWD"]},
"forward_kcxp_queryCustCapitalInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_custInfoCheckOptn":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_custInfoManage":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_findProfession":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_delChannelSMS":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zhx_idCardAuthentication":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_securitiesAccountCreation":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_updateCust":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_custBatchAnswer":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_openacc_getOcrUserInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_registAccountMaintain":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryIPOCalendar":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryShare":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_incidenceRelation":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_savaCuacctPwd":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_aoi_queryRiskTest":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_queryAccountStatus":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_qer_signNewAgreement":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_resultQuery":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_lu_bindCode":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_tsp_queryCapital":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_searchStkAcctBizInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_cts_adjustFunds":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_estimateIncome":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_account_openDelistingStockTradeRight":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_updateQualifiedInvestor":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_querySecuInfo":{"comSign":"SBTPS-OPF-AIO-WEB-DMZPTR-AUTH-APP"},
"forward_sbtpsboa_queryIPOsSetExpDataInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS","desensitizeKeyArray":["PWD","USER_PWD","pwd","AUTH_INFO","REQ_AUTH_INFO","REQ_EXT_ACC_PWD","EXT_ACC_PWD"]},
"forward_kcxp_queryControllerInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"file_lfex_fundFile":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_setCustAdequacyInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_account_queryUserBasicInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_cts_zpck":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_common_queryPersonalAsset":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kh_cybCanOpen":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kh_cybAddResultQuery":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_otc_riskDataUpload":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_xjbYieldRate":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryExtInstInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kbss_checkShareholderAccounts":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryQuotation":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_bps_idNumberPromotion":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_queryCustRecordInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_controllerInfoMainten":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_openacc_verifyAntAccount":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryNewSharePayment":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_openacc_queryVideoVerify":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryFunds":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_findOpenAccountStatus":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_account_queryUserBasicInfoExt":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_readImg":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_cancleOrder":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_custInfoCheckoptn":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_upp_privateStatusQuery":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_getUserInfoById":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_openacc_queryAccountStatus":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_openacc_getRiskLevel":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_findRisk":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_sbtpsboa_placeReservedOrder":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS","desensitizeKeyArray":["PWD","USER_PWD","pwd","AUTH_INFO","REQ_AUTH_INFO","REQ_EXT_ACC_PWD","EXT_ACC_PWD"]},
"forward_bps_operBoodsBizInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_sbtpsboa_IPOsOrderCancel":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS","desensitizeKeyArray":["PWD","USER_PWD","pwd","AUTH_INFO","REQ_AUTH_INFO","REQ_EXT_ACC_PWD","EXT_ACC_PWD"]},
"forward_boa_queryIPOsOrderInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS","desensitizeKeyArray":["PWD","USER_PWD","pwd","AUTH_INFO","REQ_AUTH_INFO","REQ_EXT_ACC_PWD","EXT_ACC_PWD"]},
"forward_bps_signAgreement":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"file_lfex_preFundFile":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_batch_getTbcStatus":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_account_queryBjhgQualification":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_xjb_cashRedeem":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_banksecurityTransIn":{"comSign":"SBTPS-OPF-AIO-WEB-DMZPTR-AUTH-APP"},
"forward_zd_managementInformation":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_qer_getSupplyPledgeList":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_updateChannelSMS":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryUserSecuInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_banksecurityTransOut":{"comSign":"SBTPS-OPF-AIO-WEB-DMZPTR-AUTH-APP"},
"forward_common_searchStkAcctBizInfoEx":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_common_queryFundsFlow":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_tsp_login":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_soas_savaBatchMessage":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kcxp_queryUserByTel":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_merchandiseOrder":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_common_searchStkAcctBizInfo":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_kess_idVerify":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_push_cancelBind":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zhx_IdCardpromotion":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_trade_queryMaxShareQty":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"},
"forward_zd_searchStkAcctBizInfoToEx":{"comSign":"SBTPS-OPF-AIO-WEB-SF-AUTH-APP-ITS"}
}

Jenkins cmd指令 if %ZIP_NAME% == SBTPS-OPF-NGX-SF-NGINX (
cd openapi-nginx-sf-conf
xcopy "differ/fat/sf" "differ/fat" /y/s/e
xcopy "differ/uat/sf" "differ/uat" /y/s/e
xcopy "differ/prd/sf" "differ/prd" /y/s/e
zip -r SBTPS-OPF-NGX-SF-NGINX.zip common differ -x "differ/fat/dmz/*" -x "differ/fat/sf/*" -x "differ/uat/dmz/*" -x "differ/uat/sf/*" -x "differ/prd/dmz/*" -x "differ/prd/sf/*"
rd /s/q "differ/fat/servers"
rd /s/q "differ/uat/servers"
rd /s/q "differ/prd/servers"
cd differ/fat
del /q "nginx.conf"
cd ../../differ/uat
del /q "nginx.conf"
cd ../../differ/prd
del /q "nginx.conf"
)
if %ZIP_NAME% == SBTPS-OPF-NGX-DMZWEB-NGINX (
cd openapi-nginx-sf-conf
xcopy "differ/fat/dmz" "differ/fat" /y/s/e
xcopy "differ/uat/dmz" "differ/uat" /y/s/e
xcopy "differ/prd/dmz" "differ/prd" /y/s/e
zip -r SBTPS-OPF-NGX-DMZWEB-NGINX.zip common differ -x "differ/fat/dmz/*" -x "differ/fat/sf/*" -x "differ/uat/dmz/*" -x "differ/uat/sf/*" -x "differ/prd/dmz/*" -x "differ/prd/sf/*"
rd /s/q "differ/fat/servers"
rd /s/q "differ/uat/servers"
rd /s/q "differ/prd/servers"
cd differ/fat
del /q "nginx.conf"
cd ../../differ/uat
del /q "nginx.conf"
cd ../../differ/prd
del /q "nginx.conf"
)