『IT 人员必备工具箱』是一个专注于 IT 人优质资源分享的导航站,包含大量好玩又实用的 AI、翻译、编程、设计、Linux、网络、云原生、安全等多款应用。
🏷️ 网站地址:https://666666.dev
我们的目标是帮助更多 IT 人发现有价值的优质资源,让更多人受益。
『IT 人员必备工具箱』也可以推荐你喜欢的软件和网站呢!如果你有什么好玩有趣又实用的酷软趣站,快『推荐』给我们吧!
🏷️ 趣站酷软推荐收集表: https://docs.qq.com/form/page/DQm1UVEJJV0ZPUXlz
『IT 人员必备工具箱』部分截图展示:
📕 关注『奇妙的 Linux 世界』公众号,带你开启有趣新生活!更多好用好玩的软件资源,可访问 https://666666.dev 免费获取。
]]>这是我第一次尝试写个人年度总结。
我的 2023 年与 2022 年不可分割,一直走在维权的路上。
早在 2022 年初,我的职业生涯发生重大转变,公司在未与我协商的情况下,单方面将我待岗,降低工资,移交工作。我从熟悉的工作环境被迫成为了一个「半自由职业者」。我之所以自认为是半自由,是因为公司未与我解除劳动合同,所以劳动关系还在原公司。法律规定劳动者只能和一家用人单位签订劳动合同,所以我在与公司存在劳动争议的情况下,无法入职下家公司。
除此之外,公司隔几个月后停发我的工资,切断了我唯一的经济来源。我开始尝试学习《劳动法》及相关法律知识,查阅和自己案件类似的裁判文书,整理书面材料,为的是跟公司对簿公堂,讨回做为劳动者的尊严和权益。
在劳资关系中,公司具有优势地位,而劳动者处于弱势的一方。尤其是面对老板、人事、法务组成的流氓团伙时,会更加被动、愤怒、迷茫、无助。希望大家在面对公司的恶意时可以更加从容,是我写下这些文字的初衷。
以前一直以为遇到劳动争议问题,要离职后才能提起仲裁,但后来从网络了解到我的案件情况更适合在职仲裁,在职期间可以随时申请,所以我**尝试提起在职仲裁。**仲裁期间可以正常上班,自己也有机会尽可能地多搜集一些证据。因为公司取消了我的办公权限,我目前已经不需要去公司打卡了。
相比在职仲裁,选择离职仲裁是最为常见的情况,可固定和搜集的证据空间有限,适合情况简单的案子。需要注意,离职仲裁有时效性,期限为一年。也就是说发生劳动争议后,需在劳动关系终止之日(离职当天)起一年内提出。否则既不能仲裁也不能诉讼。
劳动人事争议仲裁流程图(图源:附件材料)
如果已经和公司解除劳动合同, 但公司不配合,我们无法在入职新公司时提供证明该怎么办?
依照法律规定,用人单位有义务在解除劳动合同时出具离职证明。所以我们可以打电话向当地劳动监察大队投诉,或者通过劳动仲裁要求公司出具离职证明并赔偿由此带来的损失。但这么做要花费一定的时间成本。
虽然我们不能提供离职证明,但若是在客观事实中,与上一家用人单位的劳动合同已经解除或终止,则公司录用我们时已无法律障碍。我们可以提供与原公司劳动合同解除或终止的相关证明,例如:向原公司提出的《离职申请》、《工作交接记录》、《社保减员证明》等。
我们还可以向将要入职的公司,出具《承诺函》:
本人 A,身份证号:xxx,向 B 公司郑重承诺,本人已经与 C 公司劳动合同解除,若因本人做虚假承诺,给 B 公司造成任何不利后果,都由本人承担。落款:承诺人(签名),x 年 x 月 x 日。
分享一个我在仲裁前,来自律师的建议:在准备《仲裁申请书》的请求事项中,可以把能想到的都写上(未休年假、加班费、绩效提成等),每项要写清楚详细的计算方式,然后对应去搜集证据,围绕诉求中的主张和证据,阐述事实和理由。
仲裁委不一定支持当事人的全部主张,但没写在请求事项中的内容一定不会得到支持。
我将注意事项的重点内容,简单做了如下总结:
在遇到突发性岗位变动或工作安排,如:调岗、转岗、待岗等,一定要留心这当中的法律风险,不要在任何书面材料上签字,劳动者有权拒绝公司任何不合理不合法的行为。如果跟公司有协商和谈判空间,**可以尝试为自己争取一个不低于心理预期的好结果。**但如果公司想裁员,话HR 找你谈话,连 N 都没有,要诚意没诚意,还不好好说,想套路你,故意搞你心态。别犹豫,拿起法律武器吧。
法律是每个公民都可以用来保护自己的权利,做为一名普通劳动者,我觉得维权最重要的除了证据和诉讼策略,更重要的是维权的勇气和决心。
经常遇到这样一个问题:「员工被公司辞退了,应该赔偿 2N 还是 N+1」?
经济补偿金:就是我们大家口中常说的「 N 」,「 N 」是工作年限,用来指代劳动合同法中的经济补偿,计算方式是:经济补偿金= 工作年限 x 月工资。
「工作年限」也叫司龄,是补偿金和赔偿金的基数。工作满 1 年支付 1 个月工资。不满 6 个月的,支付 0.5 个月的经济补偿;满 6 个月以上但不满 1 年的,按 1 年支付。
「月工资」是指劳动者在劳动合同解除或终止前 12 个月的平均工资(包含奖金、绩效、年终奖等应得收入)。
经济赔偿金:公司「违法解除」劳动合同产生的惩罚性赔偿,计算方式是经济补偿金中「N」的 2 倍,也就是「2N 」。如果存在违法解除的情况,
据我了解,包括大厂在内的绝大多数公司都不守规矩,并不会按照法律标准去执行。有基础法律意识的公司想要裁员时,会尽量规避「违法解除」劳动合同,所以一般来说,「2N」的案子会越来越难打,员工三期(孕期、产期、哺乳期)内遭到公司违法解除劳动合同的情况能相对简单一些。公司会更倾向在「协商解除」的法律框架内做文章。
《劳动合同法》只规定了「协商解除」的最低标准是「 N 」,另外就是「N +1 」, 1 是指额外支付的 1 个月工资代通知金(即时解除)。除此之外,「N + 2 」 还是「 N + 3 」都没有法律依据。在 N 的基础上,公司想额外支付多少都没有法律限制, 只要双方协商一致,「N + 任何数字」都可以。
举个例子,公司想裁员找你协商,但你不同意,若公司一意孤行就要裁你,说你绩效不达标,单方面解除了劳动合同,此时就是违法解除,最低是「 2N 」。当然,劳资关系很复杂,公司可以找很多解除劳动合同的理由。如果想打「 2N 」,你需要提供充分的证据证明公司的行为违法,才有可能拿到「2N 」 。
以下是我整理的关于「2N」的常见情形:
用人单位解除劳动合同情形 | ||
---|---|---|
情形 | 性质 | 赔偿金 |
劳动者被依法追究刑事责任,用人单位解除劳动合同。 | 合法解除 | 0 |
劳动者在试用期间被证明不符合录用条件,用人单位解除劳动合同。 | 合法解除 | 0 |
劳动者严重违反用人单位的规章制度,用人单位解除劳动合同。 | 合法解除 | 0 |
劳动者严重失职,营私舞弊,给用人单位造成重大损害,用人单位解除劳动合同。 | 合法解除 | 0 |
劳动者同时与其他用人单位建立劳动关系,对完成本单位的工作任务造成严重影响,或者经用人单位提出,拒不改正,用人单位解除劳动合同。 | 合法解除 | 0 |
劳动者以欺诈、胁迫的手段,或者趁人之危,使对方在违背真实意思表示的情况下订立或者变更劳动合同的情形,致使劳动合同无效,用人单位解除劳动合同。 | 合法解除 | 0 |
劳动者处于法定保护期(如孕期、产期、哺乳期),用人单位解除劳动合同。 | 违法解除 | 2N |
未经劳动者同意,用人单位滥用解除权(如:试用期解除权),擅自解除合同。 | 违法解除 | 2N |
在本单位连续工作满 15 年,且距法定退休年龄不足 5 年,用人单位解除劳动合同。 | 违法解除 | 2N |
在本单位患职业病或因工负伤并被确认丧失或者部分丧失劳动能力,用人单位解除劳动合同。 | 违法解除 | 2N |
从事接触职业病危害作业的劳动者未进行离岗前职业健康检查,或者疑似职业病,病人在诊断或者医学观察期间,用人单位解除劳动合同。 | 违法解除 | 2N |
患病或者非因工负伤,在规定的医疗期内,用人单位解除劳动合同。 | 违法解除 | 2N |
连续签订 2 次固定期限劳动合同,并且劳动者没有严重过错、没有「不胜任岗位」「不能从事原岗位工作」的情况,合同期满,用人单位不同意不续签。 | 违法解除 | 2N |
中国裁判文书网(简称“裁判文书网”)是由最高人民法院主办的官方网站,主要目的是公开全国各级法院生效裁判文书,以促进司法公正和透明度。
这是我日常使用频率最高的工具之一,是非常实用的司法实践参考。这个网站提供了丰富的研究素材和数据,可以检索到全国各地法院各类案件的一审、二审或再审判决书,细分选项特别多。
虽然劳动仲裁裁决书不公开,只允许当事人及其代理人查阅。但我们依然可以从公开的他人判决书中看到一些仲裁信息,如当事人的主张及理由。
我们可以通过裁判文书网查询和研究先前的判例,了解当地法律趋势,解析审判口径。例如:劳动者和公司双方的争议点是什么,哪些主张和证据被法院支持,为什么会支持,为什么会驳回。以及在结案陈词中法官是如何看待问题的。
因为小红书有很多人将自己劳动仲裁的相关经历经验以笔记的形式分享,也有很多律师在平台上分享一些办案经验和法律知识,所以我觉得将它非常适合搜索高质量的信息,提前做好功课,多方验证信息,也可以让自己少走些弯路。
中国法律快查手册是一个 Github 上的开源项目,内容来源于国家法律法规数据库,这个项目的作者将数据库做了整合和搜索等功能,排版简洁,使用起来简单方便,非常适合平时查询和学习。作者仅提供了手机版( iOS、iPadOS)和网页版,并未提供电脑端版本,但我在 M1 MacBook Pro 也可以通过 App Store 应用商店安装使用。
劳动争议的话,建议主要关注《中华人民共和国劳动法》《中华人民共和国劳动合同法》这两个法规。
图源 / GitHub
通过研究司法案例以及本地近期公开的劳动争议相关判决书,我学到了一些劳动法基础知识,开始尝试梳理自己的案件情况,并希望可以得到律师的一些专业建议。先说说找律师来代理案子的 2 个常见误区
名气高的大律所,服务的对象一般是大公司,因为劳动者的案子都是标的金额低的小案子,不赚钱。
术业有专供,不是所有律师都擅长打劳动仲裁的案子。所以正确的方式是找主要做劳动争议案件的律所,或是主要代理劳动争议的律师。
可以是通过熟人(如:朋友、同事、家人)推荐,这样有一定的信任基础,也可以在小红书搜索,看看其他人选择的律师是否口碑好案例多。
如果在律师的履历介绍中,可以看到他擅长的领域很多,那不一定是好事。我们需要的是履历介绍中更多和劳动争议相关的律师。
我们的劳动争议案件虽然标的小且不赚钱,但不妨碍它很复杂。需要投入大量时间研究,准备和开庭,跑仲裁或法院。
律师也是如此。律师作为法律工作者,是提供专业的法律帮助,而不是代替处理相关全部事项。关于案件中的取证、和有关部门交涉都需要自己来完成,律师只是在这个过程中提供指导。律师可以帮忙争取最好的结果,但不能避免所有的风险。
作为当事人,我们对公司和自身情况最为了解,庭审时法官会围绕每个细节进行详细询问,这也是为什么自己要重视开庭并亲自参加庭审的原因。为了降低诉讼中存在的风险,我们需要多和律师交流讨论,自己要先厘清思路,整理好证据,做好充足准备,在开庭时才能自如应答,不会因为紧张而遗漏重点。
通过以上 2 个误区,我们可以对这部分做个小总结:
这个由司法部建设的网站,是于2018 年 5 月20 日正式上线运行。网站整合并收录了全国范围内的「法律服务机构」和「法律人员数据」。是
我们可以在里面选择律所或律师,也可以通过网站查询从其他渠道了解到的某家律所,或某位律师(可查询律师执业证号)。
算下来,我前后共咨询了 3 位律师(不包含法律援助)。我寻求律师帮助的路径是:在线咨询律师 A(付费)—— 线下咨询律师 B (付费)—— 线下咨询律师 C (付费)
最开始我先尝试了在网上找律师付费咨询,
虽然付费价格(支付 168 元)比线下咨询律师便宜很多,但是与价格相对等的是提问有次数、字数、时间限制(只能文字沟通不能语音),如果在律师不了解相关背景情况下进行提问,这将导致律师因无法判断案情而无法提供合理化建议。
于是我尝试尽量精简文字表述,将提问内容分为:事件背景(时间线)、我的现状、核心问题,制作成一张图片发送给律师。律师对我提问的内容一一做了解答,但是我结合自身情况,判断出律师提出的解决方案存在的风险比较高,所以我没有采纳。
在律师朋友的介绍下,我联系上了一位主要代理劳动争议案件的律师,律所离我也不算远,约了时间线下面谈。这次收获蛮大的,之前担心的问题基本都得到了解决,心里也更踏实了,只不过我的情况比较被动,需要看公司下一步的动作。
律师 C 是同事推荐,对于劳动仲裁案件的经验非常丰富。也是我综合考量后,选择了律师 C 委托代理我的一审案子。
关于律师咨询费。北京这边 200 - 3000 元 / 小时不等,我在线下咨询律师 B 两次,每次都是 500 元 / 小时。
关于律师代理费。根据案子标的额的百分比收取,不同审判阶段(仲裁、一审、二审),费用也不同。
包含劳动仲裁立案申请、证据交换、出庭使用的材料模板,可以通过北京市朝阳区人民政府官网页面底部的「点击此处下载相关附件」获取。文档中还有相关办理流程、注意事项,内容非常全面,可以仔细查阅。
本人带上身份证原件直接去附近银行营业厅的自助机上打印,可自行在机器上操作,省去拿号排队去窗口的过程,在使用银行自助机的过程中,有任何问题都可以找身边的银行经理帮忙解决,非常高效。建议在打印前了解相关费用标准并做好准备,有的银行会限制每月免费打印次数,超过则按张收取费用。我的工资是由公司通过 3 家银行(北京、交通、招商)发放,所以我需要去 3 家银行分别打印流水,北京银行和交通银行的自助机没有对流水单设立打印限制,但招商银行的打印限制我记得是 4 张 / 月,超过按 20 元 / 张收费。
在银行自助机操作时,我总结了 3 个重点:选择时间范围、筛选交易类型、包含银行盖章。
选择时间范围:依据劳动争议涉及的时间来选择流水打印范围,需按年选择,不可跨年。
筛选交易类型:要在筛选栏勾选交易类型(工资),指定筛选条件的目的,是为了方便自己和他人在查看银行流水单时,让工资发放情况一目了然,我们只需要工资流水的相关证明,不需要其他交易记录。
包含银行公章:银行自助机打印的工资明细一般默认包含公章,但也有个别银行需要在打印前勾选相关选项后,才可打印出盖了章的流水单。公章名称一般叫「回单专用章」或「会计业务章」。打印前后一定要留意打印的流水单是否包含银行的公章,如果没有公章则证据无效。
我的银行流水单盖章(左:北京银行 ;右:交通银行)
因为打印出来的纸张是黑白效果,如果直接使用手机将《劳动合同》拍照、,会导致部分信息模糊不清,这里有个便捷好用的小技巧:利用 iPhone 备忘录中的「扫描」功能,逐页将《劳动合同》扫描成 PDF 文件后再打印(相比直接打印效果更清晰,相比复印更省事)。可以避免因提交材料不合格导致返工的情况(不要重蹈我的覆辙)。
iPhone 备忘录中的「扫描文稿」功能
这是一款由国家知识产权局推出,具有法律效率的电子取证工具(仅支持 iOS、Android ),被法律工作者广泛使用。可生成不可篡改的带有时间戳的电子证据。取证范围包含:拍照、录像、录音、录屏、网页。在 App 中实名认证就可正常使用。
如果有些证据比较重要,可以选择这个工具。例如:在公司对我的办公软件停用前,我就使用了权利卫士的录屏功能,对组织架构、个人信息等情况进行了证据固定。法律规定谁主张谁举证的原则,在提起仲裁或诉讼时,我就有了通过权利卫士保存的电子版基础证据。
权利卫士可以很大程度上代替传统的公正取证方式。正确使用权利卫士,可以在价格低廉的同时,让取证变得更快捷。但它终归只是一个取证工具,证据的真实性没问题,但是否会被采纳,能发挥多大作用,是需要深度思考的问题。如果拿不准可以在律师的指导下使用,毕竟每次取证都需要付费。
权利卫士官网
录音证据大多情况都不能被仲裁庭采纳,主要原因是录音中的谈话内容,缺少适格主体等关键信息,例如:公司法人张三、人力资源负责人李四。录音属于锦上添花,能有更好,如果获取不到有用信息也没有关系。
录音的目的是通过谈话,问清事实情况,便于进一步固定证据。不能威胁恐吓对方(态度友好),否则证据无效。值得一提的是,录音证据的目的是为了佐证其他关联证据,形成证据链,不能单独使用。
谈话中不仅问题要清晰明确,还需要做到谈话人身份明确,内容清晰,具有客观真实和连贯性。可以先理清思路,再写成书面材料,通话前尝试练习几遍。
需要注意的是,录音做为证据时,要做两件事:一是要将未被剪接或者伪造,内容未被改变,无疑点的完整音频文件刻录成光盘,二是要将全部对话内容整理成文字版书面材料。
**录音内容文字版:**建议写明这些信息:时间、地点、设备、时长、当事人信息(本人名字、公司负责人职称和名字)。最后,在《证据目录》中标记好录音文件的位置,将录音光盘、文字版随其他证据一起提交。
另外,录音原视文件和载体(如手机)要保留好,用于庭审时查验。
证据材料清单样表(图源:附件材料)
法律援助更像是针对「特殊群体」设立的专项服务,主要服务于农民工或年纪较大的人。
朝阳劳动仲裁委员会也设立了法律援助咨询窗口,在服务总台领号排队,与仲裁立案窗口同在一个大厅。或许是我的案子具有复杂性,也可能窗口的工作人员并没太多时间帮我深度分析,我将手里的证据给到对方,尝试了解对于我这种情况的政策和裁判口径,但我并未收获到有用的信息和指导性建议。
所以,大家可以尝试性去咨询,但不要对法援抱有太大期望。也不要轻信仲裁委门口主动发卡片的律师咨询,我从没见过有执业证的律师需要揽客,这些卡片上的律师,很可能就不是律师,十有八九不靠谱,有很多被人被骗。
在仲裁阶段,我选择的是自己走完全流程,委托律师代理的是一审诉讼。
一是因为北漂多年,有过仲裁经历。
二是因为之前外籍二房东不退押金,并大放厥词可以去法院告他。于是我就听了他的建议,自己写诉状去法院起诉,所以有一些民事诉讼的经验。
劳动仲裁的立案、交换证据流程和法院诉讼在某些方面有相似之处,。所以我觉得仲裁相比诉讼来说在准备充分的情况下要简单很多,我的劳动争议案子偏复杂,需要经过法院审理。
以北京朝阳区为例,可以在北京人社局官网提前预约,预约后会短信告知线下提交立案材料的时间。疫情时官网预约后需要等 1 个月以上,现在要等的时间不好说。
也可以选择可以直接去线下领号立案(更推荐这种方式,更高效)。建议准备好材料,在 8 点前到门口排队, 10点前拿到号。否则可能会由于人多无法当天立案。我在疫情时和疫情后分别成功立案 2 次,是针对劳动争议中的不同时间段的仲裁请求。现在疫情已经过去,流程也简化了不少,相对来说更省事儿了。
北京市朝阳区劳动人事争议仲裁院
电 话:010-87983310
地 址:北京市朝阳区将台路5号院15号楼B座、C座
建议检查好相关材料,并将电子版备份到 U 盘,最好是带上笔记本电脑,若书面材料不符合立案窗口要求,可以即时用自己电脑修改,若有漏打材料,也可在服务台旁边的打印区付费打印。
成功立案后,窗口会现场出具举证通知等书面材料,立案后有 30 天的调解时间,期间会有工作人员打电话给当事人(公司、劳动者)双方,询问是否同意调解。接下来会收到朝阳仲裁委关于交换证据的通知邮件,以及短信提醒。
需要注意,自己和公司方的交换时间是一样的,务必要按照规定时间内到达指定窗口,办理举证及证据交换,必须是本人,否则过时不候。提交完成并签字确认后就可以离场了,如果在现场遇到公司的人也必理会。
接下来,等待开庭通知就好。开庭时要携带身份证原件、证据原件。需要注意开庭时间, **如果开庭当天本人迟到,会被视为撤诉。**仲裁开庭也比较简单,不会法院审理案件那样细致。主要是仲裁员通过问询双方当事人,录入双方确认的一些信息,如争议时间、质证理由、社保状态、薪资情况等。
仲裁案件都是随机分别仲裁员,我是2022 年底立案, 2023 年初开庭,直到 2023 年 10 月才收到裁决书。大家可以多联系自己案件的仲裁员,催问进度。
身边总有朋友认为,收到的劳动仲裁裁决书上支持了相关请求事项,就是胜利的信号,但仲裁是以调解为主,并不能最大限度维护劳动者自身权益。例如:法律规定协商解除劳动合同的最低标准是 N ,假设 N 是 10 个月,仲裁员会和公司、劳动者分别协商,讨价还价后,最终 N 可能是 6 或者 4,甚至更低,看似减少了劳动者维权的时间成本,实际最大受益者却是公司一方,公司试图用远低于法律规定的标准,带有恶意性、针对性、侮辱性的手段,低成本实现裁员目的。
所以劳动仲裁对劳动者来说只是维权之路的起点,还要经历一审诉讼、二审诉讼,即使最终赢了官司,还可能面临向法院申请强制执行的情况。
遇见过一些 HR ,千方百计地学习裁员套路和话术。一个员工为企业服务这么多年,非但不感谢员工多年的辛苦付出,反而为了达成裁员目的搞手段,扣帽子。想法设法压榨剥削打工人,员工不和你死磕跟谁磕?员工就是一个个普普通通的人,不止看重钱,还很在乎你怎么对他。
这个世界很奇妙。代理我案子的律师最近就遇见了一个离谱案件,将这个真实案例用一句话概括就是:职场当中套路虽多,但涉及底线的东西并不适用于套路。谁也说不准,后面被搞心态以及被裁的,会不会是自己。
]]>本文转载自:「 少数派 」,原文:https://url.hi-linux.com/jpw6C ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
这绝对是你应该做的事情。
并不是特别麻烦,而且效果也很突出。
这里解释了 Nginx 中的 gzip_static。
如果你不使用 gzip_static 而只是 “gzip on”,它每次都会被压缩并发送。
虽然它实际上可能缓存在内存中,但传统观点是 “每次都会执行压缩处理,因此 CPU 负载很大。”
因此,我每次都想停止处理它。
“gzip_static” 可以实现这一点。
它将搜索 “gz” 文件并为您使用!
如果是 “style.css”,它将自动搜索文件 “style.css.gz”。
如果找到,则返回它;如果没有,则照常压缩 “style.css”。
因此,如果提前创建压缩文件,就不会产生 CPU 负载,处理速度也会更快。
更好的是压缩级别。
它可以以最大压缩率保存。
通常情况下,不可能使用最高压缩设置,因为它会给 CPU 带来沉重的负载,但
由于你可以提前慢慢创建它,所以我通常将其保存在最高压缩级别(级别9)。
文件大小肯定会减小。
ngx_http_gzip_static_module 是必需的。
请使用以下命令检查是否已安装。
1 | nginx -V 2>&1 | tr ' ' 'n'|egrep _module | sed -e 's/--with-//g' |
如果未安装,系统会要求您重新安装 Nginx(可能)。
您必须使用 “–with-http_gzip_static_module” 安装它。
但是,当我从存储库安装时,所有内容都已包含在内,而无需执行任何操作。
请先检查一下。
1 | gzip_static on; |
只需在常规内容中添加 “gzip_static on” 即可。
这就是启用它所需要做的全部工作,所以很简单。
我建议你只设置这个。
如果你用谷歌搜索,你会看到很多人指定 “gzip_static always”,
这意味着在任何情况下都使用 gz 文件。
假设所有目标文件都被压缩并保存(理想情况)。
在这种情况下,将不会显示不支持的用户,因此您还需要指定 “gunzip on;”。
如果客户端不支持 gzip,文件会在服务器端解压后发送。
详情请参阅以下页面。
官方解释:ngx_http_gzip_static_module 模块
<对于 PHP>
1 | gzencode($file, 9) |
<对于 ubuntu(Linux)>
1 | gzip -9 -k style.css |
每次更新时我都会自动使用 PHP 创建它。
一般文本文件如 css、js、xml 等
可以将其视为图像之外的其他内容。
基本上,它将是 “gzip_types” 指定的文件。
但是,如果将压缩级别设置为 9,图像通常会变小。
如果你比较讲究的话,请用各种方式压缩一下看看。
这是 Nginx中 gzip static 的解释。
它非常有效,所以请尝试一下。
原文链接 https://doudonn.com/saba/2331/
]]>本文转载自:「 Lenix Blog 」,原文:https://url.hi-linux.com/o1BAZ ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
我见过太多的老鸟、新手对 SSH 基本只限于 SSH 到远程机器,实际这个命令我们一天要用很多次,但是对它的了解太少了,他的强大远远超出你的想象。当于你也许会说够用就够了,确实没错,但是你考虑过效率没有,或者还有哪些脑洞大开的功能会让你爱死他,这些功能又仅仅是一行命令就够了。
疫情期间一行 SSH 命令让我节省了 70% 的出差时间,来,让我们一起走一遍,看看会不会让你大开眼界。
绕过
跳板机直连;有时候正确上网还得靠自己,一行 SSH 命令来正确上网:
1 | nohup ssh -qTfnN -D 127.0.0.1:38080 root@1.1.1.1 "vmstat 10" 2>&1 >/dev/null & |
上面的 1.1.1.1 是你在境外的一台服务器,已经做好了免密登陆(免密见后面,要不你还得输一下密码),这句话的意思就是在本地启动一个 38080 的端口,上面收到的任何东西都会转发给 1.1.1.1:22(做了 SSH 加密),1.1.1.1:22 会解密收到的东西,然后把他们转发给 Google 之类的网站(比如你要访问的是 Google),结果依然通过原路返回
127.0.0.1:38080 就是要填入到你的浏览器中的 Socks5 代理服务器,什么都不需要装,非常简单。
原理图如下(灰色矩形框就是你本地 SSH 命令,SSH 线就是在穿墙, 国外服务器就是命令中的 1.1.1.1):
前面所说的代理是 Socks5 代理,一般浏览器都有插件支持,但是比如你的 Docker(或者其他程序)需要通过 HTTP 去拉取镜像就会出现如下错误:
1 | Sending build context to Docker daemon 8.704 kB |
如果是 Git 这样的应用内部可以配置 Socks5 和 HTTP 代理服务器,请参考另外一篇文章,但是有些应用就不能配置了,当然最终通过 SSH 大法还是可以解决这个问题:
1 | sudo ssh -L 443:108.177.125.82:443 root@1.1.1.1 // 在本地监听 443,转发给远程 108.177.125.82 的 443 端口 |
然后再在 /etc/hosts
中将域名 k8s.gcr.io 指向 127.0.0.1, 那么本来要访问 k8s.gcr.io:443 的,变成了访问本地 127.0.0.1:443 而 127.0.0.1:443 又通过 SSH 重定向到了 108.177.125.82:443 这样就实现了 HTTP 代理或者说这种特殊情况下的正确上网。这个方案不需要装任何东西,但是每个访问目标都要这样处理,好在这种情况不多。
1 | cat ~/.ssh/config |
在你的 SSH 配置文件增加上述参数,意味着 72 小时内登录同一台跳板机只有第一次需要输入密码,以后都是重用之前的连接,所以也就不再需要输入密码了。
加了如上参数后的登录过程就有这样的东东(默认没有,这是 Debug 信息):
1 | debug1: setting up multiplex master socket |
/home/ren/tmp/ssh_mux_10.16.*.*_22_corp
这个就是保存好的 Socket,下次可以重用,免密码。 in 259200 seconds 对应 72小时
比如有一批客户机房的机器 IP 都是 192.168.., 然后需要走跳板机 100.10.1.2 才能访问到,那么我希望以后在笔记本上直接 ssh 192.168.1.5 就能直接连上
1 | $ cat /etc/ssh/ssh_config |
上面配置的意思是执行 ssh 192.168.1.5
的时候命中规则 Host 192.168.*.*
所以执行 ProxyCommand
先连上跳板机再通过跳板机连向 192.168.1.5 。这样在你的笔记本上就跟 192.168.. 的机器仿佛在一起,SSH 可以上去,但是 ping 不通这个 192.168.1.5 的 IP
划重点:公司的线上跳板机做了特殊限制,限制了这个技能。日常环境跳板机支持这个功能
比如我的跳板配置:
1 | #到美国的机器用美国的跳板机速度更快 |
其实我的配置文件里面还有很多规则,懒得一个个隐藏 IP 了,这些规则是可以重复匹配的
来看一个例子
1 | ren@ren-VirtualBox:/$ ping -c 1 10.16.1.* |
本来我的笔记本跟 10.16.1.* 是不通的(ping 不通),但是 SSH 可以直接连上,实际 SSH 登录过程中自动走跳板机 139...* 就连上了
-vvv
参数是 debug,把 SSH 登录过程的日志全部打印出来。
远程机器部署了 Web Server(端口 8083),需要通过浏览器来访问这个 WEB 服务,但是 server 在隔离环境中,只能通过 SSH 访问到。一般来说会在隔离环境中部署一个 Windows 机器,通过这个 Windows 机器来访问到这个 Web Server。能不能省掉这个 Windows 机器呢?
现在我们试着用 SSH 来实现本地浏览器直接访问到这个隔离环境中的 Web Server。
假设 Web Server 是:10.1.1.123:8083, SSH 账号是:user
先配置好本地直接 ssh user@10.1.1.123
(参考前面的 ProxyCommand
配置过程,最好是免密也配置好),然后在你的笔记本上执行:
1 | ssh -CNfL 0.0.0.0:8088:10.1.1.123:8083 user@10.1.1.123 |
或者:(root@100.1.2.3 -p 54900 是可达10.1.1.123的代理服务器)
1 | ssh -CNfL 0.0.0.0:8089:10.1.1.123:8083 root@100.1.2.3 -p 54900 |
这表示在本地启动一个 8088 的端口,将这个 8088 端口映射到 10.1.1.123 的 8083 端口上,用的 SSH 账号是 user
然后在笔记本上的浏览器中输入:127.0.0.1:8088 就看到了如下界面:
反过来,也可以让隔离环境机器通过代理上网,比如安装 yum
先了解如下知识点,在 ~/.ssh/config 配置文件中:
1 | GSSAPIAuthentication=no |
禁掉 GSSAPI 认证,GSSAPIAuthentication 是个什么鬼东西请自行 Google (多一次没必要的授权认证过程,然后等待超时)。 这里要理解 SSH 登录的时候有很多种认证方式(公钥、密码等等),具体怎么调试请记住强大的命令参数 ssh -vvv
上面讲到的技巧都能通过 -vvv
看到具体过程。
比如我第一次碰到 ssh 比较慢总是需要 30 秒后才登录,不能忍受,于是登录的时候加上 -vvv 明显看到控制台停在了:GSSAPIAuthentication 然后 Google 了一下,禁掉就好了
当然还有去掉每次 SSH 都需要先输入 Yes
Expect 在有些公司是被禁止的
SSH 免密码的原理是将本机的 Pubkey 复制到目标机器的 ~/.ssh/authorized_keys
里面。可以手工复制粘贴,也可以 ssh-copy-id 等。
如果有 100 台机器,互相两两打通还是比较费事(大概需要100*99次 copy key)。 下面通过 expect 来解决输入密码,然后配合 Shell 脚本来批量解决这个问题。
这个脚本需要四个参数:目标IP、用户名、密码、Home 目录,也就是 SSH 到一台机器的时候帮我们自动填上 yes,和密码,这样就不需要人肉一个个输入了。
再在外面写一个循环对每个 IP 执行如下操作:
if 代码部分检查本机 ~/.ssh/ 下有没有 id_rsa.pub,也就是是否以前生成过密钥对,没生成的话就帮忙生成一次。
for 循环部分一次把生成的密钥对和 authorized_keys 复制到所有机器上,这样所有机器之间都不需要输入密码就能互相登陆了(当然本机也不需要输入密码登录所有机器)
最后一行代码:
1 | ssh $user@$n "hostname -i" |
验证一下没有输密码是否能成功 SSH 上去。
思考一下,为什么这么做就可以打通两两之间的免密码登录,这里没有把所有机器的 PubKey 复制到其他所有机器上去啊
答案:其实这个脚本做了一个取巧投机的事,那就是让所有机器共享一套公钥、私钥。
有时候我也会把我的 Windows 笔记本和我专用的某台虚拟机共享一套秘钥,这样任何新申请的机器打通一次账号就可以在两台机器上随便登录。请保护好自己的私钥
如果免密写入 authorized_keys 成功,但是通过 SSH Pubkey 认证的时候还是有可能失败,这是因为 Pubkey 认证要求:
1 | > StrictHostKeyChecking=no |
这里只是帮大家入门了解 SSH ,掌握好这些配置文件和 -vvv 后有好多好玩的可以去挖掘,同时也请在留言中说出你的黑技能
下面是我个人常用的 SSH Config 配置
1 | cat ~/.ssh/config |
ProxyJump 完全可以取代 ProxyCommand,比如 ProxyJump 不再依赖 nc、也更灵活一些。
1 | Host * |
参数的优先级是:命令行配置选项 > ~/.ssh/config > /etc/ssh/ssh_config
在 SSH 的 身份验证阶段,SSH只支持服务端保留公钥,客户端保留私钥的方式,所以方式只有两种:客户端生成密钥对,将公钥分发给服务端;服务端生成密钥对,将私钥分发给客户端。只不过出于安全性和便利性,一般都是客户端生成密钥对并分发公钥(阿里云服务器秘钥对–服务器将一对密钥中的公钥放在 authorized_keys, 私钥给client登陆用)
服务器上的 /etc/ssh/ssh_host*
是用来验证服务器身份的秘钥对(对应 Client的 Known_hosts), 在主机验证阶段,服务端持有的是私钥,客户端保存的是来自于服务端的公钥。注意,这和身份验证阶段密钥的持有方是相反的。
SSH 支持多种身份验证机制,它们的验证顺序如下:gssapi-with-mic,hostbased,publickey,keyboard-interactive,password,但常见的是密码认证机制 (password) 和公钥认证机制 (public key). 当公钥认证机制未通过时,再进行密码认证机制的验证。这些认证顺序可以通过 SSH 配置文件(注意,不是sshd 的配置文件)中的指令 PreferredAuthentications 改变。
大多时候隧道会失效,或者断开,我们需要有重连机制,一般可以通过 autossh(需要单独安装)搞定自动重连,再配合 Systemd 或者 Crond 搞定永久自动重连
比如以下代码在 gf 开启 2 个远程转发端口
1 | remote_port=(30081 30082) |
或者另外创建一个 Service 服务
1 | [Unit] |
好多问题我都是 Debug 发现的
/usr/sbin/sshd -ddd -p 2222
在 2222 端口对 sshd 进行 Debug,看输出信息验证为什么 pubkey 不能 Login 等. 一般都是权限不对,/root 以及 /root/.ssh 文件夹的权限和owner都要对,更不要说 /root/.ssh/authorized_keys 了1 | /usr/sbin/sshd -ddd -p 2222 |
可以用一下脚本生成一个彩色文件,放到 /etc/motd 中就行
Basic colors are numbered:
1 | !/bin/sh |
以上脚本运行结果
Banner
指定用户登录后,sshd 向其展示的信息文件(Banner /usr/local/etc/warning.txt
),默认不展示任何内容。
或者配置:
1 | cat /etc/ssh/sshd_config |
/etc/ssh/my_banner
中可以放置提示内容。
-y Read a private OpenSSH format file and print an OpenSSH public key to stdout.
cd ~/.ssh/ ; ssh-keygen -y -f id_rsa | cut -d’ ’ -f 2 ; cut -d’ ’ -f 2 id_rsa.pub
ssh-keygen -y -e -f <private key>
获取一个私钥并打印相应的公钥,该公钥可以直接与您可用的公钥进行比较
Github 可以取到你的公钥,如果别人让你查看他的服务器,直接给 https://github.com/plantegg.keys 这个链接,让他把下载的 Key 加到 ~/.ssh/authorized_keys 里面就行了。
1 | ssh-keygen -q -t rsa -N '' -f ~/.ssh/id_rsa <<<y |
删除或者修改 Passphrase
1 | > run `ssh-keygen -p` in a terminal. It will then prompt you for a keyfile (defaulted to the correct file for me, `~/.ssh/id_rsa`), the old passphrase (enter what you have now) and the new passphrase (enter nothing). |
如果用 8.0 去修改 PKCS8 格式的 key 可以指定格式参数
1 | ssh-keygen -p -m "PKCS8" -f ./id_dsa |
私钥设置了密码以后,每次使用都必须输入密码,有时让人感觉非常麻烦。比如,连续使用scp
命令远程拷贝文件时,每次都要求输入密码。
ssh-agent
命令就是为了解决这个问题而设计的,它让用户在整个 Bash 对话(session)之中,只在第一次使用 SSH 命令时输入密码,然后将私钥保存在内存中,后面都不需要再输入私钥的密码了。
第一步,使用下面的命令新建一次命令行对话。
1 | $ eval `ssh-agent` |
上面命令中,ssh-agent
会先自动在后台运行,并将需要设置的环境变量输出在屏幕上,类似下面这样。
1 | $ ssh-agent |
eval
命令的作用,就是运行上面的 ssh-agent
命令的输出,设置环境变量。
第二步,在新建的 Shell 对话里面,使用 ssh-add
命令添加默认的私钥(比如~/.ssh/id_rsa
,或~/.ssh/id_dsa
,或~/.ssh/id_ecdsa
,或~/.ssh/id_ed25519
)。
1 | $ ssh-add |
上面例子中,添加私钥时,会要求输入密码。以后,在这个对话里面再使用密钥时,就不需要输入私钥的密码了,因为私钥已经加载到内存里面了。
如果添加的不是默认私钥,ssh-add
命令需要显式指定私钥文件。
1 | $ ssh-add my-other-key-file |
上面的命令中,my-other-key-file
就是用户指定的私钥文件。
SSH agent 程序能够将您的已解密的私钥缓存起来,在需要的时候用它来解密key chanllge返回给 SSHD https://webcache.googleusercontent.com/search?q=cache:7OfvSBFki10J:https://www.ibm.com/developerworks/cn/linux/security/openssh/part2/+&cd=7&hl=en&ct=clnk&gl=hk keychain介绍
sshd 有自己的一对或多对密钥。它使用密钥向客户端证明自己的身份。所有密钥都是公钥和私钥成对出现,公钥的文件名一般是私钥文件名加上后缀.pub
。
DSA 格式的密钥文件默认为 /etc/ssh/ssh_host_dsa_key
(公钥为ssh_host_dsa_key.pub
),RSA 格式的密钥为 /etc/ssh/ssh_host_rsa_key
(公钥为 ssh_host_rsa_key.pub
)。如果需要支持 SSH 1 协议,则必须有密钥/etc/ssh/ssh_host_key
。
如果密钥不是默认文件,那么可以通过配置文件 sshd_config
的 HostKey
配置项指定。默认密钥的 HostKey
设置如下。
1 | # HostKey for protocol version 1 |
注意,如果重装 sshd,/etc/ssh
下的密钥都会重新生成(这些密钥对用于验证Server的身份),导致客户端重新 ssh 连接服务器时,会跳出警告,拒绝连接。为了避免这种情况,可以在重装 sshd 时,先备份/etc/ssh
目录,重装后再恢复这个目录。
调试:非后台(-D)和debug(-d)模式启动sshd,同时监听2222和3333端口
sshd -D -d -p 2222 -p 3333
sshd config 配置多端口
1 | #cat /etc/ssh/sshd_config |
scp -o “ProxyCommand=nc -X 5 -x [SOCKS_HOST]:[SOCKS_PORT] %h %p” [LOCAL/FILE/PATH] [REMOTE_USER]@[REMOTE_HOST]:[REMOTE/FILE/PATH]
其中[SOCKS_HOST]和[SOCKS_PORT]是 Socks 代理的 LOCAL_ADDRESS 和 LOCAL_PORT。[LOCAL/FILE/PATH]、[REMOTE_USER]、[REMOTE_HOST]和[REMOTE/FILE/PATH] 分别是要复制文件的本地路径、要复制到的远端主机的用户名、要复制到的远端主机名、要复制文件的远端路径,这些参数与不使用代理时一样。“ProxyCommand=nc” 表示当前运行命令的主机上需要有 nc 命令。
Specifies the proxy command for the connection. This command is launched prior to making the connection to Hostname. %h is replaced with the host defined in HostName and %p is replaced with 22 or is overridden by a Port directive.
在 SSH 连接目标主机前先执行 ProxyCommand 中的命令,比如 .ssh/config
中有如下配置
1 | host remote-host |
如上配置指的是,如果执行ssh remote-host 命中host规则,那么先执行命令 ssh -l root -p 52146 1.2.3.4 exec /usr/bin/nc 同时把remote-host和端口(默认22)传给nc
ProxyCommand 和 ProxyJump 很类似,ProxyJump 使用:
1 | //ssh到centos8机器上,走的是gf这台跳板机,本地一般和centos8不通 |
需要 OpenSSH 7.3
以上版本才可以使用 ProxyJump
, 相对 ProxyCommand 更简洁方便些
1 | #ssh 116 就可以通过 jumpserver:50023 连上 root@1.116.2.1:22 |
列出本地所支持默认的加密算法
1 | #ssh -Q key |
比如连服务器报如下错误:
1 | debug1: kex: algorithm: (no match) |
表示服务端支持 diffie-hellman-group1-sha1,diffie-hellman-group14-sha1 加密,但是client端不支持,那么可以指定算法来强制client端使用某种和server一致的加密方式
1 | ssh -oKexAlgorithms=+diffie-hellman-group14-sha1 -l user |
如果仍然报以下错误:
1 | debug2: first_kex_follows 0 |
那么可以配置来解决:
1 | Host * |
When an SSH client connects to a server, each side offers lists of connection parameters to the other. These are, with the corresponding ssh_config keyword:
KexAlgorithms
: the key exchange methods that are used to generate per-connection keysHostkeyAlgorithms
: the public key algorithms accepted for an SSH server to authenticate itself to an SSH clientCiphers
: the ciphers to encrypt the connectionMACs
: the message authentication codes used to detect traffic modification了解完前面的一些小知识,再来看看无所不能的三大杀招。上面的各种代理基本都是由这三种转发模式实现的。
SSH 能够做动态转发、本地转发、远程转发。先简要概述下他们的特点和使用场景
socks5协议
,当正确上网用,本地转发是 TCP 协议动态转发常用来正确上网,本地转发用来打洞,这两种转发启动的端口都是在本地;远程转发也是打洞的一种,只不过启用的端口在远程机器上。
动态转发指的是,本机与 SSH 服务器之间创建了一个加密连接,然后本机内部针对某个端口的通信,都通过这个加密连接转发。它的一个使用场景就是,访问所有外部网站,都通过 SSH 转发。
动态转发需要把本地端口绑定到 SSH 服务器。至于 SSH 服务器要去访问哪一个网站,完全是动态的,取决于原始通信,所以叫做动态转发。
动态的意思就是:需要访问的目标、端口还不确定。后面要讲的本地转发、远程转发都是针对具体 IP、Port 的转发。
1 | ssh -D 4444 ssh-server -N |
注意,这种转发采用了 SOCKS5 协议。访问外部网站时,需要把 HTTP 请求转成 SOCKS5 协议,才能把本地端口的请求转发出去。-N
参数表示,这个 SSH 连接不能执行远程命令,只能充当隧道。
下面是 SSH 隧道建立后的一个使用实例。
1 | curl -x socks5://localhost:4444 http://www.example.com |
上面命令中,curl 的 -x
参数指定代理服务器,即通过 SOCKS5 协议的本地3000
端口,访问http://www.example.com
。
官方文档关于 -D
的介绍
-D [bind_address:]port
Specifies a local “dynamic” application-level port forwarding. This works by allocat‐
ing a socket to listen to port on the local side, optionally bound to the specified
bind_address. Whenever a connection is made to this port, the connection is forwarded
over the secure channel, and the application protocol is then used to determine where
to connect to from the remote machine. Currently the SOCKS4 and SOCKS5 protocols are
supported, and ssh will act as a SOCKS server. Only root can forward privileged ports.
Dynamic port forwardings can also be specified in the configuration file.
特别注意,如果 ssh -D
要启动的本地 Port 已经被占用了是不会报错的,但是实际 Socks 代理会没启动成功。
本地转发(Local Forwarding)指的是,SSH 服务器作为中介的跳板机,建立本地计算机与特定目标网站
之间的加密连接。本地转发是在本地计算机的 SSH 客户端建立的转发规则。
典型使用场景就是,打洞,经过跳板机访问无法直接连通的服务。
它会指定一个本地端口(Local-Port),所有发向那个端口的请求,都会转发到 SSH 跳板机(ssh-server),然后 SSH 跳板机作为中介,将收到的请求发到目标服务器(target-host)的目标端口(target-port)。
1 | ssh -L :local-port:target-host:target-port ssh-server //target-host是ssh-server的target-host, target-host 域名解析、路由都是由 ssh-server 完成 |
上面命令中,-L
参数表示本地转发,local-port
是本地端口,target-host
是你想要访问的目标服务器,target-port
是目标服务器的端口,ssh-server
是 SSH 跳板机。当你访问 localhost:local-port 的时候会通过 ssh-server 把请求转给 target-host:target-port
上图对应的命令是:
1 | ssh -L 53682:remote-server:53682 ssh-server |
然后,访问本机的 53682 端口,就是访问 remote-server
的 53682 端口.
1 | $ curl http://localhost:53682 |
注意,本地端口转发采用 HTTP 协议,不用转成 SOCKS5 协议。如果需要 HTTP 的动态代理,可以先起 Socks5 动态代理,然后再起一个本地转发给动态代理的 Socks5 端口,这样就有一个 HTTP 代理了,能给 Yum、Docker 之类的使用。
这个命令最好加上-N
参数,表示不在 SSH 跳板机执行远程命令,让 SSH 只充当隧道。另外还有一个-f
参数表示 SSH 连接在后台运行。
如果经常使用本地转发,可以将设置写入 SSH 客户端的用户个人配置文件。
1 | Host test.example.com |
远程端口指的是在远程 SSH 服务器建立的转发规则。主要是执行 SSH 转发的机器别人连不上,所以需要一台 Client 能连上的机器当远程转发端口,要不就是本地转发了。
由于本机无法访问内网 SSH 跳板机,就无法从外网发起 SSH 隧道,建立端口转发。必须反过来,从 SSH 跳板机发起隧道,建立端口转发,这时就形成了远程端口转发。
1 | ssh -fNR 30.1.2.3:30081:166.100.64.1:3128 root@30.1.2.3 -p 2728 |
上面的命令,首先需要注意,不是在 30.1.2.3 或者 166.100.64.1 上执行的,而是找一台能联通 30.1.2.3 和 166.100.64.1 的机器来执行,在执行前 Remote Clients 能连上 30.1.2.3 但是 30.1.2.3 和 166.100.64.1 不通,所以需要一个中介将 30.1.2.3 和166.100.64.1打通,这个中介就是下图中的MobaXterm所在的机器,命令在 MobaXterm 机器上执行
执行上面的命令以后,跳板机 30.1.2.3 到 166.100.64.1 的隧道已经建立了,这个隧道是依赖两边都能连通的 MobaXterm 机器。然后,就可以从 Remote Client 访问目标服务器了,即在 Remote Client 上执行下面的命令。
1 | $ curl http://30.1.2.3:30081 |
执行上面的命令以后,命令就会输出服务器 166.100.64.1 的3128端口返回的内容。
如果经常执行远程端口转发,可以将设置写入 SSH 客户端的用户个人配置文件。
1 | Host test.example.com |
注意远程转发需要:
- sshd_config 里要打开
AllowTcpForwarding
选项,否则-R
远程端口转发会失败。- 默认转发到远程主机上的端口绑定的是
127.0.0.1
,如要绑定0.0.0.0
需要打开 sshd_config 里的GatewayPorts
选项(然后ssh -R 后加上 *:port )。这个选项如果由于权限没法打开也有办法,可配合ssh -L
将端口绑定到0.0.0.0
。
开通远程转发后,如果需要动态代理(比如访问所有 Web 服务),那么可以在 30081 端口机器上(30.1.2.3)执行:
1 | nohup ssh -qTfnN -D *:13658 root@127.0.0.1 -p 30081 vmstat 10 >/dev/null 2>&1 |
表示在 30081 机器上(30.1.2.3)启动了一个 Socks5 动态代理服务
curl -I --socks5-hostname localhost:13659 twitter.com
curl -x socks5://localhost:13659 twitter.com
Suppose you have a socks5 proxy running on localhost:13659 .
In curl >= 7.21.7, you can use
1 | curl -x socks5h://localhost:13659 http://www.google.com/ |
In a proxy string, socks5h:// and socks4a:// mean that the hostname is
resolved by the SOCKS server. socks5:// and socks4:// mean that the
hostname is resolved locally. socks4a:// means to use SOCKS4a, which is
an extension of SOCKS4. Let’s make urllib3 honor it.
In curl >= 7.18.0, you can use
1 | curl --socks5-hostname localhost:13659 http://www.google.com/ |
--proxy
参数含义如下:
1 | The --socks5 option is basically considered obsolete since curl 7.21.7. This is because starting in that release, you can now specify the proxy protocol directly in the string that you specify the proxy host name and port number with already. The server you specify with --proxy. If you use a socks5:// scheme, curl will go with SOCKS5 with local name resolve but if you instead use socks5h:// it will pick SOCKS5 with proxy-resolved host name. |
指定命令行参数,通过命令行指定 HTTP 代理服务器的方式如下:
wget -Y on -e “http_proxy=http://[HTTP_HOST]:[HTTP_PORT]” http://facebook.com/
其中:[HTTP_HOST]和[HTTP_PORT]是 HTTP Proxy 的 ADDRESS 和 PORT。
X.509 只是一种常用的证书格式,一般以PEM编码,PEM 编码的证书通常以 .pem
、.crt
或 .cer
为后缀。再次提醒,这只是“通常”情况,实际上某些工具可能并不遵循这些惯例。通过pem证书可以访问需要认证的 HTTPS 服务(比如Etcd、Apiserver 等)
通过命令 cat /etc/kubernetes/pki/ca.crt | openssl x509 -text
也可以得到下图信息
.pub
or .pem
,ca.crt
.prv,
.key
, or .pem
, ca.key
。并不是所有的场景都需要向这些大型的 CA 机构申请公钥证书,在任何一个企业,组织或是团体内都可以自己形这样的“小王国”,也就是说,你可以自行生成这样的证书,只需要你自己保证自己的生成证书的私钥的安全,以及不需要扩散到整个互联网。下面,我们用 openssl
命令来演示这个过程。
ca.crt
和私钥 ca.key
1 | openssl req -newkey rsa:2048 \ |
1 | openssl genrsa -out alice.key 2048 |
1 | openssl req -new -key alice.key -days 365 -out alice.csr \ |
1 | openssl x509 -req -in alice.csr \ |
]]>本文转载自:「 plantegg 的博客 」,原文:http://tinyurl.com/25py66n3 ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
国内外公共 DNS,加密 DNS 汇总,可用作各种 DNS 服务器的上游服务器,在下面的列表中 选择 2-3 个为宜。选得多并不是最优解,望周知。
作为上游服务器列表时,因为 UDP 传输时间短,公共 DNS 统一选择 udp
,而加密 DNS 统一选择:https
,当然tls
也可以。
1 | 223.5.5.5 |
1 | 119.29.29.29 |
1 | 180.76.76.76 |
1 | 电信、移动、铁通: |
1 | 114.114.114.114 |
1 | alidns_ip/dns-query |
1 | doh.pub/dns-query |
1 | 1.1.1.1 |
1 | 8.8.8.8 |
1 | 199.85.126.10 |
1 | 64.6.64.6 |
1 | 8.26.56.2 |
1 | 84.200.69.80 |
1 | 208.67.222.222 |
1 | dns.google/dns-query 类型选择:https |
1 | 1.1.1.1/dns-query 类型选择:tls |
1 | dns11.quad9.net/dns-query 类型选择:https |
1 | doh.opendns.com/dns-query |
名称 | DNS 服务器 IP 地址 | |
---|---|---|
114 DNS | 114.114.114.114 | 114.114.115.115 |
阿里 AliDNS | 223.5.5.5 | 223.6.6.6 |
百度 BaiduDNS | 180.76.76.76 | |
DNSPod DNS+ | 119.29.29.29 | 182.254.116.116 |
CNNIC SDNS | 1.2.4.8 | 210.2.4.8 |
oneDNS | 117.50.11.11 | 117.50.22.22 |
DNS 派电信/移动/铁通 | 101.226.4.6 | 218.30.118.6 |
DNS 派 联通 | 123.125.81.6 | 140.207.198.6 |
Google DNS | 8.8.8.8 | 8.8.4.4 |
Google IPv6 DNS | 2001:4860:4860::8888 | 2001:4860:4860::8844 |
IBM Quad9 | 9.9.9.9 | |
OpenDNS | 208.67.222.222 | 208.67.220.220 |
V2EX DNS | 199.91.73.222 | 178.79.131.110 |
Verizon DNS | 4.2.2.1 | 4.2.2.2 |
中国台湾中华电信 HiNet DNS | 168.95.192.1 | 168.95.1.1 |
中科大DNS | 202.141.162.123(中国电信) | 202.38.93.153 (教育网) |
202.141.176.93 (中国移动) | ||
韩国KT DNS | 168.126.63.1 | 168.126.63.2 |
名称 | DNS 服务器 IP 地址 | |
---|---|---|
FUN DNS | 119.23.248.241 | |
Pure DNS | 123.207.137.88 | 115.159.220.214 |
CuteDns | 120.77.212.84(南方) | 101.236.28.23(北方) |
aixyz DNS | 115.159.146.99(南方) | 123.206.21.48(北方) |
BAI DNS | 106.14.152.170 | |
不知名DNS | 180.97.235.30 | 115.159.96.69 |
123.206.21.48 | 123.207.137.88 |
名称 | DNS 服务器 IP 地址 | |
---|---|---|
安徽电信 DNS | 61.132.163.68 | 202.102.213.68 |
北京电信 DNS | 219.141.136.10 | 219.141.140.10 |
重庆电信 DNS | 61.128.192.68 | 61.128.128.68 |
福建电信 DNS | 218.85.152.99 | 218.85.157.99 |
甘肃电信 DNS | 202.100.64.68 | 61.178.0.93 |
广东电信 DNS | 202.96.128.86 | 202.96.128.166 |
202.96.128.68 | 202.96.134.33 | |
广西电信 DNS | 202.103.225.68 | 202.103.224.68 |
贵州电信 DNS | 202.98.192.67 | 202.98.198.167 |
河南电信 DNS | 222.88.88.88 | 222.85.85.85 |
黑龙江电信 | 219.147.198.230 | 219.147.198.242 |
湖北电信 DNS | 202.103.24.68 | 202.103.0.68 |
湖南电信 DNS | 222.246.129.80 | 59.51.78.211 |
江苏电信 DNS | 218.2.2.2 | 218.4.4.4 |
218.2.135.1 | 61.147.37.1 | |
江西电信 DNS | 202.101.224.69 | 202.101.226.68 |
内蒙古电信 | 219.148.162.31 | 222.74.39.50 |
山东电信 DNS | 219.146.1.66 | 219.147.1.66 |
陕西电信 DNS | 218.30.19.40 | 61.134.1.4 |
上海电信 DNS | 202.96.209.133 | 116.228.111.118 |
108.168.255.118 | 202.96.209.5 | |
四川电信 DNS | 61.139.2.69 | 218.6.200.139 |
天津电信 DNS | 219.150.32.132 | 219.146.0.132 |
云南电信 DNS | 222.172.200.68 | 61.166.150.123 |
浙江电信 DNS | 202.101.172.35 | 61.153.177.196 |
60.191.244.5 | 61.153.81.75 |
名称 | DNS 服务器 IP 地址 | |
---|---|---|
北京联通 DNS | 123.123.123.123 | 123.123.123.124 |
202.106.195.68 | 202.106.0.20 | |
重庆联通 DNS | 221.5.203.98 | 221.7.92.98 |
广东联通 DNS | 210.21.196.6 | 221.5.88.88 |
河北联通 DNS | 202.99.160.68 | 202.99.166.4 |
河南联通 DNS | 202.102.224.68 | 202.102.227.68 |
黑龙江联通DNS | 202.97.224.69 | 202.97.224.68 |
吉林联通 DNS | 202.98.0.68 | 202.98.5.68 |
江苏联通 DNS | 221.6.4.66 | 221.6.4.67 |
内蒙古联通 DNS | 202.99.224.68 | 202.99.224.8 |
山东联通 DNS | 202.102.128.68 | 202.102.152.3 |
202.102.154.3 | 202.102.134.68 | |
山西联通 DNS | 202.99.192.66 | 202.99.192.68 |
陕西联通 DNS | 221.11.1.67 | 221.11.1.68 |
上海联通 DNS | 210.22.70.3 | 210.22.84.3 |
四川联通 DNS | 119.6.6.6 | 124.161.87.155 |
天津联通 DNS | 202.99.104.68 | 202.99.96.68 |
浙江联通 DNS | 221.12.1.227 | 221.12.33.227 |
辽宁联通 DNS | 202.96.69.38 | 202.96.64.68 |
名称 | DNS 服务器 IP 地址 | |
---|---|---|
江苏移动 DNS | 221.131.143.69 | 112.4.0.55 |
安徽移动 DNS | 211.138.180.2 | 211.138.180.3 |
山东移动 DNS | 218.201.96.130 | 211.137.191.26 |
四川移动 DNS | 223.87.238.22 |
]]>本文转载自:「再从头的博客 」,原文:https://url.hi-linux.com/pWijO ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
其实本来另一篇文章已经写好了,但考虑到临近年底,劳动仲裁指南的实用性可能会更大,于是便有了这篇文章。作为劳动仲裁曾经的亲历者,reizhi 希望能够借这篇文章把劳动仲裁这件事给说明白。虽然希望各位都用不上,但多了解一些总是好的。
在劳动合同签立的过程中,用人单位往往处于极其强势的地位。对应的,劳动者毫无疑问属于弱势的一方。在双方就劳动合同的履行产生争议时,依靠友好协商有时并不能达成让人满意的结果,于是便有了劳动争议仲裁委员会。无论劳动者是否有与用人单位进行协商,只要产生劳动争议,均可以提交劳动仲裁申请。本文接下来将专注于因裁员导致产生劳动仲裁的情形。
除开试用期外,以下情形公司可以单方面解除劳动合同,不需要支付补偿金或者赔偿金(过失类):
以上这些情形相信大家应该不太能够遇到,同时如果提出过失类解除需要用人单位进行举证,这里便不展开介绍了。
以下这些情形是大家常说的 N 或者 N+1 补偿的情形:
其中,适用第二、三、四条进行劳动关系解除时,用人单位需要提前30日书面告知劳动者,否则需要支付额外的一个月工资,即 N+1 。其他情形需要支付 N 补偿,具体如何计算后面还会讲到。
看到这里可能有人已经开始打退堂鼓了,是否只要公司提出劳动者不能胜任工作,并以绩效完成情况作为证明就能适用 N 或 N+1 辞退呢?其实并不是,如果需要适用第三条的不能胜任工作,公司需要首先进行培训或者调整工作岗位,且能够提供对应的证明材料。其次公司需要提供的证明材料较多,包括:首次不能胜任的相关证明,进行了合理的培训或者调整岗位的证明,再次不能胜任的相关证明。
故虽然此点可能作为人事的托词,但实际举证难度较大,且绩效考核标准的订立可操作空间大,在实际仲裁乃至诉讼过程中,用人单位被判定为不利的概率极高。一旦此点认定失败,将直接被认定为非法解除劳动合同,从而致使补偿金 N+1 升级为赔偿金 2N 。
其中的第四条客观情况发生重大变化,也比较喜欢被某些人事加以利用。需要明确的是此点包含两条重要约束,即客观和重大变化。即变化所产生的原因并非用人单位所能主观决定和控制,而非单纯的因为组织架构调整就能够引用此条予以辞退。另外,在解除劳动合同前也需要与劳动者进行协商,提供相近或类似的工作岗位,并且待遇不能低于原岗位。在劳动者拒绝的情况下,才能适用本条。
最后,对于调岗的情形,需明确调岗后的职级、工作内容与原岗位相近,且待遇不低于原岗位,新岗位不存在任何歧视性、侮辱性。用人单位行使法定调岗权,应对调岗的合理依据承担举证责任。
所有不适用以上情形的情况,由公司提出解除劳动合同的情形,均属于违法解除。如果公司方面主张因为以上情形解除劳动关系,则需要由公司进行举证。在公司无法拿出充分证据予以证明的情况下,同样会被认定为违法解除。
所有的违法解除劳动合同的情形,赔偿金为 2N 。
补偿金 = 在职年限(N)x 工资基数
赔偿金 = 2 x 在职年限(N)x 工资基数
无论是第二节中提到的补偿金,还是第三节中提到的赔偿金,其金额基数均为劳动合同解除或者终止前十二个月的平均工资,含奖金、津贴和补贴。如工作不满十二个月,按照实际工作的月数计算平均工资。
其中的在职年限,除去整年部分后,满六个月以上的计一年,不满六个月的计半年。例:一年三个月按照1.5年计算,两年十个月按照3年计算。
对于第二节中有提到需提前30日书面告知劳动者的情形,如有进行告知则无需再额外补偿一个月工资。如未告知则需补偿 N+1 。
综上,对于法定范围内的补偿金或赔偿金,仅有:N, N+1, 2N 三种情形。在递交劳动仲裁申请时,务必按照对应的情形计算可能的最大补偿(或赔偿)金额。具体金额在仲裁审理时会进行重新计算,但不会超过仲裁申请时所提交的金额。
如果我们已经确定要提交劳动仲裁申请,必然要先知道所需材料。各地对于劳动仲裁申请所需的材料略有出入,但大体上包含以下这些:
具体的要求可以在网上搜索当地地名加劳动仲裁,或直接前往劳动仲裁庭咨询了解。其中的公司工商信息可在当地的“企业信用信息公示系统”网站按照企业名称搜索并打印即可,劳动仲裁申请表一般在当地政府政务网站或仲裁机构的网站上可以下载。
对于裁员请求经济补偿或赔偿的情况,离职证明属于必要材料,需由公司开具。如公司推脱或者拒绝开具,一般可以向当地劳动监察大队举报。
劳动仲裁申请的部分,可参照参考信息中的链接二,也可以在网上搜索当地地名加劳动仲裁指南。
多数情况下工作证明即劳动合同,如未有签订劳动合同也可以提交其他证明材料如社保缴纳记录,工资条,打卡考勤记录等。
其他可能用得上的证明材料如:培训记录、调岗记录、绩效考核标准、绩效考核邮件、其他内部邮件、沟通记录截图等均可以提交。
在材料递交后,仲裁庭首先会告知是否受理,以及受理后的开庭时间。劳动者本人只需按时出庭即可,用人单位是否出席不影响裁定。这里我并没有实操经验,但大家只需要据实应对,争取合法权益即可。正因为我们之前一直有提到劳动者在合同关系中处于弱势方,仲裁庭基本上还是会尽量出于维护劳动者权益的角度进行裁定。
另外一方面,在劳动仲裁中用人单位方掌握并且控制了大量的信息,所以相关法条也有进行规定:与争议事项有关的证据属于用人单位掌握管理的,用人单位应当提供;用人单位不提供的,应当承担不利后果。
既然大家已经选择了进行劳动仲裁,就应该充分信任仲裁庭能够给予公正客观的裁定结果。毕竟设立劳动仲裁庭的目的就是为了保护劳动者的合法权益,总不会有哪个用人单位去申请仲裁吧?
裁定结果出来后,如果对于我们的主张诉求予以支持,直接找公司要钱即可。如公司拒绝,在超过限定时间后可以向法院申请强制执行。这些后续细节裁定书中应该会有说明,不用太担心。
任意一方对于裁定结果不服,都可以提起诉讼。涉及到打官司已经超出本文的科普范围了,建议咨询律师。
在实际操作过程中可能存在仲裁开庭前公司注销的情况发生,reizhi 对此类问题没有操作经验,同样建议咨询律师,有可能需要通过诉讼解决。
上面这些信息对于人事来说都是必知必会,但对方有可能利用信息差在协商中占据优势。在我们了解全部的相关知识后,便可以从容的与人事展开协商谈判了。如果你已经做好了劳动仲裁的决心,以下是一些常见托词的应对逻辑:
无法胜任:需由公司举证,且举证难度大;需先调岗或培训;绩效考核存在主观性,可操作空间大,置信度低
客观情况发生重大变化:需要是客观的不可抗力;重大变化足以影响公司经营或合同执行;需提供调岗机会
时间周期长,结果不确定:仲裁庭的本质是劳动者保护组织;公司举证责任多、难度大;劳动仲裁记录在企查查等网站均可查到,无论结果对于公司都没有好处
如果公司方已经斩钉截铁的表示无法给到应有的标准,但可以适当降低进行补偿,则是否接受需要取决于自身。
https://www.gov.cn/banshi/2005-05/25/content_905.htm
https://rlsbt.zj.gov.cn/art/2020/11/19/art_1450623_58919888.html
https://www.dehenglaw.com/CN/tansuocontent/0008/029228/7.aspx?AID=&BID=00000000000000001988&MID=0902
https://www.yiyang.gov.cn/rsj/4509/4516/5735/content_305410.html
]]>本文转载自:「 reizhi 的博客 」,原文:https://url.hi-linux.com/KXvBv ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
Location 是 Nginx 中一个非常核心的配置,这篇重点讲解一下 Location 的配置问题以及一些注意事项。
关于 Location,举个简单的配置例子:
1 | http { |
大致的意思是,当你访问 www.yayujs.com
的 80
端口的时候,返回 /home/www/ts/index.html
文件。
我们看下 Location 的具体语法:
1 | location [ = | ~ | ~* | ^~ ] uri { ... } |
重点看方括号中的 [ = | ~ | ~* | ^~ ]
,其中 |
分隔的内容表示你可能会用到的语法,其中:
=
表示精确匹配,比如:1 | location = /test { |
~
表示区分大小写的正则匹配,比如:1 | location ~ ^/test$ { |
~*
表示不区分大小写的正则匹配1 | location ~* ^/test$ { |
^~
表示 uri 以某个字符串开头1 | location ^~ /images/ { |
而当你不使用这些语法的时候,只写 uri 的时候:
/
表示通用匹配:
1 | location / { |
当存在多个 location 的时候,他们的匹配顺序引用 Nginx 官方文档就是:
A location can either be defined by a prefix string, or by a regular expression. Regular expressions are specified with the preceding “~*” modifier (for case-insensitive matching), or the “~” modifier (for case-sensitive matching). To find location matching a given request, nginx first checks locations defined using the prefix strings (prefix locations). Among them, the location with the longest matching prefix is selected and remembered. Then regular expressions are checked, in the order of their appearance in the configuration file. The search of regular expressions terminates on the first match, and the corresponding configuration is used. If no match with a regular expression is found then the configuration of the prefix location remembered earlier is used.
If the longest matching prefix location has the “^~” modifier then regular expressions are not checked.
Also, using the “=” modifier it is possible to define an exact match of URI and location. If an exact match is found, the search terminates. For example, if a “/” request happens frequently, defining “location = /” will speed up the processing of these requests, as search terminates right after the first comparison. Such a location cannot obviously contain nested locations.
翻译整理后就是:
location 的定义分为两种:
~*
和 ~
修饰符的而匹配 location 的顺序为:
=
修饰符的 URI,则立刻停止匹配^~
修饰符的 URI,则也立刻停止匹配。再总结一下就是:
在顺序上,前缀字符串顺序不重要,按照匹配长度来确定,正则表达式则按照定义顺序。
在优先级上,=
修饰符最高,^~
次之,再者是正则,最后是前缀字符串匹配。
我们举几个简单的例子复习下:
1 | server { |
1 | server { |
1 | server { |
1 | server { |
当我们这样设置 root
的时候:
1 | location /i/ { |
当请求 /i/top.gif
,/data/w3/i/top.gif
会被返回。
当我们这样设置 alias
的时候:
1 | location /i/ { |
当请求 /i/top.gif
,/data/w3/images/top.gif
会被返回。
乍一看两者很像,但细一看,就能看出两者的区别,root 是直接拼接 root
+ location
而 alias 是用 alias
替换 location
,所以 root 中最后的路径里有 /i/
,而 alias 中最后的路径里没有 /i/
。
所以如果你这样使用 allias 定义一个路径:
1 | location /images/ { |
其实使用 root 会更好:
1 | location /images/ { |
server 和 location 中都可以使用 root,举个例子:
1 | http { |
如果两者都出现,是怎样的优先级呢?
简单的来说,就是就近原则,如果 location 中能匹配到,就是用 location 中的 root 配置,忽略 server 中的 root,当 location 中匹配不到的时候,则使用 server 中的 root 配置。
]]>本文转载自:「 冴羽的JavaScript博客 」,原文:https://url.hi-linux.com/e0P8z ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
最早听到旁路由这个词是在 2020 年折腾 N1 的时候,这台单网口的小盒子只能用网上所说的旁路由方案接入局域网来实现期望的功能。现在回想起来,旁路由这个词有可能就是在那个发烧友大量折腾斐讯 N1/P1/T1 的时期被发明出来的。
你没办法在发烧友圈子外的互联网及各种学术材料中找到对旁路由的描述和定义,当然也找不到合适的英文翻译(导致这篇文章的 slug 定义困难);从拓扑上看,旁路由更像是杂糅了二级路由和透明网关的概念,除了实体确实多接了根网线放在主路由旁边,其本身并没有真正地开启一条旁路,做的事情也基本可以和网关的定义对齐。
不过,由于这个说法主要集中于网友的交流讨论之中,而且几年来在有相关需求的广大用户中被广为接受,所以只要这个词不变为一个学术概念,那我倒也不觉得有什么不妥。下文也仍然会用「旁路由」一词来指代在此类拓扑结构中担任代理网关角色的路由器。
此外,本文对于原理的解释偏多且为新手向,对于已经熟知相关概念的读者则可以将本文作为 cheat sheet 食用。
有人认为旁路由的加入(手动指定方式)不会在其出现问题时影响整个网络的可用性,所以应该把非路由功能都交给旁路由来实现。但实际上如果仅仅是为了达成这个目标,那在主路由上直接分流会是个更好的方案,因为这样不仅会大量减少疑难杂症的出现,而且还可以通过设置 fallback 的方式进一步提升网络的可用性。
所以我认为旁路由实际上只适用于一个场景,那就是出于某种考虑,主路由不能被替换或被大量修改,而主路由的固件又不能满足功能诉求,这时候旁路由便是一个可选的解决方案。
网上关于旁路由的配置教程多如牛毛,其中大部分是基于 F 大的 N1 OpenWrt 固件使用教程来写的,也有很多是结合了自己的踩坑经验的细化版本。由于版本太多、完整的说明太少,而且大部分没有讲清楚教程的环境上下文和原理,导致按照这些教程来配置的用户往往不得不一遍遍地折腾重试,甚至会遇到逐步配置最终却断网、访问不了部分网站等令人头疼的问题。
授之以鱼不如授之以渔,为了能彻底讲清楚旁路由该如何配置,我们暂且不谈具体步骤,而是先搞明白旁路由的运作原理。
由于计算机网络的术语在不同时期、不同环境下,对于细节的含义其实有比较大的差异,故首先我们先定义好下文中将使用到的各类专有名词的含义,以免出现信息不对称的情况。同时为了方便理解,我们也尽量和 OpenWrt 的名词对齐,并尝试与现实生活场景进行类比。
可以看到,无论如何配置,我们都需要保证数据按图中的拓扑进行流转,才能实现我们所希望的只在旁路由增加功能而不修改主路由的目的。由于流量(至少上行流量)总会流经旁路由,所以旁路由实质上就是一层透明代理。
那么我们该怎么实现这样的网络拓扑呢?让我们先来看下数据在网络的更底层是如何流转的。
从图中可以看到,当我们从手机等终端设备发出一个数据包时,数据包总是由我们的终端设备经由网关路由至目的地址,目的地址返回数据时也是相同的路径。这是因为终端设备本身通常是不具备路由功能的,单单一个路由表终端设备就搞不定。
既然数据必然经过网关,那么我们只要强制把旁路由作为终端设备对外数据交互的第一层网关即可。至此旁路由的工作原理其实就已经解释清楚了,即在另一台路由器上实现的基于网关的透明代理。而网上各式各样的教程其实都是在教我们解决如何配置网关的问题。
在这一章节还需要说明的是为什么各个教程都要求我们把旁路由的 IP 配置在和主路由相同的 IP 网段。所谓的 IP 网段实际上就是子网,同一子网下的主机(设备)可以直接通信,跨子网则需要通过某种形式转换后才能通信,而这些转换虽然可行但比较复杂,在旁路由这个场景下显然是没有必要的。本着不改变原有网络拓扑的原则,旁路由自然也要配置在和主路由及其他设备相同的子网才行。
那么我们该如何配置网关以让数据按上文的工作原理进行流转呢?
首先我们先来解决旁路由的网关问题。在上文的拓扑图中我们可以看到,旁路由虽然挂着路由器的名字,但它本质上也是网络链路中的一个节点,因此它也需要请求上层网关才能完成数据流转。而主路由是这个网络拓扑的出口,所以旁路由的网关自然要配置为主路由的 IP 地址。这一项配置是必须且不会随终端网关配置方式的变化而改变的,无论如何指定网关请求旁路由,旁路由本身都要依赖此配置才能完成正常的流量转发。
此外,还有子网掩码需要进行配置。前面有提到,在同一子网内的主机之间才能直接通信,而 IP 和子网掩码相组合便能确定设备当前所在的子网。旁路由并不改变网络拓扑,所以需要和主路由在同一子网内。因此将旁路由的子网掩码配置设置为主路由的子网掩码即可。同理,下文的所有子网掩码配置也均需要与主路由的保持一致。
虽然配置了网关后数据流转图中的左半边已经成型,但如果不对旁路由的 DHCP 进行配置,实际上会导致各种各样的疑难杂症或直接无法联网。
原因在于 DHCP 使用了 UDP 协议,UDP 是没有连接的,如果主路由和旁路由同时开启 DHCP,则任意一个 DHCP 服务器都可能会应答终端的申请,进而导致 IP 下发和路由表的混乱造成各种无法连接的疑难杂症。当然,我们可以将两个 DHCP 的子网网段拆分开来解决共存问题,但这个行为在旁路由场景下并没有实际意义。
因此我们需要保证网络中只有一个设备承担 DHCP 功能,出于不改变原网络拓扑和避免无意义 NAT 的考虑,我们通常选择关闭旁路由的 DHCP 功能(对应到 OpenWrt 则选择「忽略此接口」)。
对于手机、电脑等终端,我们的目标是将其网关配置为旁路由的 IP 。实现方案很多,成本较低的主要为以下两种。
顾名思义,只需要在终端设备的网络设置中将网关手动配置为旁路由即可。以 iOS 系统为例:
手动填写一个网络上未被占用的 IP 地址,而子网掩码以主路由为准,网关则填写旁路由的 IP 地址。
手动指定的好处在于完全不影响原网络的使用,设备按需配置是否使用旁路由作为网关以实现特定功能。当旁路由故障时,未手动指定的设备仍能正常上网。
缺点在于操作烦琐。手机、电脑还好,但电视或根本没有屏幕的设备设置起来就会很麻烦。
DHCP 除了可以管理 IP 的分配,还会下发网关和 DNS 服务器信息,因此我们还可以借助 DHCP 的这一机制来为所有终端统一设置网关,而不再需要逐个手动修改。
前面提到,我们关闭了旁路由的 DHCP 功能,因此这个统一下发的工作就要交给主路由来完成。只需要在主路由的 DHCP 配置中将网关配置为旁路由的 IP 地址即可。
以 OpenWrt 为例,将主路由 DHCP 下发的网关配置为旁路由 IP 地址即可(3 表示网关,6 表示 DNS 服务器地址)。
不过有些路由器的默认固件没有开放该配置项,对于这些设备,除非可以 SSH 连接后手动改配置,不然无法使用此种指定方式。
这种方式的好处显而易见,一次配置全家受用;缺点在于当旁路由出现故障时,所有连接的设备都会无法上网。
细心的读者可能发现了上文只提到了网关的配置,但未提到很多教程中的 DNS 配置。实际上单单就旁路由本身来说,网关配置完成后整个网络拓扑就已经搭建完毕了。但对于一些特定诉求,比如依赖旁路由进行统一的 DNS 劫持(很多功能的底层都依赖于此),则需要将对应位置的 DNS 也配置为旁路由的 IP 以将域名解析工作也完全交由旁路由处理。
虽然配置步骤看上去很简单,但很多人在实际使用中都会遇到逐步配置却上不了网或网络慢的问题,这里挑几个典型案例来解析。
这可能是争议最大的一条,有人说这条规则加上后影响性能而且没意义,但也有很多人表示不配这条就是连不上网(大多为连接不上国内网络)。
这条规则的作用本质上是在旁路由上做 SNAT,只不过修改的地址不需要指定而是动态获取旁路由对应接口网卡的 IP 地址,在 OpenWrt 里被称为「IP 伪装」。
单从旁路由的网络拓扑来说,这条规则确实没意义,因为主路由作为对外出口必做 NAT,但旁路由本身就在局域网内且只是链路上的一环,没有必要再对内网 IP 进行耗费性能的 NAT 操作。
但不要忘了,理论和现实是两回事,物理网络拓扑中的不同设备、不同固件都有可能产生各种奇怪的兼容问题。我自己倒是没有遇到过该问题,但检索网友们的各种帖子,大概可以分为以下几种原因:
MASQUERADE 配置其实并没有定向地去解决上面这些具体问题,而是通过 NAT 来隐藏终端设备、只向主路由暴露旁路由 IP 的方式,一刀切地避免了上述原因导致的问题。但由于上下行流量都会经过旁路由,所有流量都会被二次 NAT 和二次转发,网络的吞吐会有不小的下降,直观感受就是下载速度变慢了。
所以比较理想的策略是,先不加 MASQUERADE 规则观察是否有问题(尤其是国内流量),如果确实有问题,在权衡可以接受性能的损失后再配上该规则。
具体操作是取消桥接,再设置 WAN 和 LAN 共用同一个网卡(如 eth0 )。这个操作其实和 MASQUERADE 规则的效果类似,因为绑定后经过 WAN 的流量必然会被 SNAT 。适用于确实遇到了疑难杂症且能接受性能损失场景下的备选方案。
在许多教程里,这个操作和添加 MASQUERADE 规则是配套的。但桥接与否实际上并不会影响整体的网络拓扑,这看上去又是一项没有意义的配置。我猜测可能是网上流传的添加 MASQUERADE 规则的方式是下面这条固定命令:
1 | iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE |
也就是要求 SNAT 时从 eth0 网卡动态获取 IP 。而在桥接模式下,MASQUERADE 时是需要从 br-lan (常见的桥接后的默认网卡名称,也可能是其他名称,要视实际情况而定)获取 IP 的,直接复制粘贴上面的命令会导致 IP 伪装失败。所以只要把 -o 参数的值改为 br-lan 即可:
1 | iptables -t nat -I POSTROUTING -o br-lan -j MASQUERADE |
但似乎确实有网友不取消桥接就无法联网,这种情况大概率是旁路由固件对桥接模式的处理有问题(多见于 ARM 架构的固件,这类固件通常要做很多的魔改适配),如果真的遇到这种情况的话则确实可以取消桥接尝试下,毕竟大多数情况下旁路由内部的桥接也没什么实际意义,去掉后由于少了流量判断流程可能还会有非常微小的性能提升。
至于 N1 这种自带无线功能但实际上没人用的设备,在关掉无线后,确实可以顺便取消掉没有意义的桥接。只是在 OpenWrt 取消桥接保存配置时,要确定选中了有线网卡对应的接口比如 LAN/eth0 ,不然如果后台的前端存在体验问题自动选择了 wlan ,那保存后路由器可就直接失联了。
如果我想通过 DHCP 下发网关但又不想改变主路由的原配置该怎么办呢?在 OpenWrt 的接口 DHCP 配置中有一个选项叫做「强制」,勾选后,此设备会忽略网络上已经存在的 DHCP 服务,强制启动本机的 DHCP 服务,所以似乎我们只需要在旁路由上配置此选项,并在旁路由 DHCP 中配置好网关,主路由不做任何变更,即可实现本段开头的诉求。
但上文提到同一子网中只能有一个用于分配指定网段的 DHCP 服务,因此旁路由强制开启 DHCP 后,还需要主路由具备主动判断网络情况停止提供 DHCP 服务的能力,这个流程才能真正运转起来。但并不是所有的路由器和固件都支持这个能力,目前来看 OpenWrt 作为主路由固件时可以正确检测并停用 DHCP ,其他固件则需要在使用时做下兼容性测试。从工程角度上讲,由于这样的操作过于依赖外部能力,属于和外部组件产生了强耦合,不利于未来维护,故不太推荐此种方案。
各种组件内部实现大不相同,如果某个组件的代码对网络结构做了强限定(如上文所说的校验 IP 和 MAC 的匹配关系),那旁路由的加入可能就会打破组件预期的网络结构导致其无法运行。这种情况在组件本身不做适配时基本无解,只能尝试下开启 MASQUERADE 等配置,看看多加一层 NAT 后的网络拓扑是否能符合组件要求,但代价同样是会牺牲性能。
SYN-flood 是一种常见的攻击方式,SYN 指 TCP 建连三次握手中的第一步报文,flood 指大量发起该步骤的报文。由于 TCP 的实现原理要求服务端接收到 SYN 报文后回复客户端 SYN+ACK 报文表明请求被接受,并在一段时间内等待客户端回复最终的 ACK 报文,那么大量的 SYN 报文就会导致服务端出现大量等待最终资源耗尽挂掉,而攻击者并不需要真的完成建连,只要持续发送 ACK 包即可。
那路由器的防火墙又是如何作防御的呢?以 OpenWrt 为例,虽然 OpenWrt 的防火墙配置已经迁移到了 fw3 ,但翻看代码历史我们就会看到当时 OpenWrt 直接基于 iptables 的早期实现:
1 | $IPTABLES -N syn_flood |
即借助 iptables 的 SYN 限流能力进行防御,同时此配置位于 default 配置中,会在 NAT 等具体网络操作之前执行。
对应到主路由,wan 口接收到攻击流量后便会进行限流,攻击者无法直接向后攻击到内网主机,那么同样为内网主机的旁路由自然理论上也就不会被攻击到。
但对于旁路由来说,理论上网络中的所有上行流量都会通过它来转发,那当流量超过防火墙的限流阈值时便会触发拦截,进而在终端上表现为网络时断时续。所以在主路由已开启 SYN-flood 防御的情况下,旁路由关闭该配置可以避免出现可能的网络不稳定问题。
IPv6 在家用网络中通常默认是没有 NAT 转换流程的,同时其动态地址配置方案比 IPv4 要复杂得多,比如 SLAAC 和之前的 DHCP 可以说完全不是一套机制,而 DHCPv6 又分有状态和无状态两类。而前面提到,我们实现旁路由网络拓扑的过程,其实就是在指定一个具备透明代理功能的网关的过程,但 SLAAC/DHCPv6 都没有提供网关下发能力,终端设备总是会以其所交互的主机作为网关,同时大多也不支持直接修改网关。此外,运营商不支持 DHCPv6-PD 、IPv6 子网限定范围等情况,都使得旁路由支持 IPv6 非常困难,在不同场景、不同网络下要面临不同的配置,甚至无方案可配置。
网上比较流行的旁路由 IPv6 实现是个曲线救国的折中方案,即先开启主路由的内网的 IPv6 地址分配进而让旁路由获得内网 IPv6 地址,随后再通过在旁路由开启一个 DHCPv6-Client 的方式获取到公网的 IPv6 地址,这样便可以将主路由的 DHCPv6 下发的 DNSv6 配置为旁路由的 IPv6 地址。此时除了 DNSv6 的解析是在旁路由进行,其他流程仍按原链路直连。而在需要分流的场景中,OpenWrt 的相关组件可以选择在解析域名时放过不需要处理的 IPv6 流量让其正常解析出 AAAA 记录,而对域名名单中的流量强制解析为 A 记录以继续走 IPv4 协议,从而实现和此前的类似的旁路由功能。
当然,这种解析实际上依赖于组件的能力。如果组件并不支持,那通过各种方式强制定义 IPv6 的路由表保证相关 IPv6 流量必然经过旁路由也是一种解决方案。不过无论哪种实现,由于不借助网关配置,其实都已经和本文的旁路由不相关了。
此外,还有种方案是通过 radvd 等支持配置路由单播和优先级的工具,用更高的优先级来指定终端的 IPv6 路由(可以简单理解为 IPv4 的网关),这样就替代了 IPv4 下手动配置或 DHCP 下发网关对应的功能,完美满足本文所说的旁路由网络拓扑,同时对 IPv6 动态地址配置方案的要求很低,但要求终端设备支持路由优先级配置。
旁路由实际上是运行透明代理功能的网关,有人认为这个概念很民科,但我并不认同,毕竟它只是在通过已有的能力来解决特定场景的问题,和我们写代码、做产品没有本质区别,而「旁路由」这个名词也不过是个约定俗成的叫法而已,不应该被批判。
另一方面,由于旁路由在不同设备、不同网络环境有可能遇到很多奇怪问题,其实对于非专业用户来说付出的时间成本很有可能会远大于直接替换主路由的成本。但生命不息,折腾不止,如果是为了收获折腾的快乐、学习到新的知识,那又有何不可呢?
]]>本文转载自:「 Eason Yang’s Blog 」,原文:https://url.hi-linux.com/1jwRt ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
我们每天要进行大量的线上变更操作。怎么保证这些操作安全,不会导致故障,是我每天都在思考的问题。
这篇文章从工作经历总结一些原则和想法,希望能有帮助。
线上操作有几点基本的要求:
逻辑很简答:假设我一开始做操作范围很小,可以灰度,做完之后我可以监控是否符合预期,如果不符合预期就回滚,那么,操作就是安全的。
这三步中的每一步看似很简单,但是实际做起来很难。
发布过程是最简单的一种灰度场景,现在有蓝绿发布模式:
还有滚动发布,对于每一个实例:让 Load Balancer 不再发给它新的流量,然后升级,然后开始接收流量,如果没有问题,继续以此处理其他的实例。
几乎每个人都可以理解灰度的必要性,但是不是每一种操作都是可以灰度的。
比如说数据库 DDL 的变更,很难灰度,提交到数据库,数据库就开始应用了;还有一些动态配置系统,一些全局配置,如果修改,就对所有的应用同时生效的;一般都是这样一些数据源类型的变更,很容易出现不支持灰度的情况。不可以灰度的情况也是最容易导致问题的。
一个替代方案是,搭建一套一模一样的环境,在这个环境先应用变更,测试一下是否符合预期。但是在今天分布式环境下,很难模拟出来一模一样的环境,可能规模小了,可能测试环境没有一些用户的使用场景,等等。总之,模拟的环境没有问题,不能代表生产的环境就没有问题。
最好的解决办法,还是在软件和架构,从设计上就能支持灰度。
所有的操作,一定要知道自己在做什么,效果是什么。做完之后进行验证。听起来很简单,但是实际上,很多人做事像是闭着眼睛,不知道自己在做什么,做完之后有什么效果也不管。
举一个例子,比如目前网关遇到了什么问题,经过查询,发现和 Nginx 的一个参数有关,然后根据网上的内容修改了这个参数,回头去看问题解决了没有。如果没有,继续在网上查资料,看和什么参数有关。
上述操作,一个潜在的问题是,当问题真正修复了之后,我们不知道自己做了啥才修复问题的。也有一些时候,相同的配置变了名字,实际上这个修改这个参数是可以解决问题的,只不过我们用了从网上得到的过时的参数名字,所以不生效。
所以,对于每一个操作,推荐直接去验证目前的操作结果。比如改了一个 log 参数,那么直接去看这个参数是否生效,是否符合预期,然后再去看其他的问题是否得到解决。
做操作要一步一步来,做一步验证一步。
另外,最好去验证操作的副作用,而不是验证操作本身。比如,修改了一个配置,不是去 cat
一下配置文件确认就可以了,而是要去看自己修改的配置是否真的生效了。比如路由器设备,我们执行了一些命令 ip route ...
,验证的方法并不是 show running-config
去看配置是否有这一条,而是要去看 show ip route
确定配置是否生效。
除了验证操作结果之外,也要关注业务指标是否还正常。
如果业务指标不正常了,而恰好和自己的操作时间吻合,那么就应该立即回滚。
听起来很合理?但是实际上,很多人(我也是)第一反应都会是,我的操作不可能引起这个问题,让我先看看日志,到底发生什么了。
当发生问题的时候,时间很宝贵,正确的做法是第一时间在群组里面宣布自己的操作(事实上,操作之前就宣布了,但是消息太多,没有问题的时候没有人会认真看操作历史),然后开始进行回滚。可惜的是,我发现这么做的人很少,大部分都是想去排查,直到确定是自己的操作导致的,才开始回滚。
同上,不是所有的操作都可以回滚的。一些可以补偿的方案有,操作上尽量设计成可以回滚的(有些废话)。比如,DDIA 这本书就介绍了数据上如何做向前兼容和向后兼容的方法。
举个例子,比如软件新版本的一个配置要从名字 A 改成 B,不要直接改,而是添加一个配置 B,代码里面可以读 B,如果没有的话,尝试读 A。等升级完成之后,在下一个新版本中,去掉 A 的逻辑。这样,每两个版本之间都是兼容的。
除此之外,还有一些我认为非常重要的东西。
一些复杂的操作,比如修改 DNS,配置网关,配置其他东西,可能是联动的。而且显示中也不是所有的东西都适合自动化的。这些复杂的操作,推荐在操作之前就写好操作计划,然后对着一步一步操作,贴上必要的验证结果和操作时间。万一出现什么异常,就可以将异常出现的时间和自己的操作记录对照,很有用的。操作计划也可以相互 review,如果是 gitops 的话,就更好了。
这是 Last but not least! 操作的效率至关重要。
我认为运维平台要设计成简洁,没有歧义,流程清晰的,非必要不审批。这可能跟直觉相反,尤其是领导的直觉。
领导(不知为何)觉得审批流程越多越好,出了事故就开始思考在哪一个阶段可以加上一个审批流程,来避免类似的问题发生。但其实,我觉得流程越多,出问题的概率不减反增。
程序员天生就不喜欢繁重的流程,如果流程太重,就会出现其他的问题,比如,人们会想办法绕过不必要的流程;会想办法“搭车发布”(意思就是将多个操作合并成一个,这也是违反原则的,一次应该只做一个操作);对于明显出现异常苗头的时候,因为不想重新走审批而铤而走险。
但是出现这种情况,领导不会觉得流程有问题,领导会觉得你小子不按照流程办事,开除。
最后导致 SRE 的幸福感很低,事情还是要那么多,完成工作不得不铤而走险,还得责任自负。
事实上,真正能保证安全的是架构设计简单,做事的人知道自己在做什么,操作按照如上灰度、验证,出问题回滚,而不是靠流程。SRE 之间 Review 是有价值的,审批是没有价值的,大部分的审批仅仅是请示一下领导而已,领导可能看不懂操作的后果是什么。
所以,流程是有代价的。
]]>本文转载自:「卡瓦邦噶」,原文:https://url.hi-linux.com/0LB3T ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
最近经常有很多小白朋友在后台问,公网、私网、内网、外网,这些的概念是啥样的,又该怎么去界定。
关于 IP 地址,确实没有太明确的区分,其实也不必太过咬文嚼字。
内网、外网就是一个参考系选择的结果。
毕竟对你而言是外网,其实是别人的内网,,各有各的定义,最多只能具体问题具体分析。
不过在一般情况下,还是可以区分的。
内、外网是相对于防火墙而言的,在防火墙内部叫做内网,反之就是外网。
所以在一定程度上外网等同于公网,内网等同于私网。
那具体怎么分,再展开探讨一番。
私网=内网?
前面只是浅浅说一下,那到底可以这么理解吗?
这四个名词的意思,很浅显。
公网=公共网络
私网=私有网络
内网=内部网络
外网=外部网络
假设现在我们用大写字母表示网络群组,后面括号跟上数字代表其规模表示群组中有多少台计算机。
C (567918467)-中国网络群组
W (407619781)-全球网络群组
A (57619)-阿里云服务器群组
H (3)-你家里的网络群组
然后这其中就包含了一些关系,W←→C[A,H]。
我们知道由于国内网络实际上被限制了的,可以称之为全球最大的局域网,所以国内网络群组可以(科学)访问国外网络群组,而阿里云和你自家电脑都属于C。
那么如果你的电脑在 C,W 对于你来说就叫外网,自己所在的网络就叫内网,反之同理。
局域网也是这个概念,如果你身处局域网,那么外部网络就叫外网。
那么什么是公网呢,顾名思义,人人都能访问的网络。
例如 H 和 A,他们都能访问自己的上层也就是 C,那么 C 对于 A 和 H 来说就叫公网。
**公网=外网吗?**可以是可以,但又有部分情况需要具体问题具体分析。
比如说有时候像大型网络当中会有自建的广域网,也就是我们自建的骨干网络,对这种骨干网络我们也会叫公网。
然后内部的网络的话也会叫内网,这种情况就不太一样。
比如像政务外网或者政务网此类,那政务网它的公共部分其实它并不能上互联网,但是也叫公网。
私网,私有网络,未经授权无法访问的网络。
局域网也是某种意义上的私网,路由器只提供访问外网的权限和连接,于是对于 A 和 H 来说,他们也就是互为私网。
所以其实不必过于纠结叫法,这个并没有明确的定义。
接下来展开说说。
内网也叫局域网,从范围上来讲内网就是小部分的网络。
局域网指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。
局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的日程安排、电子邮件和传真通信服务等功能。
局域网是封闭型的,可以由办公室内的两台计算机组成,也可以由一个公司内的上千台计算机组成。
我们常说的内网,从字面意思上来讲是区别于外网的。
也就是说内网一般是用于局域网内部的计算机之间的互相通信,如果需要访问 Internet,需要借助外网。
局域网主要特点:
覆盖的地理范围较小,适合小范围的组网。比如学校、工厂、机关单位等。
使用专门铺设的传输介质进行联网,数据传输速率高(10Mb/s~10Gb/s);
通信延迟时间短,可靠性较高;
局域网可以支持多种传输介质;
外网即广域网,一般情况下又称公网。
是连接不同地区局域网或者城域网计算机的通信的远程网络。
连接不同地区局域网或城域网计算机通信的远程网。
通常跨接很大的物理范围,所覆盖的范围从几十公里到几千公里,它能连接多个地区、城市和国家,或横跨几个洲并能提供远距离通信,形成国际性的远程网络。
广域网并不等同于互联网。
这里浅聊一下NAT——“网络地址转换”技术。 它是一种把内部私有网络地址(IP地址)翻译成合法网络IP地址的技术用。 大概意思是,NAT 就是在局域网中使用内部地址,而当内部节点要与外部网络进行通讯时,就在网关处,将内部地址替换成公用地址,从而在外部公网(Internet)上正常使用。
NAT可以使多台计算机共享 Internet 连接,这一功能很好地解决了公共 IP地址紧缺的问题。 通过这种方法,可以只申请一个合法IP地址,就把整个局域网中的计算机接入 Internet 中。 正是因为 NAT 技术的出现才使得内网地址能很方便的访问互联网。
2 个例子给你说明白
如图,假设我们的计算机是设备一,想要访问百度。
如果使用校园网,首先需要先通过校园网的路由器把我们的内网 IP 转为校园网的外网 IP。 然后通过这个外网 IP 先连接上湖南电信的网关,最后在连接上百度的网关。 百度把你请求的信息回传到你的校园网网关,校园网网关再把信息传给你(整个网络呈网状结构。 它会自动找到一条通往百度的路径——基于深度优先搜索或者广度优先搜索)。
这个过程就跟淘宝购物差不多,转换一下。 假设在学校里订购了一本书,淘宝那边接收到你的订单准备好物品就开始给你发货了。 他发现你的收货地址在湖南,于是它可能从杭州出发,先去了福建的中转站,然后再到江西的中转站。 突然发现江西到湖南的中转站不通,于是它只能再绕到广东的中转站,最后再到湖南中转站。 这些中转站就相当于公网上的各个网关。 到了湖南中转站,快递小哥再把包裹送到你的校门(这就是最后一级网关)。 这时快递小哥就走了,校门处的管理人员在根据的你的宿舍信息把包裹拿给你。(局域网内部的信息交流由校园网这个网关来处理) 这对刚接触互联网的人来说有些难以理解内网 IP 和公网 IP 的区别,那我们再举一个例子。
我们把酒店的 201 房比作内网 IP,那么凡是酒店都可能有 201 房,假如你饿了会对服务员说:“我在 201 房间,麻烦送些吃的过来“。 而假如你要点外卖的话你对店家仅说送来 201 房间(内网 IP),外面的人是不可能知道的。 这时你就要对店家说某某市某某区某某酒店(公网 IP)再加上 201 房店家才能找到你。 运营商所分配公网 IP 地址(某某市某某区某某酒店)也就是所住的酒店,而 201 房(内网 IP ) 则是酒店管家(路由器)所分配的。 所以一个酒店可以有很多的房间(内网 IP )但是当外面的朋友问你住哪里,你肯定不会说你住在201房间(内网 IP )而会说你住在某某市某某区某某酒店(公网 IP )。 这是内网 IP 和公网 IP 的本质区别。 一个对内,一个对外。
公网 IP 具有世界范围的唯一性,而内网 IP 只在局域网内部具有唯一性
一个局域网里所有电脑的内网IP是互不相同的,但共用一个外网 IP。 就像前面酒店的例子一样: 你所在学校的校名在整个世界上只有一个,但是你学校里面的 A 栋大楼 3 层 3 号教室只有在你的校园内部才具有唯一性。 别的学校也有 A 栋大楼 3 层 3 号教室。 你只能跟快递小哥说请帮我把包裹送到 xx 大学,而不能说请帮我把包裹送到 A 栋大楼 3 层 3 号教室。
在局域网中,每台电脑都可以自己分配自己的 IP,但是这个 IP 只在局域网中有效。 而如果你将电脑连接到互联网,你的网络提供商的服务器会为你分配一个 IP 地址,这个IP地址才是你在外网的 IP。 两个 IP 同时存在,一个对内,一个对外。
互联网上的 IP(即外网 IP)地址统一由一个叫 “IANA(互联网网络号分配机构)” 的组织来管理。 由于分配不合理以及 IPv4 协议本身存在的局限,现在互联网的IP地址资源越来越紧张。 IANA 将 A、B、C 类 IP 地址的一部分保留下来,留作局域网使用。 具体如下——IP地址空间: A 类网 10.0.0.0 ~ 10.255.255.255,B 类网172.16.0.0 ~ 172.31.255.255,C 类网 192.168.0. 0~ 192.168.255.255。 也就是说,如果你查到的 IP 地址在以上 A、B、C 类IP 地址的范围内,它一定就是局域网的 IP 地址,否则就是公网的地址。
实际生活中不仅有一级 NAT 技术,还有二级 NAT 技术。 也就是可能你的校园网关也只是个局域网。通过多级转换可以得到更多的地址。
1、一般电信 ADSL 带宽在未升级大带宽前是(动态)公网 IP。如果花费很少的钱给你升级为 100M 光纤上网,99.99% 是内网IP,那 0.01% 是我还没有发现过案例。
2、代理网络运营商 99.99% 都是内网IP,如长城带宽、聚友E家等。
3、光纤上网的 99.99% 都是内网IP。
以下 IP 段的地址都是内网 IP 地址
]]>本文转载自:「 Lenix Blog 」,原文:https://url.hi-linux.com/nDA6x ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
第一次听到的这个名词的时候觉得很是有趣,不知道是个什么意思,总觉得又是奇怪的中文翻译导致的。
复杂的说(来源于网络)TLDR;
惊群效应(thundering herd)是指多进程(多线程)在同时阻塞等待同一个事件的时候(休眠状态),如果等待的这个事件发生,那么他就会唤醒等待的所有进程(或者线程),但是最终却只能有一个进程(线程)获得这个时间的“控制权”,对该事件进行处理,而其他进程(线程)获取“控制权”失败,只能重新进入休眠状态,这种现象和性能浪费就叫做惊群效应。
简单的讲(我的大白话)
有一道雷打下来,把很多人都吵醒了,但只有其中一个人去收衣服了。
也就是:
有一个请求过来了,把很多进程都唤醒了,但只有其中一个能最终处理。
说起来其实也简单,多数时候为了提高应用的请求处理能力,会使用多进程(多线程)去监听请求,当请求来时,因为都有能力处理,所以就都被唤醒了。
而问题就是,最终还是只能有一个进程能来处理。当请求多了,不停地唤醒、休眠、唤醒、休眠,做了很多的无用功,上下文切换又累,对吧。那怎么解决这个问题呢?下面就是今天要看的重点,我们看看 nginx 是如何解决这个问题的。
第一点我们需要了解 nginx 大致的架构是怎么样的。nginx 将进程分为 master 和 worker 两类,非常常见的一种 M-S 策略,也就是 master 负责统筹管理 worker,当然它也负责如:启动、读取配置文件,监听处理各种信号等工作。
图片来自: https://aosabook.org/en/v2/nginx.html
但是,第一个要注意的问题就出现了,master 的工作有且只有这些,对于请求来说它是不管的,就如同图中所示,请求是直接被 worker 处理的。如此一来,请求应该被哪个 worker 处理呢?worker 内部又是如何处理请求的呢?
接下来我们就要知道 nginx 是如何使用 epoll 来处理请求的。下面可能会涉及到一些源码的内容,但不用担心,你不需要全部理解,只需要知道它们的作用就可以了。顺便我会简单描述一下我是如何去找到这些源码的位置的。
其实 Master 并不是毫无作为,至少端口是它来占的。
https://github.com/nginx/nginx/blob/b489ba83e9be446923facfe1a2fe392be3095d1f/src/core/ngx_connection.c#L407C13-L407C13
1 | ngx_open_listening_sockets(ngx_cycle_t *cycle) |
那么,根据我们 nginx.conf 的配置文件,看需要监听哪个端口,于是就去 bind 的了,这里没问题。
【发现源码】这里我是直接在代码里面搜 bind 方法去找的,因为我知道,不管你怎么样,你总是要绑定端口的
然后是创建 worker 的,虽不起眼,但很关键。 https://github.com/nginx/nginx/blob/b489ba83e9be446923facfe1a2fe392be3095d1f/src/os/unix/ngx_process.c#L186
1 | ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data, |
【发现源码】这里我直接搜 fork,整个项目里面需要 fork 的情况只有两个地方,很快就找到了 worker
由于是 fork 创建的,也就是复制了一份 task_struct 结构。所以 master 的几乎全部它都有。
Nginx 有一个分模块的思想,它将不同功能分成了不同的模块,而 epoll 自然就是在 ngx_epoll_module.c 中了
1 | ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer) |
其他不重要,就连 epoll_ctl 和 epoll_wait 也不重要了,这里你需要知道的就是,从调用链路来看,是 worker 创建的 epoll 对象,也就是每个 worker 都有自己的 epoll 对象,而监听的sokcet 是一样的!
【发现源码】这里更加直接,搜索 epoll_create 肯定就能找到
此时问题的关键基本就能了解了,每个 worker 都有处理能力,请求来了此时应该唤醒谁呢?讲道理那不是所有 epoll 都会有事件,所有 worker 都 accept 请求?显然这样是不行的。那么 nginx 是如何解决的呢?
解决方式一共有三种,下面我们一个个来看:
看到 mutex 可能你就知道了,锁嘛!这也是对于高并发处理的 ”基操“ 遇事不决加锁,没错,加锁肯定能解决问题。 https://github.com/nginx/nginx/blob/b489ba83e9be446923facfe1a2fe392be3095d1f/src/event/ngx_event_accept.c#L328
具体代码就不展示了,其中细节很多,但本质很容易理解,就是当请求来了,谁拿到了这个锁,谁就去处理。没拿到的就不管了。锁的问题很直接,除了慢没啥不好的,但至少很公平。
EPOLLEXCLUSIVE 是 2016 年 4.5+ 内核新添加的一个 epoll 的标识。它降低了多个进程/线程通过 epoll_ctl 添加共享 fd 引发的惊群概率,使得一个事件发生时,只唤醒一个正在 epoll_wait 阻塞等待唤醒的进程(而不是全部唤醒)。
关键是:每次内核只唤醒一个睡眠的进程处理资源
但,这个方案不是完美的解决了,它仅是降低了概率哦。为什么这样说呢?相比于原来全部唤醒,那肯定是好了不少,降低了冲突。但由于本质来说 socket 是共享的,当前进程处理完成的时间不确定,在后面被唤醒的进程可能会发现当前的 socket 已经被之前唤醒的进程处理掉了。
Nginx 在 1.9.1 版本加入了这个功能 https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/
其本质是利用了 Linux 的 reuseport 的特性,使用 reuseport 内核允许多个进程 listening socket 到同一个端口上,而从内核层面做了负载均衡,每次唤醒其中一个进程。
反应到 Nginx 上就是,每个 Worker 进程都创建独立的 listening socket,监听相同的端口,accept 时只有一个进程会获得连接。效果就和下图所示一样。
而使用方式则是:
1 | http { |
从官方的测试情况来看确实是厉害
当然,正所谓:完事无绝对,技术无银弹。这个方案的问题在于内核是不知道你忙还是不忙的。只会无脑的丢给你。与之前的抢锁对比,抢锁的进程一定是不忙的,现在手上的工作都已经忙不过来了,没机会去抢锁了;而这个方案可能导致,如果当前进程忙不过来了,还是会只要根据 reuseport 的负载规则轮到你了就会发送给你,所以会导致有的请求被前面慢的请求卡住了。
本文,从了解什么 ”惊群效应“ 到 nginx 架构和 epoll 处理的原理,最终分析三种不同的处理 “惊群效应” 的方案。分析到这里,我想你应该明白其实 nginx 这个多队列服务模型是所存在的一些问题,只不过绝大多数场景已经完完全全够用了。
]]>本文转载自:「 LinkinStar’s Blog 」,原文:https://url.hi-linux.com/1472i ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
(1)优秀的软件工程师不仅编写代码,还会考虑谁将使用它、为什么使用它、如何使用它。牢记用户需求才能创造良好的用户体验。
(2)水平再高的程序员,也会在自己擅长的领域犯错,如果遇到复杂的问题,就更是如此了。始终牢记,最好的代码是没有代码,或者不需要维护的代码。
(3)任何软件工程师的主要工作都是交付价值。软件只是达到目的的手段。
(4)警惕那些很长时间没有编写任何代码、却在设计系统的人。
(5)Bjarne Stroustrup 有一句名言:“只有两种计算机语言:人们抱怨的语言和没人使用的语言”。大型系统也是如此,每个系统最终都很糟糕。因此,不要太在意代码的优雅和完美,而要持续改进,创建一个可用的系统,让开发者喜欢在其中工作并可以提供价值。
(6)10 倍程序员是一个愚蠢的神话。我只见过程序员将代码规模增加了 10 倍,最终结果是你必须修复10倍的bug。
真正要做的不是找到神话中的10倍程序员,而是要避免出现 0.1 倍程序员。那些浪费时间、不寻求反馈、不测试代码、不考虑边缘情况等的程序员,必须保证让这样的人远离我们的团队。
(7)人们说他们想要创新,但实际上,他们想要通常的只是某种新颖性和业务成功。如果你的创新改变了人们做事的方式,大多数情况下会得到负面反馈。如果你相信你正在做的事情,并知道它真的会改善事情,那么就准备好迎接一场持久战吧。
(8)数据是系统中最重要的部分。数据可能会比你的代码寿命更长,保持数据的有序和清洁,避免脏数据,从长远来看,会得到很好的回报。
(9)一直存在的旧技术不是恐龙,而是鲨鱼。它们很好地解决了问题,所以一直活到了现在,没有被快速变化的技术浪潮淘汰。
不要轻易押注新技术,只有在充分理由的情况下才替换正在发挥作用的旧技术。那些老式的技术工具不花哨,也不令人兴奋,但它们可以完成工作,不会给你带来很多个不眠之夜。
(10)很多软件工程师除非被问到,否则不会发表意见。不要因为有人没当面发表意见,而认为他们没什么要补充的。有时,会议上嗓门最高的人是我最不想听的人。
(11)如果将人们与他们的工作成果分开,他们就会不太关心他们的工作。软件工程师和所有人一样,需要有主人翁的感觉,从头到尾拥有整个流程,直接负责交付价值。
让一群充满激情的人完全拥有设计、构建和交付软件的所有权,令人惊奇的事情就会发生。
(12)面试最好用于了解某人是谁,以及他们对特定专业领域的兴趣程度,对于试图弄清楚他们是否将成为一个优秀的团队成员,那是徒劳的。
(13)始终努力构建一个更小的系统。
有很多原因会推动你,去构建一个比原先设想的更大的系统,人类似乎有一种提供更多功能的欲望。你应该抵制这种欲望,在满足设计目标的前提下,始终努力构建一个更小的系统,这样你最终会得到一个比最初设计更好的系统。
]]>本文转载自:「阮一峰的网络日志」,原文:https://url.hi-linux.com/kGCyW ,版权归原作者所有。
网上很多人都说 DNS 根服务器只有 13 台,中国一台也没有。在网络世界,中国被美国卡住了脖子。那 DNS 根服务器真的只有 13 台吗?如果是,那原因又是什么?今天就给大家说道说道。
在回答这个问题之前,我们需要先回顾一些基本概念。DNS 是一种分层结构,这种层级就体现在域名的『点』里。以我的域名为例,TAOSHU.IN
它的完整域名其实是 TAOSHU.IN.
。注意最后有一个点。它分三个层级,结结构为.
➜ IN
➜ TAOSHU
。
DNS 又是分布式系统,每一层级都有自己的解析服务器。.
是第一层级,它的解析服务器就是根服务器。第二层级是对应我们常说的COM/NET
等顶级域名 TLD,而我用的IN
是印度的国家域名,跟中国的CN
一样,它们都是 CCTLD,也就是所谓的国家顶级域名。TAOSHU
就是普通的一级域名。每个域名都可以自行设置子域名,不受上级域名限制。
域名解析过程也是分布式的。还是以TAOSHU.IN
为例。客户端先找到根服务器的地址,并其查询IN
的解析服务器。再向IN
的服务器查询TAOSHU.IN
的服务器。最后向TAOSHU.IN
的服务器查询具体的解析记录,比如 A 记录等。
更多关于 DNS 的细节,请参考的另一篇文章。
从上面的过程可知,所有的 DNS 查询都从根服务器开始。所以根服务器是整个 DNS 系统的核心。如果根服务器出现故障,那所有 DNS 查询都会失败!为了避免出现这种问题,人们设置了多个 DNS 根服务器。发展到现在,互联网社区累计设置了 13 台,它们分别是:
主机名 | IP 地址 | 运营机构 | 国家 |
---|---|---|---|
a.root-servers.net | 198.41.0.4, 2001:503:ba3e::2:30 | Verisign, Inc. | 美国 |
b.root-servers.net | 199.9.14.201, 2001:500:200::b | University of Southern California, Information Sciences Institute | 美国 |
c.root-servers.net | 192.33.4.12, 2001:500:2::c | Cogent Communications | 美国 |
d.root-servers.net | 199.7.91.13, 2001:500:2d::d | University of Maryland | 美国 |
e.root-servers.net | 192.203.230.10, 2001:500:a8::e | NASA (Ames Research Center) | 美国 |
f.root-servers.net | 192.5.5.241, 2001:500:2f::f | Internet Systems Consortium, Inc. | 美国 |
g.root-servers.net | 192.112.36.4, 2001:500:12::d0d | US Department of Defense (NIC) | 美国 |
h.root-servers.net | 198.97.190.53, 2001:500:1::53 | US Army (Research Lab) | 美国 |
i.root-servers.net | 192.36.148.17, 2001:7fe::53 | Netnod | 瑞典 |
j.root-servers.net | 192.58.128.30, 2001:503:c27::2:30 | Verisign, Inc. | 美国 |
k.root-servers.net | 193.0.14.129, 2001:7fd::1 | RIPE NCC | 荷兰 |
l.root-servers.net | 199.7.83.42, 2001:500:9f::42 | ICANN | 国际 |
m.root-servers.net | 202.12.27.33, 2001:dc3::35 | WIDE Project | 日本 |
资料来源:IANA1 资料截止时间:2023年06月06日
在 1984 年,Jon Postel 和 Paul Mockapetris 在南加州大学设立了世界上第一台根服务器2。到了 1990 年,根服务器的数量扩展到了 7 台,分属不同的组织给护,但全都在美国。到了 1991 年,KTH 在瑞典设立一台根服务器。这是首次在美国之外部署根服务器。此后一直有旧的根服务器退役,新的服务器入役。到了 1995 年,根服务器已经扩展到 9 台。这个时候就遇到了技术瓶颈,无法添加新的根服务器了。
到底是什么技术瓶颈呢?这就得说说 DNS 的底层实现细节了。
前面说所有的 DNS 查询都从根服务器开始。那客户端怎么知道当前有哪些根服务器呢?没什么好办法,就是在各自的代码中写死!对,是硬编码。但我们前面也说了,在役的根服务器并非一成不变,写死的话新添加的服务怎么生效呢?
这就用到了所谓的 Priming Queries3。简单来说,所有 DNS 解析客户端都随软件附带一个列表文件,里面有当前所有根服务器的信息,包括域名、IP地址等信息。这个文件叫 Root Hints,可以从 IANA 官网4下载。但考虑到根服务器的列表可能会变,所以客户端需要定期从已知的根服务器查询当前最新的服务器列表,用的也是 DNS 协议,这类请求叫作 Priming 查询。
对于客户端来说,它先从 Root Hints 中根据某种规则5选出一台根服务器,然后向它查询最新的根服务器列表,并本机缓存一段时间,过期之前都以该列表为准。
因为 Priming 查询也是用 DNS 协议,自然也走 UDP 传输。互联网早期 IP 网络的最大传输单元长度(MTU)也就五百多字节,所以 DNS 回复信息的最大长度同样不能太长。所以 DNS 协议规定回复信息不能超过 512 字节。这就是添加根服务器遇到的技术瓶颈。
其实解决这个问题很容易,完全可以要求客户端使用 TCP 连接传输 Priming 查询结果嘛。可惜当时没有采用这种方案。不过,如果是我,也不会选 TCP 方案。因为所有 DNS 查询都走 UDP 协议,简单而统一。虽然 DNS 也支持使用 TCP,但让 Priming 查询单独走 TCP 明显会让系统变得很复杂。
社区最终决定想办法压缩查询结果长度。
DNS 报文结构如下,分为五个部分6。
1 | +---------------------+ |
Header 为报文头信息,长度固定为 12 字节,结构如下:
1 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 |
Header 中跟本文内内直接相关的就是 QDCOUNT/ANCOUNT/NSCOUNT/ARCOUNT 这四个字段,分别表示后续 Question/Answer/Authority/Additional 段的数量。
Question 段保存查询请求信息,通长只有一个。它分成三个部分。后两个部分表示查询类型和网络类型。含义不重要,重要的是长度固定为 4 字节。
1 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 |
第一部分 QNAME 长度可变,保存查询的域名。但存储的方式有点特别。以域名A.ROOT-SERVERS.NET.
为例,它会分成三部分A
、ROOT-SERVERS
和NET
。每一部分称作一个标签(Label), QNAME 字段只保存标签,不保存.
。每个标签用第一个字节记录当前标签长度,后面跟着标签内容。最后用一个长度为零的标签表示结尾。所以完整的 QNAME 字段编码为:
1 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
长度为 20 字节。特别的,对于根域名.
,它的 QNAME 编码是:
1 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
长度为 1 字节。
Answer 段是服务器返回的响应结果。数量为一条到多条不等。每一条称为一个 RR,全称是 Resource Record。其结构如下:
1 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 |
RR 跟前面的 Question 相比多了 TTL/RDLENGTH/RDATA 三个字段。TTL 表示有效时长, RDLENGTH 表示后续 RDATA 的长度,RDATA 保存实际的响应数据。根据 TYPE 和 CLASS 的不同,RDATA 内容也各不相同。在 Priming 查询中,RDATA 保存各根服务器的域名,编码跟前 Question 中的 QNAME 一样。
如果服务器返回如下一条 RR 数据:
1 | . 518400 IN NS a.root-servers.net. |
那么 RR 的总长度是 1+2+2+4+2+20=31 字节。
Authority 段用来返回待查询域名的权威服务器信息。比如我们尝试向根服务器直接查询 TAOSHU.IN
的 A
记录,根服务器就会在 Authority 段返回 IN
域名的解析服器。因为根服务器并不保存TAOSHU.IN
的域名信息。不过在本文中,Priming 查询的权威服务器就是根服务器,所以此段长度为零。
最后的 Additional 段用来返回一些附加信息。Answer 中只有域名信息。我们希望直接返回 IP 地址,所以需要用到 Additional 段。Additional 中也是一条一条的 PR,计算方式跟 Answer 的一模一样。
因为 Priming 查询会返回所有的根服务器域名及其对应的 IP 地址7,所以根服务器数量越多,返回的数据就越长。但 DNS 协议规定最长只能是 512 字节,这就产生了瓶颈。
到了 1995 年,已经开通了 9 台根服务器,Priming 查询结果快要超过 512 字节了。社区开始着手解决这个问题。方案是标签压缩。
压缩办法也很简单,就是在 NAME 中引入指针结构:
1 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
DNS 还有一个规定,域名的长度不能超过 63 字节。NAME 中第一个字节表示长度,最大值就是 63,二进制表示为00111111
,可见高两位是零。于是大家约定高两位设为11
的时候,后面的 14 位就表示从报文 Header 开始的偏移量。这样一来,如果多个 RR 的域名中有相同的部分,就不需要重复传输,减少响应长度。
举个例子,比如要同时返回A.ROOT-SERVERS.NET
和B.ROOT-SERVERS.NET
两个域名,显然它们有共同的后缀ROOT-SERVERS.NET
。假设A.ROOT-SERVERS.NET
的偏移量为 20,那么可以表示为:
1 | A.ROOT-SERVERS.NET |
利用标签压缩技术,域名B.ROOT-SERVERS.NET
只需要占用 5 个字节,比 A.ROOT-SERVERS.NET
节省了 15 个字节。
要想使用压缩,前提是所有域名有重复的部分。但之前的根服务器域名不相同,这就得改名。到了 1995 年,社区统一的域名为根服务器重新编组并部署标签压缩功能。
旧域名 | 新域名 | 运营机构 |
---|---|---|
NS.INTERNIC.NET | A.ROOT-SERVERS.NET | InterNIC (operated by NSI) |
NS1.ISI.EDU | B.ROOT-SERVERS.NET | Information Sciences Institute, USC |
C.PSI.NET | C.ROOT-SERVERS.NET | PSINet |
TERP.UMD.EDU | D.ROOT-SERVERS.NET | University of Maryland |
NS.NASA.GOV | E.ROOT-SERVERS.NET | NASA Ames Research Center |
NS.ISC.ORG | F.ROOT-SERVERS.NET | Internet Software Consortium |
NS.NIC.DDN.MIL | G.ROOT-SERVERS.NET | GSI (operated by NSI) |
AOS.ARL.ARMY .MIL | H.ROOT-SERVERS.NET | U.S. Army Research Lab |
NIC.NORDU.NET | I.ROOT-SERVERS.NET | NORDUnet |
上线之后,为新的根服务器留出了空间。于是在 1997 年,又上线了 J/K/L/M 四台根服务器。
这时候 Priming 查询响应的返回值有多大呢?我们可以算一下:
总共为 12+31+180+208+4+1=436 字节。剩余可用 512−436=76 字节。一组台服务器需要额外占用 15+16=31 字节。理论上还可以再添加两台根服务器,也就是最多15台。
如果只管根服务器功能,确实还可以添加。但是早期的根服务器同时也是COM/NET/ORG
的解析服务器。客户端可以向根服器发起针对特定COM
域名的 Priming 查询。因为响应结果需要包含查询域名 QNAME,所以上面说的 76 字节中至少要保留 64 字节给 QNAME。这样就只剩下 12 字节。所以就不能再添加新的根服务器了。
虽然理论上是不能再加新的根服务器了,但后来网络不断发展,UDP 报文早已不需要把长度限制到 512 字节。而且引入 IPv6 网络后,Priming 查询结果中还需要返回 AAAA 记录, 512 个字节肯定不够用。所以社区又设计了 EDNS09 来支持返回超过 512 字节的 DNS 响应。
理论上还是可以继续添加新的根服务器。但为什么不加了呢?那是因为有了更先进的技术 Anycast,中文译作任播。任播,可以简单理解为允许不同网络中的计算机共用一个 IP 地址,同时对外提供 DNS 查询服务。互联网会根据客户端的位置将请求路由到就近的计算机。
Anycast 技术将原来的单台服务器变成了一组多台服务器。到了2002年,J根服务器首次部署 Anycast 功能。到现在为止,前面说的13台根服务器严格来说是 13 个域名并且对应 13 对 IPv4 和 IPv6 地址。每对地址之后都通过 Anycast 部署了很多台实例,总计有超过 1500 台根服务器实例。这些实例又称为根镜像服务器。
虽说中国没有自己的根服务器,但境内还是有不少根镜像服务器:
编号 | 城市 |
---|---|
A | 广州 |
D | 香港 台北 |
E | 台北 |
F | 北京 重庆 杭州 高雄 南宁 台北 |
I | 北京 香港 沈阳 台北 |
J | 北京 香港 湖州 上海 |
K | 北京 广州 贵阳 台北 |
L | 北京 长沙 海口 上海 武汉 西宁 新北 郑州 |
M | 高雄 |
资料来源:https://root-servers.org 资料截止时间:2023年06月06日
大家不妨通过 Ping 命令测一下,上面的几个根服务器的延迟都在 50ms 左右,一看就是在国内,不然不会这么快。
那为什么中国没有自己的根服务器呢?这个问题涉及到世界互联网的发展史和中国的互联网发展历程,我会专门撰文讲解。敬请期待。
]]>本文转载自:「 涛叔的博客 」,原文:https://taoshu.in/dns/13-roots.html ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
我们注册有些国外的服务时,是不能用国内邮箱注册的,这时一个 Gmail 邮箱就显得很有必要。而有时候我们会需要注册多个账号,当然你可以用多个 Gmail 邮箱来注册。
但多个 Gmail 邮箱又不好管理,而且现在 Gmail 邮箱也不是那么好注册了,这时你就会需要下面我要介绍的内容了,利用 Gmail 邮箱的别名功能来实现同一邮箱获取无限多个 Gmail 邮箱号。
在这之前你需要有一个谷歌账号(Gmail邮箱)。没有的话去到这里注册:http://mail.google.com(目前注册需要正确上网,请自行搜索解决)。
这里我们假设你的 Gmail 邮箱为:abcdef@gmail.com
,那就可以通过以下办法得到无限的谷歌邮箱别名。
.
(英文半角)例如:
abc.def@gmail.com
因为 Gmail 的用户名是不区分
.
符号的,所以下面其实都是同一个用户名
+任意字符
例如:
abcdef+dev@gmail.com
+
号后面可以是任意字符,例如:
**PS:**使用“+”号法,在许多网站注册时候,并不识别邮箱地址中的“+”,会提示你邮箱错误。
例如:
aBCdef@gmail.com
可以是用户名里的某个或多个字符,
googlemail.com
因为之前 Gmail 在某些国家的商标没有谈好,不能使用 Gmail 商标,只好用 googlemail,这个很好理解。(貌似现在全球的商标都已经谈妥了)
例如: abcdef@googlemail.com
可以结合上面的4种方法来实现理论上的无限别名。例如:
了解了上面的 Gmail 别名设置的方法后,当你要注册一些服务的时候,只要在填写Gmail邮箱的时候填写通过上述的方法得到的别名邮箱就可以了。无需到 Gmail 设置,所有的别名邮箱的邮件都会发送到你原来的邮箱 abcdef@gmail.com
中。
]]>本文转载自:「涅哥社区」,原文:https://url.hi-linux.com/RiANA ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
使用 zSWAP、zstd 和 z3fold 来提高操作系统的性能
压缩可以降低占用空间,顾名思义,内存压缩就是压缩内存,节省内存空间。就目前的技术而言, I/O
的速度远远慢于这 RAM
操作速度。因此,如果频繁地做 I/O
操作,不仅影响 flash
使用寿命,还严重影响系统性能。内存压缩是一种让 I/O
过程平滑过渡的做法, 即尽量减少由于内存紧张导致的 I/O
,提升性能。
介绍 zSwap 技术
zSwap
是 Linux
内核的一个功能,它为交换页提供了一个压缩的回写缓存,作为一种虚拟内存压缩形式。当内存页要被换出时,zSwap
不会把它们移到交换设备上,而是对它们进行压缩,然后把它们存储到系统 RAM
中动态分配的内存池中。后来,向实际的交换设备的回写被推迟了,甚至完全避免了,从而大大减少了需要交换的 Linux
系统的 I/O
,其代价是需要额外的 CPU
周期来执行压缩。
zSwap
允许 Linux
更有效地利用 RAM
,因为它实际上增加了内存容量,而不是在压缩/解压缩交换页时稍微增加 CPU
的使用。zSwap
存在于内核中,但默认并没有开启,要使用它必须通过修改配置文件开启。
主流内存压缩技术
zSwap
是在 memory
与 flash
之间的一层缓存,当内存需要 swap
出去磁盘的时候,先通过压缩放到 zSwap
中去,zSwap
空间按需增长。达到一定程度后则会按照 LRU
的顺序(前提是使用的内存分配方法需要支持 LRU
)将就最旧的 page
解压写入磁盘 swap device
,之后将当前的 page
压缩写入 zSwap
。
zSwap
本身存在一些缺陷或问题:如果开启当 zSwap
满交换出 backing store
的功能, 由于需要将 zSwap
里的内存按 LRU
顺序解压再 swap out
,这就要求内存分配器支持 LRU
功能;如果不开启当 zSwap
满交换出 backing store
的功能, 和 zRam
是类似的。
zRram
即压缩的内存,使用内存模拟 block device
的做法。实际不会写到块设备中去,只会压缩后写到模拟的块设备中,其实也就是还是在 RAM
中,只是通过压缩了。由于压缩和解压缩的速度远比读写 I/O
好,因此在移动终端设备广泛被应用。
zRram
本身存在一些缺陷或问题:zRam
大小是可灵活配置的,配置多少成为了一个问题;使用 zRam
可能会在低内存场景由于频繁的内存压缩导致 kswapd
进程占 CPU
高;增大了 zRam
配置,对系统内存碎片是否有影响
zCache
是 oracle
提出的一种实现文件页压缩技术,也是 memory
与 block dev
之间的一层存储,与 zSwap
比较接近,但 zCache
目前压缩的是文件页,而 zSwap
和 zRAM
压缩是匿名页。
介绍 zstd 压缩算法
Zstandard
是一种实时压缩算法,提供高压缩率。它提供了非常广泛的压缩/速度权衡,同时有一个非常快的解码器支持。它还为小数据提供了一种特殊的模式,称为字典压缩,并可以从任何样本集中创建字典。Zstandard
库是作为使用 BSD
许可证的开源软件提供的。
开启 zSwap 的方法
1 | # edit grub as root |
1 | # install zstd and Z3fold |
参考链接地址
]]>本文转载自:「 Escape 的博客 」,原文:https://url.hi-linux.com/oIcpv ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
一般来说,sudo
会忽略通过.bashrc
文件、.bash_aliases
文件或者alias
命令设置的别名命令(aliased commands)。
比如,我们经常将ll
用作ls -lh
命令的别名。然后,我们输入ll
,终端将会返回一个关于当前目录的长列表。但是,当我们输入sudo ll
时,终端将会返回:
1 | $ sudo ll |
我们给shutdown
命令创建一个别名,当rotorrent
运行的时候尝试输入这个别名去关机,我们可以看到系统不会关机。想要运行/sbin/shutdown
需要root权限,然而sudo
会完全忽略shutdown
的这个别名。解决办法是,我们需要添加另一个别名:
1 | alias sudo='sudo ' |
sudo
后面的那个空格将会告诉bash
,去检查跟在空格后面的命令是否也是一个别名。bash手册(通过man bash
查看)上面是这么描述的:
If the last character of the alias value is a blank, then the next command word following the alias is also checked for alias expansion. 如果别名值的最后一个字符是空格,将会检查”跟在别名后的下一个命令”是否也是别名扩展。
下面是我机器.bash_aliases
文件中的一些别名设置(因为系统才安装,可能以后会加更多别名设置):
1 | # Shortcuts |
]]>本文转载自:「 Legolas’ Blogs 」,原文:https://url.hi-linux.com/lAqFF ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
大多数 Linux
用户通常在没有复杂 DNS
服务的路由器上拥有多个 Linux
系统。本文将介绍一种方法,让你不必修改 /etc/hosts
之类文件的情况下实现按名称与多个系统通信。
mDNS
是一种零配置网络服务,它允许系统在本地网络上按名称广播查询其他资源。大多数 Linux
都提供一个包含 mDNS
的零配置的软件包 Avahi
。mDNS 同样也支持 macOS,是 Bonjour 软件包中的一部分。
本文假设你有两个支持 Avahi
软件包的 Linux
系统,这里以 Ubuntu 20.04
为例,它们的主机名分别是 Mike-Ubuntu-01
和 Mike-Ubuntu-02
。
1 | # Ubuntu / Debian |
avahi-tools
软件包包括许多方便的实用程序,可用于检查系统上的 mDNS
服务的工作情况。比如:
1 | # 查看局域网内所有已注册的 mDNS 服务 |
安装完成后,你可以检查下 /etc/nsswitch.conf
文件,它控制你的系统使用哪些服务来进行名称解析以及服务优先级。 你应该在该文件中看到如下一行:
1 | $ cat /etc/nsswitch.conf |
注意:命令
mdns4_minimal [NOTFOUND=return]
,它告诉你的系统使用多播 DNS 解析器将主机名解析为 IP 地址。当名称无法解析时,也会尝试剩余的服务。通常nss-mdns
包会为你处理此问题,如果你没有看到与此类似的配置,可以编辑这个文件加上它。
以上所有操作在两台机器上,都需要进行。
现在你已经完成了常见的配置工作,请通过以下方式为每个主机设置名称:
1 | # Mike-Ubuntu-01 |
注:你也可以编辑
/etc/avahi/avahi-daemon.conf
文件,通过host-name
来设置主机名称。 但默认情况下,Avahi 优先使用系统提供的主机名。
接下来,重新启动 Avahi
服务,以便它接受变更:
1 | $ sudo systemctl restart avahi-daemon.service |
如果你需要开机自启 Avahi
服务,可以使用以下命令来实现:
1 | $ sudo systemctl enable --now avahi-daemon.service |
最后,我们能够登录到 Mike-Ubuntu-01
并 ping
另一台机器的主机名 Mike-Ubuntu-02
。
1 | # 你应该使用默认的 .local 域名,以便解析正常工作。 |
同样的,你在 Mike-Ubuntu-02
上也能 ping
通 Mike-Ubuntu-01
:
1 | $ ping Mike-Ubuntu-01.local |
这样通过网络名称访问你的系统服务,是不是更加方便呢?
Web 3.0 并不是什么新鲜词,参考维基百科和百度百科上的描述。总结下个人理解。
Web 3.0 并没有一个非常明确的定义,Web 3.0 概念从 2016 年提出,随着技术的不断变化,一直在调整和延伸其定义。以下列举三个比较说明性的定义:
Netflix 创始人 Reed Hastings 于 2016 年 11 月的 Technet 峰会上提出:Web 1.0 是拨号上网,50K 平均带宽,Web 2.0 是 1M 平均带宽那 Web 3.0 就该是 10M 带宽,全影像的网络,这才感觉像 Web 3.0。
以太坊联合创始人 Gavin Wood(加文·伍德)提出的定义:主要与基于区块链的去中心化、加密货币以及非同质化代币有关。Web 3.0 被用来描述互联网潜在的下一阶段,一个运行在“区块链”技术之上的“去中心化”的互联网。
美国企业家兼风险投资家诺瓦·斯皮瓦克建议将 Web 3.0 的定义延伸至当前各大技术潮流迈向新的成熟阶段的具体体现:
无处不联网:宽带网普及和发展,移动通信设备的互联网介入。(例如:平板电脑)
网络计算:“软件即服务”的商业模型,Web服务互用性,分布式计算,网格计算和效用计算(又称“云端计算”)。
开放技术:开放API和协议,开放数据格式,开源软件平台和开放数据(如创作共用、开放数据许可)。
开放身份:OpenID,开放名声,跨域身份和个人数据。
智能网络:语义网技术比如:资源描述框架,网络本体语言,SWRL,SPARQL,语义应用程序平台和基于声明的数据储备。
分布式数据库:万维数据库(“World Wide Database”,由语义网的技术实现)。
智能应用程序:普通语言的处理,机器学习,机器推理,自主代理。
这是比较老的一张图,但很能形象的说明问题:
Web 1.0 下用户无法主动创建内容,不享有网络发言权,只是把传统的纸媒搬上了网络,典型的就是yahoo、新浪、网易这类门户网站,数据的交互是单向的;
Web 2.0 时代伴随着智能手机的普及、社交网络的兴起和云计算的发展而到来,所有用户都可以在社交媒体分享自己的所思所想并和他人交互,互联网世界的话语权从商业巨头转移到每一位终端用户手中。典型的就是 Facebook、QQ 这种社交媒体类应用,其是一个“可读+可写”的双向交互时代;
Web 3.0 相较 Web 2.0 出现了更多的内容创建者,出现了更多的信息交互。在物联网、人工智能、AR等新技术出现后,在新的概念里就提出了人与人、物与人、物与物信息交互,这个对应上面诺瓦·斯皮瓦克提出的无处不联网的概念(万物互联)。其大概率是去中心化,多向交互式的。
web1.0 | web2.0 | web3.0 | |
---|---|---|---|
信息互动方式 | 只读 | 交互 | 去中心化 |
描述 | 网站提供内容,用户阅读内容 | 用户可生成内容,与他人、网站交互 | (数字)身份、资产和数据回归个人 |
典型代表 | yahoo、新浪等门户网站 | facebook、QQ等社交平台 | 各类Dapp |
由于 Web 3.0 的定义还是模糊的,我们只能大致的推断 Web 3.0 应该是如下的架构:
Web 1.0 的本质是联合,那么 Web 2.0 的本质就是互动,它让网民更多地参与信息产品的创造、传播和分享,而这个过程是有价值的。Web 2.0 的缺点是没有体现出网民劳动的价值,所以 2.0 很脆弱,缺乏商业价值。Web 3.0 是在 Web 2.0 的基础上发展起来的能够更好地体现网民的劳动价值,并且能够实现价值均衡分配的一种互联网方式。(抖音和头条代表的自媒体时代多少有点这个意思了,实现了网民劳动价值的分配,不过这还是中心化的,另外对于创造的数字资产也未能利用区块链技术实现版权的强保护。)
按照普适各种技术的 Web 3.0的定义,在 3D 维度上的 Web 3.0 的生态见下图:
Web 3.0主要辐射的赛道包括 “应用层”、“交互层”、“软件层” 三个维度。其分别对应 “NFT数字藏品、游戏、社交等应用”、“可穿戴设备或者操控设备等硬件设备” 和 “人工智能和操作系统”。
Web3.0 造富创作者,整个 Web 3.0 生态就是对数据、所有权和用户关系的改革。Web 3.0 能够更明确的确权和分润,即,所有使用内容的用户都需要用虚拟货币或者 NFT 等工具向作者进行 ‘支付’,创作者也保留作品 100% 的所有权。
未来云基础设施是面向应用快速落地的,人们不用再去关心底层是如何实现的。就像有人买了电器,只用关心插上插头能不能用,不会关心电是怎么过来的。不需要再自己生产电,拉电线进到屋子里,这是基础设施,买或租了房子后,天然具备的。(云原生类比 Web 3.0,Web 3.0 就是冰箱、彩电、洗衣机,云原生就是房子里的电插头。)
在 Web 3.0 时代,云计算会以云原生的方式出现。当电灯出现时,用电的主要目的是照明,后面人们发现电的用途还可以更多,于是出现了冰箱、彩电、洗衣机等家用电器。因为有了电,我们开始创造发明各种终端的用电工具。
云计算也一样。云计算原本是为数据存储和终端提供平台,但当云计算平台系统和生态建立起来时,大量的应用基于云计算发生。也就是说,云计算的出现会激发更多的基于云计算的应用出现。云计算应用大规模兴起后,数据和计算还在云计算平台。所以,云计算会变成原生的应用平台,帮助元宇宙的应用广泛发展。所以,元宇宙的世界一定是由云计算承载的云原生世界。
触碰到红利天花板的互联网大厂们已经走到了一个时代的路口,从今年 Facebook(Meta)、腾讯、阿里、美团等大厂下滑的财报数据也能窥见一二。互联网巨头们需要一个新的故事来引领一个新的时代 — Web 3.0。
Web 3.0 想要较好的发展,需要先解决三个重要的基础设施建设主题——隐私计算、元宇宙和可编程金融,基于这些主题,Web 3.0的内涵和外延能得以不断丰富和拓展,使互联网更加开放、普惠和安全,向更高阶发展。
]]>本文转载自:「 云原生之路 」,原文:https://url.hi-linux.com/JcHqB ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
内网提交需要校验企业邮箱,但有时邮箱设置错误导致 commit
的邮箱有问题,此时可以通过修改已提交记录中的邮箱来修复,无需重新提交。
经过检索,发现两种方法,分别适用于修改一次和修改多次,引文在最后都有注明。
1 | $ git commit --amend --author="NewAuthor <NewEmail@address.com>" |
使用该脚本,替换其中 [Your Old Email]
[Your New Author Name]
[Your New Email]
之后在 git 目录中执行即可。
1 | #!/bin/sh |
A previous backup already exists in refs/original/
1 | Cannot create a new backup. |
出现这一句说明之前曾经执行过 git filter-branch
,在 refs/original/
有一个备份,这个时候只要删掉那个备份即可,删除备份命令为:
1 | $ git update-ref -d refs/original/refs/heads/master |
]]>本文转载自:「Frytea’s Blog」,原文:https://url.hi-linux.com/MEm0k/ ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
HTTP 全称 Hypertext Transfer Protocol,中文是超文本传输协议。网上讲 HTTP 协议的资料可以说是五花八门,但大多数都在罗列 HTTP 协议具体的规定,很少有讲 HTTP 协议这样设计的原因。今天我就尝试从解决问题的角度分析 HTTP 协议主要特性,希望能帮助大家快速理解 HTTP 协议。
HTTP 是一种通过网络传输数据的协议。我们不希望数据在传输的过程中出现丢失或者损坏的问题。所以 HTTP 选用 TCP 作为底层网络协议,因为 TCP 是一种可靠的传输层协议。
通信双方就建立 TCP 连接后立马发现一个新问题:服务端要给客户端发送什么数据呢?所以客户端必需在连接建立后将自己想要的内容发送给服务端,这就是所谓的「请求」,也就 HTTP Request。由此就确立了 HTTP 协议最根本的设计,即由客户端主导的请求应答式协议。
客户端上来就给服务端发了一个「请求」。但服务端有可能收到的内容跟客户端并不完全一样。等等,TCP不是可靠传输协议吗?接收到的数据怎么会不一样?这就涉及到数据分段的问题。比如客户端发送”abcdef”,底层 TCP 协议可能分两次传输 “abc” 和 “def”,也可能分好多次传输。不论分几次,它们的顺序是固定的,跟客户端发送的顺序完全一致。服务端可能会收到多段数据,所以服务端需要把收到的数据「攒」起来,等到客户端的数据全部收到之后才能看到客户端「请求」的全貌。
那到什么时候算全部收到呢?这是 TCP 通信的一个基本问题。解决这个问题有两个流派:长度流和分隔符流。
所谓长度流就是在实际发送数据之前,先发送数据的长度。服务端先读取长度信息,然后再根据长度来「攒」后面的数据。那服务端在读取长度的时候不会碰到分段问题吗?其实不会,因为 TCP 只会对比较长的数据做分段。前面说的”abcdef”分两段只是一种极端的例子,实际上很难发生。所以,只要先发送的长度数据不要太长,服务端就能一次性收到。退一步,即便是真的会分段,这类长度流协议都会规定长度数据自身的长度。比如用两个字节表示长度,那范围就是数据长度的范围就是0-65535。服务端可以先收两个字节,然后再根据数据长度来接收后面的内容。
长度流最大的优点就是实现简单,内存效率高,服务端不用事先分配很多内存。但缺点也比较突出,长度的范围不够灵活。如果我们规定长度字段为两个字节,但就不能传输超过64k的数据。但如果规定长度字段为八个字节,那在传输比较短的数据时就造成浪费。如何设置最优长度字段,大家可以参考我的另一篇文章。
此外,长度流的扩展性也比较差。如果我们想在长度之外传输其他信息,比如数据类型、版本号之类,我们都需要提前规定好这些数据的长度。长度一旦定好,以后就很难扩展了。最典型的长度流协议就是 IP 报文。有兴趣的朋友可以去看看 IP 协议是怎么规定数据长度的。
有鉴于长度流的不足,人们又搞出了分割符流。简单来说就是用一个特殊的分割符表示数据的结尾。最经典的例子就是C语言的字符串,结尾用\0
来表示。使用这个流派的服务端程序要不停地从客户端接收数据,直到收到某一个分割符,就表明已经收到了完整的「请求」。
因为不需要事先指定数据的长度,所以分割符流派一下子就解决了长度流长度范围不灵活的问题。分割符流派的协议可以接收任意长度的数据。但是,分割符流派为些也付出了代价。因为长度不固定,服务端必须分配比较大的内存或者多次动态分配内存,这会产生比较大的资源消耗。恶意用户可能通过构造很长的数据来占满服务器的内存。
但是 HTTP 协议还是加入了这个流派,它用的分割符是\r\n
。这里的\r
表示回车,就是让打印机把打印头回到最左边的位置。\n
表示换行,就是让打印机把纸向上挪一行,准备打印新的实符。上古时代的电脑没用现在的液晶屏,用电传打印机来「显示」内容,所以需要传输\r\n
两个字符。现在这些都淘汰了,理论上用\n
也可以,像 Nginx 就支持只用\n
。
所以,一个最简单的 HTTP 请求长这个样子:
1 | GET /mypage.html\r\n |
这里的GET
是一种拟人的说法,从服务拿什么东西。这也是 HTTP 语义化设计的开端(所谓语义化就是普通人能看懂)。后面跟一个空格,再后面是文件的路径。最后是分割符\r\n
。因为最后是\r\n
,所以上面的数据也叫请求行(request line)。
客户端跟服务器建立连接后就立即发送上面的数据。服务端等收到\r\n
后开始解析,也就是把/mypage.html
提取出来,然后找到对应的文件,把文件内容发送给客户端。
到这里,客户端就收到了服务端发送的文件内容,也叫「响应」。但是,客户端马上面临服务端同样的问题:如何确定已经收到了 mypage.html 的完整的内容呢?服务端要不要在最后发送分割符\r\n
呢?不能!因为 mypage.html 的内容里本身就可能包含\r\n
。如果客户端还是以\r\n
当作结束标记,那可能会丢失数据。
为此 Tim Berners-Lee (HTTP 协议之父) 采用了更简单的办法——关闭连接。也就是说,服务器在传输完成之后要主动关闭 TCP 连接,这样客户端就明确知道所有的内容已经传输完成了。
以上就是最原始的 HTTP 协议,大约在1990发布。现在称这个时代的 HTTP 协议为 HTTP/0.9,主要是跟后面标准化之后的 1.x 进行区分。就这样,万维网的时代开启了。
HTTP/0.9 发布后得到了广泛的应用。但它的功能太简单了,所以很多浏览器都在它的基础上做了扩展。最主要的扩展功能有如下几个:
添加版本信息是为了方便客户端和服务端相互识别,这样才能开启扩展功能。添加之后的请求行如下:
1 | GET /mypage.html HTTP/1.0\r\n |
添加扩展头信息是为了传递更多的扩展信息。比如,这时候不同的浏览器会在请求中标记自己的身份。为方便后续添加各种不同的扩展信息,HTTP协议继续使用「行」和分割符的概念。
首先,跟请求行保持一致,每一条扩展信息占一行,以冒号分割,以\r\n
结尾,比如:
1 | User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)\r\n |
其次,这种信息可以有多行。那服务端怎么确定到底有几行呢,这还得用到分割符\r\n
。HTTP 协议用一个空行表示后面扩展信息都结束了。所以完整的请求是:
1 | GET /mypage.html HTTP/1.0\r\n |
服务端先接收一行,提取文件路程,然后再根据\r\n
逐行提取扩展信息。如果收到一个空行,则说明扩展信息接收完成。
这些扩展信息也叫头信息(header),后续 HTTP 协议的各种特性都是基于它来实现。
HTTP/0.9 收到请求后直接传输文件内容。但用些场景需要返回其他信息,比如文件不存在之类的,所以人们给它添加了返回状态信息。此外,扩展后的 HTTP 协议也支持服务端在发送数据前返回多个头信息。一个典型的扩展响应为:
1 | 200 OK\r\n |
服务器首先会发一行数据200 OK\r\n
。这里的200
是状态码,表示成功。后面的OK
是给人看的语义部分。这一行也叫 status code line。紧接着就是扩展信息,形式跟请求里的一模一样,每行一条,以空行表示结束。最后才是文件内容。
因为有了头信息,HTTP协议的扩展性直接起飞。人们不断给 HTTP 协议添加各种种样的特性。
HTTP/0.9 只能传输纯文本文件。因为有了 Header,我们可以传输更多的描述信息,比如文件在的类型、长度、更新时间等等。这些传输数据的描述信息也被称为 Entity Header,数据本身称为 Entiy。
常见的 Entiy Header 有:
Content-Type 表示数据类型,比如 gif 的类型是image/gif
。类型的取值最终被标准化为 Multipurpose Internet Mail Extensions(MIME)。
Content-Length 表示数据长度。但我们前面说过,HTTP/0.9 的服务器不需要返回文件长度,等传输完毕后关闭 TCP 连接就好了。为什么又要定义长度信息呢?
这里有两个问题。第一个是在请求里支持上传内容,第二个是连接优化问题。
HTTP/0.9 只有一种 GET 请求。显然光下载是不够的。人们陆续引入了 HEAD 和 POST 等请求,用来给服务器提交数据。一但要提交数据,光用分割符就不够了。因为提交的数据本身就可能包含分割符。所以需要事先指定数据的长度。这个长度用的就是 Content-Length 头来指定。
另外一个是连接优化问题。其实 HTTP 协议的发展史很大程度上就是传输性能的优化史。
HTTP/0.9每次请求都会创建一个 TCP 连接,读取结束后连接就会被关闭。如果一次只下载一个文件也没什么问题。但后来 HTML 页面支持嵌入图片等内容,一个页面可能有多个图片。这样浏览器打开一个 HTML 页面的时候就需要发起多次 HTTP 请求,每次请求都要反复建立和关闭 TCP 连接。不但浪费服务器资源,还会拖慢页面的加载速度。
所以,大家就想办法复用底层的 TCP 连接。简单来说就是服务器在内容发送完成后不主动关闭连接。但不关闭就会出现前面说的问题,客户端不知道响应内容什么时候传输完毕。所以需要事先指定数据的长度。因为 HTTP 协议已经有了 header 机制,所以添加 Content-Length 就是最自然的办法。
这里还有一个兼容性问题。如果客户端不支持复用 TCP 连接,那服务端不关闭连接的话客户端就会一直在等待。所以复用 TCP 连接这个功能不能默认开启,而是应该由客户端决定要不要使用。这就引出了Connection:Keep-Alive
这个头信息。如果客户在请求中指定 Keep-Alive,服务端才不会主动关闭 TCP 连接。
除了复用 TCP 连接之外,HTTP/0.9 另一个值得优化的地方就是数据压缩。那个时代网速很慢,如果能把数据压缩之后再传输可以显著降低传输耗时。服务端不能随意压缩,因为有的客户端可能不支持。所以就先引入了Accept-Encoding
这个头,可能的取值如compress
或者gzip
。服务端收到这个请求之后才对内容做压缩。因为浏览器可能支持多种压缩算法,浏览器需要选择一种自己也支持的来压缩数据,所以就需要在返回内容的时候指定自己用了哪种算法。这就是Content-Encoding
头的用途。
不论是前面的 Connection 还是后面的 Accept-Encoding,为了尽可能地兼容不同客户端,HTTP 协议会通过添加新的 header 来协商是否使用扩展特性。这种协商由客户端来主导,服务器需要根据客户端的请求来配合完成。
还是因为网络比较慢而且成本很高,HTTP协议需要进一步优化数据传输效率。一个典型的场景是客户端已经下载过某文件内容。当客户端再次请求的时候,服务端还要不要返回。如果不返回,则客户端拿不到最新的内容;如果返回,当服务端的文件没有变化的时候,客户端会花很长时间加载一个已经下载过的文件。怎么优化这个问题呢?
人们引入了如下 Entity Header:
如果文件不经常改动,服务器可以对过 Last-Modified 把最近修改时间发送给浏览器。浏览器如果支持,可以在下次请求该资源的时候带上这个时间,也就是在请求里添加下面的头:
1 | If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT\r\n |
服务器收到后会跟文件的当前修改时间做对比,如果没有修改则直接返回304:
1 | 304 Not Modified\r\n |
这种叫作条件请求,可以显著减少不必要的网络传输。
即使如此,客户端还是发起一次 HTTP 请求才能拿到 304 响应,也会产生网络传输和服务端开销。为了进一步优化,HTTP又引入了 Expires 头,它的含义是一个未来的过期时间。在这个时间之前浏览器可以安全使用本地缓存的副本,不需要从服务器下载。这样连条件请求都不需要发起了。
不过 Expires 特性有一个副作用,文件一旦下发,在过期之前根本无法修改。
大约是在1991-1995这个时间,各浏览器厂商陆续实现了上述功能。但不同浏览器和服务端软件支持的功能不同,带来各种兼容问题。于是到 1996 年,IETF 发布 RFC1945。RFC1945 只能说是当前最佳实践的总结,并不是推荐标准。但人们还是称它为 HTTP/1.0。
没过一年,也就是1997年,IETF就发布了RFC2068,也就是大名鼎鼎的 HTTP/1.1 协议规范。
HTTP/1.1 是对 HTTP/1.0 的梳理和扩展。核心的改动有:
所谓的 pipeline 特性是对 HTTP 协议传输效率的进一步优化,但最终失败了。
HTTP 协议是请求应答式协议。客户端发一个请求,然后等待服务端返回内容。虽然在 HTTP/1.0 时代就有了 TCP 连接复用、内容压缩和条件请求等优化机制,但客户端发起新请求之前必须等待服务器返回内容。换言之就是客户端无法在一个连接上并行发起多个请求。为此,HTTP/1.1 的 pipeline 就规定客户端可以依次发起多个 HTTP 请求,然后等待服务器返回结果。服务器需要按照请求顺序依次返回对应的响应内容。
1 | c s c s |
虽然服务器收到多个请求的时候可以并发处理,这种并发带来的优化有限,而且 pipeline 特性并没有减少实际的网络传输。几乎没有软件实现 pipeline 特性,所以这个优化设计以失败告终。
chunked 编码是一项非常成功的优化,主要解决服务端动态生成响应内容的情况。
HTTP/1.0 只能使用 Content-Length 指定内容长度,而且是先发送 header 再发送 body。这就要求必须在传输内容之前确定内容的长度。对于静态文件,这当然不是问题。但如果要加载一个由 PHP 动态渲染的 HTML 就有问题了。因为 HTML 是程序动态生成的,没法事先确定内容长度。如果还用原来的办法,只能先把内容生成好保存到一个临时文件,再发送给客户端。显然这种性能太差。
为了解决这个问题,HTTP/1.1 引入 chunked 编码。简单来说就是回到之前的长度流,将数据逐段发送给客户端,每一段前面加上长度信息:
1 | HTTP/1.1 200 OK\r\n |
Transfer-Encoding 指定为 chunked。接下来的数据也是分行传输。一行长度,一行数据。结束的时候长度指定为零,然后再加一个空行。这样服务端就不需要事先确定响应内容的长度,PHP 就可以有边渲染一边发送。这个特性还是 WebSocket 没有普及的年代被用于实现消息推送。大家可以搜索 Comet 或者 HTTP 长轮询了解更多信息。
HTTP/1.1 对缓存做了更粗细化的定义,引入了 Cache-Control 扩展信息。这一部分内容比较复杂,除了会影响浏览器的缓存行为之外,还会影响 CDN 节点的行为。部分 CDN 厂商还会扩展 标准缓存指令的语义。限于篇幅,在此就不展开了。
但 HTTP/1.1 对条件请求做了扩展,可以说一下。
操作系统会自动记录文件的修改时间,读取该时间也非常方便,但 Last-Modified 不能覆盖所有情况。有时候我们需要用程序定时生成某些文件,它的修改时间会周期性变化,但内容不一定有改变。所以光用 Last-Modified 还是可能产生不必要的网络传输。于是 HTTP 协议引入了一个新的头信息 Etag。
Etag 的语义是根据文件内容计算一个值,只有在修改内容的时候才会产生新的 Etag。客户端每次请求的时候把上一次的 Etag 带回来,也就是添加下面的头:
1 | If-None-Match: "c3piozzzz"\r\n |
服务端收到后会对比 Etag,只有发生变化的时候才会返回新的文件内容。
那个时候的网络很不稳定,断网是家常便饭。想想一个文件下载到99%然后断网了是一种怎样的体验。为了减少不必要的数据传输,人们很快就给 HTTP 协议添加了「断点续传」功能。其实断点续传是从客户端视角来看的。从协议角度来看,需要添加的功能是根据指定范围传输数据。也就是说原来的文件是100字节,客户端可以指定只下载最后的10字节:
1 | Content-Range: bytes 91-100/100\r\n |
这里的91-100
表示要下载的范围,后面的100表示整个文件的长度。如果服务器支持,则会返回:
1 | HTTP/1.1 206 Partial content\r\n |
该功能除了用于断点续传外,还可以实现并行下载加速。客户端可以起多个线程,建立多条 TCP 连接,每个线程下载一部分,最后把有的内容连到一直。就这么简单。
另外,HTTP/1.1 还要求客户端在请求的时候必须发送 Host 头信息。这里面保存着当前请求对应的网站域名。服务器收到请求后会根据 Host 里的域名和请求行里的路径来确定需要返回的内容。这样就能实现在同一个 IP 上搭建不同域名的网站,也就是所谓的虚拟主机。这大大降低了网站的建设成本,对 Web 生态的发展起到了至关重要的作用。
除了扩展 HTTP/1.0 原来的功能外,HTTP/1.1 还引入了连接升级功能。其实这个功能后面用的不多,但有一个重量级的协议 WebSocket 在用,所以不得不说。
所以连接升级就是把当前用于 HTTP 会话的 TCP 连接切换到其他协议。以 WebSocket 为例:
1 | GET /chat HTTP/1.1 |
这里把 Connection 设成了 Upgrade,表示希望切换协议。而 Upgrade:websocket 表示要切换到 websocket 协议。在切换之前,这还是一个普通的 HTTP 请求。服务器可以对该请求做各种鉴权等 HTTP 动作。服务器如果接受用户的请求,则会返回:
1 | HTTP/1.1 101 Switching Protocols |
从这一该起,双方就不能在该 TCP 连接上发送 HTTP 协议数据了。因为协议已经切换到 WebSocket。
从 1999 年开始,到 2015 年 HTTP/2 发布,HTTP 协议有15年的时候没有大的变化。与此同时,互联网蓬勃发展,从 Web 1.0 过渡到 Web 2.0,从 PC 互联网发展到移动互联网,从明文 HTTP 也切换到加密 HTTPS。整个过程 HTTP 协议都发挥了核心作用。这从侧面也说明 HTTP 协议是一种扩展性非常好的协议。
但 HTTP/1.1 毕竟是九十年代设计的协议。2010年之后,移动互联网兴起,业界希望对 HTTP 的问题做够进一步优化。那还有哪些问题可以优化呢?主要有几个方面:
文本格式其实是 HTTP 的一大特色。我们在调试的时候可以直接使用 telnet 连接服务器,然后用肉眼看服务器的返回结果。但对人类友好的设计对机器一定不友好。HTTP协议使用\r\n
作为分割符,双不限制头信息的数量,这必然导致解析的时候需要动态分配内存。而且还要把数字、日期等信息转换成对应的二进制格式,这都需要额外的解析成本。
HTTP/1.x 支持压缩数据内容,而且使用头信息保存压缩算法。所以就不能用相同的算法压缩头信息了。只能另辟蹊径。
HTTP/1.1 的 pipeline 已然失败,无法充分复用 TCP 连接。HTTP 从一开始就是请求应答式的设计,服务器没办法主动推送内容到客户端。
为了解决这几个问题,Google 挟 YouTube 和 Chrome 两大杀器,推出了 SPDY 协议。该协议有两个特点:
SPDY 引入了帧做为最小的传输单位:
1 | +-----------------------------------------------+ |
每一帧前三个字节表示数据长度,然后用一个字节表示类型,再用一个字节保存一些扩展标记。然后就是四个字节的 stream ID,最后是真正的数据。这其实就表明 HTTP 协议从分割符流转向了长度流。
在同一个 TCP 连接上,数据帧可以交替发送,不再受请求应答模式制约。也就是说服务端也可以主动给客户端发消息了。同一个请求的 header 和数据部分也可以分开发送,不再要求先发 header 再发 body。也正是因为数据帧交错传输,同一个会话下的数据需要能关联起来,所以 SPDY 给每一帧添加了 stram ID。换句话说 SPDY 在一个 TCP 连接上虚拟出了多个 stream,每一个 stream 从效果看都是一个 TCP 连接。不同的 HTTP 请求和响应数据可以使用自己的 stream 并发传输,互不影响。这样一下子就解决了上面的一、三和四这三个问题。
第二个问题比较麻烦。但解决思路也很简单。HTTP/1.x 的头信息都是 K-V 型的,而且都是字符串。这里的 K-V 都很少变化。比如只要是访问我的博客,不论有多少请求,都得发送 Host: taoshu.in
。对于这种不变的,我们完全可以在两端各保存一张映射表,给每个 Key 和 Value 都指定一个编号。这样后续的请求只要传 Key 和 Value 的编号就行了,从而实现压缩的效果。单看 Host 可能不觉得有多少进步。但大家想想自己的 cookie,里面有登录会话信息,每次都重复发送浪费相当惊人。所以压缩头信息带来的优化还是惊人的。
因为谷歌一边控制着市场份额最大的 Chrome 浏览器,另一边又控制像 Google/YouTube 这样的内容服务,所以开发下一代 HTTP 协议便一件非常容易的事情。SPDY 于 2012 年发布,最终在 IETF 完成标准化,并于 2015 年发布,也就是RFC7540。
随着社会的发展,隐私保护成了人们关注的重要课题。为了保护用户信息,业界一真在推动 HTTP + TLS 也就是 HTTPS 的普及。HTTPS 服务使用 443 端口。我们前面讲过,HTTP/2 使用二进制编码,跟 HTTP/1.x 并不兼容。但客户端又不会一夜之间都升级的 HTTP/2。那怎么才能在一个端口上同时支持两种 HTTP 协议呢?这就用到了 TLS 协议的 ALPN 扩展。简单来说就是客户端在发起 TLS 会话的时候会通过 ALPN 扩展附带自己支持的应用层协议,比如 http/1.1 和 h2。服务端收到后会把自己支持的应用层协议返回给客户端。这样双方就能确定接下来在 TLS 会话是使用什么协议。
理论上 HTTP/2 可以通过 HTTP/1.1 的升级机制来协商,这样也能解决两个版本共用 TLS 会话的问题。但这种升级会再来额外的延迟,所以主流的浏览器都不支持。
HTTP/2 发布之后,整个业界都在积极迁移到新的协议。但实践证明,HTTP/2并没有想象中的那么好。为什么呢?因为对于同一个域名,浏览器默认只会开一个连接,所有请求都使用一个TCP连接收发。虽然不同的请求使用不同的 stream,但底层的连接只有一个。如果网络出现抖动,不论是哪一个请求的数据需要重传,其他请求的数据都必须等待。这就是所谓的 Head of Line blocking 问题。HTTP/2 非但没有优化,甚至还比 HTTP/1.x 还要差。因为在 HTTP/1.x 时代,浏览器自知 HTTP 无法复用连接,所以会为同一个域名创建多个 TCP 连接。不同的请求可能会分布到不同的连接上,出现网络抖动的影响比只用一个连接要好一点。
HTTP/2 的另一个问题就是功能太复杂。比如它支持在服务器主动推送资源(比如 CSS 文件)到浏览器,这样客户端在加载的时候就需要等待网络传输。但该功能非常复杂,而且效果有限,最终连 Chrome 自己都放弃支持该功能了。这部分功能被 HTTP 103 Early Hints 状态码代替,具体可以参考RFC8297。
一计不成,再生一计。谷歌的工程师跟 Head of Line blocking 问题死磕。这次他们把矛头指向了问题的根源 TCP 协议。因为 TCP 是可靠传输协议,数据必须按顺序收发,而且要边确认边发送。如果底层用 TCP 连接,就不可能解决 Head of Line blocking 问题。为此,他们基于 UDP 协议设计了 QUIC 协议。
QUIC 协议简单来说就是一种面向消息的传输协议(TCP 是面向数据流的传输协议)。QUIC 也有 stream 的概念,每个会话可以有多个流。不同的流的数据都使用 UDP 收发,互不干扰。跟 TCP 一样,数据发出后也需要对方确认。然后再把 QUIC 跟 HTTP/2 的帧映射到一起,最终形成 HTTP/3 协议,也就是RFC9114。
那 QUIC 有没有问题呢?也有,但基本都不是设计上的问题。
第一个问题就是运营商可能对 UDP 流量做限流,很多防火墙可能会阻止 QUIC 流量。这是之前 UDP 通信使用不广泛导致的。 随着 HTTP/3 技术的普及,这些问题会逐渐改善。
第二个问题是 HTTP/3 启动延迟的问题。HTTP/3 使用 UDP 通信,跟 HTTP/1.x 和 HTTP/2 不兼容,所以浏览器没法判断服务器是否支持 HTTP/3。
目前主流的做法是网站同时支持 HTTP/2 和 HTTP/3。浏览器先通过过 TCP 连接访问服务器。服务器在第一个响应中返回一个特殊的 Header:
1 | Alt-Svc: h3=":4430"; ma=3600 |
这里的意思是在 UDP 的 4430 端口提供 HTTP/3 服务,该信息的有效时间为 3600 秒。后面浏览器就可以使用 QUIC 连接 4430 端口了。
明眼人一看就知道这里有问题,建立 HTTP/3 会话之前还得先用一下 HTTP/2 启动有把。这不科学🔬而且这会带来额外的耗时。为此,人们又开始想别的办法,这就是 DNS SVCB/HTTPS 记录。
DNS SVCB/HTTPS 简单来说就是用一种特殊的 DNS 记录把前面的 Alt-Svc 信息曝露出来。浏览器在访问网站之前先通过 DNS 查询是否支持 HTTP/3 以及对应的 UDP 端口,然后就直接发起 HTTP/3 会话就好。这样就完全不依赖 TCP 连接了。关于 DNS SVCB/HTTPS 记录的更多信息请看我的专门文章。
顺便说一句,HTTP/3 默认可以工作在任意 UDP 端口,不像 HTTPS 那样默认工作在 443 端口。如果运营商封掉 443 就没法对外服务。等 HTTP/3 普及了,所有人都可以使用自家的宽带搭建网站😄具体做法可以参考我的这篇文章。
好了,到现在快肝了一万字了。我认为基本讲清楚了 HTTP 协议的发展脉络。现于篇幅,没能详细讨论 HTTP/2 和 HTTP/3 的技术细节,不能说不是个遗憾。先开个坑,后面有时间再补上。希望本文能帮助你更好地理解 HTTP 协议。
参考链接:
]]>本文转载自:「涛叔」,原文:https://taoshu.in/net/http.html ,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。