• RHCA---DO477---变量和过滤


    实验—变量和过滤

    1. 启动环境lab data-filters start
    2. 克隆环境 git clone http://git.lab.example.com:8081/git/data-filters.gi
    3. 部署负载均衡
    4. 部署apache
    5. 部署webapp
    6. 实现使用servera对两台webapp进行轮询访问
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1. 启动环境

    [student@workstation ~]$lab data-filters start
    
    Setting up workstation for the GE: Processing Variables Using Filters
    
     · Checking python on remote hosts.............................  SUCCESS
     · Installing git..............................................  SUCCESS
     · Installing tree.............................................  SUCCESS
     · Configuring Git.............................................  SUCCESS
     · Configuring Git credentials.................................  SUCCESS
     · Ensuring git.lab.example.com is reachable...................  SUCCESS
     · Getting GitLab status.......................................  SUCCESS
     · Adding content to Git repo..................................  SUCCESS
     · Cloning the data-filters repository.........................  SUCCESS
     · Retrieving setup playbook...................................  SUCCESS
     · Executing setup playbook....................................  SUCCESS
     · Removing temporary repository clone.........................  SUCCESS
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2. 克隆实验环境需要的yaml

    [student@workstation ~]$cd /home/student/git-repos/
    [student@workstation git-repos]$git clone http://git.lab.example.com:8081/git/data-filters.git
    Cloning into 'data-filters'...
    remote: Enumerating objects: 61, done.
    remote: Counting objects: 100% (61/61), done.
    remote: Compressing objects: 100% (44/44), done.
    remote: Total 61 (delta 5), reused 0 (delta 0)
    Unpacking objects: 100% (61/61), done.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3. 部署haproxy

    [student@workstation git-repos]$cd data-filters/
    [student@workstation data-filters (master)]$ansible-playbook deploy_haproxy.yml 
    
    PLAY [Ensure HAProxy is deployed] **************************************************************
    
    TASK [Gathering Facts] *************************************************************************
    ok: [loadbalancer_01]
    
    TASK [firewall : Ensure Firewall Sources Configuration] ****************************************
    changed: [loadbalancer_01] => (item={'port': '80/tcp'})
    
    TASK [haproxy : Ensure haproxy packages are present] *******************************************
    changed: [loadbalancer_01]
    
    TASK [haproxy : Ensure haproxy is started and enabled] *****************************************
    changed: [loadbalancer_01]
    
    TASK [haproxy : Ensure haproxy configuration is set] *******************************************
    changed: [loadbalancer_01]
    
    RUNNING HANDLER [firewall : reload firewalld] **************************************************
    changed: [loadbalancer_01]
    
    RUNNING HANDLER [haproxy : reload haproxy] *****************************************************
    changed: [loadbalancer_01]
    
    PLAY RECAP *************************************************************************************
    loadbalancer_01            : ok=7    changed=6    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
    
    • 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

    此时servera无法访问

    [student@workstation data-filters (master)]$curl http://servera
    <html><body><h1>503 Service Unavailable</h1>
    No server is available to handle this request.
    </body></html>
    
    • 1
    • 2
    • 3
    • 4

    4. 部署apache

    4.1 apache_optional_packages变量

    $cat roles/apache/defaults/main.yml 
    # Default variables for the apache role.
    
    # The 'apache_additional_packages' variable
    # allows you to specify additional
    # optional packages that must be present on
    # the web server.
    #
    # As an example, to enable a web server
    # to clone a source code repository, and execute a
    # PHP application that connects to a MySQL database:
    #
    # apache_optional_packages:
    #   - php
    #   - git
    #   - php-mysqlnd
    #
    # By default, no optional packages are installed.
    apache_optional_packages: []
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    获取到apache_optional_packages的内容,将变量写入group_vars中

    $vi group_vars/web_servers/apache.yml
    
    • 1

    文件内容如下

    apache_optional_packages:
      - php
      - git
      - php-mysqlnd
    
    • 1
    • 2
    • 3
    • 4

    4.2 修改roles/apache/tasks/main.yml

    这里涉及到2个点,
    1. 拼接apache_base_packages和apache_optional_packages内容
    1. 判断apache_enablerepos_list内容是否为空,如果空则不执行,当然就本题而言加和不加是一样的,但需要知道这个用法.

    ---
    # tasks file for apache
    
    - name: Calculate the package list
      set_fact:
        # TODO: Combine the apache_base_packages and
        # apache_optional_packages variables into one list.
        apache_package_list: "{{ apache_base_packages | union(apache_optional_packages) }}"
    
    - name: Ensure httpd packages are installed
      yum:
        name: "{{ apache_package_list }}"
        state: present
        #   TODO: omit the 'enablerepo' directive
        #   below if the apache_enablerepos_list is empty;
        #   otherwise use the list as the value for the
        #   'enablerepo' directive.
        enablerepo: "{{ apache_enablerepos_list | default(omit, true) }}"
    
    - name: Ensure SELinux allows httpd connections to a remote database
      seboolean:
        name: httpd_can_network_connect_db
        state: true
        persistent: yes
    
    - name: Ensure httpd service is started and enabled
      service:
        name: httpd
        state: started
        enabled: yes
    
    • 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

    4.3 部署apache

    这里可以看到:{“apache_package_list”: [“httpd”, “php”, “git”, “php-mysqlnd”]}, “changed”: false}

    packages installed的时候也符合我们的预期

    [student@workstation data-filters (master *%)]$ansible-playbook deploy_apache.yml -v
    略...
    TASK [firewall : Ensure Firewall Sources Configuration] ******************************************************************************************************************************************
    changed: [webserver_01] => (item={'zone': 'internal', 'service': 'http', 'source': '172.25.250.10'}) => {"ansible_loop_var": "item", "changed": true, "item": {"service": "http", "source": "172.25.250.10", "zone": "internal"}, "msg": "Permanent and Non-Permanent(immediate) operation, Changed service http to enabled, Permanent and Non-Permanent(immediate) operation, Added 172.25.250.10 to zone internal"}
    changed: [webserver_02] => (item={'zone': 'internal', 'service': 'http', 'source': '172.25.250.10'}) => {"ansible_loop_var": "item", "changed": true, "item": {"service": "http", "source": "172.25.250.10", "zone": "internal"}, "msg": "Permanent and Non-Permanent(immediate) operation, Changed service http to enabled, Permanent and Non-Permanent(immediate) operation, Added 172.25.250.10 to zone internal"}
    
    TASK [apache : Calculate the package list] *******************************************************************************************************************************************************
    ok: [webserver_01] => {"ansible_facts": {"apache_package_list": ["httpd", "php", "git", "php-mysqlnd"]}, "changed": false}
    ok: [webserver_02] => {"ansible_facts": {"apache_package_list": ["httpd", "php", "git", "php-mysqlnd"]}, "changed": false}
    TASK [apache : Ensure httpd packages are installed] **********************************************************************************************************************************************
    changed: [webserver_02] => {"changed": true, "msg": "", "rc": 0, "results": ["Installed: httpd", "Installed: php", "Installed: git", "Installed: php-mysqlnd", "Installed: git-2.18.1-3.el8.x86_64", "Installed: httpd-2.4.37-10.module+el8+2764+7127e69e.x86_64", "Installed: mod_http2-1.11.3-1.module+el8+2443+605475b7.x86_64", "Installed: php-7.2.11-1.module+el8+2561+1aca3413.x86_64", "Installed: perl-Git-2.18.1-3.el8.noarch", "Installed: php-mysqlnd-7.2.11-1.module+el8+2561+1aca3413.x86_64"]}
    changed: [webserver_01] => {"changed": true, "msg": "", "rc": 0, "results": ["Installed: httpd", "Installed: php", "Installed: git", "Installed: php-mysqlnd", "Installed: git-2.18.1-3.el8.x86_64", "Installed: httpd-2.4.37-10.module+el8+2764+7127e69e.x86_64", "Installed: mod_http2-1.11.3-1.module+el8+2443+605475b7.x86_64", "Installed: php-7.2.11-1.module+el8+2561+1aca3413.x86_64", "Installed: perl-Git-2.18.1-3.el8.noarch", "Installed: php-mysqlnd-7.2.11-1.module+el8+2561+1aca3413.x86_64"]}
    ...略
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4.4 访问测试

    此时访问servera返回内容如下,显然还不是我们想要的

    [student@workstation data-filters (master *%)]$curl -v http://servera
    * Rebuilt URL to: http://servera/
    *   Trying 172.25.250.10...
    * TCP_NODELAY set
    * Connected to servera (172.25.250.10) port 80 (#0)
    > GET / HTTP/1.1
    > Host: servera
    > User-Agent: curl/7.61.1
    > Accept: */*
    > 
    < HTTP/1.1 403 Forbidden
    < Date: Wed, 07 Jun 2023 03:27:40 GMT
    < Server: Apache/2.4.37 (Red Hat Enterprise Linux)
    < Last-Modified: Wed, 06 Feb 2019 14:46:32 GMT
    < ETag: "f91-5813acaad6200"
    < Accept-Ranges: bytes
    < Content-Length: 3985
    < Content-Type: text/html; charset=UTF-8
    < 
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
    	<head>
    		<title>Test Page for the Apache HTTP Server on Red Hat Enterprise Linux</title>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    5. 部署webapp

    5.1 配置修改

    vi roles/webapp/tasks/main.yml 
    
    • 1

    如果这个时候不清楚webapp_find_files的值会是什么样的话,可以先价格debug看下内容

    ---
    # tasks file for webapp
    
    - name: Ensure stub web content is deployed
      copy:
        content: "{{ webapp_message }} (version {{ webapp_version }})\n"
        dest: "{{ webapp_content_root_dir }}/index.html"
    
    - name: Find deployed webapp files
      find:
        paths: "{{ webapp_content_root_dir }}"
        recurse: yes
      register: webapp_find_files
    
    - name: print webapp_find_files
      debug:
        msg: "{{ webapp_find_files }}"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    运行后可以吐出以下内容:

    可以看到它是一个字典,我们需要的内容在files中

    TASK [webapp : print webapp_find_files] **********************************************************************************************************************************************************
    ok: [webserver_01] => {
        "msg": {
            "changed": false,
            "examined": 2,
            "failed": false,
            "files": [
                {
                    "atime": 1686107330.6328595,
                    "ctime": 1686107330.6378593,
                    "dev": 64513,
                    "gid": 0,
                    "gr_name": "root",
                    "inode": 54273,
                    "isblk": false,
                    "ischr": false,
                    "isdir": false,
                    "isfifo": false,
                    "isgid": false,
                    "islnk": false,
                    "isreg": true,
                    "issock": false,
                    "isuid": false,
                    "mode": "0644",
                    "mtime": 1686107330.2838566,
                    "nlink": 1,
                    "path": "/var/www/html/test.html",
                    "pw_name": "root",
                    "rgrp": true,
                    "roth": true,
                    "rusr": true,
                    "size": 10,
                    "uid": 0,
                    "wgrp": false,
                    "woth": false,
                    "wusr": true,
                    "xgrp": false,
                    "xoth": false,
                    "xusr": false
                },
                {
                    "atime": 1686108986.6641345,
                    "ctime": 1686108986.6711345,
                    "dev": 64513,
                    "gid": 0,
                    "gr_name": "root",
                    "inode": 16797669,
                    "isblk": false,
                    "ischr": false,
                    "isdir": false,
                    "isfifo": false,
                    "isgid": false,
                    "islnk": false,
                    "isreg": true,
                    "issock": false,
                    "isuid": false,
                    "mode": "0644",
                    "mtime": 1686108986.3391318,
                    "nlink": 1,
                    "path": "/var/www/html/index.html",
                    "pw_name": "root",
                    "rgrp": true,
                    "roth": true,
                    "rusr": true,
                    "size": 40,
                    "uid": 0,
                    "wgrp": false,
                    "woth": false,
                    "wusr": true,
                    "xgrp": false,
                    "xoth": false,
                    "xusr": false
                }
            ],
            "matched": 2,
            "msg": ""
        }
    }
    ok: [webserver_02] => {
        "msg": {
            "changed": false,
            "examined": 1,
            "failed": false,
            "files": [
                {
                    "atime": 1686108986.6565332,
                    "ctime": 1686108986.6615334,
                    "dev": 64513,
                    "gid": 0,
                    "gr_name": "root",
                    "inode": 12586847,
                    "isblk": false,
                    "ischr": false,
                    "isdir": false,
                    "isfifo": false,
                    "isgid": false,
                    "islnk": false,
                    "isreg": true,
                    "issock": false,
                    "isuid": false,
                    "mode": "0644",
                    "mtime": 1686108986.3305306,
                    "nlink": 1,
                    "path": "/var/www/html/index.html",
                    "pw_name": "root",
                    "rgrp": true,
                    "roth": true,
                    "rusr": true,
                    "size": 40,
                    "uid": 0,
                    "wgrp": false,
                    "woth": false,
                    "wusr": true,
                    "xgrp": false,
                    "xoth": false,
                    "xusr": false
                }
            ],
            "matched": 1,
            "msg": ""
        }
    }
    
    • 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

    修改webapp/task/main.yml

    ---
    # tasks file for webapp
    
    - name: Ensure stub web content is deployed
      copy:
        content: "{{ webapp_message }} (version {{ webapp_version }})\n"
        dest: "{{ webapp_content_root_dir }}/index.html"
    
    - name: Find deployed webapp files
      find:
        paths: "{{ webapp_content_root_dir }}"
        recurse: yes
      register: webapp_find_files
    
    - name: Compute the webapp file list
      set_fact:
        # TODO: Use the map filter to extract
        # the 'path' attribute of each entry
        # in the 'webapp_find_files'
        # variable 'files' list.
        webapp_deployed_files: "{{ webapp_find_files.files | map(attribute='path') |list }}"
    
    - name: Compute the relative webapp file list
      set_fact:
        # TODO: Use the 'map' filter, along with
        # the 'relpath' filter, to create the
        # 'webapp_rel_deployed_files' variable
        # from the 'webapp_deployed_files' variable.
        #
        # Files in the 'webapp_rel_deployed_files'
        # variable should have a path relative to
        # the 'webapp_content_root_dir' variable.
        webapp_rel_deployed_files: "{{ webapp_deployed_files | map(relpath,webapp_content_root_dir) }}"
    
    - name: Remove Extraneous Files
      file:
        path: "{{ webapp_content_root_dir }}/{{ item }}"
        state: absent
      # TODO: Loop over a list of files
      # that are in the 'webapp_rel_deployed_files'
      # list, but not in the 'webapp_file_list' list.
      # Use the difference filter.
      loop: "{{ webapp_rel_deployed_files | difference(webapp_file_list) }}"
    
    
    • 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

    5.2 部署webapp

    [student@workstation data-filters (master *%)]$ansible-playbook deploy_webapp.yml 
    
    PLAY [Ensure Web App is deployed] ****************************************************************************************************************************************************************
    
    TASK [webapp : Ensure stub web content is deployed] **********************************************************************************************************************************************
    ok: [webserver_02]
    ok: [webserver_01]
    
    TASK [webapp : Find deployed webapp files] *******************************************************************************************************************************************************
    ok: [webserver_01]
    ok: [webserver_02]
    
    TASK [webapp : Compute the webapp file list] *****************************************************************************************************************************************************
    ok: [webserver_01]
    ok: [webserver_02]
    
    TASK [webapp : Compute the relative webapp file list] ********************************************************************************************************************************************
    ok: [webserver_01]
    ok: [webserver_02]
    
    TASK [webapp : Remove Extraneous Files] **********************************************************************************************************************************************************
    ok: [webserver_02] => (item=<)
    ok: [webserver_01] => (item=<)
    ok: [webserver_02] => (item=g)
    ok: [webserver_01] => (item=g)
    ok: [webserver_02] => (item=e)
    ok: [webserver_01] => (item=e)
    ok: [webserver_01] => (item=n)
    ok: [webserver_02] => (item=n)
    ok: [webserver_01] => (item=r)
    ok: [webserver_02] => (item=r)
    ok: [webserver_01] => (item=a)
    ok: [webserver_02] => (item=a)
    ok: [webserver_02] => (item=t)
    ok: [webserver_01] => (item=t)
    ok: [webserver_02] => (item=o)
    ok: [webserver_01] => (item=o)
    ok: [webserver_02] => (item= )
    ok: [webserver_01] => (item= )
    ok: [webserver_02] => (item=b)
    ok: [webserver_01] => (item=b)
    ok: [webserver_02] => (item=j)
    ok: [webserver_01] => (item=j)
    ok: [webserver_02] => (item=c)
    ok: [webserver_01] => (item=c)
    ok: [webserver_02] => (item=d)
    ok: [webserver_01] => (item=d)
    ok: [webserver_02] => (item=_)
    ok: [webserver_01] => (item=_)
    ok: [webserver_02] => (item=m)
    ok: [webserver_01] => (item=m)
    ok: [webserver_02] => (item=p)
    ok: [webserver_01] => (item=p)
    ok: [webserver_02] => (item=0)
    ok: [webserver_01] => (item=0)
    ok: [webserver_02] => (item=x)
    ok: [webserver_01] => (item=x)
    ok: [webserver_02] => (item=7)
    ok: [webserver_01] => (item=7)
    ok: [webserver_02] => (item=f)
    ok: [webserver_01] => (item=f)
    ok: [webserver_02] => (item=9)
    ok: [webserver_01] => (item=9)
    ok: [webserver_02] => (item=6)
    ok: [webserver_01] => (item=6)
    ok: [webserver_02] => (item=3)
    ok: [webserver_01] => (item=3)
    ok: [webserver_02] => (item=8)
    ok: [webserver_01] => (item=8)
    ok: [webserver_02] => (item=>)
    ok: [webserver_01] => (item=>)
    
    PLAY RECAP ***************************************************************************************************************************************************************************************
    webserver_01               : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    webserver_02               : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
    
    • 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

    5.3 访问测试

    此时已经能正常在2台webserver之间进行轮询了

    [student@workstation data-filters (master *%)]$curl servera
    Hello from webserver_01. (version v1.1)
    [student@workstation data-filters (master *%)]$curl servera
    Hello from webserver_02. (version v1.1)
    [student@workstation data-filters (master *%)]$curl servera
    Hello from webserver_01. (version v1.1)
    [student@workstation data-filters (master *%)]$curl servera
    Hello from webserver_02. (version v1.1)
    [student@workstation data-filters (master *%)]$curl servera
    Hello from webserver_01. (version v1.1)
    [student@workstation data-filters (master *%)]$curl servera
    Hello from webserver_02. (version v1.1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    6. 清理环境

    [student@workstation data-filters (master *%)]$lab data-filters finish
    
    Cleaning up the lab on workstation:
    
     · Cloning the data-filters repository.........................  SUCCESS
     · Retrieving cleanup playbook.................................  SUCCESS
     · Executing cleanup playbook..................................  SUCCESS
     · Removing temporary repository clone.........................  SUCCESS
    
    [student@workstation data-filters (master *%)]$cd ..
    [student@workstation git-repos]$rm -rf data-filters/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    7. 本次实验的一些关键字

    default(omit,ture)		如果定义了值,则使用
    map(attirbute='path')	取字典里的path键的值
    map(relpath,webapp_content_root_dir)   从绝对路径中截取出文件名
    deference(webapp_file_list) 取和webapp_file_list不一样的文件列表
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    Python爬虫项目实战:百度任意图片抓取
    ik分词器
    Java项目:SSM学业预警平台信息管理系统
    【吃瓜之旅】第三章吃瓜学习
    EurekaLog C++Builder异常跟踪工具
    apex应用程序创建和对比
    c++构造函数
    文件管理模块总结
    Spring Data JPA语法详解
    我国跨国企业外汇风险管理——以海尔公司为例
  • 原文地址:https://blog.csdn.net/qq_29974229/article/details/131085084