• 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. 代码内容

    # 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:]))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167

    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)

  • 相关阅读:
    Java学习笔记(十八)
    领域驱动设计:领域模型与代码模型的一致性
    mysql 查询某字段中以逗号分隔的字符串的方法
    【Web前端】怎样用记事本写一个简单的网页-html
    第四十一篇 指令中的VNode
    LeetCode 42. Trapping Rain Water
    javaI/O包中的包装模式
    全渠道电商 | 国内知名的药妆要如何抓住风口实现快速增长?
    Compose原理-compose中是如何实现事件分法的
    c++ chrono
  • 原文地址:https://blog.csdn.net/a13407142317/article/details/132667308