• 关于

    状态空间表示是什么

    的搜索结果

回答

算法是比较复杂又基础的学科,每个学编程的人都会学习大量的算法。而根据统计,以下这18个问题是面试中最容易遇到的,本文给出了一些基本答案,供算法方向工程师或对此感兴趣的程序员参考。 1)请简单解释算法是什么? 算法是一个定义良好的计算过程,它将一些值作为输入并产生相应的输出值。简单来说,它是将输入转换为输出的一系列计算步骤。 2)解释什么是快速排序算法? 快速排序算法能够快速排序列表或查询。它基于分割交换排序的原则,这种类型的算法占用空间较小,它将待排序列表分为三个主要部分: ·小于Pivot的元素 ·枢轴元素Pivot(选定的比较值) ·大于Pivot的元素 3)解释算法的时间复杂度? 算法的时间复杂度表示程序运行完成所需的总时间,它通常用大O表示法来表示。 4)请问用于时间复杂度的符号类型是什么? 用于时间复杂度的符号类型包括: ·Big Oh:它表示小于或等于目标多项式 ·Big Omega:它表示大于或等于目标多项式 ·Big Theta:它表示与目标多项式相等 ·Little Oh:它表示小于目标多项式 ·Little Omega:它表示大于目标多项式 5)解释二分法检索如何工作? 在二分法检索中,我们先确定数组的中间位置,然后将要查找的值与数组中间位置的值进行比较,若小于数组中间值,则要查找的值应位于该中间值之前,依此类推,不断缩小查找范围,直至得到最终结果。 6)解释是否可以使用二分法检索链表? 由于随机访问在链表中是不可接受的,所以不可能到达O(1)时间的中间元素。因此,对于链表来说,二分法检索是不可以的(对顺序链表或排序后的链表是可以用的)。 7)解释什么是堆排序? 堆排序可以看成是选择排序的改进,它可以定义为基于比较的排序算法。它将其输入划分为未排序和排序的区域,通过不断消除最小元素并将其移动到排序区域来收缩未排序区域。 8)说明什么是Skip list? Skip list数据结构化的方法,它允许算法在符号表或字典中搜索、删除和插入元素。在Skip list中,每个元素由一个节点表示。搜索函数返回与key相关的值的内容。插入操作将指定的键与新值相关联,删除操作可删除指定的键。 9)解释插入排序算法的空间复杂度是多少? 插入排序是一种就地排序算法,这意味着它不需要额外的或仅需要少量的存储空间。对于插入排序,它只需要将单个列表元素存储在初始数据的外侧,从而使空间复杂度为O(1)。 10)解释什么是“哈希算法”,它们用于什么? “哈希算法”是一个哈希函数,它使用任意长度的字符串,并将其减少为唯一的固定长度字符串。它用于密码有效性、消息和数据完整性以及许多其他加密系统。 11)解释如何查找链表是否有循环? 要知道链表是否有循环,我们将采用两个指针的方法。如果保留两个指针,并且在处理两个节点之后增加一个指针,并且在处理每个节点之后,遇到指针指向同一个节点的情况,这只有在链表有循环时才会发生。 12)解释加密算法的工作原理? 加密是将明文转换为称为“密文”的密码格式的过程。要转换文本,算法使用一系列被称为“键”的位来进行计算。密钥越大,创建密文的潜在模式数越多。大多数加密算法使用长度约为64到128位的固定输入块,而有些则使用流方法。 13)列出一些常用的加密算法? 一些常用的加密算法是: ·3-way ·Blowfish ·CAST ·CMEA ·GOST ·DES 和Triple DES ·IDEA ·LOKI等等 14)解释一个算法的最佳情况和最坏情况之间有什么区别? ·最佳情况:算法的最佳情况解释为算法执行最佳的数据排列。例如,我们进行二分法检索,如果目标值位于正在搜索的数据中心,则这就是最佳情况,最佳情况时间复杂度为0。 ·最差情况:给定算法的最差输入参考。例如快速排序,如果选择关键值的子列表的最大或最小元素,则会导致最差情况出现,这将导致时间复杂度快速退化到O(n2)。 15)解释什么是基数排序算法? 基数排序又称“桶子法”,是通过比较数字将其分配到不同的“桶里”来排序元素的。它是线性排序算法之一。 16)解释什么是递归算法? 递归算法是一个解决复杂问题的方法,将问题分解成较小的子问题,直到分解的足够小,可以轻松解决问题为止。通常,它涉及一个调用自身的函数。 17)提到递归算法的三个定律是什么? 所有递归算法必须遵循三个规律: ·递归算法必须有一个基点 ·递归算法必须有一个趋向基点的状态变化过程 ·递归算法必须自我调用 18)解释什么是冒泡排序算法? 冒泡排序算法也称为下沉排序。在这种类型的排序中,要排序的列表的相邻元素之间互相比较。如果它们按顺序排列错误,将交换值并以正确的顺序排列,直到最终结果“浮”出水面。 满意记得采纳哈

玄学酱 2019-12-02 01:18:44 0 浏览量 回答数 0

回答

HTML5 引入了 history.pushState() 和 history.replaceState() 方法,它们分别可以添加和修改历史记录条目。 let stateObj = { foo: "bar" }; history.pushState(stateObj, "page 2", "bar.html"); 假设当前页面为 foo.html,执行上述代码后会变为 bar.html,点击浏览器后退,会变为 foo.html,但浏览器并不会刷新。 pushState() 需要三个参数: 一个状态对象, 一个标题 (目前被忽略), 和 (可选的) 一个 URL. 让我们来解释下这三个参数详细内容: 状态对象 — 状态对象 state 是一个 JavaScript 对象,通过 pushState () 创建新的历史记录条目。无论什么时候用户导航到新的状态,popstate 事件就会被触发,且该事件的 state 属性包含该历史记录条目状态对象的副本。 状态对象可以是能被序列化的任何东西。原因在于 Firefox 将状态对象保存在用户的磁盘上,以便在用户重启浏览器时使用,我们规定了状态对象在序列化表示后有 640k 的大小限制。如果你给 pushState() 方法传了一个序列化后大于 640k 的状态对象,该方法会抛出异常。如果你需要更大的空间,建议使用 sessionStorage 以及 localStorage. 标题 — Firefox 目前忽略这个参数,但未来可能会用到。传递一个空字符串在这里是安全的,而在将来这是不安全的。二选一的话,你可以为跳转的 state 传递一个短标题。 URL — 该参数定义了新的历史 URL 记录。注意,调用 pushState() 后浏览器并不会立即加载这个 URL,但可能会在稍后某些情况下加载这个 URL,比如在用户重新打开浏览器时。新 URL 不必须为绝对路径。如果新 URL 是相对路径,那么它将被作为相对于当前 URL 处理。新 URL 必须与当前 URL 同源,否则 pushState() 会抛出一个异常。该参数是可选的,缺省为当前 URL。

茶什i 2019-12-02 03:20:07 0 浏览量 回答数 0

问题

【算法】五分钟算法小知识:Linux的进程、线程、文件描述符是什么?

游客ih62co2qqq5ww 2020-05-09 11:28:57 0 浏览量 回答数 0

阿里云试用中心,为您提供0门槛上云实践机会!

0元试用32+款产品,最高免费12个月!拨打95187-1,咨询专业上云建议!

问题

为什么文件系统和快照空间大小不一致?

boxti 2019-12-01 21:49:28 1610 浏览量 回答数 0

问题

状态压缩技巧:动态规划的降维打击 7月14日 【今日算法】

游客ih62co2qqq5ww 2020-07-14 23:53:52 6 浏览量 回答数 1

问题

经典动态规划:高楼扔鸡蛋 6月2日 【今日算法】

游客ih62co2qqq5ww 2020-06-02 16:06:52 3 浏览量 回答数 1

回答

本人乃一个数据痴迷者,在计算机的道路上,也是一个数据结构的痴迷者,现在大学里面和同学搞开发也痴迷于数据库,我就我个人的理解给你谈一谈: 首先,数据结构是一门计算机语言学的基础学科,它不属于任何一门语言,其体现的是几乎所有标准语言的算法的思想。 上面的概念有一些模糊,我们现在来具体说一说,相信你门的数据结构使用的是一门具体的语言比如C/C++语言来说明,那是为了辅助的学习数据结构,而数据结构本身不属于任何语言(相信你把书上的程序敲到电脑里面是不能通过的吧,其只是描述了过程,要调试程序,还需要修改和增加一些东西)。你们的书上开始应该在讲究数据的物理存储结构/逻辑存储结构等概念,说明数据结构首先就是“数据的结构”,在内存上的存储方式,就是物理的存储结构,在程序使用人员的思想上它是逻辑的,比如: 你们在C/C++中学习到链表,那么链表是什么一个概念,你们使用指针制向下一个结点的首地址,让他们串联起来,形成一个接一个的结点,就像显示生活中的火车一样。而这只是对于程序员的概念,但是在内存中存储的方式是怎样的那。对于你程序员来说这是“透明”的,其内部分配空间在那里,都是随机的,而内存中也没有一个又一根的线将他们串联起来,所以,这是一个物理与逻辑的概念,对于我们程序员只需要知道这些就可以了,而我们主要要研究的是“逻辑结构”。 我可以给你一个我自己总结的一个概念:所有的算法必须基于数据结构生存。也就是说,我们对于任何算法的编写,必须依赖一个已经存在的数据结构来对它进行操作,数据结构成为算法的操作对象,这也是为什么算法和数据结构两门分类不分家的概念,算法在没有数据结构的情况下,没有任何存在的意义;而数据结构没有算法就等于是一个尸体而没有灵魂。估计这个对于算法的初学者可能有点晕,我们在具体的说一些东西吧: 我们在数据结构中最简单的是什么:我个人把书籍中线性表更加细化一层(这里是为了便于理解在这样说的):单个元素,比如:int i;这个i就是一个数据结构,它是一个什么样的数据结构,就是一个类型为int的变量,我们可以对它进行加法/减法/乘法/除法/自加等等一系列操作,当然对于单个元素我们对它的数据结构和算法的研究没有什么意义,因为它本来就是原子的,某些具体运算上可能算法存在比较小的差异;而提升一个层次:就是我们的线性表(一般包含有:顺序表/链表)那么我们研究这样两种数据结构主要就是要研究它的什么东西那。一般我们主要研究他们以结构为单位(就是结点)的增加/删除/修改/检索(查询)四个操作(为什么有这样的操作,我在下面说到),我们一般把“增加/删除/修改”都把它称为更新,对于一个结点,若要进行更新一类的操作比如:删除,对于顺序表来说是使用下标访问方式,那么我们在删除了一个元素后需要将这个元素后的所有元素后的所有元素全部向前移动,这个时间是对于越长的顺序表,时间越长的,而对于链表,没有顺序的概念,其删除元素只需要将前一个结点的指针指向被删除点的下一个结点,将空间使用free()函数进行释放,还原给操作系统。当执行检索操作的时候,由于顺序表直接使用下标进行随机访问,而链表需要从头开始访问一一匹配才可以得到使用的元素,这个时间也是和链表的结点个数成正比的。所以我们每一种数据结构对于不同的算法会产生不同的效果,各自没有绝对的好,也没有绝对的不好,他们都有自己的应用价值和方式;这样我们就可以在实际的项目开发中,对于内部的算法时间和空间以及项目所能提供的硬件能力进行综合评估,以让自己的算法能够更加好。 (在这里只提到了基于数据结构的一个方面就是:速度,其实算法的要素还应该包括:稳定性、健壮性、正确性、有穷性、可理解性、有输入和输出等等) 为什么要以结点方式进行这些乱七八糟的操作那。首先明确一个概念就是:对于过程化程序设计语言所提供的都是一些基础第一信息,比如一些关键字/保留字/运算符/分界符。而我们需要用程序解决现实生活中的问题,比如我们要程序记录某公司人员的情况变化,那么人员这个数据类型,在程序设计语言中是没有的,那么我们需要对人员的内部信息定义(不可能完全,只是我们需要那些就定义那些),比如:年龄/性别/姓名/出生日期/民族/工作单位/职称/职务/工资状态等,那么就可以用一些C/C++语言描述了,如年龄我们就可以进行如下定义: int age;/*age变量,表示人员公司人员的年龄*/ 同理进行其他的定义,我们用结构体或类把他们封装成自定义数据类型或类的形式,这样用他们定义的就是一个人的对象的了,它内部包含了很多的模板数据了。 我就我个人的经历估计的代码量应该10000以内的(我个人的经理:只是建议,从你的第一行代码开始算,不论程序正确与否,不论那一门语言,作为一个标准程序员需要十万行的代码的功底(这个是我在大学二年级感觉有一定时候的大致数据,不一定适合其他人),而十万行代码功底一般需要四门基础远支撑,若老师没有教,可以自学一些语言)。

马铭芳 2019-12-02 01:22:06 0 浏览量 回答数 0

问题

【算法】五分钟算法小知识:动态规划之博弈问题

游客ih62co2qqq5ww 2020-05-14 09:56:12 5 浏览量 回答数 1

问题

经典动态规划:高楼扔鸡蛋(进阶篇) 6月3日【今日算法】

游客ih62co2qqq5ww 2020-06-03 15:10:38 7 浏览量 回答数 1

问题

【算法】五分钟算法小知识:动态规划详解

游客ih62co2qqq5ww 2020-05-07 14:48:09 25 浏览量 回答数 1

回答

设计微服务五个建议:1.它不会与其他服务共享数据库表2.它拥有最少量的数据库表3.它设计为有状态的或无状态的4.其数据可用性需求5.这是真相的唯一来源避免任意规则在设计和创建微服务时,不要陷入使用任意规则的陷阱。如果你阅读了足够多的建议,你会遇到下面的一些规则。虽然吸引人,但这些并不都是划分微服务边界的正确方法。如下:1.“微服务应该有X行代码”让我们弄清楚一件事。对于微服务中有多少行代码没有限制。微服务不会因为你写了几行额外的代码而突然变成单体巨石。关键是确保服务中的代码具有很高的凝聚力(稍后会详细介绍)。2.“将每个函数变成微服务”如果一个函数是根据三个输入值计算出某些东西,并返回一个结果,那么这个函数就是一个微服务吗?这个函数是否是一个可单独部署的应用程序吗?其实真的取决于函数是什么以及它如何服务于整个系统。其他任意规则包括那些不考虑整个上下文的规则,例如团队的经验,DevOps容量,服务在做什么以及数据的可用性需求等。精心设计的服务的特点如果您已阅读过有关微服务的文章,毫无疑问,您会发现有关设计良好的服务的建议。简而言之:高凝聚力和松散耦合。如果你不熟悉这些概念,有很多关于这些概念的文章。虽然合理的建议,但这些概念是相当抽象的。 我已经和数十位CTO就这个话题进行了交流,向他们学习他们如何划分微服务界限,下面为你们提供了一些潜在的特性。特性#1:它不会与其他服务共享数据库表当设计一个微服务时,如果你有多个引用同一个表的服务,这是一个红色警告,因为它可能意味着你的数据库是耦合的来源。“每个服务都应该有自己的表[并且]不应共享数据库表。” - Darby Frey,Lead Honestly共同创始人这实际上是关于服务与数据的关系,这正是Elastic Swiftype SRE的负责人Oleksiy Kovrin告诉我的:“我们在开发新服务时使用的主要基本原则之一是它们不应该跨越数据库边界。每项服务都应该依靠自己的一套底层数据存储。这使我们能够集中访问控制,审计日志记录,缓存逻辑等等,“他说。Kovyrin继续解释说,如果数据库表的一部分“与数据集的其余部分没有或很少有关系,这是一个强烈的信号,即组件可能可以被隔离到一个单独的API或单独的服务中。”特性#2:它具有最少量的数据库表正如第1章所提到的,微服务的理想尺寸应该足够小,但不能过小一点。每个服务的数据库表的数量也是一样。Scaylr工程负责人Steven Czerwinski在接受采访时向我解释说,Scaylr的甜蜜点是“一个服务 + 一个或两个数据库表”。特点#3:它有设计为有状态或无状态在设计微服务时,您需要问自己是否需要访问数据库,或者它是否将成为处理TB数据(如电子邮件或日志)的无状态服务。“我们通过定义服务的输入和输出来定义服务的边界。有时服务是网络API,但它也可能是一个处理输入文件并在数据库中生成记录的过程(这是我们的日志处理服务的情况)“ - Julien Lemoine要清楚这个前沿,它会导致更好的设计服务。特点#4:它的数据可用性需求被考虑在内在设计微服务时,您需要记住哪些服务将依赖于这项新服务,以及如果数据不可用,对系统的影响是什么。考虑到这一点,您可以为此服务正确设计数据备份和恢复系统。 当与Steven Czerwinski谈话时,他提到他们的关键客户行空间映射数据由于其重要性而以不同方式复制和分离到不同分区。“而每个分片信息,都是在自己的小分区中。 如果所在分区宕机,那么就没有备份可用,但它只影响5%的客户,而不是100%的客户,“Czerwinski解释说。特点#5:这是一个真理的单一来源要牢记的最后一个特点是设计一个服务,使其成为系统中某件事情的唯一真理来源。举例来说,当您从电子商务网站订购某物品时,会生成订单ID。此订单ID可供其他服务用于查询订单服务以获取有关订单的完整信息。使用pub / sub概念,在服务之间传递的数据应该是订单ID,而不是订单本身的属性/信息。只有订单服务具有完整的信息,并且是给定订单的唯一真实来源。考虑更大的团队对于大型系统而言,在确定服务边界时,组织架构考虑将发挥作用。有两点需要注意:独立发布时间表和不同的上线时间的重要性。Cloud66首席执行官Khash Sajadi表示:“我们所见过的最成功的微服务实施要么基于软件设计原则,例如基于领域驱动设计、面向服务架构SOA或反映组织方式的架构。“所以对于支付团队来说,”Sajadi继续说道,“他们有支付服务或信用卡验证服务,这是他们向外界提供的服务。这主要是关于向外界提供更多服务的业务部门。““[亚马逊CEO:杰夫贝佐斯]提出了'两个比萨饼'的规则 - 一个团队不能多到两个披萨饼还不够他们吃的地步。” - Iron.io首席技术官Travis Reeder亚马逊是拥有多个团队的大型组织的完美典范。正如在API推荐人发表的一篇文章中提到的,杰夫贝佐斯向所有员工发布了一份授权通知他们,公司内的每个团队都必须通过API进行沟通。任何不会的人将被解雇。这样,所有的数据和功能都通过接口暴露出来。贝佐斯还设法让每个团队解耦,定义他们的资源,并通过API使其可用。亚马逊总是自底而上从头开始建立一个系统。这可以让公司内的每个团队成为彼此的合作伙伴。我与Iron.io的首席技术官Travis Reeder谈到了贝佐斯的内部计划。“杰夫贝佐斯强制所有team都必须建立API来与其他team进行沟通,他也提出了'两个披萨'规则,一个团队不能多到两个披萨饼还不够他们吃的地步。”他说。“我认为这同样适用于这样情况:当一个小团队在开发、管理和生产方面开始变得笨拙或开始变慢,这说明这个团队可能已经太大了,“Reeder告诉我。如何判断服务是否太小,或许没有正确定义在微服务系统的测试和实施阶段,需要牢记下面两条出现现象。要注意的第一个现象是服务之间的任何过度依赖。如果两个服务不断地互相调用,那么这已经是一个强烈的耦合信号,他们如果并成一个服务可能更好。第二个现象:建立服务的开销超过了让其独立的好处。在这种情况下不如合并成一个服务。Darby Frey解释说:“每个应用程序需要将其日志汇总到某处并需要进行监控。您需要设置报警。然后需要有标准的响应操作程序,并在事情中断时运行。你必须管理SSH的访问权限。为了让应用程序正常运行,必须准备大量基础设施支持。“

wangccsy 2019-12-02 01:46:40 0 浏览量 回答数 0

回答

MQTT协议 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)最早是IBM开发的一个即时通讯协议,MQTT协议是为大量计算能力有限且工作在低带宽、不可靠网络的远程传感器和控制设备通讯而设计的一种协议。 MQTT协议的优势是可以支持所有平台,它几乎可以把所有的联网物品和互联网连接起来。 它具有以下主要的几项特性:1、使用发布/订阅消息模式,提供一对多的消息发布和应用程序之间的解耦;2、消息传输不需要知道负载内容;3、使用 TCP/IP 提供网络连接;4、有三种消息发布的服务质量:QoS 0:“最多一次”,消息发布完全依赖底层 TCP/IP 网络。分发的消息可能丢失或重复。例如,这个等级可用于环境传感器数据,单次的数据丢失没关系,因为不久后还会有第二次发送。QoS 1:“至少一次”,确保消息可以到达,但消息可能会重复。QoS 2:“只有一次”,确保消息只到达一次。例如,这个等级可用在一个计费系统中,这里如果消息重复或丢失会导致不正确的收费。5、小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量;6、使用 Last Will 和 Testament 特性通知有关各方客户端异常中断的机制;在MQTT协议中,一个MQTT数据包由:固定头(Fixed header)、 可变头(Variable header)、 消息体(payload)三部分构成。MQTT的传输格式非常精小,最小的数据包只有2个bit,且无应用消息头。下图是MQTT为可靠传递消息的三种消息发布服务质量 发布/订阅模型允许MQTT客户端以一对一、一对多和多对一方式进行通讯。 下图是MQTT的发布/订阅消息模式 CoAP协议 CoAP是受限制的应用协议(Constrained Application Protocol)的代名词。由于目前物联网中的很多设备都是资源受限型的,所以只有少量的内存空间和有限的计算能力,传统的HTTP协议在物联网应用中就会显得过于庞大而不适用。因此,IETF的CoRE工作组提出了一种基于REST架构、传输层为UDP、网络层为6LowPAN(面向低功耗无线局域网的IPv6)的CoAP协议。 CoAP采用与HTTP协议相同的请求响应工作模式。CoAP协议共有4中不同的消息类型。CON——需要被确认的请求,如果CON请求被发送,那么对方必须做出响应。NON——不需要被确认的请求,如果NON请求被发送,那么对方不必做出回应。ACK——应答消息,接受到CON消息的响应。RST——复位消息,当接收者接受到的消息包含一个错误,接受者解析消息或者不再关心发送者发送的内容,那么复位消息将会被发送。 CoAP消息格式使用简单的二进制格式,最小为4个字节。 一个消息=固定长度的头部header + 可选个数的option + 负载payload。Payload的长度根据数据报长度来计算。 主要是一对一的协议 举个例子: 比如某个设备需要从服务器端查询当前温度信息。 请求消息(CON): GET /temperature , 请求内容会被包在CON消息里面响应消息 (ACK): 2.05 Content “22.5 C” ,响应内容会被放在ACK消息里面 CoAP与MQTT的区别 MQTT和CoAP都是行之有效的物联网协议,但两者还是有很大区别的,比如MQTT协议是基于TCP,而CoAP协议是基于UDP。从应用方向来分析,主要区别有以下几点: 1、MQTT协议不支持带有类型或者其它帮助Clients理解的标签信息,也就是说所有MQTT Clients必须要知道消息格式。而CoAP协议则相反,因为CoAP内置发现支持和内容协商,这样便能允许设备相互窥测以找到数据交换的方式。 2、MQTT是长连接而CoAP是无连接。MQTT Clients与Broker之间保持TCP长连接,这种情形在NAT环境中也不会产生问题。如果在NAT环境下使用CoAP的话,那就需要采取一些NAT穿透性手段。 3、MQTT是多个客户端通过中央代理进行消息传递的多对多协议。它主要通过让客户端发布消息、代理决定消息路由和复制来解耦消费者和生产者。MQTT就是相当于消息传递的实时通讯总线。CoAP基本上就是一个在Server和Client之间传递状态信息的单对单协议。 HTTP协议http的全称是HyperText Transfer Protocol,超文本传输协议,这个协议的提出就是为了提供和接收HTML界面,通过这个协议在互联网上面传出web的界面信息。 HTTP协议的两个过程,Request和Response,两个都有各自的语言格式,我们看下是什么。请求报文格式:(注意这里有个换行) 响应报文格式:(注意这里有个换行) 方法method:       这个很重要,比如说GET和POST方法,这两个是很常用的,GET就是获取什么内容,而POST就是向服务器发送什么数据。当然还有其他的,比如HTTP 1.1中还有:DELETE、PUT、CONNECT、HEAD、OPTIONS、TRACE等一共8个方法(HTTP Method历史:HTTP 0.9 只有GET方法;HTTP 1.0 有GET、POST、HEAD三个方法)。请求URL:       这里填写的URL是不包含IP地址或者域名的,是主机本地文件对应的目录地址,所以我们一般看到的就是“/”。版本version:       格式是HTTP/.这样的格式,比如说HTTP/1.1.这个版本代表的就是我们使用的HTTP协议的版本,现在使用的一般是HTTP/1.1状态码status:       状态码是三个数字,代表的是请求过程中所发生的情况,比如说200代表的是成功,404代表的是找不到文件。原因短语reason-phrase:       是状态码的可读版本,状态码就是一个数字,如果你事先不知道这个数字什么意思,可以先查看一下原因短语。首部header:       注意这里的header我们不是叫做头,而是叫做首部。可能有零个首部也可能有多个首部,每个首部包含一个名字后面跟着一个冒号,然后是一个可选的空格,接着是一个值,然后换行。实体的主体部分entity-body:       实体的主体部分包含一个任意数据组成的数据块,并不是所有的报文都包含实体的主体部分,有时候只是一个空行加换行就结束了。 下面我们举个简单的例子: 请求报文:GET /index.html HTTP/1.1    Accept: text/*Host: www.myweb.com 响应报文:HTTP/1.1 200 OKContent-type: text/plainContent-length: 3  HTTP与CoAP的区别 CoAP是6LowPAN协议栈中的应用层协议,基于REST(表述性状态传递)架构风格,支持与REST进行交互。通常用户可以像使用HTTP协议一样用CoAP协议来访问物联网设备。而且CoAP消息格式使用简单的二进制格式,最小为4个字节。HTTP使用报文格式对于嵌入式设备来说需要传输数据太多,太重,不够灵活。 XMPP协议 XMPP(可扩展通讯和表示协议)是一种基于可扩展标记语言(XML)的协议, 它继承了在XML环境中灵活的发展性。可用于服务类实时通讯、表示和需求响应服务中的XML数据元流式传输。XMPP以Jabber协议为基础,而Jabber是即时通讯中常用的开放式协议。   基本网络结构 XMPP中定义了三个角色,客户端,服务器,网关。通信能够在这三者的任意两个之间双向发生。 服务器同时承担了客户端信息记录,连接管理和信息的路由功能。网关承担着与异构即时通信系统 的互联互通,异构系统可以包括SMS(短信),MSN,ICQ等。基本的网络形式是单客户端通过 TCP/IP连接到单服务器,然后在之上传输XML。 功能 传输的是与即时通讯相关的指令。在以前这些命令要么用2进制的形式发送(比如QQ),要么用纯文本指令加空格加参数加换行符的方式发送(比如MSN)。而XMPP传输的即时通讯指令的逻辑与以往相仿,只是协议的形式变成了XML格式的纯文本。举个例子看看所谓的XML(标准通用标记语言的子集)流是什么样子的?客户端:123456<?xmlversion='1.0'?>to='example_com'xmlns='jabber:client'xmlns:stream='http_etherx_jabber_org/streams'version='1.0'>服务器:1234567<?xmlversion='1.0'?>from='example_com'id='someid'xmlns='jabber:client'xmlns:stream='http_etherx_jabber_org/streams'version='1.0'>工作原理XMPP核心协议通信的基本模式就是先建立一个stream,然后协商一堆安全之类的东西, 中间通信过程就是客户端发送XML Stanza,一个接一个的。服务器根据客户端发送的信息 以及程序的逻辑,发送XML Stanza给客户端。但是这个过程并不是一问一答的,任何时候 都有可能从一方发信给另外一方。通信的最后阶段是关闭流,关闭TCP/IP连接。  网络通信过程中数据冗余率非常高,网络流量中70% 都消耗在 XMPP 协议层了。对于物联网来说,大量计算能力有限且工作在低带宽、不可靠网络的远程传感器和控制设备,省电、省流量是所有底层服务的一个关键技术指标,XMPP协议看起来已经落后了。 SoAP协议 SoAP(简单对象访问协议)是交换数据的一种协议规范,是一种轻量的、简单的、 基于可扩展标记语言(XML)的协议,它被设计成在WEB上交换结构化的和固化的信息。  SOAP 可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议(HTTP), 简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。它还支持从消息系统到 远程过程调用(RPC)等大量的应用程序。SOAP使用基于XML的数据结构和超文本传输协议 (HTTP)的组合定义了一个标准的方法来使用Internet上各种不同操作环境中的分布式对象。 总结: 从当前物联网应用发展趋势来分析,MQTT协议具有一定的优势。因为目前国内外主要的云计算服务商,比如阿里云、AWS、百度云、Azure以及腾讯云都一概支持MQTT协议。还有一个原因就是MQTT协议比CoAP成熟的要早,所以MQTT具有一定的先发优势。但随着物联网的智能化和多变化的发展,后续物联网应用平台肯定会兼容更多的物联网应用层协议。 作者:HFK_Frank 来源:CSDN 原文:https://blog.csdn.net/acongge2010/article/details/79142380 版权声明:本文为博主原创文章,转载请附上博文链接!

auto_answer 2019-12-02 01:55:21 0 浏览量 回答数 0

问题

什么是Linux 实例常用内核网络参数介绍与常见问题处理

boxti 2019-12-01 22:01:36 2069 浏览量 回答数 0

回答

Reredis未设置密码报错errorERR operation not permitted 一,什么是overcommit or oom问题 Linux对大部分申请内存的请求都回复"yes",以便能跑更多更大的程序。因为申请内存后,并不会马上使用内存。这种技术叫做Overcommit。当linux发现内存不足时,会发生OOM killer(OOM=out-of-memory)。它会选择杀死一些进程(用户态进程,不是内核线程),以便释放内存。 当oom-killer发生时,linux会选择杀死哪些进程?选择进程的函数是oom_badness函数(在mm/oom_kill.c中),该函数会计算每个进程的点数(0~1000)。点数越高,这个进程越有可能被杀死。每个进程的点数跟oom_score_adj有关,而且oom_score_adj可以被设置(-1000最低,1000最高)。 内核参数overcommit_memory,它是 内存分配策略 可选值:0、1、2。 0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。 1, 表示内核允许分配所有的物理内存,而不管当前的内存状态如何。 2, 表示内核允许分配超过所有物理内存和交换空间总和的内存 二,出现的问题 redis运行一段时间后就挂掉了,ps查看进程没啥问题,查看redis的log,发现有warning,如下: [26145] 07 Dec 19:54:54 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect. telnet到6379,任何操作都会返回not permitted, [root@localhost redis]# telnet 127.0.0.1 6379 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. keys * -ERR operation not permitted redis并没有设置requirepass,不可能是因为权限问题导致not permitted。 三,解决办法 1,修改内核参数,3种方法,任选其一: (1)编辑/etc/sysctl.conf ,改vm.overcommit_memory=1,然后sysctl -p 使配置文件生效 (2)sysctl vm.overcommit_memory=1 (3)echo 1 > /proc/sys/vm/overcommit_memory 2,修改redis.conf,然后重启redis maxmemory 5368709120 maxmemory-policy allkeys-lru maxmemory-samples 3 设置一下maxmemory,建议设置为物理内存的1/2到3/4,大小不要超过最大物理内存。

sendinfo 2019-12-02 01:17:26 0 浏览量 回答数 0

问题

【精品问答】Java必备核心知识1000+(附源码)

问问小秘 2019-12-01 22:00:28 870 浏览量 回答数 1

问题

不搞清这8大算法思想,刷再多题效果也不好的 7月23日 【今日算法】

游客ih62co2qqq5ww 2020-07-29 11:10:09 3 浏览量 回答数 1

回答

你这个问题可以抽象一下。令每个用户和每个IP存在一个以时间轴为基础的登陆数组(一维,下标是历史时间到现在的时间差,值是对应时间片比如分钟内的总登陆次数)。需要有以下基准动作。 每个时间,比如分钟,对整个数组进行移动。 当有新登陆上来,检测整个时间窗内的登陆总次数,比如你的时间窗是30分钟。如果总次数超过你设定的K(30),则对其禁止T(30)。如果没有超过K(30),你对时间窗最后的数据,进行较窄窗口(例如10分钟)再判断。如果总次数超过 K(10) 则对其禁止T(10)。如果小于 K(10),则对最小窗口进行判断,例如10分钟,如果总次数超过K(1),则对其禁止 T(10)。 禁止过程中,该IP,该用户被直接否定,但是上述对应数组的内容,仍然根据时间进行移动修正。将较老的数据刷掉。 当然这个是原理算法。如果这个算法思路符合你的目标。则后续会需要有优化的简化算法。基本思路是压缩上述所谓“数组”的存储空间,以及压缩上述刷新和移动,判断的计算步骤。 上述具备IP和用户对应的数组是动态的。每分钟,刷新时,需要将即便下一分钟产生一次登陆但不存在禁止的数组给删除掉。 而所谓数组,是通过bit来描述,比如每4个bit表示当前的分钟内的登录次数,如果是15次以上,假设你一定会禁止他,则仍然等于15次。类似这样。 而在刷新左移时,对每个分钟的登陆次数,修正加权值,并反馈到最新存储空间内,此时所有的判断都集中在最新存储空间判断,而不用任意判断都要累加操作。这种近似的优化算法,只要能达到目的就可以了。没有必要考虑因为精度问题导致结果的不完全一致性。######回复 @waney : 其实很简单。但是我难得搞公式编辑器了。######好复杂 听不懂,谢谢你。###### 登陆验证码, 登陆验证问题, 同用户名访问失败多次直接封用户一段时间, 如果还是继续尝试失败,直接封IP。 以上为个人意见。######回复 @JustForFly : 因为discuz有这些,根本起不到作用。######那我就不知道你还想要什么了######discuz 这些都有的###### 增加验证码,可避免一些简单的模拟登录; 增加登录失败次数检查,超过N次后禁用用户或IP若干时间; ######discuz 都有的######直接把用户隐射到MAP,不用查数据库,直接查询MAP ######先把数据库的用户查出来,引射到一个map对象,然后用户登录就直接去map对象里面匹配,比如5分钟或者10分钟把在把map里面的用户和数据库同步一次,呵呵,这个办法有点傻。######这个怎讲?听不懂。######这个事情很麻烦,一楼的方法是有效的。但是是针对用户存在IP绑定信息的情况下。当然大多数时刻也是如此。如果抽象来看,楼主也说了,模拟提交,或者从不同IP上大量测试用户名的方式,回避一楼的方案。这个问题但抽象的来看,几乎无解,因为问题和设计目标是矛盾的。还要看楼主其他方面的需求。最终想防止什么。 ######回复 @waney : 延迟,如果发现不匹配,SERVER等待2到3秒后在告知客户端。但客户端会采用无论是否回复,仍然发送新用户方式。######回复 @中山野鬼 : ip是变化的,验证那些都没有用,如果拒绝这类特定请求的频率过高的。######回复 @waney : 两个方案。延迟,绑定IP的锁定。前者方法很多,那些图片内部字符识别本身就是个延迟目的。不是考智商用的。######有人模拟大量提交,匹配然后获得匹配正确的用户名和密码。###### 既然是字典匹配 那肯定会出现大量 同一账号使用不同的错误密码登陆的记录了.. 可以从这方面下手...我的方案是:当检测到某一账号在一段时间内连续输错密码达到一定次数 则帐号进入内部锁定状态.当该帐号成功登陆之后,将无法进行任何操作.而是会进入一个锁定页面. 系统会要求该帐号进行解锁操作.解锁成功后,才能继续操作. 至于解锁操作的话最简单就是发一封邮件给用户注册邮箱,用户根据邮件提示解锁. 这样即使别人凭字典匹配到了密码也没用.而且一旦用户登陆之后发现自己的帐号被锁定就知道肯定有人尝试破解自己帐号的密码.那么此时也可以提示用户修改密码.这样最大限度的可以保证帐号安全了。######而且我没说要禁止...只是帐号置为 内部锁定状态. 你只需要检测用户是否登陆的时候检测是否处于锁定状态就可以了. 基本上只需要加一个字段和一小段代码的######呵呵 如果别人第一次就匹配到了密码 你怎么能知道这个人是不是帐号的拥有者呢. 不可能有100% 完美解决的方案的.######你不能保证别人不会第一次就匹配到正确的啊。而且全都加入禁止,那量不是一般的大啊,所以 我想寻求一个彻底的办法就是如何设置条件抛弃这个请求。###### 当然 上述方法也有缺陷.如果有人恶意用错误的密码尝试登陆某一账号将导致该帐号的用户每次登陆都要进行解锁操作. 那么就还需要一些其他的补充措施来进行完善了.例如:可设置一段时间内 帐号禁止进入锁定状态。###### 楼主,你这个是个博弈的过程。主要策略是延缓对方或者将对方行为区别于正常用户。如果是绑定IP比如3,4次登陆就锁定1分钟,对方可以替换IP,只要IP数量N足够多。上限仍然由他的IP数量决定。 如果你认为1分钟内如果登陆4次以上,就锁定这个IP30分钟。他完全可以每个IP每分钟就登陆4次,则没分钟也达到了4万次的用户访问检测。 但攻击者的IP数量如果不是非常多时,你可以尝试累计对IP进行长时间累计滤波观测。如果一个IP在1分钟内登录4次,在5分钟内登陆 10次,在30分钟内登陆 20次,则均对其锁定。 这样的目的是降低攻击者独立IP的使用价值。以和传统用户行为区别开来。 我先吃饭。回头给你个算法描述,解决这种问题。动态时间窗内的信号检测。######谢谢。

kun坤 2020-06-08 11:25:44 0 浏览量 回答数 0

问题

第6篇 指针数组字符串(下)补充:报错

kun坤 2020-06-08 11:02:03 3 浏览量 回答数 1

回答

1 syslogd的配置文件 syslogd的配置文件/etc/syslog.conf规定了系统中需要监视的事件和相应的日志的保存位置 cat /etc/syslog.conf # Log all kernel messages to the console. # Logging much else clutters up the screen. #kern.* /dev/console # Log anything (except mail) of level info or higher. # Don't log private authentication messages! .info;mail.none;authpriv.none;cron.none /var/log/messages #除了mail/news/authpriv/cron以外,将info或更高级别的消息送到/var/log/messages,其中是通配符,代表任何设备;none表示不对任何级别的信息进行记录 # The authpriv file has restricted access. authpriv.* /var/log/secure #将authpirv设备的任何级别的信息记录到/var/log/secure文件中,这主要是一些和认证,权限使用相关的信息. # Log all the mail messages in one place. mail.* -/var/log/maillog #将mail设备中的任何级别的信息记录到/var/log/maillog文件中, 这主要是和电子邮件相关的信息. # Log cron stuff cron.* /var/log/cron #将cron设备中的任何级别的信息记录到/var/log/cron文件中, 这主要是和系统中定期执行的任务相关的信息. # Everybody gets emergency messages .emerg * #将任何设备的emerg级别或更高级别的消息发送给所有正在系统上的用户. # Save news errors of level crit and higher in a special file. uucp,news.crit /var/log/spooler #将uucp和news设备的crit级别或更高级别的消息记录到/var/log/spooler文件中. # Save boot messages also to boot.log local7. /var/log/boot.log #将和本地系统启动相关的信息记录到/var/log/boot.log文件中. 2. syslogd语法 该配置文件的每一行的格式如下: facility.priority action 设备.级别 动作 3. Syslogd设备字段 设备字段用来指定需要监视的事件.它可取的值如下: authpriv cron daemon kern lpr syslog user uucp mail news 报告认证活动通常,口令等私有信息不会被记录 报告与cron和at有关的信息 报告与xinetd有关的信息 报告与内核有关的信息 报告与打印服务有关的信息 由syslog生成的信息 报告由用户程序生成的任何信息由UUCP生成的信息 报告与邮件服务有关的信息 报告与网络新闻服务有关的信息 4. syslogd级别字段 级别字段用于指明与每一种功能有关的级别和优先级: alert crit err warning notice info debug none * emerg 需要立即引起注意的情况 危险情况的警告 除了emerg,alert,crit的其他错误 警告信息需要引起注意的情况 值得报告的消息 由运行于debug模式的程序所产生的消息 用于禁止任何消息 所有级别,除了none 出现紧急情况使得该系统不可用 5. syslogd动作字段 动作字段用于描述对应功能的动作 file username device @hostname 指定一个绝对路径的日志文件名记录日志信息 发送信息到指定用户,*表示所有用户 将信息发送到指定的设备中,如/dev/console将信息发送到可解析的远程主机hostname,且该主机必须正在运行syslogd并可以识别syslog的配置文件 6. 查看日志文件 常见的日志文件日志文件通常存放在/var/log目录下.在该目录下除了包括syslogd 记录的日志之外,同时还包含所有应用程序的日志. 为了查看日志文件的内容必须要有root权限.日志文件中的信息很重要,只能让超级用户有访问这些文件的权限. 7. log cups/ httpd/ mail/ news/ boot.log dmesg maillog messages secure wtmp 存储CUPS打印系统的日志目录 记录apache的访问日志和错误日志目录 存储mail日志目录 存储INN新闻系统的日志目录 记录系统启动日志记录系统启动时的消息日志 记录邮件系统的日志 由syslogd记录的info或更高级别的消息日志 由syslogd记录的认证日志 一个用户每次登录进入和退出时间的永久记录 8. 查看文本日志文件 绝大多数日志文件是纯文本文件,每一行就是一个消息.只要是在Linux下能够处理纯文本的工具都能用来查看日志文件.可以使用 cat,tac, more,less,tail和grep进行查看文件中每一行表示一个消息,而且都由四个域的固定格式组成: 时间标签(Timestamp):表示消息发出的日期和时间. 主机名(Hostname):表示生成消息的计算机的名字. 生成消息的子系统的名字:可以是"Kernel",表示消息来自内核或者 是进程的名字,表示发出消息的程序的名字. 在方括号里的是进程的PID. 消息(Message),即消息的内容. syslog发出的消息,说明了守护进程已经在 Dec 16,03:32:41 重新启动了. Dec 16 03:32:41 cnetos5 syslogd 1.4.1: restart. # 在 Dec 19,00:20:56 启动了内核日志 klogd Dec 19 00:20:56 cnetos5 kernel: klogd 1.4.1, log source = /proc/kmsg started. # 在 Dec 19,00:21:01 启动了xinetd Dec 19 00:21:01 cnetos5 xinetd[2418]: xinetd Version 2.3.14 started with libwrap loadavg labeled-networking options compiled in. 9. 查看非文本日志文件Lastlog 也有一些日志文件是二进制文件,需要使用相应的命令进行读取. 使用lastlog命令来检查某特定用户上次登录的时间,并格式化输出上次登录日志 /var/log/lastlog 的内容 rpc 从未登录过 rpcuser 从未登录过 sshd 从未登录过 pcap 从未登录过 haldaemon 从未登录过 xfs 从未登录过 gdm 从未登录过 boobooke 从未登录过 baobao pts/1 192.168.1.2 三 11月 26 12:44:32 +0800 2008 abc 从未登录过 test pts/1 192.168.1.5 四 11月 27 17:30:53 +0800 2008 test01 从未登录过 last命令往回搜索/var/log/wtmp来显示自从文件第一次创建以来登录过用户 root pts/1 116.226.69.195 Fri Aug 31 15:48 - 18:37 (02:49) 10. 查看非文本日志文件lastb lastb命令搜索/var/log/btmp来显示登录未成功的信息. root ssh:notty 222.143.27.97 Thu Sep 6 19:43 - 19:43 (00:00) 11. 查看非文本日志文件who who命令查询wtmp文件并报告当前登录的每个用户.who命令的缺省输出包括用户名,终端类型,登录日期及远程主机. [root@server ~]# who root pts/0 2012-09-08 10:18 (116.226.69.195) [root@server ~]# w 10:41:31 up 212 days, 20:19, 1 user, load average: 0.21, 0.16, 0.14 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT root pts/0 116.226.69.195 10:18 0.00s 0.09s 0.00s w 12.日志滚动 为什么使用日志滚动所有的日志文件都会随着时间的推移和访问次数的增加而迅速增长,因此必须对日志文件进行定期清理以免造成磁盘空间的不必要的浪费.同时也 加快了管理员查看日志所用的时间,因为打开小文件的速度比打开大文件的速度要快. Logrotate 其命令格式为: logrotate [选项] <configfile> -d:详细显示指令执行过程,便于排错或了解程序执行的情况. -f:强行启动记录文件维护操作,即使logrotate指令认为无需要亦然 -m command:指定发送邮件的程序,默认为 /usr/bin/mail. -s statefile:使用指定的状态文件. -v:在执行日志滚动时显示详细信息. 13. 日志滚动 logrotate 默认的主配置文件是 /etc/logrotate.conf /etc/logrotate.d 的目录下的文件,这些文件被 include 到主配置文件 /etc/logrotate.conf 中 # see "man logrotate" for details # 每周清理一次日志文件 weekly #保存过去四周的日志文件 rotate 4 #清除旧日志文件的同时,创建新的空日志文件 create #若使用压缩的日志文件,请删除下面行的注释符 #compress #包含/etc/logrotate.d目录下的所有配置文件 include /etc/logrotate.d #设置/var/log/wtmp的日志滚动 /var/log/wtmp { monthly minsize 1M create 0664 root utmp rotate 1 } 可以使用ls命令显示/etc/logrotate.d目录: [root@server ~]# ls /etc/logrotate.d mgetty psacct rpm setroubleshoot snmpd syslog yum 每个文件的基本格式均相同 [root@server ~]# cat /etc/logrotate.d/syslog /var/log/messages /var/log/secure /var/log/maillog /var/log/spooler /var/log/boot.log /var/log/cron { #对日志文件 sharedscripts #调用日志滚动通用函数sharedscripts postrotate #在日志滚动之后执行语句括号postrotate和endscript之间的命令postrotate /bin/kill -HUP cat /var/run/syslogd.pid 2> /dev/null 2> /dev/null || true /bin/kill -HUP cat /var/run/rsyslogd.pid 2> /dev/null 2> /dev/null || true #重新启动syslogd endscript } logrotate是由crond运行的,在默认配置中,可以发现在/etc/cron.daily目录中有一个名为logrotate的文件 [root@server ~]# cat /etc/cron.daily/logrotate #!/bin/sh /usr/sbin/logrotate /etc/logrotate.conf EXITVALUE=$? if [ $EXITVALUE != 0 ]; then /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]" fi exit 0 答案来源于网络

养狐狸的猫 2019-12-02 03:06:55 0 浏览量 回答数 0

回答

递归4—递归的弱点 之所以没有把这段归为算法的讨论,因为这里讨论的不在是算法,而只是讨论一下滥用递归的不好的一面。 递归的用法似乎是很容易的,但是递归还是有她的致命弱点,那就是如果运用不恰当,滥用递归,程序的运行效率会非常的低,低到什么程度,低到出乎你的想像。当然,平时的小程序是看不出什么的,但是一旦在大项目里滥用递归,效率问题将引起程序的实用性的大大降低。 例子:求1到200的自然数的和。 第一种做法: #include <stdio.h> void main() { int i; int sum=0; for(i=1;i<=200;i++) { sum+=i; } printf("%d\n",sum); } 该代码中使用变量2个,计算200次。再看下个代码: #include <stdio.h> int add(int i) { if(i==1) { return i; } else { return i+add(i-1); } } void main() { int i; int sum=0; sum=add(200); printf("%d\n",sum); } 但看add()函数,每次调用要声明一个变量,每次调用要计算一次,所以应该是200个变量,200次计算,对比一下想想,如果程序要求递归次数非常多的时候,而且类似与这种情况,我们还能用递归去做吗。这个时候宁愿麻烦点去考虑其他办法,也要尝试摆脱递归的干扰。 21:21 | 添加评论 | 固定链接 | 引用通告 (0) | 记录它 | 计算机与 Internet 程序算法5—递归3—递归的再次挖掘 递归的魅力就在于递归的代码,写出来实在是太简练了,而且能解决很多看起来似乎有规律但是又不是一下子能表达清楚的一些问题。思路清晰了,递归一写出来问题立即就解决了,给人一重感觉,递归这么好用。我们在此再更深的挖掘一下递归的用法。 之前再强调一点,也许有人会问,你前边的例子用递归似乎是更麻烦了。是,是麻烦了,因为为了方便理解,只能举一些容易理解的例子,一般等实际应用递归的时候,远远不是这种状态。 好了我们现在看一个数字的序列;有一组数的集合{1,2,4,7,11,16,22,29,37,46,56……}我故意多给几项,一般是只给前4项让你找规律的。序列给了,要求是求前50项的和。规律。有。还是没有。一看就象有,但是又看不出来,我多给了几项,应该很快看出来了,哦,原来每相邻的两项的差是个自然数排列,2-1=1,4-2=2,7-4=3,11-7=4,16-11=5…… 好了,把规律找出来了,一开始可能觉得没头绪,没问题,咱们把这个序列存放到一个数组总可以吧。那我们就声明一个数组,存放前50个数据,一个一个相加总可以了。于是有了下边的写法: #include <stdio.h> void main() { int i,a[50],sum=0; a[0]=1; for(i=1;i<50;i++) { a[i]=a[i-1]+i; } for(i=0;i<50;i++) { sum+=a[i]; } printf("%d\n",sum); } 好了,代码运行一下,结果出来了,正确不正确呢。自己测试吧,把50项改成1、2、3、4、5……项,试试前多少项是不是正确,虽然这不是正确的测试方法,但是的确是常用的测试方法。 等到这个代码已经完全理解了,完全明白了正个计算过程,我们就应该对这段代码进行改写优化了,毕竟这个代码还是不值得用一个数组的,那么我们尝试着只用变量去做一下: #include <stdio.h> void main() { int i; int number=1; int sum=0; for(i=0;i<50;i++) { number+=i; sum+=number; } printf("%d\n",sum); } 不知道我这样写是不是跨度大了点,但是我不准备详细解释了,很多东西需要你去认真分析的,所以很多东西如果不懂,自己想清楚比别人解释的效果会更好,因为别人讲只能让你理解,如果你自己去想,你就在理解的同时学会了思考。 这个代码写出来,不要继续看下去,先自己尝试着把这个题目用递归做一下看看自己能不能写出来,当然,递归并不是那么轻松就能使用的,有时候也是需要去细心设计的。如果做出来了,对比一下下边的代码,如果没有写出来,建议认真分析后边的代码,然后最好是能完全掌握,能自己随时把这行代码写出来: #include <stdio.h> int add(int n,int num,int i) { num+=i; if(i>=n-1) { return num; } else { return num+add(n,num,i+1); } } void main() { int sum; sum=add(50,1,0); /*50表示前50象项*/ printf("%d\n",sum); } 当然这个代码中的n只是一个参考变量,如果把if(i>=n-1)中的n该成50,那么就不需要这个n了,函数两个参数就可以了,这样写是为了修改方便。 20:28 | 添加评论 | 固定链接 | 引用通告 (0) | 记录它 | 计算机与 Internet 程序算法4—递归2—递归的魅力 两天没有再写下去,因为毕竟有时候会有点心情问题,有时候觉得心情不好,一下子什么东西都想不起来了,很多时候写一些东西是需要状态的,一旦状态有了,想的东西才能顺利的写出来,虽然有些东西写出来在别人看来很垃圾,但是起码自己觉得还是相当满意的,我写这个本来就没有多少技术含量,只是想给初学程序的人一些指引,加快他们对程序的领悟。 好了,言归正传,继续上次递归的讨论,看看递归的魅力所在。 有这样一个问题,说一个猴子和一堆苹果,猴子一天吃一半,然后再吃一个,10天后剩下一个了,也就是说吃了10次,剩下1个了。问原来一共有多少苹果。 当然我们的目的不是求出苹果的数量,而是寻求一种解决问题的方法,这个问题一出来,通常对程序掌握深度不一样的朋友对这个题会有不同的认识,首先介绍一种解决方法,这种人脑袋还是比较聪明的,思路非常的明确,也有可能语言工具掌握的也不错,代码写出来非常准确,先看一下代码再做评价吧: #include <stdio.h> void main() { int day=10; int apple; int i,j; for(i=1;;i++) { apple=i; for(j=0;j<day;j++) { if(apple%2==0&&apple>0) { apple/=2; apple--; } else { break; } } if(j==day&&apple==1) { printf("%d\n",i); return; } } } 程序的大概思路很明确,简单介绍一下,这种写法就是从一个苹果开始算起,for(i=1;;i++)的作用就是改变苹果的数量,如果1个符合条件,那就试试2个,然后3个、4个一直到适合为止,里边的for循环就是把每一次取得的苹果的数目进行计算,如果每次都能顺利的被2整除(也就是说每次都能保证猴子能正好吃一半),然后再减一一直到最后,如果最后苹果剩下是一个而且天数正好是10天,那么就输出一下苹果的数目,整个程序退出,如果看不明白的没关系,这个写法非常的不适用,我们叫写出这种算法的人傻X,虽然这种人脑袋也挺聪明,能写出一些新鲜的写法,但是又脏又臭,代码既不简练又不高效。 所以说,有时候有些人以为自己学的很好了,自己所做的一切都是最好的,这种想法是不正确的,也许有些初学者没有什么经验写出来的代码却更让人容易明白点,那么也是先看看代码: #include <stdio.h> void main() { int day[11]; int i; day[0]=1; for(i=1;i<11;i++) { day[i]=(day[i-1]+1)*2; } printf("%d\n",day[10]); } 代码不长,而且也恰当的应用了题目中的规律,不是说要吃一半然后再吃一个吗。那我用数组来存放每天苹果的数量,用day[0]表示最后一天的苹果数量,那就是剩下的一个,然后就是找规律了,什么规律。就是如果猴子不多吃一个的话,那就是正好吃了一半,也就是说猴子当天吃了之后剩余的苹果的数目加1个然后再乘以2就是前一天的数目了,这样一想这个题目就简单的多了,于是这个题用数组就轻松的做出来了。 那么这个代码究竟是不是已经很好了呢,我们注意到,这里边每个数组元素只用了一次并没有被重复使用,再这种情况下我们是不是可以用一种方法代替数组呢。于是就有了更优化的写法,这个写法似乎已经是相当简练了: #include <stdio.h> void main() { int apple=1; int i; for(i=0;i<10;i++) { apple=(apple+1)*2; } printf("%d\n",apple); } 代码写到这里已经把问题完全抽象化了,所以我们就应该站在数学的角度去分析了。也许我们就应该结束了讨论,但是偏偏这个时候,又来了递归,悄悄的通过美丽的调用显示了一下她的魅力: #include <stdio.h> int apple(int i) { if(i==0) { return 1; } else { return (apple(i-1)+1)*2; } } void main() { int i; i=apple(10); printf("%d\n",i); } 原理都还是一样的,但是写出来的格式已经完全变掉了,没有了for循环。假想一个复杂的问题远比这个问题复杂,而且没有固定循环次数,那么我们再使用循环虽然也能解决问题,但是可能面临循环难以设计、控制等问题,这个时候用递归可能就会让问题变的非常的清晰。 另外说一点,一般我这里的代码,并不是从最差到最好的,基本排列是从最差到最合适的代码(当然是本人认为最合适的,也许还有更好的,本人能力所限了),然后最后给出一种比较违反常规的代码,一般是不赞成用最后一种代码的,当然有时候最后一种代码也许是最好的选择,看情况吧。 20:25 | 添加评论 | 固定链接 | 引用通告 (0) | 记录它 | 计算机与 Internet 10月15日 程序算法3—递归1—递归小显威力 现在用C语言实现一个字符串的倒序输出,当然,方法也是很多的,但是如果程序中能有相对优化的方法或者简单明了易读的方法,那对你自己或者别人都是一种幸福。 第一种写法,这类写法既浪费内存又不实用,一般是刚学程序的才这样做,程序的结构很简单,利用的是数组: #include <stdio.h> void main() { char c[2000]; int i,length=0; for(i=0;i<2000;i++) { scanf("%c",&c[i]); if(c[i]=='\n') { break; } else { length++; } } for(i=length;i>0;i--) { printf("%c",c[i-1]); } printf("\n"); } 这段代码中的数组,声明大了浪费内存空间,声明小了又怕不够,所以写这种代码的人一般写完之后会祈祷,祈祷测试的人不要输入的太多,太多就不能完全显示了。 与其这么提心吊胆,于是又有人想出了第二种方法,终于解决了一些问题,而且完全实现了程序的实际要求,于是,这种人经过一番苦想,觉得问题终于可以解决了,这种方法看起来是一种很不错的方法。 #include <stdio.h> #include <malloc.h> void main() { int i; char *c; c=(char *)malloc(1*sizeof(char)); for(i=0;;i++) { *(c+i)=getchar(); if(*(c+i)=='\n') { *(c+i)='\0'; break; } else c=(char *)realloc(c,(i+2)*sizeof(char)); } for(--i;i>=0;i--) { putchar(*(c+i)); } printf("\n"); free(c); } 怎么样。不错,准确的应用内存,几乎没有浪费什么空间,这种方法也体现了一下指针的强大功能,写这个程序虽然不敢说这个人已经掌握了指针的应用,但是起码可以说他已经会用指针了。代码写出来,看起来已经有点美感。 但是也有一些人还是比较喜欢动脑筋的,经过一番思考,终于想出了第三种比较容易写的方法,也许有写初学者可能觉得有些难度,但是事实上这个东西一点都不难,如果稍微有点程序功底之后再看这段代码,应该是相当轻松。 #include <stdio.h> void run() { char c; c=getchar(); if(c!='\n') { run(); } else { return; } putchar(c); } void main() { run(); printf("\n"); } 写出的代码让人眼前一亮,哇。原来递归功能简单而又好用,那我们为什么不好好利用呢。但是递归也不一定就是最好的选择,因为有时候虽然递归用起来很方便,但是效率却不高,以后的讨论中还会详细说明。

一键天涯 2019-12-02 01:24:01 0 浏览量 回答数 0

回答

很多人电脑是不是会出现各种蓝屏故障问题啊,出现问题又不知道怎么样解决。 1.故障检查信息 *STOP 0x0000001E(0xC0000005,0xFDE38AF9,0x0000001,0x7E8B0EB4)KMODE_EXCEPTION_NOT_HANDLED * 其中错误的第一部分是停机码(Stop Code)也就是STOP 0x0000001E, 用于识别已发生错误的类型, 错误第二部分是被括号括起来的四个数字集, 表示随机的开发人员定义的参数(这个参数对于普通用户根本无法理解, 只有驱动程序编写者或者操作系统的开发人员才懂). 第三部分是错误名. 信息第一行通常用来识别生产错误的驱动程序或者设备. 2.推荐操作蓝屏第二部分是推荐用户进行的操作信息. 有时, 推荐的操作仅仅是一般性的建议; 有时, 也就是显示一条与当前问题相关的提示. 一般来说, 惟一的建议就是重启. 3.调试端口告诉用户内存转储映像是否写到磁盘商了, 使用内存转储映像可以确定发生问题的性质, 还会告诉用户调试信息是否被传到另一台电脑商, 以及使用了什么端口完成这次通讯. 蓝屏时的处理办法:1.重启有时只是某个程序或驱动程序一时犯错, 重启后有可能就会正常。 2.新硬件首先, 应该检查新硬件是否插牢, 这个被许多人忽视的问题往往会引发许多莫名其妙的故障. 如果确认没有问题, 将其拔下, 然后换个插槽试试, 并安装最新的驱动程序. 同时还应对照微软网站的硬件兼容类别检查一下硬件是否与操作系统兼容. 3.新驱动和新服务如果刚安装完某个硬件的新驱动, 或安装了某个软件, 而它又在系统服务中添加了相应项目, 在重启或使用中出现了蓝屏故障, 请到安全模式来卸载或禁用它们. 4.检查病毒比如冲击波和振荡波等病毒有时会导致Windows蓝屏死机, 因此查杀病毒必不可少. 同时一些木马间谍软件也会引发蓝屏, 所以最好再用相关工具进行扫描检查. 5.检查BIOS和硬件兼容性对于新装的电脑经常出现蓝屏问题, 应该检查并升级BIOS到最新版本, 同时关闭其中的内存相关项, 比如:缓存和映射. 另外, 还应该对照微软的硬件兼容列表检查自己的硬件. 还有就是, 如果主板BIOS无法支持大容量硬盘也会导致蓝屏, 需要对其进行升级. 6.检查系统曰志在开始-->菜单中输入:EventVwr.msc, 回车出现"事件查看器", 注意检查其中的"系统曰志"和"应用程序曰志"中表明"错误"的项. 7.最后一次正确配置 最后一次正确配置界面 一般情况下, 蓝屏都出现于更新了硬件驱动或新加硬件并安装其驱动后, 这时Windows 2K/XP提供的"最后一次正确配置"就是解决蓝屏的快捷方式. 重启系统, 在出现启动菜单时按下F8键就会出现高级启动选项菜单, 接着选择"最后一次正确配置". 常见的蓝屏代码 0X0000000操作完成 0X0000001不正确的函数 0X0000002系统找不到指定的文件 0X0000003系统找不到指定的路径 0X0000004系统无法打开文件 0X0000005拒绝存取 0X0000006无效的代码 0X0000007内存控制模块已损坏 0X0000008内存空间不足,无法处理这个指令 0X0000009内存控制模块位址无效 0X000000A环境不正确 0X000000B尝试载入一个格式错误的程序 0X000000C存取码错误 0X000000D资料错误 0X000000E内存空间不够,无法完成这项操作 0X000000F系统找不到指定的硬盘 0X0000010无法移除目录 0X0000011系统无法将文件移到其他的硬盘 0X0000012没有任何文件 0X0000019找不到指定扇区或磁道 0X000001A指定的磁盘或磁片无法存取 0X000001B磁盘找不到要求的装置 0X000001C打印机没有纸 0X000001D系统无法将资料写入指定的磁盘 0X000001E系统无法读取指定的装置 0X000001F连接到系统的某个装置没有作用 0X0000021文件的一部分被锁定,现在无法存取 0X0000024开启的分享文件数量太多 0X0000026到达文件结尾 0X0000027磁盘已满 0X0000036网络繁忙 0X000003B网络发生意外的错误 0X0000043网络名称找不到 0X0000050文件已经存在 0X0000052无法建立目录或文件 0X0000053 INT24失败 0X000006B因为代用的磁盘尚未插入,所以程序已经停止 0X000006C磁盘正在使用中或被锁定 0X000006F文件名太长 0X0000070硬盘空间不足 0X000007F找不到指定的程序 0X000045B系统正在关机 0X000045C无法中止系统关机,因为没有关机的动作在进行中 0X000046A可用服务器储存空间不足 0X0000475系统 BIOS无法变更系统电源状态 0X000047E指定的程序需要新的windows版本 0X000047F指定的程序不是windwos或ms-dos程序 0X0000480指定的程序已经启动,无法再启动一次 0X0000481指定的程序是为旧版的windows所写的 0X0000482执行此应用程序所需的程序库文件之一被损 0X0000483没有应用程序与此项操作的指定文件建立关联 0X0000484传送指令到应用程序无效 0X00005A2指定的装置名称无效 0X00005AA系统资源不足,无法完成所要求的服务 0X00005AB系统资源不足,无法完成所要求的服务 0X00005AC系统资源不足,无法完成所要求的服务 110 0x006E系统无法开启指定的装置或档案。 111 0x006F档名太长。 112 0x0070磁碟空间不足。 113 0x0071没有可用的内部档案识别字。 114 0x0072目标内部档案识别字不正确。 117 0x0075由应用程式所执行的IOCTL 呼叫不正确。 118 0x0076写入验证参数值不正确。 119 0x0077系统不支援所要求的指令。 120 0x0078此项功能仅在 Win32 模式有效。 121 0x0079 semaphore超过逾时期间。 122 0x007A传到系统呼叫的资料区域太小。 123 0x007B档名、目录名称或储存体标 124 0x007C系统呼叫层次不正确。 125 0x007D磁碟没有设定标 126 0x007E找不到指定的模组。 127 0x007F找不到指定的程序。 128 0x0080没有子行程可供等待。 129 0x0081 %1这个应用程式无法在 Win32 模式下执行。 130 0x0082 Attempt to use a file handle to an open disk partition for an operation other than raw disk I/O. 131 0x0083尝试将档案指标移至档案开头之前。 132 0x0084无法在指定的装置或档案,设定档案指标。 133 0x0085 JOIN 或 SUBST指令无法用於内含事先结合过的磁碟机。 134 0x0086尝试在已经结合的磁碟机,使用JOIN 或 SUBST 指令。 135 0x0087尝试在已经替换的磁碟机,使用 JOIN 或 SUBST 指令。 136 0x0088系统尝试删除未连结过的磁碟机的连结关系。 137 0x0089系统尝试删除未替换过的磁碟机的替换关系。 138 0x008A系统尝试将磁碟机结合到已经结合过之磁碟机的目录。 139 0x008B系统尝试将磁碟机替换成已经替换过之磁碟机的目录。 140 0x008C系统尝试将磁碟机替换成已经替换过之磁碟机的目录.

独步清客 2019-12-02 00:43:56 0 浏览量 回答数 0

问题

ECS-CentOS  /etc/fstab格式简介

ethnicity 2019-12-01 21:03:38 10993 浏览量 回答数 1

回答

Arraylist和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加插入元素,都允许直接序号索引元素,但是插入数据要涉及到数组元素移动等内存操作,所以插入数据慢,查找有下标,所以查询数据快,Vector由于使用了synchronized方法-线程安全,所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项前后项即可,插入数据较快。线性表,链表,哈希表是常用的数据结构,在进行java开发时,JDK已经为我们提供了一系列相应的类实现基本的数据结构,这些结构均在java.util包中,collection├List│├LinkedList│├ArrayList│└Vector│ └Stack└SetMap├Hashtable├HashMap└WeakHashMapCollection接口Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(elements),一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:    Iterator it = collection.iterator(); // 获得一个迭代子    while(it.hasNext()) {      Object obj = it.next(); // 得到下一个元素    }  由Collection接口派生的两个接口是List和Set。List接口  List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。和下面要提到的Set不同,List允许有相同的元素。  除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。  实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。ArrayList类  ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。  每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。  和LinkedList一样,ArrayList也是非同步的(unsynchronized)。Vector类  Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。Stack 类  Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。Map接口  请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。Hashtable类  Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。  添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。使用Hashtable的简单示例如下,将1,2,3放到Hashtable中,他们的key分别是”one”,”two”,”three”:    Hashtable numbers = new Hashtable();    numbers.put(“one”, new Integer(1));    numbers.put(“two”, new Integer(2));    numbers.put(“three”, new Integer(3));  要取出一个数,比如2,用相应的key:    Integer n = (Integer)numbers.get(“two”);    System.out.println(“two = ” + n);  由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。  如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。  Hashtable是同步的。HashMap类  HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。WeakHashMap类  WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。总结  如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。  如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。  要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。  尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。同步性Vector是同步的。这个类中的一些方法保证了Vector中的对象是线程安全的。而ArrayList则是异步的,因此ArrayList中的对象并不是线程安全的。因为同步的要求会影响执行的效率,所以如果你不需要线程安全的集合那么使用ArrayList是一个很好的选择,这样可以避免由于同步带来的不必要的性能开销。数据增长从内部实现机制来讲ArrayList和Vector都是使用数组(Array)来控制集合中的对象。当你向这两种类型中增加元素的时候,如果元素的数目超出了内部数组目前的长度它们都需要扩展内部数组的长度,Vector缺省情况下自动增长原来一倍的数组长度,ArrayList是原来的50%,所以最后你获得的这个集合所占的空间总是比你实际需要的要大。所以如果你要在集合中保存大量的数据那么使用Vector有一些优势,因为你可以通过设置集合的初始化大小来避免不必要的资源开销。使用模式在ArrayList和Vector中,从一个指定的位置(通过索引)查找数据或是在集合的末尾增加、移除一个元素所花费的时间是一样的,这个时间我们用O(1)表示。但是,如果在集合的其他位置增加或移除元素那么花费的时间会呈线形增长:O(n-i),其中n代表集合中元素的个数,i代表元素增加或移除元素的索引位置。为什么会这样呢?以为在进行上述操作的时候集合中第i和第i个元素之后的所有元素都要执行位移的操作。这一切意味着什么呢?这意味着,你只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或ArrayList都可以。如果是其他操作,你最好选择其他的集合操作类。比如,LinkList集合类在增加或移除集合中任何位置的元素所花费的时间都是一样的?O(1),但它在索引一个元素的使用缺比较慢-O(i),其中i是索引的位置.使用ArrayList也很容易,因为你可以简单的使用索引来代替创建iterator对象的操作。LinkList也会为每个插入的元素创建对象,所有你要明白它也会带来额外的开销。最后,在《Practical Java》一书中Peter Haggar建议使用一个简单的数组(Array)来代替Vector或ArrayList。尤其是对于执行效率要求高的程序更应如此。因为使用数组(Array)避免了同步、额外的方法调用和不必要的重新分配空间的操作。

wangccsy 2019-12-02 01:48:37 0 浏览量 回答数 0

问题

【算法】五分钟算法小知识:学习数据结构和算法的框架思维

游客ih62co2qqq5ww 2020-04-17 09:56:03 10 浏览量 回答数 1

回答

DevOps 这个概念最早是在 2007 年提出的,那时云计算基础设施的概念也才刚刚提出没多久,而随着互联网的逐渐普及,应用软件的需求爆发式增长,软件开发的理念也逐渐从瀑布模型(waterfall)转向敏捷开发(agile)。传统的软件交付模式(应用开发人员专注于软件开发、IT 运维人员负责将软件部署到服务器运行),再也无法满足互联网软件快速迭代的需求。于是,DevOps 作为一种打破研发和运维之间隔阂、加快软件交付流程、提高软件交付质量的文化理念和最佳实践 逐渐普及至今。 DevOps 的现状 DevOps 的流行得益于业界对于应用软件敏捷开发、高质量交付的诉求,所以为开发和运维开辟了一块“公共的空间”,让双方可以在这里紧密合作。那时软件研发依旧属于一个新兴行业,人们习惯于向成熟的制造业学习,制造业解决大规模生产的方式,就是构建流水线,通过流水线规范化每个步骤对接的内容,而流水线上的工人们则只需要各司其职,快速熟练的完成自己这部分生产内容。 所以,DevOps 借鉴了制造业的经验,开始构建持续集成 / 持续交付(CI/CD)的流水线,催生出了一系列自动化 / 半自动化工具(如 puppet、chef、ansible 等),结合编写脚本的可扩展能力,将研发和运维的大量操作规范化,从而达到彼此协作的目标。但是最终还是要有人投入到这些工具的构建中,于是就出现了 DevOps 团队。DevOps 团队构建的工具和平台,帮助研发更容易地接近生产环境,让研发在持续集成、持续交付的过程中可以一键部署、快速试错,从而很大程度提前暴露和避免了软件在实际运行过程中的问题。 从本质上讲,DevOps 是为运维服务的。 它把生产环境的运维流程通过自动化的工具提供出来了,屏蔽了基础设施细节,同时让软件本身的问题更容易暴露,从而把这些问题尽量提前交给研发去解决。这些,其实都是在帮助运维减轻负担。 这一套模式在一开始运转良好,但是问题也随着时间的推移慢慢暴露出来了。DevOps 本身不为企业带来直接的利润,也不增加产品的功能,它们是企业的成本中心,所以许多企业不愿意为 DevOps 投入太多的成本。久而久之,DevOps 的能力便无法与研发人员增长的需求所匹配,不愿意继续伴随着云和开源社区的发展向前演进,反而成为软件研发的瓶颈。试想一下,有多少大公司的技术人员,对自己公司里的“研发效能”工具表示满意呢? 云计算的普及 聪明的企业总能从自己的需求中发现业界共有的需求,AWS 便是这么诞生的,他们早在 2006 年便首次把软件部署需要的网络、计算、存储等基础设施当做服务提供给用户,允许任何人在不购买服务器等物理硬件的情况下构建互联网应用程序,规模化使得整体的成本比用户自建更低。而云计算 IaaS、PaaS、SaaS 的概念也正是在那一年开始逐渐清晰的。 云计算的初期,用户主要使用的是 IaaS 服务,如虚拟机、存储等,使用云计算服务的企业依旧需要运维来管理这一类基础设施,只是运维管理的对象从物理机切换到虚拟机而已,并没有太本质的区别。 而随着云计算的快速发展,云的能力不断补充、增强,渐渐将原先由运维提供的方方面面的能力都转换成为了云上的服务,这其中自然包含了管理软件完整生命周期的各类服务,从代码托管、持续集成、持续交付,到监控、报警、自动扩缩容等一系列的能力,均能在云上找到对应的服务。品类之多、数量之巨,令人瞠目结舌。 但是 DevOps 依然有着用武之地。云的对接难度实在太大了,涉及到的云服务又多,不同云厂商提供的服务还不统一,为了使用云上的产品不得不投入大量的时间学习,而为了防止云厂商的绑定又不得不做多厂商的适配,DevOps 依旧需要像过去一样为开发屏蔽实际环境的复杂性,只不过这次他们要负责管理的基础设施变成了云资源。 改变一切的 Kubernetes Kubernetes 的本质是现代应用基础设施,它关注如何将应用与“云”天然地集成在一起,将“云”的最大价值发挥出来。Kubernetes 强调让基础设施能更好的配合应用、以更高效的方式为应用“输送”基础设施能力,而不是反之。在这个过程中,Kubernetes 、Docker、Operator 等在云原生生态中起到了关键作用的开源项目,正在在把应用管理与交付推上一个跟以前完全不一样的境况:Kubernetes 的使用者只通过声明式的方式描述自己应用的终态是什么,然后一切就结束了。Kubernetes 会处理后面的所有事情。 这也是为什么 Kubernetes 非常强调声明式 API。通过这种方式,Kubernetes 本身接入的基础设施能力越强,Kubernetes 的使用者能够声明的终态就越丰富,他的职责也就约单纯。现在,我们不仅能够通过 Kubernetes 声明应用的运行终态,比如;“这个应用需要 10 个实例”,我们还能够声明应用的很多运维终态,比如:“这个应用使用金丝雀发布策略进行升级”,以及 “当它的 CPU 使用量大于 50% 时,请自动扩展 2 个实例出来”。 这就让传统的 DevOps 工具和团队受到了挑战:如果一个业务研发自己只需要通过声明式 API 声明他的应用的所有终态甚至包括完整的 SLA,后面的一切就都会有 Kubernetes 来自动的搞定,那么他还有什么理由去对接和学习各式各样的 DevOps 流水线呢? 换句话说,长久以来,DevOps 实际上是在充当研发与基础设施之间的那一层“胶水”。而现在,Kubernetes 通过它极具生命力的声明式 API 和无限接入的应用基础设施能力,正在完美的扮演这个“胶水层”的作用。这也提醒了我们,上一个正在被 Kubernetes 体系强烈挑战的“胶水层”,其实叫做“传统中间件”:它正遭受到 Service Mesh 的巨大冲击。 DevOps 会消失吗? 近几年,Kubernetes 项目经常被描述成 DevOps 的“最佳拍档”。类似的观点认为, Kubernetes 跟 Docker 一样,解决的是软件运行时的问题。这意味着 Kubernetes 更像一种“时髦”的 IaaS,只不过运行时从虚拟机变成了容器。所以,只要能够将现有 DevOps 思想和流程对接到 Kubernetes 上来,就可以享受到容器技术带来的轻量级与弹性。这对于提倡“敏捷”的 DevOps 来说,显然是最好的组合。 不过,至少目前看来,Kubernetes 的发展路径并不是一个类 IaaS 的角色。它虽然关注接入底层的基础设施能力,但它本身却又不是基础设施能力的提供方。而且,相比于软件运行时,Kubernetes 似乎更关心软件的生命周期和状态流转。不仅如此,它还提供了一种叫做“控制器模型”的机制来将软件的实际状态与期望状态不断逼近,这显然都已经超出了一个“软件运行时”的范畴。 Kubernetes 项目对应用本身的“额外关注”,让它与一个类 IaaS 基础设施有着明显的区别,也让它“胶水”的定位更加明显。而如果 Kubernetes 的能力足够强大,那么作为研发与基础设施之间现有的“胶水层”, DevOps 是否还有必要存在?在所谓的云原生时代,应用研发与交付是不是真的会走向“一次声明”就可以“撒手不管”,从而让 DevOps 彻底消失呢? 不过,至少目前看来,Kubernetes 项目距离这个愿景,还有不少困难需要克服。 “Platform for Platform” API 的局限性 Kubernetes 是一个典型的 “Platform for Platform”项目,所以它的 API,距离纯研发视角还是非常遥远的。就比如一个 Deployment 对象,就既包括了研发侧关心的镜像,也包括了基础设施侧的资源配置,甚至是容器安全配置。此外, Kubernetes API 并没有提供出对“运维能力”的描述与定义方式,这也使得声明之后的“撒手不管”变得遥不可及。这也是为什么目前 DevOps 依然被需要的原因:Kubernetes 的大多数字段,还是必须经过研发和运维共同协作的流程来进行填充。 无法对更多的云资源进行描述 K8s 的原生 API 只包含了云资源的很少一部分,比如用 PV/PVC 表达存储,用 Ingress 表达负载均衡,但这对于一个完全声明式的应用描述来说是完全不够的。比如,研发希望在 K8s 上找到一个概念来表达数据库、VPC、消息队列等需求的时候,就会感到非常困惑。而现有的所有方案则完全依赖于云厂商的实现从而带来了新的 vendor lock-in 困惑。 Operator 体系缺乏互操作性 Kubernetes 的 Operator 机制是这个项目的能力能够无限增长的公开秘密。但令人遗憾的是,目前所有 Operator 之间的关系,就像是一个又一个的烟囱,互相之间没有任何交互与协作的可能。比如,我们把云上的 RDS 通过 CRD 和 Operator 扩展到了 K8s 声明式 API 的体系中,但是当第三方希望写一个定时备份 RDS 持久化文件的 CRD Operator 去配合的时候,却往往无从下手。这就又需要 DevOps 的体系介入来解决问题。 未来? 显然,现在的 Kubernetes 项目,依然需要借助 DevOps 体系来真正完成软件的高效迭代与交付工作。这是不可避免的:尽管 Kubernetes 声称自己是“以应用为中心”的基础设施,但它作为一个从 Google Borg 衍生出来的系统级项目,其本身的设计和工作层次还是更多的基础设施领域徘徊。但另一方面,我们绝不可否认的是,Kubernetes 在它的关键路径上,始终保持着对研发侧 “NoOps” 的追求。这种渴望,从它第一天提出“声明式应用管理”理论的时候就已经“昭然若揭”,而 CRD 和 Operator 体系的建立,更让这种应用级别的关心终于有了落地的机会。我们已经看到很多 DevOps 流程正在“下沉”为 Kubernetes 里的声明式对象与控制循环,比如 Tekton CD 项目。 如果 Kubernetes 的未来是 100% 的声明式应用管理,那么我们有理由相信 DevOps 最终会从技术领域消失然后彻底蜕变成一种文化。毕竟,那个时候的运维工程师,可能都会成为 Kubernetes Controller/Operator 的编写者或者设计者。而研发呢?他们可能根本不会知道原来 Kubernetes 这个东西曾经如此显赫的存在过。

有只黑白猫 2020-01-07 11:35:38 0 浏览量 回答数 0

回答

遍历一个 List 有哪些不同的方式?每种方法的实现原理是什么?Java 中 List 遍历的最佳实践是什么? 遍历方式有以下几种: for 循环遍历,基于计数器。在集合外部维护一个计数器,然后依次读取每一个位置的元素,当读取到最后一个元素后停止。 迭代器遍历,Iterator。Iterator 是面向对象的一个设计模式,目的是屏蔽不同数据集合的特点,统一遍历集合的接口。Java 在 Collections 中支持了 Iterator 模式。 foreach 循环遍历。foreach 内部也是采用了 Iterator 的方式实现,使用时不需要显式声明 Iterator 或计数器。优点是代码简洁,不易出错;缺点是只能做简单的遍历,不能在遍历过程中操作数据集合,例如删除、替换。 最佳实践:Java Collections 框架中提供了一个 RandomAccess 接口,用来标记 List 实现是否支持 Random Access。 如果一个数据集合实现了该接口,就意味着它支持 Random Access,按位置读取元素的平均时间复杂度为 O(1),如ArrayList。如果没有实现该接口,表示不支持 Random Access,如LinkedList。 推荐的做法就是,支持 Random Access 的列表可用 for 循环遍历,否则建议用 Iterator 或 foreach 遍历。 说一下 ArrayList 的优缺点 ArrayList的优点如下: ArrayList 底层以数组实现,是一种随机访问模式。ArrayList 实现了 RandomAccess 接口,因此查找的时候非常快。ArrayList 在顺序添加一个元素的时候非常方便。 ArrayList 的缺点如下: 删除元素的时候,需要做一次元素复制操作。如果要复制的元素很多,那么就会比较耗费性能。插入元素的时候,也需要做一次元素复制操作,缺点同上。 ArrayList 比较适合顺序添加、随机访问的场景。 如何实现数组和 List 之间的转换? 数组转 List:使用 Arrays. asList(array) 进行转换。List 转数组:使用 List 自带的 toArray() 方法。 代码示例: ArrayList 和 LinkedList 的区别是什么? 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。内存空间占用:LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全; 综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。 补充:数据结构基础之双向链表 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。 ArrayList 和 Vector 的区别是什么? 这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合 线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。性能:ArrayList 在性能方面要优于 Vector。扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。 Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。 Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist。 插入数据时,ArrayList、LinkedList、Vector谁速度较快?阐述 ArrayList、Vector、LinkedList 的存储性能和特性? ArrayList、LinkedList、Vector 底层的实现都是使用数组方式存储数据。数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢。 Vector 中的方法由于加了 synchronized 修饰,因此 Vector 是线程安全容器,但性能上较ArrayList差。 LinkedList 使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但插入数据时只需要记录当前项的前后项即可,所以 LinkedList 插入速度较快。 多线程场景下如何使用 ArrayList? ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用。例如像下面这样: 为什么 ArrayList 的 elementData 加上 transient 修饰? ArrayList 中的数组定义如下: private transient Object[] elementData; 再看一下 ArrayList 的定义: public class ArrayList extends AbstractList implements List<E>, RandomAccess, Cloneable, java.io.Serializable 可以看到 ArrayList 实现了 Serializable 接口,这意味着 ArrayList 支持序列化。transient 的作用是说不希望 elementData 数组被序列化,重写了 writeObject 实现: 每次序列化时,先调用 defaultWriteObject() 方法序列化 ArrayList 中的非 transient 元素,然后遍历 elementData,只序列化已存入的元素,这样既加快了序列化的速度,又减小了序列化之后的文件大小。 List 和 Set 的区别 List , Set 都是继承自Collection 接口 List 特点:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。 Set 特点:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及 TreeSet。 另外 List 支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。 Set和List对比 Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。 List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变 Set接口 说一下 HashSet 的实现原理? HashSet 是基于 HashMap 实现的,HashSet的值存放于HashMap的key上,HashMap的value统一为PRESENT,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。 HashSet如何检查重复?HashSet是如何保证数据不可重复的? 向HashSet 中add ()元素时,判断元素是否存在的依据,不仅要比较hash值,同时还要结合equles 方法比较。 HashSet 中的add ()方法会使用HashMap 的put()方法。 HashMap 的 key 是唯一的,由源码可以看出 HashSet 添加进去的值就是作为HashMap 的key,并且在HashMap中如果K/V相同时,会用新的V覆盖掉旧的V,然后返回旧的V。所以不会重复( HashMap 比较key是否相等是先比较hashcode 再比较equals )。 以下是HashSet 部分源码: hashCode()与equals()的相关规定: 如果两个对象相等,则hashcode一定也是相同的 两个对象相等,对两个equals方法返回true 两个对象有相同的hashcode值,它们也不一定是相等的 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖 hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。 ** ==与equals的区别** ==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同 ==是指对内存地址进行比较 equals()是对字符串的内容进行比较3.==指引用是否相同 equals()指的是值是否相同 HashSet与HashMap的区别 Queue BlockingQueue是什么? Java.util.concurrent.BlockingQueue是一个队列,在进行检索或移除一个元素的时候,它会等待队列变为非空;当在添加一个元素时,它会等待队列中的可用空间。BlockingQueue接口是Java集合框架的一部分,主要用于实现生产者-消费者模式。我们不需要担心等待生产者有可用的空间,或消费者有可用的对象,因为它都在BlockingQueue的实现类中被处理了。Java提供了集中BlockingQueue的实现,比如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,、SynchronousQueue等。 在 Queue 中 poll()和 remove()有什么区别? 相同点:都是返回第一个元素,并在队列中删除返回的对象。 不同点:如果没有元素 poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常。 代码示例: Queue queue = new LinkedList (); queue. offer("string"); // add System. out. println(queue. poll()); System. out. println(queue. remove()); System. out. println(queue. size()); Map接口 说一下 HashMap 的实现原理? HashMap概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 HashMap的数据结构: 在Java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。 HashMap 基于 Hash 算法实现的 当我们往Hashmap中put元素时,利用key的hashCode重新hash计算出当前对象的元素在数组中的下标存储时,如果出现hash值相同的key,此时有两种情况。(1)如果key相同,则覆盖原始值;(2)如果key不同(出现冲突),则将当前的key-value放入链表中获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值。理解了以上过程就不难明白HashMap是如何解决hash冲突的问题,核心就是使用了数组的存储方式,然后将冲突的key的对象放入链表中,一旦发现冲突就在链表中做进一步的对比。 需要注意Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率,从原来的O(n)到O(logn) HashMap在JDK1.7和JDK1.8中有哪些不同?HashMap的底层实现 在Java中,保存数据有两种比较简单的数据结构:数组和链表。数组的特点是:寻址容易,插入和删除困难;链表的特点是:寻址困难,但插入和删除容易;所以我们将数组和链表结合在一起,发挥两者各自的优势,使用一种叫做拉链法的方式可以解决哈希冲突。 JDK1.8之前 JDK1.8之前采用的是拉链法。拉链法:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。 JDK1.8之后 相比于之前的版本,jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。 JDK1.7 VS JDK1.8 比较 JDK1.8主要解决或优化了一下问题: resize 扩容优化引入了红黑树,目的是避免单条链表过长而影响查询效率,红黑树算法请参考解决了多线程死循环问题,但仍是非线程安全的,多线程时可能会造成数据丢失问题。 HashMap的put方法的具体流程? 当我们put的时候,首先计算 key的hash值,这里调用了 hash方法,hash方法实际是让key.hashCode()与key.hashCode()>>>16进行异或操作,高16bit补0,一个数和0异或不变,所以 hash 函数大概的作用就是:高16bit不变,低16bit和高16bit做了一个异或,目的是减少碰撞。按照函数注释,因为bucket数组大小是2的幂,计算下标index = (table.length - 1) & hash,如果不做 hash 处理,相当于散列生效的只有几个低 bit 位,为了减少散列的碰撞,设计者综合考虑了速度、作用、质量之后,使用高16bit和低16bit异或来简单处理减少碰撞,而且JDK8中用了复杂度 O(logn)的树结构来提升碰撞下的性能。 putVal方法执行流程图 ①.判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容; ②.根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如果table[i]不为空,转向③; ③.判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals; ④.判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向⑤; ⑤.遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可; ⑥.插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容。 HashMap的扩容操作是怎么实现的? ①.在jdk1.8中,resize方法是在hashmap中的键值对大于阀值时或者初始化时,就调用resize方法进行扩容; ②.每次扩展的时候,都是扩展2倍; ③.扩展后Node对象的位置要么在原位置,要么移动到原偏移量两倍的位置。 在putVal()中,我们看到在这个函数里面使用到了2次resize()方法,resize()方法表示的在进行第一次初始化时会对其进行扩容,或者当该数组的实际大小大于其临界值值(第一次为12),这个时候在扩容的同时也会伴随的桶上面的元素进行重新分发,这也是JDK1.8版本的一个优化的地方,在1.7中,扩容之后需要重新去计算其Hash值,根据Hash值对其进行分发,但在1.8版本中,则是根据在同一个桶的位置中进行判断(e.hash & oldCap)是否为0,重新进行hash分配后,该元素的位置要么停留在原始位置,要么移动到原始位置+增加的数组大小这个位置上 HashMap是怎么解决哈希冲突的? 答:在解决这个问题之前,我们首先需要知道什么是哈希冲突,而在了解哈希冲突之前我们还要知道什么是哈希才行; 什么是哈希? Hash,一般翻译为“散列”,也有直接音译为“哈希”的,这就是把任意长度的输入通过散列算法,变换成固定长度的输出,该输出就是散列值(哈希值);这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。 所有散列函数都有如下一个基本特性**:根据同一散列函数计算出的散列值如果不同,那么输入值肯定也不同。但是,根据同一散列函数计算出的散列值如果相同,输入值不一定相同**。 什么是哈希冲突? 当两个不同的输入值,根据同一散列函数计算出相同的散列值的现象,我们就把它叫做碰撞(哈希碰撞)。 HashMap的数据结构 在Java中,保存数据有两种比较简单的数据结构:数组和链表。数组的特点是:寻址容易,插入和删除困难;链表的特点是:寻址困难,但插入和删除容易;所以我们将数组和链表结合在一起,发挥两者各自的优势,使用一种叫做链地址法的方式可以解决哈希冲突: 这样我们就可以将拥有相同哈希值的对象组织成一个链表放在hash值所对应的bucket下,但相比于hashCode返回的int类型,我们HashMap初始的容量大小DEFAULT_INITIAL_CAPACITY = 1 << 4(即2的四次方16)要远小于int类型的范围,所以我们如果只是单纯的用hashCode取余来获取对应的bucket这将会大大增加哈希碰撞的概率,并且最坏情况下还会将HashMap变成一个单链表,所以我们还需要对hashCode作一定的优化 hash()函数 上面提到的问题,主要是因为如果使用hashCode取余,那么相当于参与运算的只有hashCode的低位,高位是没有起到任何作用的,所以我们的思路就是让hashCode取值出的高位也参与运算,进一步降低hash碰撞的概率,使得数据分布更平均,我们把这样的操作称为扰动,在JDK 1.8中的hash()函数如下: static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);// 与自己右移16位进行异或运算(高低位异或) } 这比在JDK 1.7中,更为简洁,相比在1.7中的4次位运算,5次异或运算(9次扰动),在1.8中,只进行了1次位运算和1次异或运算(2次扰动); JDK1.8新增红黑树 通过上面的链地址法(使用散列表)和扰动函数我们成功让我们的数据分布更平均,哈希碰撞减少,但是当我们的HashMap中存在大量数据时,加入我们某个bucket下对应的链表有n个元素,那么遍历时间复杂度就为O(n),为了针对这个问题,JDK1.8在HashMap中新增了红黑树的数据结构,进一步使得遍历复杂度降低至O(logn); 总结 简单总结一下HashMap是使用了哪些方法来有效解决哈希冲突的: 使用链地址法(使用散列表)来链接拥有相同hash值的数据;使用2次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均;引入红黑树进一步降低遍历的时间复杂度,使得遍历更快; **能否使用任何类作为 Map 的 key? **可以使用任何类作为 Map 的 key,然而在使用之前,需要考虑以下几点: 如果类重写了 equals() 方法,也应该重写 hashCode() 方法。 类的所有实例需要遵循与 equals() 和 hashCode() 相关的规则。 如果一个类没有使用 equals(),不应该在 hashCode() 中使用它。 用户自定义 Key 类最佳实践是使之为不可变的,这样 hashCode() 值可以被缓存起来,拥有更好的性能。不可变的类也可以确保 hashCode() 和 equals() 在未来不会改变,这样就会解决与可变相关的问题了。 为什么HashMap中String、Integer这样的包装类适合作为K? 答:String、Integer等包装类的特性能够保证Hash值的不可更改性和计算准确性,能够有效的减少Hash碰撞的几率 都是final类型,即不可变性,保证key的不可更改性,不会存在获取hash值不同的情况 内部已重写了equals()、hashCode()等方法,遵守了HashMap内部的规范(不清楚可以去上面看看putValue的过程),不容易出现Hash值计算错误的情况; 如果使用Object作为HashMap的Key,应该怎么办呢? 答:重写hashCode()和equals()方法 重写hashCode()是因为需要计算存储数据的存储位置,需要注意不要试图从散列码计算中排除掉一个对象的关键部分来提高性能,这样虽然能更快但可能会导致更多的Hash碰撞; 重写equals()方法,需要遵守自反性、对称性、传递性、一致性以及对于任何非null的引用值x,x.equals(null)必须返回false的这几个特性,目的是为了保证key在哈希表中的唯一性; HashMap为什么不直接使用hashCode()处理后的哈希值直接作为table的下标 答:hashCode()方法返回的是int整数类型,其范围为-(2 ^ 31)~(2 ^ 31 - 1),约有40亿个映射空间,而HashMap的容量范围是在16(初始化默认值)~2 ^ 30,HashMap通常情况下是取不到最大值的,并且设备上也难以提供这么多的存储空间,从而导致通过hashCode()计算出的哈希值可能不在数组大小范围内,进而无法匹配存储位置; 那怎么解决呢? HashMap自己实现了自己的hash()方法,通过两次扰动使得它自己的哈希值高低位自行进行异或运算,降低哈希碰撞概率也使得数据分布更平均; 在保证数组长度为2的幂次方的时候,使用hash()运算之后的值与运算(&)(数组长度 - 1)来获取数组下标的方式进行存储,这样一来是比取余操作更加有效率,二来也是因为只有当数组长度为2的幂次方时,h&(length-1)才等价于h%length,三来解决了“哈希值与数组大小范围不匹配”的问题; HashMap 的长度为什么是2的幂次方 为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法。 这个算法应该如何设计呢? 我们首先可能会想到采用%取余的操作来实现。但是,重点来了:“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。” 并且 采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。 那为什么是两次扰动呢? 答:这样就是加大哈希值低位的随机性,使得分布更均匀,从而提高对应数组存储下标位置的随机性&均匀性,最终减少Hash冲突,两次就够了,已经达到了高位低位同时参与运算的目的; HashMap 与 HashTable 有什么区别? 线程安全: HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!); 效率: 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它; 对Null key 和Null value的支持: HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。但是在 HashTable 中 put 进的键值只要有一个 null,直接抛NullPointerException。 **初始容量大小和每次扩充容量大小的不同 **: ①创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。 推荐使用:在 Hashtable 的类注释可以看到,Hashtable 是保留类不建议使用,推荐在单线程环境下使用 HashMap 替代,如果需要多线程使用则用 ConcurrentHashMap 替代。 如何决定使用 HashMap 还是 TreeMap? 对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。基于你的collection的大小,也许向HashMap中添加元素会更快,将map换为TreeMap进行有序key的遍历。 HashMap 和 ConcurrentHashMap 的区别 ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进行保护,相对于HashTable的synchronized锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。(JDK1.8之后ConcurrentHashMap启用了一种全新的方式实现,利用CAS算法。) HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。 ConcurrentHashMap 和 Hashtable 的区别? ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。 底层数据结构: JDK1.7的 ConcurrentHashMap 底层采用 分段的数组+链表 实现,JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 数组+链表 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的; 实现线程安全的方式(重要): ① 在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。(默认分配16个Segment,比Hashtable效率提高16倍。) 到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;② Hashtable(同一把锁) :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。 两者的对比图: HashTable: JDK1.7的ConcurrentHashMap: JDK1.8的ConcurrentHashMap(TreeBin: 红黑二叉树节点 Node: 链表节点): 答:ConcurrentHashMap 结合了 HashMap 和 HashTable 二者的优势。HashMap 没有考虑同步,HashTable 考虑了同步的问题。但是 HashTable 在每次同步执行时都要锁住整个结构。 ConcurrentHashMap 锁的方式是稍微细粒度的。 ConcurrentHashMap 底层具体实现知道吗?实现原理是什么? JDK1.7 首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。 在JDK1.7中,ConcurrentHashMap采用Segment + HashEntry的方式进行实现,结构如下: 一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和HashMap类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个HashEntry数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment的锁。 该类包含两个静态内部类 HashEntry 和 Segment ;前者用来封装映射表的键值对,后者用来充当锁的角色;Segment 是一种可重入的锁 ReentrantLock,每个 Segment 守护一个HashEntry 数组里得元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment 锁。 JDK1.8 在JDK1.8中,放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现,synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。 结构如下: 如果该节点是TreeBin类型的节点,说明是红黑树结构,则通过putTreeVal方法往红黑树中插入节点;如果binCount不为0,说明put操作对数据产生了影响,如果当前链表的个数达到8个,则通过treeifyBin方法转化为红黑树,如果oldVal不为空,说明是一次更新操作,没有对元素个数产生影响,则直接返回旧值;如果插入的是一个新节点,则执行addCount()方法尝试更新元素个数baseCount; 辅助工具类 Array 和 ArrayList 有何区别? Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。 对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。 如何实现 Array 和 List 之间的转换? Array 转 List: Arrays. asList(array) ;List 转 Array:List 的 toArray() 方法。 comparable 和 comparator的区别? comparable接口实际上是出自java.lang包,它有一个 compareTo(Object obj)方法用来排序comparator接口实际上是出自 java.util 包,它有一个compare(Object obj1, Object obj2)方法用来排序 一般我们需要对一个集合使用自定义排序时,我们就要重写compareTo方法或compare方法,当我们需要对某一个集合实现两种排序方式,比如一个song对象中的歌名和歌手名分别采用一种排序方法的话,我们可以重写compareTo方法和使用自制的Comparator方法或者以两个Comparator来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的Collections.sort(). 方法如何比较元素? TreeSet 要求存放的对象所属的类必须实现 Comparable 接口,该接口提供了比较元素的 compareTo()方法,当插入元素时会回调该方法比较元素的大小。TreeMap 要求存放的键值对映射的键必须实现 Comparable 接口从而根据键对元素进 行排 序。 Collections 工具类的 sort 方法有两种重载的形式, 第一种要求传入的待排序容器中存放的对象比较实现 Comparable 接口以实现元素的比较; 第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator 接口的子类型(需要重写 compare 方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java 中对函数式编程的支持)。

剑曼红尘 2020-03-24 14:41:57 0 浏览量 回答数 0

问题

【精品问答】python技术1000问(2)

问问小秘 2019-12-01 22:03:02 3129 浏览量 回答数 1

回答

从比特币出现到现在,我们一直为比特币的理念--去中心化而着迷不已,但是我们真的能做到去中心化吗?这是否只是一个乌托邦? 首先,人类社会是多中心化的,即使全世界所有的人被随机打乱,还是很快形成一个个国家,社会和利益团体。为什么?因为这样你才能有更大的话语权,你才能更好的保证自己的利益。一方面,大的利益机构之间不断抱团,另一方面,小的利益机构的生存空间不断被压缩,乃至消亡。所以有位人生导师和专员说,未来社会可能是自治的,专员不这么认为,至少我认为在自己有生之前,应该只能看到人治的社会。 那区块链呢?我们无法在现实世界完成的理想,能否在区块链中得到实现?专员对此表示悲观状态。 以比特币为例,中本聪利用PoW算法并且在区块链尝试,成功的做到了一个小目标--人人平等,所有人都要遵循比特币的挖矿协议,没有人能够例外。但是有一件事他没办法阻止--上天对每个人的给与是不同的。有一些人的机器好,有一些人的机器差,挖出来的比特币数量完全不一样,而前者可以有更多的前去买更好的机器,长此以往,贫富差距越来越大。 这个时候弱者能怎么办? 要么退出这个游戏,要么抱团,依附强者。所以,一个个矿池出现,开始以一个共同体进行发声,和其他强者进行对话,竞争。所以你看,比特币虽然号称是去中心化的,然而现在早已经是多中心化的,很多大的矿池都垄断着10%以上的算力,而专员前段时间还听到一个新闻--比特大陆旗下三家子公司的算力总和超过了51%.也就是说,如果他们愿意,他们可以操纵比特币网络的走向,获得至少99%的收益。 以太坊和EOS 以太坊也是一样,也是多中心化的。EOS人家更直接,一开始上来就说,我要搞21个超级节点,反正都是多中心的,我就直接摆在明面上,我就那么多中心节点,你们自己去争,能争到都是你们的。 专员上面说了那么多,只是想表达一个观点--人类社会是多中心的,区块链也是。 那区块链就做不到完全去中心化吗? 专员倒觉得有机会,但是条件比较苛刻。 首先,完全的去中心化意味着无人监管。 无人监管看起来很理想,但是实际上暗潮涌动,你不能假设所有人都是好人,事实上所有人都可能是坏人。比特币一开始饱受抨击,为啥,因为有很多大毒枭利用比特币进行非法的操作,比如洗qian.这就是技术的阴暗面,你可以用技术造福人类,就有人会用技术来完成自己的私欲。当然,技术本身是无罪的,有罪的是利用技术犯罪的那些人,但是你无法保证所有人都不去踩这条红线。国家作为治理犯罪、维护平衡的主体,必须要对上述这些行为进行监管,否则会出乱子。 其次,完全的去中心化意味着投机成本降低,投机者的行为更不好预测。 还是以比特币为例,去年一大堆团队搞IFO,进行比特币分叉,忽悠散户去买IFO的代币。糖果嘛,反正是不要钱的,随便砸呗,总会有人接盘的。还是那句话,所有人都赚钱了,钱从哪里来? 第三,完全的去中心化意味着开发进度缓慢。 以以太坊为例,以太坊作为一个自发的社区,其对社区成员没有一个约束力,如果你是社区开发者,项目开发到一半忽然发现自己手里的代币已经让你财务自由了,还写毛线代码,所以以太坊的PoS、分片搞了那么久才出来,也是这个原因。 而EOS前期通过Block.one这家公司进行宣传、开发的运作,以BM为主要负责人进行推进,才在短时间内迅速成为有机会挑战ETH的产品。如果使用纯社区的形式,估计这个进程要推迟2-3年。 第四,有人的地方就有江湖。 对于利益无关方来说,当然是希望越公开透明,越去中心化越好,但是对于有利益冲突方而言,当然尽量希望保证自己的利益。打个比如,如果这个时候BTC修改共识算法,弱化了矿池的能力,说不准会引起大的矿场场主立刻硬分叉,到时候主链变侧链,侧链变主链,事实上,这并不是没有可能的一件事。 而EOS就直接推动了这一进程的发展,一开始就划了21个位置,你们自己争好了,最后能成为超级节点的机构一定不弱,即使弱也不会跟其他节点差很多。

问问小秘 2019-12-02 03:07:13 0 浏览量 回答数 0

回答

Nacos 服务发现提供与其他服务发现产品不太一样的机制以及概念,在这里稍作介绍,下文中的内容都会多次提到这里介绍的概念,因此掌握这些概念,对于用好 Nacos 服务发现至关重要。 不同于 Consul, Eureka, Nacos 的服务发现使用的领域数据模型是服务 - 集群 - 实例这样的三层结构。最上面是服务,注册端(服务发布者)和订阅端(服务消费者)使用服务来与其他服务做区分,服务发现中,服务是必须指定的。集群则是中间一层,一个服务又会划分为多个集群,每个集群都有它的自定义配置,Nacos 提供了一个默认集群和相应的默认配置,在不需要多集群的场景下,可以不用指定集群。最下一层是实例,每个集群又会包含多个实例,这样对服务进行发现时,可以发现多个集群的所有实例,也可以指定集群,来发现特定集群的实例。 环境准备 首先,需要有一个 Nacos Server 部署起来,目前 Nacos 支持单机模式,也支持集群模式,部署文档可以参考 Nacos 快速入门。然后添加 Nacos 客户端最新版本依赖: <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>[latest-version]</version></dependency> 你可以配置从中央仓库直接依赖,也可以将 Nacos 最新源码下载下来,本地构建客户端版本。 Hello World 我们先来进行一个最简单的服务注册与发现。Nacos 支持从客户端注册服务实例和订阅服务,具体步骤如下: 配置 Nacos 客户端 Properties:Properties properties = new Properties();properties.setProperty(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848"); 创建 Nacos Naming 客户端:NamingService namingService = NacosFactory.createNamingService(properties); 注册一个实例:namingService.registerInstance("nacos.test.1", InetAddress.getLocalHost().getHostAddress(), 8080); 查找这个服务的实例:System.out.println(namingService.getAllInstances("nacos.test.1")); 至此一个最简单的 Nacos 服务发现的使用已经完成了。这里要对一些细节稍作解释。首先在第一步中,属性 PropertyKeyConst.SERVER_ADDR 表示的是 Nacos 服务端的地址,这个地址的格式为 IP:port,IP:port。对于单机版,只需要指定一个 IP:port。甚至您可以将端口省略,这样将会访问 Nacos 的默认端口 8848。在第二步中,将创建一个 NamingService 实例,客户端将为该实例创建单独的资源空间,包括缓存、线程池以及配置等。Nacos 客户端没有对该实例做单例的限制,请小心维护这个实例,以防新建了多于预期的实例。第三步注册服务中,使用的是最简单的 API 注册方式,只需要传入服务名、IP、端口就可以。第四步是获取服务下的所有实例列表,包括健康和不健康的。 构建自定义实例 在一些场景中,我们希望注册的实例中,有一些能够被分配更多的流量,而另外一些分配较少的流量,或者能够传入一些实例的元信息存储到 Nacos 服务端,例如 IP 所属的应用或者所在的机房,这样在客户端可以根据服务下挂载的实例的元信息,来自定义负载均衡模式。别担心,我们有另外的注册实例接口,让你可以在注册的时候指定实例的属性: /** * Register a instance to service with specified instance properties * * @param serviceName name of service * @param instance instance to register * @throws NacosException / void registerInstance(String serviceName, Instance instance) throws NacosException; 这个方法可以在注册服务的时候,传入一个 Instanc 实例,而在 Instance 实例中,可以设置实例的若干属性: public class Instance { /* * Unique ID of this instance. / private String instanceId; /* * Instance ip / private String ip; /* * Instance port / private int port; /* * Instance weight / private double weight = 1.0D; /* * Instance health status / @JSONField(name = "valid") private boolean healthy = true; /* * Cluster information of instance / @JSONField(serialize = false) private Cluster cluster = new Cluster(); /* * Service information of instance / @JSONField(serialize = false) private Service service; /* * User extended attributes / private Map<String, String> metadata = new HashMap<String, String>(); ....} 其中,InstanceId 是由服务端生成返回给客户端,用于唯一标识该实例。IP、端口是实例的基本属性,除此之外,还有 weight 权重,可以设置该实例所分配流量的多少,这应该也比较好理解,权重越大,实例分配的流量就会越大。healthy 字段代表该实例是否健康,这个值也是由服务端返回给客户端的。cluster 和 service 分别表示该实例对应的集群和服务的一些信息,这些信息会在下面做介绍。最后是实例的元数据,这个元数据一个 String 对 String 的 Map。那么可以用如下代码来注册一个自定义实例: Instance instance = new Instance();instance.setIp(InetAddress.getLocalHost().getHostAddress());instance.setPort(8080);instance.setWeight(100);Map<String, String> metadata = new HashMap<String, String>(16);metadata.put("app", "nacos");metadata.put("site", "beijing");instance.setMetadata(metadata);namingService.registerInstance("nacos.test.1", instance); 构建自定义集群 Nacos 引入了集群的概念,在服务这个维度下面,可以对服务下的实例列表再做个划分。这在阿里巴巴内部非常普遍。一个典型的场景是这个服务下的实例,需要配置多种健康检查方式,有一些实例使用 TCP 的健康检查方式,另外一些使用 HTTP 的健康检查方式。另一个场景是,这个服务下挂载的机器分属不同的环境,我们希望能够在某些情况下(包括演练)将某个环境的流量全部切走,这样可以通过配置一个环境属于一个集群,来做到一次性切流。 在客户端构建自定义集群,有一些陷阱需要小心。当前我们只有注册实例的接口,实例内部的 cluster 字段可以配置集群的属性。但是多个实例之间如果配置了不同的集群属性,这时候会发生什么呢?Nacos 只会接受第一次注册该集群所传入的集群属性,也就是说,先注册的实例,获得优先权,将它对应的集群信息注册到 Nacos 服务端。只有 Nacos 服务端已经存在该集群的配置,后续的注册请求里的集群信息,都会被忽略。为了确保你的应用保持预期的行为,请务必让同一个集群下的实例使用相同的集群配置。 下面来看看可以为集群定义哪些配置: public class Cluster { /* * Name of belonging service / private String serviceName; /* * Name of cluster / private String name = ""; /* * Health check config of this cluster / private AbstractHealthChecker healthChecker = new AbstractHealthChecker.Tcp(); /* * Default registered port for instances in this cluster. / private int defaultPort = 80; /* * Default health check port of instances in this cluster. / private int defaultCheckPort = 80; /* * Whether or not use instance port to do health check. / private boolean useIPPort4Check = true; private Map<String, String> metadata = new HashMap<String, String>(); ...} 首先是集群对应的服务名,用来表示该集群所属的服务;然后是集群的名字、健康检查方式、默认的端口、默认的健康检查端口以及是否使用是的端口做健康检查。我们先来说简单的,默认端口表示注册时实例默认的端口,这个在客户端并没有体现,但是当使用 API 注册实例的时候,端口是可以不传入的,此时就会用这个默认端口作为实例的端口。然后是默认的健康检查端口,当健康检查方式中没有配置端口时,就会用这个端口来和实例通信,进行健康检查。下一个属性是是否使用实例端口做健康检查,如果设为 true,则会使用实例注册的端口来和实例进行通信。最后一个属性是集群的元数据,Nacos 支持多个维度的元数据,实例支持,集群支持,下面介绍的服务属性也支持。 健康检查方式,客户端心跳是一种模式,由客户端主动上报健康状态。服务端检测是另外一种模式,Nacos 目前支持三种:TCP、HTTP 和 MYSQL。TCP 方式会从 Nacos 服务端建立一个连接到实例,如果连接建立成功,则表示该实例健康。HTTP 方式则会从 Nacos 服务端想实例发起一个 HTTP 请求,可以配置的属性有访问的相对路径,访问的 HTTP 头部,这个头部使用竖线进行分割,以及预期的请求返回码,默认为 200: private String path = "";private String headers = "";private int expectedResponseCode = 200; MYSQL 健康检查方式,则可以让 Nacos 往实例执行一条 MySQL 命令,可以配置的属性有用户名、密码和执行的命令。执行结果如果不抛异常,则表示实例健康: private String user;private String pwd;private String cmd; 构建自定义服务 同理,服务也可能需要自定义的配置,Nacos 的服务随着实例的注册而存在,并随着所有实例的注销而消亡。目前除了使用 HTTP API 可以修改服务的属性外(这将在未来的篇章中进行介绍),就只能使用注册实例时传入服务属性来进行服务的自定义配置。这里的服务与 Consul 或者 Eureka 不同,Consul 与 Eureka 的服务其实就是指的实例,而每个实例有一个服务名,通过这个服务名来获取相同服务名下的实例列表,服务本身并不是一个数据实体。在真正的生产环境中,我们认为服务本身也是具有数据存储需求的,例如作用于服务下所有实例的配置、权限控制等。虽然有一些配置可以放到实例级别,例如健康检查是否开启。但是当服务的规模成千上万后,想要整体修改这些实例的健康检查开关,就是一个繁重的运维操作。另一些配置,例如下文会提到的健康保护阈值,是一定是一个服务只有一个唯一的值的,多个值将会造成逻辑上的冲突。 /* * Service name / private String name; /* * Protect threshold / private float protectThreshold = 0.0F; /* * Application name of this service / private String app; /* * Service group which is meant to classify services into different sets. / private String group; /* * Health check mode. / private String healthCheckMode; private Map<String, String> metadata = new HashMap<String, String>(); 服务的属性存储在 Service 类中,自上而下,依次是服务的名称、服务的健康保护阈值、服务的应用名、服务的分组、服务的健康检查模式以及服务的元数据。相关概念这里不再做一一陈述,你可以参考 Nacos 官网 概念介绍。这里要提到的是服务的健康保护阈值,在阿里巴巴内部,这个值被广泛的设置和调优。在这里对该属性的初衷做一个简单的介绍。分布式服务场景下的一个问题是在部分实例不健康的情况下,是否能够将流所有流量引向其他健康实例?在一些情况下,这可能造成雪崩效应。即本来健康的实例被多余的流量冲击,也变得不健康,然后导致健康的实例越来越少,最后整个服务崩溃。此时可以使用这个健康保护阈值,当健康实例与所有实例的比例小于这个值的时候,则认为所有实例都是健康的,这样虽然部分流量流向了不健康的实例,但是剩余健康的实例还是能够正常访问的。 服务发现 Nacos 的服务发现,有主动拉取和推送两种模式,这与一般的服务发现架构相同。在拉取方式中,提供了三个方法,一个是查询所有注册的实例,一个是只查询健康且上线的实例,还有一个是获取一个健康且上线的实例。一般情况下,订阅端并不关心不健康的实例或者权重设为 0 的实例,但是也不排除一些场景下,有一些运维或者管理的场景需要拿到所有的实例。目前的版本同时还支持根据服务端设定的负载均衡策略,来查询单个可用的实例。就好像 DNS 解析一样,虽然每次都返回一个后端 IP,但是整体可以保证域名挂载的所有 IP 会按照一定的策略都能够被客户端解析到。 /* * Get all instances of a service * * @param serviceName name of service * @return A list of instance * @throws NacosException /List<Instance> getAllInstances(String serviceName) throws NacosException;/* * Get qualified instances of service * * @param serviceName name of service * @param healthy a flag to indicate returning healthy or unhealthy instances * @return A qualified list of instance * @throws NacosException /List<Instance> selectInstances(String serviceName, boolean healthy) throws NacosException;/* * Select one healthy instance of service using predefined load balance strategy * * @param serviceName name of service * @return qualified instance * @throws NacosException /Instance selectOneHealthyInstance(String serviceName) throws NacosException; 前两个查询方法会返回所有实例的列表,这允许用户通过额外的工作,将实例的权重或者元数据运用到负载均衡中。对于一般的微服务场景,针对每个实例轮询,这样已经足够了。事实上,不管是在 Eureka 还是 Consul 里,其原生客户端都是只负责服务的发现,并不支持负载均衡。这样就需要第三方的 ribbon 或者 fabio 来完成负载均衡工作,此时它们的负载均衡,是完全放在客户端的。 Nacos 也会支持客户端侧的负载均衡,并支持用户扩展的负载均衡策略。不过在阿里巴巴内部,通常只需要由服务端来配置负载均衡策略,所有的调用端不区分业务的使用同一套负载均衡策略。因为实际上,调用端往往并不关心自身访问的服务的流量分配,而只需要一个可用的服务节点就可以了。而服务提供端,则由于其部署规模很大和部署环境的复杂,需要对环境信息敏感的流量分配以及对流量的绝对控制权。这时,往往需要提供端审慎的配置好统一的负载均衡策略,来保证所有订阅端按照这个策略来进行访问。 除了主动查询实例列表,Nacos 还提供订阅模式来感知服务下实例列表的变化,包括服务配置或者实例配置的变化。可以使用下面的接口来进行订阅或者取消订阅: /* * Subscribe service to receive events of instances alteration * * @param serviceName name of service * @param listener event listener * @throws NacosException /void subscribe(String serviceName, EventListener listener) throws NacosException;/* * Unsubscribe event listener of service * * @param serviceName name of service * @param listener event listener * @throws NacosException */void unsubscribe(String serviceName, EventListener listener) throws NacosException; 控制台使用 Nacos 0.3.0 版本上线了控制台,作为生产环境基本的运维工具,服务发现也通过控制台释放了部分的运维能力。虽然控制台承担的是运维为主的工作,但是开发人员也需要通过控制台来查看当前服务的注册状态和健康状态等,服务发现的控制台页面介绍可以参考 https://nacos.io/en-us/blog/discovery-console.html。虽然这篇文章中的一些页面通过社区的反馈而做了细微的调整,但是通过这篇文章应该可以掌握怎么使用服务发现的控制台了。控制台的启动方式也很简单,将 Nacos 安装包下载安装启动(安装教程)之后,直接访问:http://localhost:8848/nacos/index.html 即可打开最新的控制台界面。 小 结 Nacos 目前的版本,集成了服务发现和配置管理的基本能力以及部分高级特性。作为最小生产可用版本,Nacos 未来还会继续开放新特性,结合 SpringCloud、K8S、Dubbo 等生态,为开发者提供极致易用和稳定的服务管理和配置管理能力。在可预期的几个版本内,将会支持元数据的管理及 DNS 的服务发现。争取将使用 Nacos,作为服务发现和配置管理选型的最佳实践。 答案来源网络,供参考,希望对您有帮助

问问小秘 2019-12-02 03:00:16 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 发送访问OSS的请求 您可以直接使用OSS提供的RESTful API接口访问或者使用对API接口进行完整封装的SDK开发包。而每一次向OSS的请求根据当前Bucket权限和操作不同要求用户进行身份验证或者直接匿名访问。对OSS的资源访问的分类如下: 按访问者的角色可分为拥有者访问和第三方用户访问。这里的拥有者指的是Bucket的Owner,也称为开发者。第三方用户是指访问Bucket里资源的用户。 按访问者的身份信息可分为匿名访问和带签名访问。对于OSS来说,如果请求中没有携带任何和身份相关的信息即为匿名访问。带签名访问指的是按照OSS API文档中规定的在请求头部或者在请求URL中携带签名的相关信息。 AccessKey 类型 目前访问 OSS 使用的 AK(AccessKey)有三种类型,具体如下: 阿里云账号AccessKey 阿里云账号AK特指Bucket拥有者的AK,每个阿里云账号提供的AccessKey对拥有的资源有完全的权限。每个阿里云账号能够同时拥有不超过5个active或者inactive的AK对(AccessKeyId和AccessKeySecret)。 用户可以登录AccessKey管理控制台,申请新增或删除AK对。 每个AK对都有active/inactive两种状态。 Active 表明用户的 AK 处于激活状态,可以在身份验证的时候使用。 Inactive 表明用户的 AK 处于非激活状态,不能在身份验证的时候使用。 说明 请避免直接使用阿里云账户的 AccessKey。 RAM子账号AccessKey RAM (Resource Access Management) 是阿里云提供的资源访问控制服务。RAM账号AK指的是通过RAM被授权的AK。这组AK只能按照RAM定义的规则去访问Bucket里的资源。通过RAM,您可以集中管理您的用户(比如员工、系统或应用程序),以及控制用户可以访问您名下哪些资源的权限。比如能够限制您的用户只拥有对某一个Bucket的读权限。子账号是从属于主账号的,并且这些账号下不能拥有实际的任何资源,所有资源都属于主账号。 STS账号AccessKey STS(Security Token Service)是阿里云提供的临时访问凭证服务。STS账号AK指的是通过STS颁发的AK。这组AK只能按照STS定义的规则去访问Bucket里的资源。 身份验证具体实现 目前主要有三种身份验证方式: AK验证 RAM验证 STS验证 当用户以个人身份向OSS发送请求时,其身份验证的实现如下: 用户将发送的请求按照OSS指定的格式生成签名字符串。 用户使用AccessKeySecret对签名字符串进行加密产生验证码。 OSS收到请求以后,通过AccessKeyId找到对应的AccessKeySecret,以同样的方法提取签名字符串和验证码。 如果计算出来的验证码和提供的一样即认为该请求是有效的。 否则,OSS将拒绝处理这次请求,并返回HTTP 403错误。 对于用户来说可以直接使用OSS提供的SDK,配合不同类型的AccessKey即可实现不同的身份验证。 权限控制 针对存放在Bucket的Object的访问,OSS提供了多种权限控制,主要有: Bucket级别权限 Object级别权限 账号级别权限(RAM) 临时账号权限(STS) Bucket级别权限 Bucket权限类型 OSS提供ACL(Access Control List)权限控制方法,OSS ACL提供Bucket级别的权限访问控制,Bucket目前有三种访问权限:public-read-write,public-read和private,它们的含义如下: 权限值 中文名称 权限对访问者的限制 public-read-write 公共读写 任何人(包括匿名访问)都可以对该Bucket中的Object进行读/写/删除操作;所有这些操作产生的费用由该Bucket的Owner承担,请慎用该权限。 public-read 公共读,私有写 只有该Bucket的Owner或者授权对象可以对存放在其中的Object进行写/删除操作;任何人(包括匿名访问)可以对Object进行读操作。 private 私有读写 只有该Bucket的Owner或者授权对象可以对存放在其中的Object进行读/写/删除操作;其他人在未经授权的情况下无法访问该Bucket内的Object。 Bucket权限设定和读取方法 功能使用参考: API:Put BucketACL SDK:Java SDK-设置Bucket ACL 控制台:创建Bucket权限设置 API:Get BucketACL SDK:Java SDK-获取Bucket ACL Object级别权限 Object权限类型 OSS ACL也提供Object级别的权限访问控制。目前Object有四种访问权限:private, public-read, public-read-write, default。Put Object ACL操作通过Put请求中的“x-oss-object-acl”头来设置,这个操作只有Bucket Owner有权限执行。 权限值 中文名称 权限对访问者的限制 public-read-write 公共读写 该ACL表明某个Object是公共读写资源,即所有用户拥有对该Object的读写权限。 public-read 公共读,私有写 该ACL表明某个Object是公共读资源,即非Object Owner只有该Object的读权限,而Object Owner拥有该Object的读写权限。 private 私有读写 该ACL表明某个Object是私有资源,即只有该Object的Owner拥有该Object的读写权限,其他的用户没有权限操作该Object。 default 默认权限 该ACL表明某个Object是遵循Bucket读写权限的资源,即Bucket是什么权限,Object就是什么权限。 说明 如果没有设置Object的权限,即Object的ACL为default,Object的权限和Bucket权限一致。 如果设置了Object的权限,Object的权限大于Bucket权限。举个例子,如果设置了Object的权限是public-read,无论Bucket是什么权限,该Object都可以被身份验证访问和匿名访问。 Object权限设定和读取方法 功能使用参考: API:Put Object ACL SDK:Java SDK-ObjectACL 中设定Object ACL API:Get Object ACL SDK:Java SDK-ObjectACL 中读取Object ACL 账号级别权限(RAM) 使用场景 如果您购买了云资源,您的组织里有多个用户需要使用这些云资源,这些用户只能共享使用您的云账号AccessKey。这里有两个问题: 您的密钥由多人共享,泄露的风险很高。 您无法控制特定用户能访问哪些资源(比如Bucket)的权限。 解决方法:在您的阿里云账号下面,通过RAM可以创建具有自己AccessKey的子用户。您的阿里云账号被称为主账号,创建出来的账号被称为子账号,使用子账号的AccessKey只能使用主账号授权的操作和资源。 具体实现 有关RAM详情,请参考RAM用户手册。 对于授权中需要的Policy的配置方式可以参考本章最后一节:RAM和STS授权策略(Policy)配置。 临时账号权限(STS) 使用场景 对于您本地身份系统所管理的用户,比如您的App的用户、您的企业本地账号、第三方App,也有直接访问OSS资源的可能,将这部分用户称为联盟用户。此外,用户还可以是您创建的能访问您的阿里云资源的应用程序。 对于这部分联盟用户,通过阿里云STS (Security Token Service) 服务为阿里云账号(或RAM用户)提供短期访问权限管理。您不需要透露云账号(或RAM用户)的长期密钥(如登录密码、AccessKey),只需要生成一个短期访问凭证给联盟用户使用即可。这个凭证的访问权限及有效期限都可以由您自定义。您不需要关心权限撤销问题,访问凭证过期后会自动失效。 用户通过STS生成的凭证包括安全令牌(SecurityToken)、临时访问密钥(AccessKeyId, AccessKeySecret)。使用AccessKey方法与您在使用阿里云账户或RAM用户AccessKey发送请求时的方法相同。此外还需要注意的是在每个向OSS发送的请求中必须携带安全令牌。 具体实现 STS安全令牌、角色管理和使用相关内容详情,请参考RAM用户指南中的角色管理。关键是调用STS服务接口AssumeRole来获取有效访问凭证即可,也可以直接使用STS SDK来调用该方法。 RAM和STS应用场景实践 对于不同的应用场景,涉及到的访问身份验证方式可能存在差异。下面以几种典型的应用场景来说明访问身份验证中几种使用方式。 以一个移动App举例。假设您是一个移动App开发者,打算使用阿里云OSS服务来保存App的终端用户数据,并且要保证每个App用户之间的数据隔离,防止一个App用户获取到其它App用户的数据。 方式一:使用AppServer来做数据中转和数据隔离如上图所示,您需要开发一个AppServer。只有AppServer能访问云服务,ClientApp的每次读写数据都需要通过AppServer,AppServer来保证不同用户数据的隔离访问。 对于该种使用方式,使用阿里云账号或者RAM账号提供的密钥来进行签名验证访问。建议您尽量不要直接使用阿里云账号(主账号)的密钥访问OSS,避免出现安全问题。 方式二:使用STS让用户直接访问OSS STS方案描述如下图所示:方案的详细描述如下: App用户登录。App用户和云账号无关,它是App的终端用户,AppServer支持App用户登录。对于每个有效的App用户来说,需要AppServer能定义出每个App用户的最小访问权限。 AppServer请求STS服务获取一个安全令牌(SecurityToken)。在调用STS之前,AppServer需要确定App用户的最小访问权限(用Policy语法描述)以及授权的过期时间。然后通过扮演角色(AssumeRole)来获取一个代表角色身份的安全令牌。 STS返回给AppServer一个有效的访问凭证,包括一个安全令牌(SecurityToken)、临时访问密钥(AccessKeyId, AccessKeySecret)以及过期时间。 AppServer将访问凭证返回给ClientApp。ClientApp可以缓存这个凭证。当凭证失效时,ClientApp需要向AppServer申请新的有效访问凭证。比如,访问凭证有效期为1小时,那么ClientApp可以每30分钟向AppServer请求更新访问凭证。 ClientApp使用本地缓存的访问凭证去请求Aliyun Service API。云服务会感知STS访问凭证,并会依赖STS服务来验证访问凭证,正确响应用户请求。 RAM和STS授权策略(Policy)配置 对于RAM或者STS授权中使用Policy,详细规则如下。 示例 先看下面的一个Policy示例: { "Version": "1", "Statement": [ { "Action": [ "oss:GetBucketAcl", "oss:ListObjects" ], "Resource": [ "acs:oss:*:1775305056529849:mybucket" ], "Effect": "Allow", "Condition": { "StringEquals": { "acs:UserAgent": "java-sdk", "oss:Prefix": "foo" }, "IpAddress": { "acs:SourceIp": "192.168.0.1" } } }, { "Action": [ "oss:PutObject", "oss:GetObject", "oss:DeleteObject" ], "Resource": [ "acs:oss:*:1775305056529849:mybucket/file*" ], "Effect": "Allow", "Condition": { "IpAddress": { "acs:SourceIp": "192.168.0.1" } } } ] } 这是一个授权的Policy,用户用这样的一个Policy通过RAM或STS服务向其他用户授权。Policy当中有一个Statement(一条Policy当中可以有多条Statement)。Statement里面规定了相应的Action、Resource、Effect和Condition。 这条Policy把用户自己名下的mybucket和mybucket/file*这些资源授权给相应的用户,并且支持GetBucketAcl、GetBucket、PutObject、GetObject和DeleteObject这几种操作。Condition中的条件表示UserAgent为java-sdk,源IP为192.168.0.1的时候鉴权才能通过,被授权的用户才能访问相关的资源。Prefix这个Condition是在GetBucket(ListObjects)的时候起作用的,关于这个字段的解释详见OSS的API文档。 配置细则 Version Version定义了Policy的版本,本文档中sw2q的配置方式,设置为1。 Statement 通过Statement描述授权语义,其中可以根据业务场景包含多条语义,每条包含对Action、Effect、Resource和Condition的描述。每次请求系统会逐条依次匹配检查,所有匹配成功的Statement会根据Effect的设置不同分为通过(Allow)、禁止(Deny),其中禁止(Deny)的优先。如果匹配成功的都为通过,该条请求即鉴权通过。如果匹配成功有一条禁止,或者没有任何条目匹配成功,该条请求被禁止访问。 Action Action分为三大类:Service级别操作,对应的是GetService操作,用来列出所有属于该用户的Bucket列表。 Bucket级别操作,对应类似于oss:PutBucketAcl、oss:GetBucketLocation之类的操作,操作的对象是Bucket,它们的名称和相应的接口名称一一对应。 Object级别操作,分为oss:GetObject、oss:PutObject、oss:DeleteObject和oss:AbortMultipartUpload,操作对象是Object。 如想授权某一类的Object的操作,可以选择这几种的一种或几种。另外,所有的Action前面都必须加上oss:,如上面例子所示。Action是一个列表,可以有多个Action。具体的Action和API接口的对应关系如下: Service级别 API Action GetService(ListBuckets) oss:ListBuckets Bucket级别 API Action PutBucket oss:PutBucket GetBucket(ListObjects) oss:ListObjects PutBucketAcl oss:PutBucketAcl DeleteBucket oss:DeleteBucket GetBucketLocation oss:GetBucketLocation GetBucketAcl oss:GetBucketAcl GetBucketLogging oss:GetBucketLogging PutBucketLogging oss:PutBucketLogging DeleteBucketLogging oss:DeleteBucketLogging GetBucketWebsite oss:GetBucketWebsite PutBucketWebsite oss:PutBucketWebsite DeleteBucketWebsite oss:DeleteBucketWebsite GetBucketReferer oss:GetBucketReferer PutBucketReferer oss:PutBucketReferer GetBucketLifecycle oss:GetBucketLifecycle PutBucketLifecycle oss:PutBucketLifecycle DeleteBucketLifecycle oss:DeleteBucketLifecycle ListMultipartUploads oss:ListMultipartUploads PutBucketCors oss:PutBucketCors GetBucketCors oss:GetBucketCors DeleteBucketCors oss:DeleteBucketCors PutBucketReplication oss:PutBucketReplication GetBucketReplication oss:GetBucketReplication DeleteBucketReplication oss:DeleteBucketReplication GetBucketReplicationLocation oss:GetBucketReplicationLocation GetBucketReplicationProgress oss:GetBucketReplicationProgress Object级别 API Action GetObject oss:GetObject HeadObject oss:GetObject PutObject oss:PutObject PostObject oss:PutObject InitiateMultipartUpload oss:PutObject UploadPart oss:PutObject CompleteMultipart oss:PutObject DeleteObject oss:DeleteObject DeleteMultipartObjects oss:DeleteObject AbortMultipartUpload oss:AbortMultipartUpload ListParts oss:ListParts CopyObject oss:GetObject,oss:PutObject UploadPartCopy oss:GetObject,oss:PutObject AppendObject oss:PutObject GetObjectAcl oss:GetObjectAcl PutObjectAcl oss:PutObjectAcl Resource Resource指代的是OSS上面的某个具体的资源或者某些资源(支持*通配),resource的规则是acs:oss:{region}:{bucket_owner}:{bucket_name}/{object_name}。对于所有Bucket级别的操作来说不需要最后的斜杠和{object_name},即acs:oss:{region}:{bucket_owner}:{bucket_name}。Resource也是一个列表,可以有多个Resource。其中的region字段暂时不做支持,设置为*。 Effect Effect代表本条的Statement的授权的结果,分为Allow和Deny,分别指代通过和禁止。多条Statement同时匹配成功时,禁止(Deny)的优先级更高。 例如,期望禁止用户对某一目录进行删除,但对于其他文件有全部权限: { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:*" ], "Resource": [ "acs:oss:*:*:bucketname" ] }, { "Effect": "Deny", "Action": [ "oss:DeleteObject" ], "Resource": [ "acs:oss:*:*:bucketname/index/*", ] } ] } Condition Condition代表Policy授权的一些条件,上面的示例里面可以设置对于acs:UserAgent的检查、acs:SourceIp的检查、还有oss:Prefix这项用来在GetBucket的时候对资源进行限制。 OSS支持的Condition如下: condition 功能 合法取值 acs:SourceIp 指定ip网段 普通的ip,支持*通配 acs:UserAgent 指定http useragent头 字符串 acs:CurrentTime 指定合法的访问时间 ISO8601格式 acs:SecureTransport 是否是https协议 “true”或者”false” oss:Prefix 用作ListObjects时的prefix 合法的object name 更多示例 针对具体场景更多的授权策略配置示例,可以参考教程示例:控制存储空间和文件夹的访问权限和OSS授权常见问题。 Policy在线图形化便捷配置工具,请单击这里。 最佳实践 RAM和STS使用指南

2019-12-01 23:12:47 0 浏览量 回答数 0
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 企业信息查询 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站