py脚本解决ArcGIS Server服务内存过大的问题

在一台服务器上,使用ArcGIS Server发布地图服务,但是地图服务较多,在发布之后,服务器的内存持续处在95%上下的高位状态,导致服务器运行状态不稳定,经常需要重新启动。重新启动后重新进入这种内存高位的陷阱。

1. 现象

打开任务管理器发现大量ArcSOC.exe进程,这些进程CPU使用率不高,但基本都在50-90m之间,直接占用绝大部分的内存资源。

2. 解决方法

我们打开ArcMap,从右侧ArcCatlog中找到发布的ArcGIS Server服务名称,然后右键选择"服务属性",如下图所示:

在弹出的服务编辑器中,选择"池化",将每台机器的最小实例数修改成0,如下图所示:

重启服务即可

3. 在浏览器中打开

4. 代码批量修改

从2、3中我们可以看到,无非就是修改这些属性,通过接口的调用,动态修改。以下为python代码,以下代码使用的python3.

4.1. 代码内容

python 复制代码
# 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. 代码解读

  1. 导入模块:

    • http.clienturllibjsonrequests:用于处理 HTTP 请求和 JSON 数据的模块。
    • sys:用于处理命令行参数和退出脚本的模块。
    • getpass:用于安全地输入密码而不回显的模块。
  2. 定义 main 函数:

    • main 函数是脚本的入口点,它负责执行主要的操作。
    • 打印一些信息,包括脚本的描述。
    • 获取服务器名称、端口、用户名、密码、最小实例数和最大实例数等输入参数。
    • 检查输入参数的有效性,并确保最小实例数不大于最大实例数。
    • 获取令牌(Token):调用 getToken 函数,使用提供的用户名和密码获取 ArcGIS Server 的令牌。令牌用于身份验证。
    • 获取所有服务列表:调用 getAllServer 函数,获取 ArcGIS Server 上所有的服务名称。
  3. 定义 getToken 函数:

    • getToken 函数用于获取 ArcGIS Server 的令牌(Token),以便进行身份验证。
    • 构建令牌请求的 URL 和参数。
    • 发送 HTTP POST 请求以获取令牌。
    • 解析响应并提取令牌。
  4. 定义 getAllServer 函数:

    • getAllServer 函数用于获取 ArcGIS Server 上的所有服务的名称。
    • 构建服务列表请求的 URL 和参数。
    • 发送 HTTP GET 请求以获取服务列表。
    • 解析响应并提取服务名称,存储在 service_names 列表中。
  5. 定义 AlterServerPerNode 函数:

    • AlterServerPerNode 函数用于修改指定服务的最小和最大实例数量。
    • 构建修改服务属性的请求 URL 和参数。
    • 发送 HTTP POST 请求以修改服务属性。
    • 检查响应以确保修改成功。
  6. 定义 assertJsonSuccess 函数:

    • assertJsonSuccess 函数用于检查 JSON 响应是否包含错误信息。
    • 如果 JSON 响应包含错误信息,函数返回 False,否则返回 True
  7. 在脚本的末尾,使用 if __name__ == "__main__": 来指示当脚本作为主程序运行时执行 main 函数。

参考资源

示例:编辑服务属性---ArcGIS Server | ArcGIS Enterprise 文档

ArcGIS Server服务中ArcSOC进程占用过多内存-百度经验 (baidu.com)

相关推荐
赵钰老师1 天前
【SWAT模型应用】AI辅助下基于ArcGIS Pro的SWAT模型全流程高效建模实践与深度进阶应用
人工智能·arcgis·chatgpt·数据分析
白水先森3 天前
ArcGIS Pro中创建最低成本路径的详尽教程
经验分享·arcgis·arcgispro
小仙有礼了3 天前
ArcGis for js 4.x实现测量,测距,高程的功能
javascript·算法·arcgis
白水先森3 天前
ArcGIS Pro中打造精美高程渲染图的全面指南
经验分享·arcgis·信息可视化·arcgispro
GIS瞧葩菜3 天前
ArcGis和Super Map
arcgis·supermap
白水先森4 天前
ArcGIS Pro在洪水淹没分析中的应用与实践
经验分享·arcgis
白水先森4 天前
ArcGIS Pro热力图制作指南:从基础到进阶
经验分享·arcgis·信息可视化
白水先森5 天前
如何使用ArcGIS Pro高效查找小区最近的地铁站
经验分享·arcgis·信息可视化·数据分析
白水先森5 天前
ArcGIS Pro制作人口三维地图教程
arcgis·信息可视化·数据分析
摆烂老大6 天前
SWAT| 水文 | SWAT模型(四):气象数据库制备(附Python代码)
python·arcgis·水文·swat模型