在一台服务器上,使用ArcGIS Server发布地图服务,但是地图服务较多,在发布之后,服务器的内存持续处在95%上下的高位状态,导致服务器运行状态不稳定,经常需要重新启动。重新启动后重新进入这种内存高位的陷阱。
1. 现象
打开任务管理器发现大量ArcSOC.exe
进程,这些进程CPU使用率不高,但基本都在50-90m之间,直接占用绝大部分的内存资源。
2. 解决方法
我们打开ArcMap,从右侧ArcCatlog中找到发布的ArcGIS Server服务名称,然后右键选择“服务属性”,如下图所示:
在弹出的服务编辑器中,选择“池化”,将每台机器的最小实例数修改成0,如下图所示:
重启服务即可
3. 在浏览器中打开
4. 代码批量修改
从2、3中我们可以看到,无非就是修改这些属性,通过接口的调用,动态修改。以下为python代码,以下代码使用的python3.
4.1. 代码内容
# Demonstrates how to modify the min and max instances for a service
# For Http calls
import http.client, urllib, json,requests
# For system tools
import sys
# For reading passwords without echoing
import getpass
# Defines the entry point into the script
def main(argv=None):
# Print some info
print
print("This tool is a sample script that resets the minimum and maximum instances allowed for a service.")
print
serverName ="127.0.01" #raw_input("Enter Server name: ")
serverPort = 6080
username ="arcgis" #raw_input("Enter user name: ")
password ="arcgis" #getpass.getpass("Enter password: ")
minInstances =0 #raw_input("Enter the new minimum: ")
maxInstances =2 #raw_input("Enter the new maximum: ")
# Check to make sure the minimum and maximum are numerical
try:
minInstancesNum = int(minInstances)
maxInstancesNum = int(maxInstances)
except ValueError:
print("Numerical value not entered for minimum, maximum, or both.")
return
# Check to make sure that the minimum is not greater than the maximum
if minInstancesNum > maxInstancesNum:
print("Maximum number of instances must be greater or equal to minimum number.")
return
# Get a token
token = getToken(username, password, serverName, serverPort)
if token == "":
print("Could not generate a token with the username and password provided.")
return
service_names=getAllServer(token)
for index, service in enumerate(service_names):
print(f"{index}/{len(service_names)}:开始修改服务{service}...")
AlterServerPerNode(serverName, serverPort,token,service,minInstancesNum,maxInstancesNum)
print(f"服务{service}修改完成")
# A function to generate a token given username, password and the adminURL.
def getToken(username, password, serverName, serverPort):
# Token URL is typically http://server[:port]/arcgis/admin/generateToken
tokenURL = "/arcgis/tokens/generateToken"
params = urllib.parse.urlencode({'username': username, 'password': password, 'client': 'requestip', 'f': 'json'})
headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
# Connect to URL and post parameters
httpConn = http.client.HTTPConnection(serverName, serverPort)
httpConn.request("POST", tokenURL, params, headers)
# Read response
response = httpConn.getresponse()
if (response.status != 200):
httpConn.close()
print("Error while fetching tokens from admin URL. Please check the URL and try again.")
return
else:
data = response.read()
httpConn.close()
# Check that data returned is not an error object
if not assertJsonSuccess(data):
return
# Extract the token from it
token = json.loads(data)
return token['token']
def getAllServer(serverName, serverPort,token):
service_names = []
service_base_url = f"{serverName}:{serverPort}/arcgis/admin/services"
# This request only needs the token and the response formatting parameter
params = urllib.parse.urlencode({'token': token, 'f': 'json'})
serviceURL=service_base_url+"?"+params
response=requests.get(serviceURL)
# httpConn.request("Post", serviceURL, params, headers)
# Read response
if (response.status_code == 200):
#data = response.json()
data = json.loads(response.text)
if "folders" in data:
for folder in data["folders"]:
service_base_folder_url =f"{service_base_url}/{folder}"
folder_url = service_base_folder_url+"?"+params
# folder_url = urllib.parse.quote(folder_url, safe='/:')
folder_response = requests.get(folder_url)
folder_data = json.loads(folder_response.text)
for service in folder_data["services"]:
if(service["type"]=="MapServer"):
service_names.append(f"{folder}/{service['serviceName']}")
# if "folders" in data:
for service in data["services"]:
if(service["type"]=="MapServer"):
service_names.append(service['serviceName'])
return service_names
def AlterServerPerNode(serverName, serverPort,token,service,minInstancesNum,maxInstancesNum):
service=service+".MapServer"
serviceURL = urllib.parse.quote("/arcgis/admin/services/" + service, safe='/:')
# This request only needs the token and the response formatting parameter
params = urllib.parse.urlencode({'token': token, 'f': 'json'})
headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
# Connect to service to get its current JSON definition
httpConn = http.client.HTTPConnection(serverName, serverPort)
httpConn.request("POST", serviceURL, params, headers)
# Read response
response = httpConn.getresponse()
if (response.status != 200):
httpConn.close()
print("Could not read service information.")
return
else:
data = response.read()
# Check that data returned is not an error object
if not assertJsonSuccess(data):
print("Error when reading service information. " + str(data))
else:
print("Service information read successfully. Now changing properties...")
# Deserialize response into Python object
dataObj = json.loads(data)
if dataObj["minInstancesPerNode"]!=minInstancesNum or dataObj["maxInstancesPerNode"] != maxInstancesNum:
# Edit desired properties of the service
dataObj["minInstancesPerNode"] = minInstancesNum
dataObj["maxInstancesPerNode"] = maxInstancesNum
# Serialize back into JSON
updatedSvcJson = json.dumps(dataObj)
# Call the edit operation on the service. Pass in modified JSON.
editSvcURL = urllib.parse.quote("/arcgis/admin/services/" + service + "/edit", safe='/:')
params = urllib.parse.urlencode({'token': token, 'f': 'json', 'service': updatedSvcJson})
httpConn.request("POST", editSvcURL, params, headers)
# Read service edit response
editResponse = httpConn.getresponse()
if (editResponse.status != 200):
httpConn.close()
print("Error while executing edit.")
return
else:
editData = editResponse.read()
# Check that data returned is not an error object
if not assertJsonSuccess(editData):
print("Error returned while editing service" + str(editData))
else:
print("Service edited successfully.")
httpConn.close()
return
# A function that checks that the input JSON object
# is not an error object.
def assertJsonSuccess(data):
obj = json.loads(data)
if 'status' in obj and obj['status'] == "error":
print("Error: JSON object returns an error. " + str(obj))
return False
else:
return True
# Script start
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
4.2. 代码解读
-
导入模块:
http.client
、urllib
、json
、requests
:用于处理 HTTP 请求和 JSON 数据的模块。sys
:用于处理命令行参数和退出脚本的模块。getpass
:用于安全地输入密码而不回显的模块。
-
定义
main
函数:main
函数是脚本的入口点,它负责执行主要的操作。- 打印一些信息,包括脚本的描述。
- 获取服务器名称、端口、用户名、密码、最小实例数和最大实例数等输入参数。
- 检查输入参数的有效性,并确保最小实例数不大于最大实例数。
- 获取令牌(Token):调用
getToken
函数,使用提供的用户名和密码获取 ArcGIS Server 的令牌。令牌用于身份验证。 - 获取所有服务列表:调用
getAllServer
函数,获取 ArcGIS Server 上所有的服务名称。
-
定义
getToken
函数:getToken
函数用于获取 ArcGIS Server 的令牌(Token),以便进行身份验证。- 构建令牌请求的 URL 和参数。
- 发送 HTTP POST 请求以获取令牌。
- 解析响应并提取令牌。
-
定义
getAllServer
函数:getAllServer
函数用于获取 ArcGIS Server 上的所有服务的名称。- 构建服务列表请求的 URL 和参数。
- 发送 HTTP GET 请求以获取服务列表。
- 解析响应并提取服务名称,存储在
service_names
列表中。
-
定义
AlterServerPerNode
函数:AlterServerPerNode
函数用于修改指定服务的最小和最大实例数量。- 构建修改服务属性的请求 URL 和参数。
- 发送 HTTP POST 请求以修改服务属性。
- 检查响应以确保修改成功。
-
定义
assertJsonSuccess
函数:assertJsonSuccess
函数用于检查 JSON 响应是否包含错误信息。- 如果 JSON 响应包含错误信息,函数返回
False
,否则返回True
。
-
在脚本的末尾,使用
if __name__ == "__main__":
来指示当脚本作为主程序运行时执行main
函数。
参考资源
示例:编辑服务属性—ArcGIS Server | ArcGIS Enterprise 文档
ArcGIS Server服务中ArcSOC进程占用过多内存-百度经验 (baidu.com)