Python自动化测试用例:如何优雅的完成Json格式数据断言

目录

前言

直接使用

优化

封装

小结

进阶

总结

 资料获取方法


前言

记录Json断言在工作中的应用进阶。

直接使用

很早以前写过一篇博客,记录当时获取一个多级json中指定key的数据:

#! /usr/bin/python
# coding:utf-8 
""" 
@author:Bingo.he 
@file: get_target_value.py 
@time: 2017/12/22 
"""
def get_target_value(key, dic, tmp_list):
    """
    :param key: 目标key值
    :param dic: JSON数据
    :param tmp_list: 用于存储获取的数据
    :return: list
    """
    if not isinstance(dic, dict) or not isinstance(tmp_list, list):  # 对传入数据进行格式校验
        return 'argv[1] not an dict or argv[-1] not an list '
    if key in dic.keys():
        tmp_list.append(dic[key])  # 传入数据存在则存入tmp_list
    for value in dic.values():  # 传入数据不符合则对其value值进行遍历
        if isinstance(value, dict):
            get_target_value(key, value, tmp_list)  # 传入数据的value值是字典,则直接调用自身
        elif isinstance(value, (list, tuple)):
            _get_value(key, value, tmp_list)  # 传入数据的value值是列表或者元组,则调用_get_value
    return tmp_list


def _get_value(key, val, tmp_list):
    for val_ in val:
        if isinstance(val_, dict):  
            get_target_value(key, val_, tmp_list)  # 传入数据的value值是字典,则调用get_target_value
        elif isinstance(val_, (list, tuple)):
            _get_value(key, val_, tmp_list)   # 传入数据的value值是列表或者元组,则调用自身

优化

后来在写用例生成过程中,发现重复的断言描述信息较多,大多数数据返回其实都是标准的json,所以将整个返回的json放到断言数据中:

continue_run_flag = True


def assert_json(base, juge, contain=(), reg=()):
    # 返回值,不符合预期时会设置为False
    flag = True
    for key, value in base.items():

        # 不进行断言的数据不进一步处理
        if key not in juge:
            continue

        if key in COMMON_RE_CHECK:

            if not re.match(COMMON_RE_CHECK[key], base[key]):
                flag = False
                logger.error("字段[{}]使用通用的正则匹配,不符合预期:预期正则匹配【{}】== 【{}】".format(key, COMMON_RE_CHECK[key], juge[key]))
            else:
                logger.warning("字段【{}】使用通用字段的正则匹配, 符合预期".format(key))
            continue

        if key in contain:
            if str(value) not in juge[key]:
                flag = False
                logger.error("字段[{}]不符合预期:预期【{}】包含 【{}】".format(key, juge[key], value))
                continue
            logger.info("字段[{}]符合预期:预期[{}] 包含 [{}]".format(key, juge[key], value))
            continue

        if key in reg:
            if not re.match(juge[key], value):
                flag = False
                logger.error("字段[{}]不符合预期:预期正则匹配【{}】== 【{}】".format(key, value, juge[key]))
                continue
            logger.info("字段[{}]断言成功:预期[{}]== 实际[{}]".format(key, value, juge[key]))
            continue
        if isinstance(value, str) or isinstance(value, int):
            if juge[key] != value:
                logger.error("字段[{}]不符合预期:预期【{}】!= 实际【{}】".format(key, value, juge[key]))
                flag = False
                continue
        elif isinstance(value, dict):
            assert_json(value, juge[key], contain=contain, reg=reg)
        elif isinstance(value, list):
            for i, v in enumerate(value):
                if isinstance(value, str) or isinstance(value, int):
                    if v != juge[key][i]:
                        logger.error("字段[{}]不符合预期:预期【{}】!= 实际【{}】".format(key, value, juge[key]))
                    else:
                        logger.info("字段[{}]断言成功:预期[{}]== 实际[{}]".format(key, value, juge[key]))
                elif isinstance(value, dict):
                    assert_json(value[i], juge[key][i], contain=contain, reg=reg)
                else:
                    assert_json(value[i], juge[key][i], contain=contain, reg=reg)

        else:
            pass

        logger.info("字段[{}]符合预期: 预期[{}]== 实际[{}]".format(key, value, juge[key]))

    # 失败是否继续执行,默认为TRUE
    if not continue_run_flag:
        assert flag

    return flag

调用:

rsp = requests.get('http://localhost:8800/get', params={"name": "bingo", "age": 18}).json()
assert_json(rsp, {
    "args": {
        "name": "bingo"
    },
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Cache-Control": "max-age=259200",
        "Host": "httpbin.org",
        "User-Agent": "python-requests/2.27.1",
        "X-Amzn-Trace-Id": r"Root=\d-\w{8}-\w{24}"
    },
    "req_param": [
        {
            "name": "bingo",
            "age": "18"
        }
    ],
    "origin": r"",
    "url": "http://httpbin.org/get?name=bingo"
},
contain=(), reg=("X-Amzn-Trace-Id", "origin",))

日志效果:

2022-05-05 14:25:49.967 | INFO     | __main__:assert_json:173 - 字段[name]符合预期: 预期[bingo]== 实际[bingo]
2022-05-05 14:25:49.968 | INFO     | __main__:assert_json:173 - 字段[args]符合预期: 预期[{'name': 'bingo'}]== 实际[{'name': 'bingo'}]
2022-05-05 14:25:49.968 | INFO     | __main__:assert_json:173 - 字段[Accept]符合预期: 预期[*/*]== 实际[*/*]
2022-05-05 14:25:49.968 | INFO     | __main__:assert_json:173 - 字段[Accept-Encoding]符合预期: 预期[gzip, deflate]== 实际[gzip, deflate]
2022-05-05 14:25:49.968 | INFO     | __main__:assert_json:173 - 字段[Cache-Control]符合预期: 预期[max-age=259200]== 实际[max-age=259200]
2022-05-05 14:25:49.968 | INFO     | __main__:assert_json:173 - 字段[Host]符合预期: 预期[httpbin.org]== 实际[httpbin.org]
2022-05-05 14:25:49.968 | INFO     | __main__:assert_json:173 - 字段[User-Agent]符合预期: 预期[python-requests/2.27.1]== 实际[python-requests/2.27.1]
2022-05-05 14:25:49.968 | INFO     | __main__:assert_json:149 - 字段[X-Amzn-Trace-Id]断言成功:预期[Root=1-62734553-430db0707e1a3656043cd165]== 实际[Root=\d-\w{8}-\w{24}]
2022-05-05 14:25:49.968 | INFO     | __main__:assert_json:173 - 字段[headers]符合预期: 预期[{'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Cache-Control': 'max-age=259200', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.27.1', 'X-Amzn-Trace-Id': 'Root=1-62734553-430db0707e1a3656043cd165'}]== 实际[{'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Cache-Control': 'max-age=259200', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.27.1', 'X-Amzn-Trace-Id': 'Root=\\d-\\w{8}-\\w{24}'}]
2022-05-05 14:25:49.969 | WARNING  | __main__:assert_json:133 - 字段【origin】使用通用字段的正则匹配, 符合预期
2022-05-05 14:25:49.969 | INFO     | __main__:assert_json:173 - 字段[age]符合预期: 预期[18]== 实际[18]
2022-05-05 14:25:49.969 | INFO     | __main__:assert_json:173 - 字段[name]符合预期: 预期[bingo]== 实际[bingo]
2022-05-05 14:25:49.969 | INFO     | __main__:assert_json:173 - 字段[req_param]符合预期: 预期[[{'age': '18', 'name': 'bingo'}]]== 实际[[{'name': 'bingo', 'age': '18'}]]
2022-05-05 14:25:49.969 | INFO     | __main__:assert_json:173 - 字段[url]符合预期: 预期[http://httpbin.org/get?name=bingo]== 实际[http://httpbin.org/get?name=bingo]

封装

将方法简单封装到调用类中:

class HttpBin:
    def __init__(self):
        self.continue_run_flag = True  # 失败是否继续执行
        self.base_url = 'http://localhost:8800'
        self.base_param = {"local_class": self.__class__.__name__}

    def get(self, param):
        path = "/get"
        param.update(self.base_param)
        self.rsp = requests.get(self.base_url + path, params=param)
        self.ans = self.rsp.json()
        logger.info(json.dumps(self.rsp.json(), indent=4))
        return self

    def set(self, param):
        path = "/set"
        param.update(self.base_param)
        self.rsp = requests.get(self.base_url + path, params=param)
        self.ans = self.rsp.json()
        logger.info(json.dumps(self.rsp.json(), indent=4))
        return self

    def assert_statusCode(self, result_code):
        """
        :param result_code: 包含关系断言
        :return: bool <= self.rsp.resultinfo
        """
        # 返回值,不符合预期时会设置为False
        flag = True

        if int(result_code) != self.rsp.status_code:
            logger.error(f"返回状态码[result_code]不符合预期:预期【{result_code}】!= 实际【{self.rsp.status_code}】")
            flag = False
        else:
            logger.info(f"返回状态码[result_code]符合预期:预期【{result_code}】!= 实际【{self.rsp.status_code}】")

        if not self.continue_run_flag:
            assert flag

        return self

    def assert_json_body(self, base, juge, contain=(), reg=()):
    ...

用例调用

# 如果仅仅断言状态码
HttpBin().get({"name": "bingo", "age": 18}).assert_statusCode(200)

# 级连调用多个接口,使用同一个初始化数据
HttpBin().get({"name": "bingo", "age": 18}).assert_statusCode(200).\
        set({"name": "he", "age": 18}).assert_statusCode(200).assert_json_body(
        juge={
            "args": {
                "name": "bingo"
            },
            "headers": {
                "Accept": "*/*",
                "Accept-Encoding": "gzip, deflate",
                "Cache-Control": "max-age=259200",
                "Host": "httpbin.org",
                "User-Agent": "python-requests/2.27.1",
                "X-Amzn-Trace-Id": r"Root=\d-\w{8}-\w{24}"
            },
            "req_param": [
                {
                    "name": "he",
                    "age": "18"
                }
            ],
            "origin": r"",
            "url": "http://httpbin.org/set?name=bingo"
        }, contain=(), reg=("X-Amzn-Trace-Id", "origin",)
    )

运行效果:

2022-05-05 19:39:36.951 | INFO     | __main__:assert_statusCode:53 - 返回状态码[result_code]符合预期:预期【200】!= 实际【200】
2022-05-05 19:39:36.951 | INFO     | __main__:assert_json_body:117 - 字段[name]符合预期: 预期[bingo]== 实际[bingo]
2022-05-05 19:39:36.951 | INFO     | __main__:assert_json_body:117 - 字段[args]符合预期: 预期[{'name': 'bingo'}]== 实际[{'name': 'bingo'}]
2022-05-05 19:39:36.952 | INFO     | __main__:assert_json_body:117 - 字段[Accept]符合预期: 预期[*/*]== 实际[*/*]
2022-05-05 19:39:36.952 | INFO     | __main__:assert_json_body:117 - 字段[Accept-Encoding]符合预期: 预期[gzip, deflate]== 实际[gzip, deflate]
2022-05-05 19:39:36.952 | INFO     | __main__:assert_json_body:117 - 字段[Cache-Control]符合预期: 预期[max-age=259200]== 实际[max-age=259200]
2022-05-05 19:39:36.952 | INFO     | __main__:assert_json_body:117 - 字段[Host]符合预期: 预期[httpbin.org]== 实际[httpbin.org]
2022-05-05 19:39:36.952 | INFO     | __main__:assert_json_body:117 - 字段[User-Agent]符合预期: 预期[python-requests/2.27.1]== 实际[python-requests/2.27.1]
2022-05-05 19:39:36.952 | INFO     | __main__:assert_json_body:93 - 字段[X-Amzn-Trace-Id]断言成功:预期[Root=1-62734553-430db0707e1a3656043cd165]== 实际[Root=\d-\w{8}-\w{24}]
2022-05-05 19:39:36.952 | INFO     | __main__:assert_json_body:117 - 字段[headers]符合预期: 预期[{'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Cache-Control': 'max-age=259200', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.27.1', 'X-Amzn-Trace-Id': 'Root=1-62734553-430db0707e1a3656043cd165'}]== 实际[{'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Cache-Control': 'max-age=259200', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.27.1', 'X-Amzn-Trace-Id': 'Root=\\d-\\w{8}-\\w{24}'}]
2022-05-05 19:39:36.953 | WARNING  | __main__:assert_json_body:77 - 字段【origin】使用通用字段的正则匹配, 符合预期
2022-05-05 19:39:36.953 | INFO     | __main__:assert_json_body:117 - 字段[age]符合预期: 预期[18]== 实际[18]
2022-05-05 19:39:36.953 | INFO     | __main__:assert_json_body:117 - 字段[name]符合预期: 预期[he]== 实际[he]
2022-05-05 19:39:36.953 | INFO     | __main__:assert_json_body:117 - 字段[req_param]符合预期: 预期[[{'age': '18', 'local_class': 'HttpBin', 'name': 'he'}]]== 实际[[{'name': 'he', 'age': '18'}]]
2022-05-05 19:39:36.953 | INFO     | __main__:assert_json_body:117 - 字段[url]符合预期: 预期[http://httpbin.org/set?name=bingo]== 实际[http://httpbin.org/set?name=bingo]

小结

  • 可以作为独立函数使用,也可以和所有的接口一起封装,灵活度、复用度高
  • 用例极简且逻辑清晰
  • 支持断言失败,用例继续执行(开关控制),方便一次性发现所有的差异
  • 极易编写的统一断言(从日志获取轻松获取
    • 用例运行日志,断言数据字段清晰明确
    • 可统一化处理新版本修改字段,无需修改每个用例
    • 支持正则匹配
    • 支持**包含匹配 **
    • 支持错误码直接断言
  • 集中初始化原始常用数据,不同业务适配简单,重写初始化方法即可

进阶

上面的方法虽然使用日志的方法记录了所有的差异,但是面对大json对比的时候,很难直接标记出具体的差异位置。在做现网引流对比测试的时候就出现了这样的需求,从现网拉取的账户数据可能存在几百个子账户,每个子账户有20多个属性字段,怎么准确标记他们在新旧系统的写操作后不一致的情况成为了一个小卡点。

话不多说,思路:利用列表可变特性和生成器关键字yield特性递归分解json,生成一个固定的数组,最终比较数组中的数据

代码:

def recurse(d, prefix=None, sep='.'):
    if prefix is None:
        prefix = []
    for key, value in d.items():
        if isinstance(value, dict):
            yield from recurse(value, prefix + [key])
        elif isinstance(value, list):

            for i, v in enumerate(value):
                if isinstance(v, dict):
                    yield from recurse(v, prefix + [key, f"${i}"])

                # 兼容 包含数字的类型
                elif isinstance(v, int) or isinstance(v, str):
                    yield sep.join(prefix + [key, str(value)])  # 会嵌套上value
        else:
            # print(key)
            yield sep.join(prefix + [key, str(value)])  # 会嵌套上value

效果:

print(json.dumps(list(recurse({
            "args": {
                "name": "bingo"
            },
            "headers": {
                "Accept": "*/*",
                "Accept-Encoding": "gzip, deflate",
                "Cache-Control": "max-age=259200",
                "Host": "httpbin.org",
                "User-Agent": "python-requests/2.27.1",
                "X-Amzn-Trace-Id": r"Root=\d-\w{8}-\w{24}"
            },
            "req_param": [
                {
                    "name": "bingo",
                    "age": "18"
                },
                {
                    "name": "he",
                    "age": "19"
                },
                {
                    "name": "detector",
                    "age": "20"
                }
            ],
            "origin": r"",
            "url": "http://httpbin.org/set?name=bingo"
        })), indent=4))
# 输出
[
    "args.name.bingo",
    "headers.Accept.*/*",
    "headers.Accept-Encoding.gzip, deflate",
    "headers.Cache-Control.max-age=259200",
    "headers.Host.httpbin.org",
    "headers.User-Agent.python-requests/2.27.1",
    "headers.X-Amzn-Trace-Id.Root=\\d-\\w{8}-\\w{24}",
    "req_param.$0.name.bingo",
    "req_param.$0.age.18",
    "req_param.$1.name.he",
    "req_param.$1.age.19",
    "req_param.$2.name.detector",
    "req_param.$2.age.20",
    "origin.",
    "url.http://httpbin.org/set?name=bingo"
]

总结

项目实践中总会遇到这样那样的需求,每个方法都有适用的场景,直接高效解决问题是第一要务。

  • json查找数据
  • json数据用例封装对比
  • json数据转化

 资料获取方法

【留言777】

各位想获取源码等教程资料的朋友请点赞 + 评论 + 收藏,三连!

三连之后我会在评论区挨个私信发给你们~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/66318.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

3分钟创建新生分班查询二维码,无需技术、0成本

作为教师&#xff0c;我们深知分班是一项极其重要的任务&#xff0c;需要综合考虑学生的性格、能力和兴趣等多个方面&#xff0c;以确保每个学生都能够获得最佳的学习环境和成绩。在本文中&#xff0c;我将分享一种便捷的方式来告知家长有关分班录取情况的方法。 通常&#xf…

2. 软件需求 面向对象分析

目录 1. 软件需求 1.1 需求分类 1.2 需求获取 1.3 需求分析 2. 面向对象分析&#xff08;OOA&#xff09; 2.1 统一建模语言 UML 2.2 用例模型 2.2.1 用例图的元素 2.2.2 识别参与者 2.2.3 合并需求获得用例 2.2.4 细化用例描述 2.3 分析模型 2.3.1 定义概念类 …

深入JVM - JIT分层编译技术与日志详解

深入JVM - JIT分层编译技术与日志详解 文章目录 深入JVM - JIT分层编译技术与日志详解1. 背景简介2. JIT 编译器2.1. 客户端版本的编译器: C12.2. 服务端版本的编译器: C22.3. Graal JIT 编译器 3. 分层编译技术(Tiered Compilation)3.1. 汇聚两种编译器的优点3.2. 精准优化(Ac…

Redis内网主从节点搭建

Redis内网主从节点搭建 1、文件上传2、服务安装3、服务启动4、配置主从复制 1、文件上传 内网环境手动上传gcc-c、redis.tar文件 2、服务安装 # 解压 unzip gcc-c.zip unzip gcc_rpm.zip tar -zxvf redis-6.2.13.tar.gz# 安装 cd gcc_rpm/ rpm -ivh *.rpm --nodeps --force…

gitlab 503 错误的解决方案

首先使用 sudo gitlab-ctl status 命令查看哪些服务没用启动 sudo gitlab-ctl status 再用 gitlab-rake gitlab:check 命令检查 gitlab。根据发生的错误一步一步纠正。 gitlab-rake gitlab:check 查看日志 tail /var/log/gitlab/gitaly/current删除gitaly.pid rm /var/opt…

Webpack怎么使用?

Webpack 使用 前几篇文章中已经介绍了如何初始化包管理器 package.json 这里不再重复介绍&#xff0c;如有需要请查看 搭建工程化项目。 安装 :::warning 注意 请确保你已经安装了 yarn&#xff0c;如有需要请查看 搭建工程化开发环境。 ::: 通过命令 yarn add webpack web…

三天吃透Java面试八股文(2023最新整理),面试通过率高达90%

什么样的求职者能够获得面试官的青睐&#xff1f;求职者需要准备哪些内容来面对形形色色的面试官&#xff1f;这两份资料是我在几十场面试中被面试官问到的问题&#xff0c;比其他复制粘贴的面试题强一百倍&#xff0c;堪称全网最强&#xff08;我不太喜欢“全网最强”这样的字…

mybatis打印sql语句出现多余的limit关键字

1、事情起因 在项目中使用了PageHelper分页插件&#xff0c;由于需求特殊&#xff0c;需要自定义分页&#xff0c;代码编写完成后&#xff0c;事故出现了。 前端传参: {pageNum: 1,pageSize: 10, }已知表中数据10条&#xff0c;但是每次分页查询只有10条数据&#xff0c;排查…

【C语言】初识C语言+进阶篇导读

✨个人主页&#xff1a; Anmia.&#x1f389;所属专栏&#xff1a; C Language &#x1f383;操作环境&#xff1a; Visual Studio 2019 版本 本篇目的是面向编程新手&#xff0c;没接触过编程的人。以及C进阶的导读。 内容是C语言重要知识点的简单解释&#xff0c;不做详解。给…

uniapp echarts 点击失效

这个问题网上搜了一堆&#xff0c;有的让你降版本&#xff0c;有的让你改源码。。。都不太符合预期&#xff0c;目前我的方法可以用最新的echarts。 这个方法就是由npm安装转为CDN&#xff0c;当然你可能会质疑用CDN这样会不稳定&#xff0c;那如果CDN的地址是本地呢&#xff1…

【MySQL系列】表约束的学习

「前言」文章内容大致是MySQL的表的约束。 「归属专栏」MySQL 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、MySQL表的约束1.1 空属性1.2 默认值&#xff08;default&#xff09;1.3 列描述&#xff08;comment&#xff09;1.4 zerofill1.5 主键&#xff08;primary ke…

简化AD管理减少IT工作负担

管理和保护混合 AD 环境 IT 管理员几乎每天都要创建和管理多个 AD 对象&#xff0c;利用本机AD工具&#xff08;如Active Directory用户和计算机控制台以及PowerShell脚本&#xff09;来执行这些任务并不理想&#xff0c;因为它们必须在多个控制台之间切换才能执行这些任务&am…

CTF PWN之精确覆盖变量数据

刚开始接触pwn的朋友在做pwn练习时可能会有这样的疑问&#xff0c;怎么做到精确覆盖变量数据呢&#xff1f; 我们做pwn练习之前需要先知道&#xff1a;命令行参数C语言的main函数拥有两个参数&#xff0c;为int类型的argc参数&#xff0c;以及char**类型argv参数。其中argc参数…

npm install时出现的问题Failed at the node-sass@4.14.1 postinstall script

从阿里云上拉取下来项目后&#xff0c;首先使用npm install 命令进行安装所需依赖&#xff0c;意想不到的事情发生了&#xff0c;报出了Failed at the node-sass4.14.1 postinstall script&#xff0c;这个问题&#xff0c;顿时一脸懵逼&#xff1b;询问前端大佬&#xff0c;给…

Tomcat线程池原理

1. 一个 SpringBoot 项目能同时处理多少请求&#xff1f;tomcat容器&#xff0c; 200 次。 2. 怎么来的&#xff1f; 而点击这些线程&#xff0c;查看其堆栈消息&#xff0c;可以看到 Tomcat、threads、ThreadPoolExecutor 等关键字 基于“短时间内有 200 个请求被立马处理…

CentOS虚拟机 NAT模式连网

1、查看本地VMnet8的网络信息 cmd ipconfig2、编辑VMware虚拟网络编辑器 &#xff08;1&#xff09;打开网络编辑器 &#xff08;2&#xff09;打开NET设置 &#xff08;3&#xff09;修改网络配置 修改子网ip和windows查到的ip的最后一位不一样就行和子网掩码照抄 3、在VMw…

宝尊电商短期前景堪忧,宝尊国际能否取得成功还有待验证

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 核心业务面临短期逆风 在2023年第一季度财报中&#xff0c;宝尊电商&#xff08;BZUN&#xff09;表示其电商业务(简称BEC)主要包括&#xff1a;品牌的门店运营、客户服务以及物流和供应链管理、IT和数字营销等增值服务”。…

Spring Data学习笔记Day01-SpringData入门

Spring Data基本介绍 目录 Spring Data Redis 官方API参考手册&#xff01;★ Spring Data的价值★ Spring Data及其子项目★ 强大的Spring Data★ Repository接口★ 具体Repository接口★ Spring Data JPA开发★ Spring Boot如何选择DataSource★ 数据源相关配置★ 配置第三方…

大数据技术之Hadoop:HDFS集群安装篇(三)

目录 分布式文件系统HDFS安装篇 一、为什么海量数据需要分布式存储 二、 分布式的基础架构分析 三、 HDFS的基础架构 四 HDFS集群环境部署 4.1 下载安装包 4.2 集群规划 4.3 上传解压 4.4 配置HDFS集群 4.5 准备数据目录 4.6 分发hadoop到其他服务器 4.7 配置环境变…

C# Equals()方法报错:NullReferenceException was unhandled

下面是一个C# Equals()方法的例子&#xff0c;执行时报错了 static void Main(string[] args) {string name "sandeep";string myName null;Console.WriteLine(" operator result is {0}", name myName);Console.WriteLine("Equals method result…