;`
-
-这个有趣的标签语法既不是字符串也不是 HTML。
-
-它被称为 JSX,它是 JavaScript 的语法扩展。我们建议将它与 React 一起使用来描述 UI 应该是什么样子。JSX 可能会让您想起模板语言,但它具有 JavaScript 的全部功能。
-
-JSX 产生 React “元素”。[我们将在下一节](https://reactjs.org/docs/rendering-elements.html)探索将它们渲染到 DOM 。下面,您可以找到入门所需的 JSX 基础知识。
-
-By default, React DOM [escapes](https://stackoverflow.com/questions/7381974/which-characters-need-to-be-escaped-on-html) any values embedded in JSX before rendering them. Thus it ensures that you can never inject anything that’s not explicitly written in your application. Everything is converted to a string before being rendered. This helps prevent [XSS (cross-site-scripting)](https://en.wikipedia.org/wiki/Cross-site_scripting) attacks.使用大括号,来在属性值中插入一个 JavaScript 表达式****
-
-React DOM 在渲染所有输入内容之前,默认会进行[转义](https://stackoverflow.com/questions/7381974/which-characters-need-to-be-escaped-on-html)
-。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 [XS](https://en.wikipedia.org/wiki/Cross-site_scripting)
-
-### **Hook**
-
-Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则:
-
-- 只能在**函数最外层**调用 Hook。不要在循环、条件判断或者子函数中调用。
-- 只能在 **React 的函数组件**中调用 Hook。不要在其他 JavaScript 函数中调用。(还有一个地方可以调用 Hook —— 就是自定义的 Hook 中)
-
-是 React 16.8 的新增特性。它可以让你在**不编写 class 的情况下使用 state 以及其他的 React 特性。**
-
-Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。
-
-2、为什么要使用 hooks
-难以理解的 class 、组件中必须去理解 javascript 与 this 的工作方式、需要绑定事件处理器、纯函数组件与 class 组件的差异存在分歧、甚至需要区分两种组件的使用场景;Hook 可以使你在非 class 的情况下可以使用更多的 React 特性。
-
-在组件之间复用状态逻辑很难、大型组件很难拆分和重构,也很难测试。**Hook 使你在无需修改组件结构的情况下复用状态逻辑。没有 class 继承、没有 this、没有生命周期、代码更加简洁、这就是使用 hooks 的意义**
-
-React 提供了一些内置的 Hooks,比如 useState。您还可以创建自己的 Hooks 以在不同组件之间重用有状态行为。
-
-Usestate hook等价于写class & render
-
-**向 class 组件中添加局部的 state,state是异步的**
-
-当您调用 时`setState()`,React 会将您提供的对象合并到当前状态。
-
-```
-const [state, setState] = useState(initialState);
-```
-
-setState返回一个 state,以及更新 state 的函数。在初始渲染期间,返回的状态 (`state`) 与传入的第一个参数 (`initialState`) 值相同。
-
-`setState` 函数用于更新 state。它接收一个新的 state 值并将组件的一次重新渲染加入[队列](https://so.csdn.net/so/search?q=%E9%98%9F%E5%88%97&spm=1001.2101.3001.7020)。Or函数式更新
-
-**useRef 与 useState区别**
-
-1与状态不同,即使在组件重新渲染之后,存储在引用或 ref 中的数据或值仍保持不变。因此,**引用不会影响组件渲染,但状态会影响。**
-
-2useState 返回 2 个属性或一个数组。一个是值或状态,另一个是更新状态的函数。相反,useRef用来储存persitent value 只返回一个值,即实际存储的数据。
-
-3当参考值改变时,它会被更新而不需要刷新或重新渲染。但是在 useState 中,组件必须再次渲染以更新状态或其值。
-
-何时使用 Refs 和 States
-
-Refs 在获取用户输入、DOM 元素属性和存储不断更新的值时很有用。但是,如果您在组件状态中存储组件相关信息或use methods in components states 是最好的选择。
-
-不管是父组件或是子组件都无法知道某个组件是有状态的还是无状态的,并且它们也并不关心它是函数组件还是 class 组件。
-
-这就是为什么称 state 为局部的或是封装的的原因。除了拥有并设置了它的组件,其他组件都无法访问。当你在定时器中操作 state 的时候,而 setState 更新就是同步的。
-
-**useEffect是什么?**
-
-副作用钩子:用于处理组件中的副作用,用来取代生命周期函数。the function passed to `useEffect`fires **after** layout and paint, during a deferred event. This makes it suitable for the many common side effects, like setting up subscriptions and event handlers, because most types of work shouldn’t block the browser from updating the screen.
-
-```
-useEffect(
- () => {
- //func
- },
- [props.source],
-);
-```
-
-当props.source变动时 执行 //func
-
-### Lifecycle
-
-Component 组件的生命周期**可分成三个状态:**
- **Mounting(挂载):已插入真实DOM**. **Updating(更新):正在被重新渲染**
- **Unmounting(卸载):已移出真实DOM**
-
-![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d5b07601-37a4-4929-8939-6be7a2ef3977/Untitled.png)
-
-index.js为入口 `ReactDOM.render(`
-
-函数式组件
-
-**`export`**声明用于从 JavaScript 模块中导出值。然后可以使用`[import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)`声明或[动态导入将](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import)导出的值导入其他程序。导入绑定的值会在导出它的模块中发生变化——当模块更新它导出的绑定的值时,更新将在其导入值中可见。
-
-## Typescript
-
-TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。
-
-Deno可以运行.ts ,or npm install tsc to compile ts into js, then run in node ; or webpack
-
-tsconfig.json
-
-动态编译 type inference不run难以发现错误, 传入参数缺失默认undefine
-
-unlesss `a: number` **类型限定**
-
-### 语法
-
-{ } 大括号,表示定义一个对象,大部分情况下要有成对的属性和值,或是函数
-
-**[ ]中括号,表示一个数组,也可以理解为一个数组对象**
-
-`data? : {}[]; *// 表示data这个数组中的每一个元素是对象,且data缺省*`
-
-**`?:`**
- is a [ternary operato](https://en.wikipedia.org/wiki/Ternary_operator)r that is part of the syntax for basic [conditional expressions](https://en.wikipedia.org/wiki/Conditional_(programming)) in several [programming languages](https://en.wikipedia.org/wiki/Programming_language).
-
-? 作用
-
-**成员**
-
-`// 这里的?表示这个name属性有可能不存在
-class A {
- name?: string
-}
-
-interface B {
- name?: string
-}`
-
-: colon配合 label 属性使用,表示是否显示 label 后面的冒号
-
-### **安全链式调用**
-
-`// 这里 Error对象定义的stack是可选参数,如果这样写的话编译器会提示
-// 出错 TS2532: Object is possibly 'undefined'.
-return new Error().stack.split('\n');
-
-// 我们可以添加?操作符,当stack属性存在时,调用 stack.split。若stack不存在,则返回空
-return new Error().stack?.split('\n');`
-
-组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
-
-[泛型](https://www.tslang.cn/docs/handbook/generics.html)**即是类型的形参**
-
-**接口和别名的区别**
-
-1. 都可以用来自定义类型
-2. 类型别名支持联合和交叉类型定义
-3. 别名不支持重复命名,接口可以
-
-### umi
-
-可插拔的企业级 React 应用程序框架。 “umi 是一个基于路由的框架,支持 next.js 式的常规路由和各种高级路由功能,比如路由级别的按需加载
-
-**Ant design**
-
-Ant Design System 是企业级 UI 设计语言和 React UI 库的开源代码。它带有一组高质量的 React 组件,具有主题定制能力
-
-• The difference between `Switch` and `Checkbox` is that `Switch` will trigger a state change directly when you toggle it, while `Checkbox` is generally used for state marking, which should work in conjunction with submit operation.
-
-### Protable
-
-ProTable 在 antd 的 Table 上进行了一层封装,支持了一些预设,并且封装了一些行为。这里只列出与 antd Table 不同的 api。
-
-### **request**
-
-`request` 是 ProTable 最重要的 API,`request` 会接收一个对象。对象中必须要有 `data` 和 `success`,如果需要手动分页 `total` 也是必需的。`request` 会接管 `loading` 的设置,同时在查询表单查询和 `params` 参数发生修改时重新执行。同时 查询表单的值和 `params` 参数也会带入。
-
-iauth result success不符合param对successs的要求
-
-## ES6
-
-ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现。
-
-新增:1.类 2.模块化
-
-在ES6中模块作为重要的组成部分被添加进来。模块的功能主要由 export 和 import 组成。每一个模块都有自己单独的作用域,模块之间的相互调用关系是通过 export 来规定模块对外暴露的接口,通过 import 来引用其它模块提供的接口。同时还为模块创造了命名空间,防止函数的命名冲突。
-
-**3.箭头函数**
-
-=>不只是关键字 function 的简写,它还带来了其它好处。箭头函数与包围它的代码**共享同一个 this,**能帮你很好的解决this的指向问题。比如 var self = this;或 var that =this这种引用外围this的模式。但借助 =>,就不需要这种模式了。`(a,b) => a+b` 相当于function(a,b) {a+b;}
-
-```
-(param1, param2, …, paramN) => { statements }
-(param1, param2, …, paramN) => expression
-//相当于:(param1, param2, …, paramN) =>{ return expression; }
-
-//加括号的函数体返回对象字面量表达式:
-params => ({foo: bar})
-```
-
-由于JavaScript函数对`this`绑定的错误处理,下面的例子无法得到预期结果:
-
-```
-var obj = {
- birth: 1990,
- getAge:function () {
-var b =this.birth;// 1990var fn =function () {
-return new Date().getFullYear() -this.birth;// this指向window或undefined
- };
-return fn();
- }
-};
-
-```
-
-现在,箭头函数完全修复了`this`的指向,`this`总是指向词法作用域,也就是外层调用者`obj`:
-
-4.`使用模板字符串
-var name = `Your name is ${first} ${last}.``
-
-在 ES6 中通过 `${}`就可以完成字符串的拼接,只需要将变量放在大括号之中。
-
-5.解构赋值
-
-定义:ES6允许按照一定模式,从[数组](https://so.csdn.net/so/search?q=%E6%95%B0%E7%BB%84&spm=1001.2101.3001.7020)和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。解构的语法:
-
-**const {dispatch} = this.props;**
-
-**这段代码你可以认为是这样:**
-
-**const dispatch = this.props.dispatch;**
-
-7.Promise
-
-Promise 是异步编程的一种解决方案,比传统的解决方案 callback 更加的优雅。它最早由社区提出和实现的,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。一个 Promise 只能成功或失败一次,如果一个 Promise 成功或失败并且您稍后添加一个成功/失败回调,则将调用正确的回调
-
-A`[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)`是表示异步操作最终完成或失败的对象。由于大多数人都是已经创建的 Promise 的消费者
-
-Async 函数会 返回一个已完成的 [promise](https://so.csdn.net/so/search?q=promise&spm=1001.2101.3001.7020) 对象,实际在使用的时候会和await操作符配合使用,在介绍await之前,我们先看看 async 函数本身有哪些特点。
-
-8.let 与 const
-
-在之前 JS 是没有块级作用域的,const与 let 填补了这方便的空白,const与 let 都是块级作用域。
-
-在 TypeScript 函数里,如果我们定义了参数,则我们必须传入这些参数,除非将这些参数设置为可选,可选参数使用问号标识 ?
-
-`const`定义常量与使用`let` 定义的变量相似:
-
-- 二者都是块级作用域
-- 都不能和它所在作用域内的其他变量或函数拥有相同的名称
-
-两者还有以下两点区别:
-
-- `const`声明的常量必须初始化,而`let`声明的变量不用
-- const 定义常量的值不能通过再赋值修改,也不能再次声明。而 let 定义的变量值可以修改。
-
-const 的本质: const 定义的变量并非常量,并非不可变,它定义了一个常量引用一个值。使用 const 定义的对象或者数组,其实是可变的。下面的代码并不会报错:
-
-```perl
-// 创建常量对象
-const car = {type:"Fiat", model:"500", color:"white"};
-// 修改属性:
-car.color = "red";
-```
-
-### callback
-
-[客户端 JavaScript](https://link.zhihu.com/?target=https%3A//dzone.com/articles/full-stack-development-from-client-side-javascript) 在浏览器中运行,并且浏览器的主进程是单线程[事件循环](https://link.zhihu.com/?target=https%3A//blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/)。如果我们尝试在单线程事件循环中执行长时间运行的操作,则会阻止该过程。从技术上讲这是不好的,因为过程在等待操作完成时会停止处理其他事件。
-
-例如,`alert` 语句被视为浏览器中 javascript 中的阻止代码之一。如果运行 alert,则在关闭 alert 对话框窗口之前,你将无法在浏览器中进行任何交互。为了防止阻塞长时间运行的操作,我们使用了回调。
-
-JavaScript 被认为是单线程脚本语言。单线程是指 JavaScript 一次执行一个代码块。当 JavaScript 忙于执行一个块时,它不可能移到下一个块。
-
-换句话说,我们可以认为 JavaScript 代码本质上总是阻塞的。但是这种阻塞性使我们无法在某些情况下编写代码,因为在这些情况下我们没有办法在执行某些特定任务后立即得到结果。
-
-我谈论的任务包括以下情况:
-
-- 通过对某些端点进行 API 调用来获取数据。
-- 通过发送网络请求从远程服务器获取一些资源(例如,文本文件、图像文件、二进制文件等)。
-
-**为了处理这些情况,必须编写异步代码,而回调函数是处理这些情况的一种方法。所以从本质上上说,回调函数是异步的。**
-
-不用回调不行么,可以啊,可以一直傻等,这也符合同步的原则。代码可读性好,就是性能搓。就是不人性啊,等的捉急啊,加上JS单线程的设计,你这样是要操作一个IO其他啥都不能干了,那不玩死人。
-
-于是有了回调,在JS中,回调最大的好处是 调用者和回调者分开了,刚好实现这个异步事件机制
-
-# TEST
-
-**单元测试**是针对软件设计中的最小单位-程序模块,进行正确性检查的测试工作。 单元在软件代码中指一个函数或一个类,在图形化的软件中,单元一般指一个窗口,一个菜单。
-
-当软件项目中相关单元都开发完成并完成单元测试后只能确保每个独立单元没有问题, 但多个单元整合成完整功能时, 需要再次进行质量验证。这个步骤就是**集成测试**。
-
-**集成测试**又叫组装测试,通常在单元测试的基础上,将所有程序模块进行有序的、递增的测试。重点测试不同模块的接口部分。
-
-无论单元测试还是集成测试,都是根据不同的测试阶段划分的。一个项目首先要进行的就是单元测试,单元测试完成后进行**集成测试**。 **集成测试**完成后还有系统测试和验收测试等等才能完成软件项目的最终验收和交付。
-
-![https://picx.zhimg.com/80/v2-a2ad9d01f870da1f35d15b1cb8b34754_1440w.jpg?source=1940ef5c](https://picx.zhimg.com/80/v2-a2ad9d01f870da1f35d15b1cb8b34754_1440w.jpg?source=1940ef5c)
-
-在具体的测试执行过程中又分为是否查看源代码的测试方式, 不查看源代码的测试方式为**黑盒测试**,查看源代码的测试方式为**白盒测试**。
-
-黑盒测试是指测试的时候完全不考虑程序内部结构和内部特性,注重于测试软件的功能需求,只关心软件的输入数据和 输出数据。
-
-# DB
-
-字段 一个成员,它表示与对象或类关联的变量。 在数据库中,大多数时,**表的“列”称为“字段” ,每个字段包含某一专题的信息**
-。 就像“通讯录”数据库中,“姓名”、“联系电话”这些都是表中所有行共有的属性,所以把这些列称为“姓名”字段和“联系电话”字段。
-
-## 数据仓库
-
-数据仓库的两个重要的概念是:
-
-- 进入仓库的数据不可变
-- 记录数据的变化历史
-
-如何理解呢?不可变,意味着进到仓库的数据就类似归档了。原则上,不能对仓库里面的数据进行修改;如果随意的对仓库里面的数据进行修改,这个“仓库”就和交易系统没区别了,无法起到正确反映业务过程的作用。此外,适合于数据仓库的存储服务,如早年Oracle和DB2都有针对数据仓库的Data Warehouse产品,以及Hadoop体系的一系列组件,都是针对“批量插入,无更改或少量更改”而专门设计的,所以才能达到查询效率的最优化。也因此产生了OLTP系统和OLAP系统的两大模式。
-
-因此,“数据不可变”这是一个基准原则。
-
-**纯增量**
-
-类似交易流水、交易日志、登记簿之类的数据,数据发生的时候,就有明确的时间戳,并且数据发生之后不会改变的,比如上面说的账户交易流水表,记录产生之后不可变更。可以直接根据时间戳把当天的数据挑选出来,这批数据直接插入全量表,每日追加数据即可。
-
-一般会单独增加一个日期字段表示数据什么时候进来的。
-
-**对比增量**
-
-类似账户表、用户信息表之类主数据信息表或者状态表,在交易系统中往往只会记录最新状态而不会记录变化时间。当然,也有系统保留操作日志,记录变更情况。
-
-对于前者,需要我们自己把最新数据和仓库里的数据做一个对比,找出被变更过的数据。
-
-当一个数据需要存储多份时,会出现一致性问题,所以就需要进行同步,同步分为两种:增量和全量。
-
-不管哪种方式,永远要切记的是:
-
-`不要对仓库里的表做更新(update)操作!`复制
-
-空间和时间的对比是软件届是永恒的矛盾话题。在这里也是一样。
-
-全量
-简单来说,就是在一定的周期中,把当前系统在周期时间内所有数据复制到目标表/系统这样的同步方式就叫做—>全量
-
-增量
-增量同步的前提是全量,然后再更具规则增量同步;
-
-增量的基础是全量,就是你要使用某种方式先把全量数据拷贝过来,然后再采用增量方式同步更新。
-
-增量的话,就是指抓取某个时刻(更新时间)或者检查点(checkpoint)以后的数据来同步,不是无规律的全量同步。这里引入一个关键性的前提:副本一端要记录或者知道(通过查询更新日志或者订阅更新)哪些更新了。
-
-![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7a0e412f-8cc9-445c-896d-a89ee8affda7/Untitled.png)
-
-两个系统之间需要同步数据,同步的方法可以分为全量和增量两种形式。多年的经验告诉我,能用全量就别用增量。增量有三个问题
-
-1.数据提供方,很难制作增量包,事无巨细都要记录,稍微记错就全完了
-
-2.数据接收方,理解并且实施增量包的逻辑比较复杂
-
-3.中间过程一旦出了问题,很难定位
-
-# Elasticsearch
-
-是一个**实时**的**分布式存储、搜索、分析**的引擎。
-
-Relational DB ‐> Databases ‐> Tables ‐> Rows ‐> Columns
-
-Elasticsearch ‐> Indices ‐> Types ‐> Documents ‐> Fields
-
-为什么要使用Elasticsearch呢?我们在日常开发中,**数据库**也能做到(实时、存储、搜索、分析)。
-
-相对于数据库,Elasticsearch的强大之处就是可以**模糊查询**。
-
-有的同学可能就会说:我数据库怎么就不能模糊查询了??我反手就给你写一个SQL:
-
-`select * from user where name like '%公主号Java3y%'`
-
-这不就可以把**公主号Java3y**相关的内容搜索出来了吗?
-
-的确,这样做的确可以。但是要明白的是:`name like %Java3y%`这类的查询是不走**索引**的,不走索引意味着:只要你的数据库的量很大(1亿条),你的查询肯定会是**秒**级别的
-
-Mysql
-
-- **INNER JOIN**:如果表中有至少一个匹配,则返回行
-- **LEFT JOIN**:即使右表中没有匹配,也从左表返回所有的行
-- **RIGHT JOIN**:即使左表中没有匹配,也从右表返回所有的行
-- **FULL JOIN**:只要其中一个表中存在匹配,则返回行
-
-Elasticsearch的数据结构是怎么样的呢?看下面的图:
-
-![https://pic4.zhimg.com/80/v2-ada6a56e22eaa7541552527f64e59e17_1440w.jpg](https://pic4.zhimg.com/80/v2-ada6a56e22eaa7541552527f64e59e17_1440w.jpg)
-
-我们输入一段文字,Elasticsearch会根据分词器对我们的那段文字进行**分词**(也就是图上所看到的Ada/Allen/Sara..),这些分词汇总起来我们叫做`Term Dictionary`,而我们需要通过分词找到对应的记录,这些文档ID保存在`PostingList`
-
-在`Term Dictionary`中的词由于是非常非常多的,所以我们会为其进行**排序**,等要查找的时候就可以通过**二分**来查,不需要遍历整个`Term Dictionary`
-
-由于`Term Dictionary`的词实在太多了,不可能把`Term Dictionary`所有的词都放在内存中,于是Elasticsearch还抽了一层叫做`Term Index`,这层只存储 部分 **词的前缀**,`Term Index`会存在内存中(检索会特别快)
-
-`Term Index`在内存中是以**FST**(Finite State Transducer)
-
-![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3217d73a-df51-4510-bffc-2033afa24e22/Untitled.png)
-
-Bitmap
-
-Examples of bitmap graphic formats include **GIF, JPEG, PNG, TIFF, XBM, BMP, and PCX as well as bitmap (i.e., screen) fonts**
-. The image displayed on a computer monitor is also a bitmap, as are the outputs of printers, scanners, and similar devices. They are created using paint programs like Adobe Photoshop.
-
-**Index**:Elasticsearch的Index相当于数据库的Table
-
-**Type**:这个在新的Elasticsearch版本已经废除(在以前的Elasticsearch版本,一个Index下支持多个Type--有点类似于消息队列一个topic下多个group的概念)
-
-**Document**:Document相当于数据库的一行记录
-
-**Field**:相当于数据库的Column的概念
-
-**Mapping**:相当于数据库的Schema的概念
-
-**DSL**:相当于数据库的SQL(给我们读取Elasticsearch数据的API)
-
-一个Index的数据我们可以分发到不同的Node上进行存储,这个操作就叫做**[分片](https://www.zhihu.com/search?q=%E5%88%86%E7%89%87&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A%22981341195%22%7D)**。
-
-比如现在我集群里边有4个节点,我现在有一个Index,想将这个Index在4个节点上存储,那我们可以设置为4个分片。这4个分片的数据**合起来**就是Index的数据
-
-数据写入的时候是**写到主分片**,副本分片会**复制**主分片的数据,读取的时候**主分片和副本分片都可以读**。
-
-> Index需要分为多少个分片和副本分片都是可以通过配置设置的
->
-
-### **监控**
-
-Elasticsearch 经常以多节点集群的方式部署。有多种 API 让你可以管理和监控集群本身,而不用和集群里存储的数据打交道。
-
-****使用 Docker 启动单节点集群****
-
-如果您在 Docker 容器中启动单节点 Elasticsearch 集群,则会自动为您启用和配置安全性。首次启动 Elasticsearch 时,会自动进行以下安全配置:
-
--
-
- 为传输层和 HTTP 层生成 [证书和密钥。](https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#elasticsearch-security-certificates)
-
-- 传输层安全 (TLS) 配置设置被写入 `elasticsearch.yml`
-- 为`elastic`用户生成密码。
-- 为 Kibana 生成一个注册令牌。
-
-然后您可以[启动 Kibana](https://www.elastic.co/guide/en/kibana/8.3/docker.html)并输入有效期为 30 分钟的注册令牌。此令牌自动应用您的 Elasticsearch 集群中的安全设置,通过 `kibana_system`用户向 Elasticsearch 进行身份验证,并将安全配置写入`kibana.yml`.
-
-# Git
-
-[Untitled](https://www.notion.so/8e58d4936d1f4d02a16a70b131fbed8f)
-
-HEAD
-HEAD 是当前分支引用的指针,它总是指向该分支上的最后一次提交。 这表示 HEAD 将是下一次提交的父结点。 通常,可以把 HEAD 看做你的上一次提交的快照。可以简单理解为: HEAD 指向分支(branch),分支指向提交。
-
-Index
-Index(索引,或暂存区)是你预期的下一次提交。这就是当你运行 git commit 时 Git 看起来的样子。Git 将上一次检出到工作目录中的所有文件填充到 Index,之后你会将其中一些文件替换为新版本,接着通过 git commit 将它们转换为树来用作新提交。
-
-工作目录
-另外两棵树以一种高效但并不直观的方式,将它们的内容存储在 .git 文件夹中。工作目录会将它们解包为实际的文件以便编辑。 你可以把工作目录当做 “沙盒”,在你将修改提交到暂存区并记录到历史之前,可以随意更改。
-
-**Pull**
-
-- `merge`操作会生成一个新的节点,之前的提交分开显示。而`rebase`操作不会生成新的节点,是将两个分支融合成一个线性的提交。
-- 解决冲突时。merge操作遇到冲突的时候,当前merge不能继续进行下去。手动修改冲突内容后,add 修改,commit就可以了。而`rebase`操作的话,会中断rebase,同时会提示去解决冲突。解决冲突后,将修改add后执行`git rebase –continue`继续操作,或者`git rebase –skip`忽略冲突。
-- `git pull`和`git pull --rebase`区别:`git pull`做了两个操作分别是”获取”和”合并”。所以加了rebase就是以rebase的方式进行合并分支,默认为merge。
-
-**总结**:选择 merge 还是 rebase?
-
-- merge 是一个合并操作,会将两个分支的修改合并在一起,默认操作的情况下会提交合并中修改的内容
-- merge 的提交历史忠实地记录了实际发生过什么,关注点在真实的提交历史上面
-- rebase 并没有进行合并操作,只是提取了当前分支的修改,将其复制在了目标分支的最新提交后面
-- rebase 的提交历史反映了项目过程中发生了什么,关注点在开发过程上面
-
-merge和rebase都不能避免conflict,实际区别是对待conflict的方式,merge是合并过来解决冲突,并不改变分支拉出的位置,并生成一个merge commit。而rebase相当于将分支2中的提交cherry-pick到主分支上解决冲突,不会生成多余的commit,但是丢失了分支拉出的位置信息。无论哪种方式最终的结果一定是一样的,但是git历史不一样,对于master分支,merge的历史有两条线最终通过merge commit汇总,而rebase的历史只有一条线比较干净。
-
-从 master 分支 checkout 出几个本地 feature 分支,你或者你的团队在协同开发某个 feature-a 时,可能别人已把 feature-b 的代码 merge 回 master 了,所以应该及时将 master 的改动 rebase 到你的本地分支,顺便 fix conflicts。即:
-
-`$ git switch feature-a
-$ git rebase master
-fix [conflicts](https://www.zhihu.com/search?q=conflicts&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A1990894567%7D)...
-$ git rebase --continue`
-
-并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?
-
-幸好,Git还提供了一个`stash`功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作
-
-- 当您想记录工作目录和索引的当前状态,但又想回到干净的工作目录时。
-- 这会保存您的本地修改并恢复工作目录以匹配 HEAD 提交。
-
-![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/decda594-73c9-4dad-b21f-c38079a9d62f/Untitled.png)
-
-默认情况下,用户的 SSH 密钥存储在其 `~/.ssh` 目录下。 进入该目录并列出其中内容,你便可以快速确认自己是否已拥有密钥:
-
-我们需要寻找一对以 `id_dsa` 或 `id_rsa` 命名的文件,其中一个带有 `.pub` 扩展名。 `.pub` 文件是你的公钥,另一个则是与之对应的私钥。 如果找不到这样的文件(或者根本没有 `.ssh` 目录),你可以通过运行 `ssh-keygen` 程序来创建它们。 在 Linux/macOS 系统中,`ssh-keygen` 随 SSH 软件包提供;在 Windows 上,该程序包含于 MSysGit 软件
-
-首先 `ssh-keygen` 会确认密钥的存储位置(默认是 `.ssh/id_rsa`),然后它会要求你输入两次密钥口令。 如果你不想在使用密钥时输入口令,将其留空即可。 然而,如果你使用了密码,那么请确保添加了 `-o` 选项,它会以比默认格式更能抗暴力破解的格式保存私钥。 你也可以用 `ssh-agent` 工具来避免每次都要输入密码。
-
-现在,进行了上述操作的用户需要将各自的公钥发送给任意一个 Git 服务器管理员 (假设服务器正在使用基于公钥的 SSH 验证设置)。 他们所要做的就是复制各自的 `.pub` 文件内容,并将其通过邮件发送。 公钥看起来是这样的:
-
-# Docker
-
-**虚拟机是[硬件虚拟化](https://www.zhihu.com/search?q=%E7%A1%AC%E4%BB%B6%E8%99%9A%E6%8B%9F%E5%8C%96&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A%22824515963%22%7D),容器是操作系统虚拟化,虚拟化是在硬件性能过剩的前提下,为不同应用提供某个级别的运行环境隔离**
-
-## **镜像**
-
-我们都知道,操作系统分为 **内核** 和 **用户空间**。对于 `Linux` 而言,内核启动后,会挂载 `root` 文件系统为其提供用户空间支持。而 **Docker 镜像**(`Image`),就相当于是一个 `root` 文件系统。比如官方镜像 `ubuntu:18.04` 就包含了完整的一套 Ubuntu 18.04 最小系统的 `root` 文件系统。
-
-**Docker 镜像** 是一个**静态的,特殊的文件系统**,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像 **不包含任何动态数据,其内容在构建之后也不会被改变。**
-
-**分层存储**
-
-因为镜像包含操作系统完整的 `root` 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 [Union FS](https://en.wikipedia.org/wiki/Union_mount) 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 `ISO` 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。
-
-镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
-
-## 容器
-
-镜像(`Image`)和容器(`Container`)的关系,就像是面向对象程序设计中的 `类` 和 `实例` 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
-
-容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 [命名空间](https://en.wikipedia.org/wiki/Linux_namespaces)。因此容器可以拥有自己的 `root` 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。
-
-前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为 **容器存储层**。
-
-容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
-
-按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 [数据卷(Volume)](notion://www.notion.so/docker_practice/data_management/volume)、或者 [绑定宿主目录](notion://www.notion.so/docker_practice/data_management/bind-mounts),在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。
-
-**容器技术 vs 虚拟机**
-
-与虚拟机通过操作系统实现隔离不同,容器技术**只隔离应用程序的运行时环境但容器之间可以共享同一个操作系统**,这里的运行时环境指的是程序运行依赖的各种库以及配置。容器是一种便于管理的轻量化虚拟机
-
-我们知道和一个单纯的应用程序相比,**操作系统是一个很重而且很笨的程序**,简称笨重,有多笨重呢?
-
-我们知道操作系统运行起来是需要占用很多资源的,大家对此肯定深有体会,刚装好的系统还什么都没有部署,单纯的操作系统其磁盘占用至少几十G起步,内存要几个G起步。
-
-假设我有一台机器,16G内存,需要部署三个应用,那么使用虚拟机技术可以这样划分:
-
-![https://pic4.zhimg.com/v2-c20cb49c88034e73e09059668b8cecfb_b.jpg](https://pic4.zhimg.com/v2-c20cb49c88034e73e09059668b8cecfb_b.jpg)
-
-在这台机器上开启三个虚拟机,每个虚拟机上部署一个应用,其中VM1占用2G内存,VM2占用1G内存,VM3占用了4G内存。
-
-我们可以看到虚拟本身就占据了总共7G内存,因此**我们没有办法划分出更过虚拟机从而部署更多的应用程序**,可是我们部署的是应用程序,要用的也是应用程序而**不是操作系统**。
-
-如果有一种技术可以让我们避免把内存浪费在“无用”的操作系统上岂不是太香?这是问题一,主要原因在于操作系统太重了。
-
-还有另一个问题,那就是启动时间问题,我们知道操作系统重启是非常慢的,因为操作系统要从头到尾把该检测的都检测了该加载的都加载上,这个过程非常缓慢,动辄数分钟,因此操作系统还是太笨了。
-
-那么有没有一种技术可以让我们获得虚拟机的好处又能克服这些缺点从而一举实现鱼和熊掌的兼得呢?
-
-答案是肯定的,这就是容器技术。
-
-**什么是容器**
-
-容器一词的英文是[container](https://www.zhihu.com/search?q=container&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22%3A%22article%22%2C%22sourceId%22%3A%22187505981%22%7D),其实container还有集装箱的意思
-
-- 集装箱之间相互隔离
-- 长期反复使用
-- 快速装载和卸载
-- 规格标准,在港口和船上都可以摆放
-
-容器技术可实现不同云计算之间应用程序的可移植性,以及提供了一个把应用程序拆分为分布式组件的方法。此外,用户还可以管理和扩展这些容器成为集群。
-
-Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的[镜像](https://baike.baidu.com/item/%E9%95%9C%E5%83%8F/1574)
-中,然后发布到任何流行的 [Linux](https://baike.baidu.com/item/Linux)或[Windows](https://baike.baidu.com/item/Windows/165458)操作系统的机器上,也可以实现[虚拟化](https://baike.baidu.com/item/%E8%99%9A%E6%8B%9F%E5%8C%96/547949)。容器是完全使用[沙箱](https://baike.baidu.com/item/%E6%B2%99%E7%AE%B1/393318)机制,相互之间不会有任何接口。
-
-![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/151f837c-3734-4d8e-ab58-aa923abeddc2/Untitled.png)
-
-![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9097a033-79fd-4010-9f80-09767cbbac95/Untitled.png)
-
-## **docker的底层实现**
-
-docker基于Linux内核提供这样几项功能实现的:
-
-- **NameSpace**我们知道Linux中的PID、IPC、网络等资源是全局的,而NameSpace机制是一种资源隔离方案,在该机制下这些资源就不再是全局的了,而是属于某个特定的NameSpace,各个NameSpace下的资源互不干扰,这就使得每个NameSpace看上去就像一个独立的操作系统一样,但是只有NameSpace是不够。
-- **Control groups**虽然有了NameSpace技术可以实现资源隔离,但进程还是可以不受控的访问系统资源,比如CPU、内存、磁盘、网络等,为了控制容器中进程对资源的访问,Docker采用control groups技术(也就是[cgroup](https://www.zhihu.com/search?q=cgroup&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22%3A%22article%22%2C%22sourceId%22%3A%22187505981%22%7D)),有了cgroup就可以控制容器中进程对系统资源的消耗了,比如你可以限制某个容器使用内存的上限、可以在哪些CPU上运行等等。
-
-有了这两项技术,容器看起来就真的像是独立的操作系统了。
-
-Image镜像 为虚拟机的snapshot, 镜像在container中run
-
-当输入cmd的docker run时本地主机无法访问REST服务器,如果我们想将容器内的端口暴露给容器外的端口,用 - -publish / -p 80:5000,将docker中的80端口map暴露给到host 5000port
-
-我们希望 docker 的服务是在后台运行的,我们可以过 **-d** 指定容器的分离模式,默认cmd返回,不进入容器
-
-docker ps列出
-
-[Volumes](https://docs.docker.com/storage/volumes/)
- are the preferred mechanism for persisting data generated by and used by Docker containers.
-
-### Dockerfile
-
-当我们使用该`FROM`命令时,我们告诉 Docker 在我们的镜像中包含镜像中的所有功能`golang:1.16-alpine`。我们所有的后续命令都将建立在该“基础”映像之上。
-
-该`COPY`命令采用两个参数。第一个参数告诉 Docker 要将哪些文件复制到镜像中。最后一个参数告诉 Docker 你希望将该文件复制到哪里。
-
-`RUN` command to execute the command `go mod download`
- there as well. This works exactly the same as if we were running `go`
- locally on our machine, but this time these Go modules will be installed into a directory inside the image.
-
-multistage build is smaller than normal build in magnitude 这是因为 我们用来部署 Go 应用程序的[“无发行版”基础镜像非常简单,适用于静态二进制文件的精益部署。](https://github.com/GoogleContainerTools/distroless)
-
-## Kubernetes
-
-Kubernetes分布式架构运维平台,它实现了对容器资源的编排与控制 作者:
-
-Kubernetes基础介绍 Borg / Kubernetes 框架 KUbernetes关键字含义
-
-基础概念:什么是 Pod 控制器类型 K8S 网络通讯模式
-
-工具部署:单节点 构建 K8S 集群
-
-资源清单:资源 掌握资源清单的语法 编写 Pod 掌握 Pod 的生命周期***
-
-Pod 控制器:掌握各种控制器的特点以及使用定义方式
-
-服务发现:掌握 SVC 原理及其构建方式
-
-存储:掌握多种存储类型的特点 并且能够在不同环境中选择合适的存储方案(有自己的简介)
-
-调度器:掌握调度器原理 能够根据要求把Pod 定义到想要的节点运行
-
-安全:集群的认证 鉴权 访问控制 原理及其流程
-
-HELM:Linux yum 掌握 HELM 原理 HELM 模板自定义 HELM 部署一些常用插件
-
-运维:修改Kubeadm 达到证书可用期限为 10年 能够构建高可用的 Kubernetes 集群
-
-开发: Kubernetes 自开发实现特殊功能
-
-# dependency manage
-
-## Maven
-
-**`是`的超集**
-
-``和``元素是两个不同的东西,但它们之间仍然存在着重要的关系。
-
-简单来说就是`parent`定义了当前pom的父pom,`dependencies`定义了当前pom的实际依赖关系。父 pom 可以定义`dependencies`,但也可以定义子 Maven 项目继承的许多其他东西(特别是允许配置许多东西的`dependencyManagement`元素和`build`元素),因此可以以某种方式视为`dependencies`元素的超集。
-
-![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cc4bf43f-45ce-41b9-a72b-ffe166840fd8/Untitled.png)
-
-本地仓库:
-存放在本地服务器中,当运行项目的时候,maven会自动根据配置文件查找本地仓库,再从本地仓库中调用jar包使用。
-远程仓库(私服):
-当本地仓库中没有项目所需要的jar包时,那么maven会继续查找远程仓库,一般远程仓库指的是公司搭建的私有服务器,也叫私服;
-当jar包在私服中查找到之后,maven会将jar包下载到本地仓库中,下次使用的时候就不用再去找远程仓库。
-中央仓库:
-当远程仓库获取不到jar包时,就需要到中央仓库去查找,并下载在远程仓库中,本地仓库再从远程仓库中下载回来使用。
-
-mvn clean install 依次执行了clean、resources、compile、testResources、testCompile、test、jar(打包)、install等8个阶段。
-
-mvn clean deploy 依次执行了clean、resources、compile、testResources、testCompile、test、jar(打包)、install、deploy等9个阶段。
-
-**mvn -U clean install**
-
- **Forces a check for missing releases and updated snapshots on
-remote repositories**
-
-
-clean 命令清除项目target文件夹
-package 命令完成了项目编译、单元测试、打包功能,但没有把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库和远程maven私服仓库
-install 命令完成了项目编译、单元测试、打包功能,同时把打好的可执行jar包(war包或其它形式的包)**布署到本地maven仓库,但没有布署到远程maven私服仓库**
-deploy 命令完成了项目编译、单元测试、打包功能,同时把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库和远程maven私服仓库
-
-**Artifacts【项目的打包部署设置,这个是项目配置里面比较关键的地方】**
-
-**artifact 可以作为存档文件 ,或者作为包含以下结构元素的目录结构:**
-
-一个或多个编译模块、模块依赖的类库、Resources 集合、其他 artifacts、独立的文件目录或存档
-
-> 再白话一点,就是说某个 module 要如何打包
->
->
-> 例如 war exploded、war、jar、ear 等等这种打包形式
->
-> 某个 module 有了 Artifacts 就可以部署到应用服务器中了
->
-
-`jar:Java ARchive`,通常用于**聚合大量**的 Java 类文件、相关的元数据和资源(文本、图片等)文件到一个文件,以便分发 Java 平台应用软件或库
-
-`war:Web application ARchive`,**一种 JAR 文件,其中包含**用来分发的 JSP、Java Servlet、Java 类、XML 文件、标签库、静态网页(HTML 和相关文件),以及构成 Web 应用程序的**其他资源**
-
-`exploded`:在这里你可以理解为展开,不压缩的意思。**也就是 war、jar 等没压缩前的目录结构**。建议在开发的时候使用这种模式,便于修改了文件的效果立刻显现出来
-
-**默认情况下,IDEA 的 Modules 和 Artifacts 的 output 目录已经设置好了,不需要更改,打成 war 包的时候会自动在 WEB-INF 目录下生成 classes,然后把编译后的文件放进去。**
-
-Yarn是一个快速可靠安全的依赖管理工具。
-主要的三个特点:
-
-极其快速,Yarn会缓存它下载的每个包,所以无需重复下载。它还能并行化操作以最大化资源利用率。
-特别安全,Yarn会在每个安装包被执行前校验其完整性
-超级可靠, Yarn使用格式详尽而又简洁的lockfile文件和确定性算法来安装依赖,能够保证在一个系统上的运行的安装过程也会以同样的方式运行在其他系统上
-
-# Iauth
-
-Hotstar internal auth is to provide complete role based permission control and authentication services to internal dashboard and micro-services via JWT and SSO
-
-![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8cbd8e27-1d71-4bdb-969a-ee1787171141/Untitled.png)
-
-### SDK
-
-![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/edcc517c-f6e1-4da6-804e-cc5676b3ad65/Untitled.png)
-
-![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/13ebf408-f93f-45a8-9836-febf6c4c29b4/Untitled.png)
-
-The components of the URL are combined and delimited as follows:
-
-`scheme://host:port/path?query`
-
-### logout
-
-[https://seceng-rex-portal.hotstar-labs.com/](https://seceng-rex-portal.hotstar-labs.com/)
-
-[https://iauth-manage.pp.hotstar-labs.com/Service](https://iauth-manage.pp.hotstar-labs.com/Service)
-
-[https://iauth.hotstar-labs.com/?id=REX-Dashboard%3Aauvxmcphru9asmwufgjkbpjtcudvlkfb&redirect_url=https%3A%2F%2Fseceng-rex-portal.hotstar-labs.com%2F&signin_request=tru](https://iauth.hotstar-labs.com/?id=REX-Dashboard%3Aauvxmcphru9asmwufgjkbpjtcudvlkfb&redirect_url=https%3A%2F%2Fseceng-rex-portal.hotstar-labs.com%2F&signin_request=true)
-
-1. 账号密码是如何登录的
-
-启动服务过程: wire将router interface与authrouter绑定,启动server自动生成的injector完成依赖注入(与数据库操作 router controller绑定?)
-
-创建authrouter的同时启动service和 各种操作用户权限的函数 login,当server接收到用户请求时启动一个Context c记录 用户id和对应操作的服务
-
-2. 其他服务如何通过IAuth登录的?
-
-router group的api方法
-
-3. 设计实现一个/v1/signout接口
-
-前端可以隐藏v1, 比如users页面(/partner),前端会调用 /v1/partners,拿到用户列表,然后加载到这个表格里
-
-前端给后端api发送请求,我的想法是后端router执行user group绑定的DELETE下的logoutController的setCookie方法把Iauthkey的value设为“”,然后redirect到登入界面. 两次redirect 只能用GET
-
-1. Permission 下related entities user/service
-
-request function calls umi request method to backend, then it fetchs idtype& other info in the Gin.Context and passes to frontend to display.
-
-frontend Debug: [https://cloud.tencent.com/developer/article/1471757](https://cloud.tencent.com/developer/article/1471757)
-
-1. defaultrole
-
-Switch widget checked has changed pass into function,⇒ switch access state constant `defaultRoleEnabled`
-
-context.Redirect calls Render,which writes the **response** headers and calls render.Render to render data.
-
-1. Search bug : filter 问题
-
-1. comment
-
-**Array.prototype.map()**
-
-The **`map()`** function lets you manipulate the items in an array by iterating and accessing individual items.**`map()`** method **creates a new array** populated with the results of calling a provided function on every element in the calling array.
-
-创建mock test:
-
-`context, _ := gin.CreateTestContext(httptest.NewRecorder())`
-
-通常,您的网络服务器不应具有会话或注销功能。REST 服务应该是无状态的,并且身份验证信息与每个请求一起发送。
-
-但是,如果您正在使用TOKEN对用户进行身份验证,并且您想明确告诉服务器使该令牌过期,*并且*您想以 RESTful 方式表达这一点,那么对我来说这是有意义的:
-
-- 您的令牌以 /sessions/[id] 之类的 url 表示
-- 您`DELETE`在该网址上发出`/webservice/logout/`对我来说,你有一个你删除的网址是没有意义的。
-
-发出带有一些信息的 POST 请求以发出“注销”操作是一种明智的 HTTP API 设计方法,但它不是 REST。
-
-need context and hooks
-
-但实际上登录信息保存在一个用`httponly`属性标记的cookie中,以防止一些xss风险,这意味着它只能从服务器重置(没有手动清除cookie)
-
-SDK是其他团队接入接口
-
-![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/659b79f2-3c4c-44aa-ae45-5c3193ef066a/Untitled.png)
-
-- Expiration time: configurable for each service, default to 12h
-- **SDK Initialization (Service Sign-In)S2S**
-
-**Panic Token in vault**
-
-- Also a **S2S Token**, but with very long expiration time.
-
-2000是后端端口 8000是前端
-
-- 写后台的过程是提供接口,是被调用方的开发者视角
-- **写sdk的过程是使用接口,是调用方的开发者视角**
-
-**SDK: We provide SDK of each language that does authN and authZ.**
-
-Pros:
-
-- Applicable to every scenario: inter/intra cluster, with/without ambassador, with/without sidecar
-- No performance overhead due to network call
-- Isolation between services. One cannot affect another.
-- AuthN customization. Besides API based control, services can invoke customized permission checks with SDK. And SDK can provide lifecycle hooks for service to ingest customized logics.
-
-Cons:
-
-- We need to build SDK for each language
-- Services need to integrate with SDK
-- Services need to bump SDK version if they want to have new features or fixes
-
-where’s ORM diagram for RBAC,
-
-solidclient is client or failover client
-
-gin `Context` 优先 -》`defaultRedirectURL` -〉referer
-
-verify —— release
-
-*For the S2S scenario, services will always work even though the IAuth crashed.* This means IAuth SDK will handle all these exceptions caused by the IAuth backend service. *For the C2S scenario, services will downgrade when the IAuth crashed.* This means the **IAuth SDK will lose some functions while the IAuth backend service is down**. In this paper, we give all the details about that.
-
-1. Setting panic service token
-
-In the S2S scenario, we have a caller and a callee. The caller must carry a valid service token to prove its identity to the callee. Generally, a valid service token is generated through the IAuth SDK calling the IAuth backend service. In the case where the IAuth backend service crashed, the IAuth SDK needs a **backup panic service token**. Therefore, *IAuth provides each service **a long-lived panic service token** and developers need to set this token in the SDK*. In this way, the caller will always send requests carrying a valid service token
-
-2. Failover of authentication
-
-In the S2S scenario, we have a caller and a callee. The callee needs to check the service token each time it receives a request. The dedicated public key is needed to verify a service token. Generally, the public key is retrieved from the IAuth backend service. In the case where the IAuth backend service crashed, the IAuth SDK needs a backup mechanism. *Therefore, IAuth SDK stores the corresponding public keys locally and does a failover automatically once the IAuth backend service is down*. In that way, no matter IAuth backend service is up or down, authentication will always work.
-
-3. Failover of authorization
-
-We have two cases where the failover of authorization is needed. One case is that the service works well while the IAuth backend service crashes. *IAuth SDK caches the data of authorization locally periodically and the cache won’t delete the old data once the new data can’t be pulled from the IAuth backend service*, which makes itself work well in this case. Another case is that the service needs to be deployed when the IAuth backend service crashes. For this case, *IAuth SDK will startup in an s2s failover mode, where all requests are allowed directly, without the need to check permissions.* *Meanwhile, IAuth SDK will automatically switch to the normal mode once the IAuth backend service recovers from the crash.*
-
-4. Failover of audit
-
-Generally, the IAuth SDK collects all the audit logs and then sends them to the IAuth backend service. Once the IAuth backend service is down, the logs won’t be received successfully. *These logs will stay in the memory until the IAuth backend service is up again or the limitation of occupied memory is reached (50MB)*.
-
-public key所有服务共享一样, private key iauth校验
-
-Q:1.middleware shadow mode? shadow mode 记录没有权限的请求,先允许访问`Requests without valid tokens aren't blocked. Redirects are stopped.`
-
-2.Theoretically backend returns a 302 response and abort
-
-context.next() → multiple handlers 异步执行, like `chain.doFilter(request, response)`
-
-redirect+abort
-
-// Cron keeps track of any number of entries, invoking the associated func as
-// specified by the schedule. It may be started, stopped, and the entries may
-// be inspected while running.
-
-在Linux系统中,定时任务管理系统一般是由cron承担,我们可以把cron设置为开机时自动启动。cron启动后,它会读取它的所有配置文件(全局性配置文件/etc/crontab,以及每个用户的计划任务配置文件),然后cron会根据命令和执行时间来按时来调用度工作任务。
-cron是一个linux下的定时执行工具,可以在无需人工干预的情况下运行作业。由于Cron 是Linux的内置服务,但它不自动起来,可以用以下的方法启动、关闭这个服务:
-
-# GO
-
-在Go语言出现之前,开发者们总是面临非常艰难的抉择,究竟是使用执行速度快但是编译速度并不理想的语言(如:C++),还是使用编译速度较快但执行效率不佳的语言(如:.NET、Java),或者说开发难度较低但执行速度一般的动态语言呢?显然,Go语言在这 **3 个条件**之间做到了最佳的平衡:**快速编译,高效执行,易于开发。**
-
-Go语言支持交叉编译,比如说你可以在运行 Linux 系统的计算机上开发可以在 Windows 上运行的应用程序。这是第一门完全支持 UTF-8 的编程语言,这不仅体现在它可以处理使用 UTF-8 编码的字符串,就连它的源码文件格式都是使用的 UTF-8 编码。
-
-Go语言在C语言的基础上取其精华,弃其糟粕,将C语言中较为容易发生错误的写法进行调整,做出相应的编译提示。
-
-[https://go.dev/doc/faq#Is_Go_an_object-oriented_language](https://go.dev/doc/faq#Is_Go_an_object-oriented_language)
-
-We decided to take a step back and think about what major issues were going to dominate software engineering in the years ahead as technology developed, and how a new language might help address them. For instance, the rise of multicore CPUs argued that a language should provide first-class support for some sort of concurrency or parallelism. And to make resource management tractable in a large concurrent program, garbage collection, or at least some sort of safe automatic memory management was required.
-
-**强类型静态编译型语言。隐式继承**
-
-可以在事后添加接口,而无需注释原始类型。因为类型和接口之间没有明确的关系,所以没有要管理或讨论的类型层次结构。
-
-UTF-8 是被广泛使用的编码格式,是文本文件的标准编码,其它包括 XML 和 JSON 在内,也都使用该编码。由于该编码对占用字节长度的不定性,Go 中的字符串也可能根据需要占用 1 至 4 个字节(示例见第 4.6 节),这与其它语言如 C++、Java 或者 Python 不同(Java 始终使用 2 个字节)。Go 这样做的好处是不仅减少了内存和硬盘空间占用,同时也不用像其它语言那样需要对使用 UTF-8 字符集的文本进行编码和解码。
-
-字符串是一种值类型,且值不可变,即创建某个文本后你无法再次修改这个文本的内容;更深入地讲,字符串是字节的定长数组。Go 支持以下 2 种形式的字面值:
-
-解释字符串:
-
-该类字符串使用双引号括起来,其中的相关的转义字符将被替换
-
-该类字符串使用反引号括起来,支持换行,例如
-
-**语法糖**
-
-‘…’ 其实是go的一种语法糖。用法一:表示多个不确定数量的参数
-
-用法二:slice打散传递
-
-1. arr2 := []int{1,2,3}
-2. arr1 = append(arr1,0)
-3. arr1 = append(arr1,**arr2...**)
-
-### 数据类型
-
-Go语言中数组、字符串和切片三者是密切相关的数据结构。这三种数据类型,在底层原始数据有着相同的内存结构,在上层,因为语法的限制而有着不同的行为表现。首先,**Go语言的数组是一种值类型,虽然数组的元素可以被修改,但是数组本身的赋值和函数传参都是以整体复制的方式处理的**。Go语言字符串底层数据也是对应的字节数组,但是字符串的只读属性禁止了在程序中对底层字节数组的元素的修改。字符串赋值只是复制了数据地址和对应的长度,而不会导致底层数据的复制。切片的行为更为灵活,切片的结构和字符串结构类似,但是解除了只读限制。切片的底层数据虽然也是对应数据类型的数组,但是每个切片还有独立的长度和容量信息,切片赋值和函数传参数时也是将切片头信息部分按传值方式处理。因为切片头含有底层数据的指针,所以它的赋值也不会导致底层数据的复制。其实Go语言的赋值和函数传参规则很简单,除了闭包函数以引用的方式对外部变量访问之外,其它赋值和函数传参数都是以传值的方式处理。要理解数组、字符串和切片三种不同的处理方式的原因需要详细了解它们的底层数据结构。
-
-数组是一个由**固定长度**的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。数组的长度是数组类型的组成部分。因为数组的长度是数组类型的一个部分,不同长度或不同类型的数据组成的数组都是不同的类型,因此在Go语言中很少直接使用数组(不同长度的数组因为类型不同无法直接赋值)。和数组对应的类型是切片,切片是可以动态增长和收缩的序列,切片的功能也更加灵活,但是要理解切片的工作原理还是要先理解数组。
-
-转换: Atoi (string to int) and Itoa (int to string).
-
-iota
-
-rune类型是Go语言中的一个基本类型,其实就是一个**int32的别名**
-,主要用于表示一个字符类型大于一个字节小于等于4个字节的情况下,特别是**中文字符。**
-
-A `byte` in Go is an unsigned 8-bit integer. It has type `uint8`
-. A `byte` has a limit of 0 – 255 in numerical range. It can represent an ASCII character.
-
-```
-fmt.Println([]byte("falcon"))
- fmt.Println([]byte("čerešňa"))
-}
-
-$ go run str2bytes.go
-[102 97 108 99 111 110]
-[196 141 101 114 101 197 161 197 136 97]
-```
-
-string与[]byte在底层结构上是非常的相近(后者的底层表达仅多了一个cap属性,因此它们在内存布局上是可对齐的),这也就是为何builtin中内置函数copy会有一种特殊情况`copy(dst []byte, src string) int`的原因了。对于[]byte与string而言,**两者之间最大的区别就是string的值不能改变**
-
-字符串的值不能被更改,但可以被替换。 string在底层都是结构体`stringStruct{str: str_point, len: str_len}`,string结构体的str指针指向的是一个字符常量的地址, 这个地址里面的内容是不可以被改变的,因为它是只读的,但是这个指针可以指向不同的地址。
-
-那么,以下操作的含义是不同的:
-
-```
-s := "S1" // 分配存储"S1"的内存空间,s结构体里的str指针指向这块内存
-s = "S2" // 分配存储"S2"的内存空间,s结构体里的str指针转为指向这块内存
-
-b := []byte{1} // 分配存储'1'数组的内存空间,b结构体的array指针指向这个数组。
-b = []byte{2} // 将array的内容改为'2'
-```
-
-Golang 有 time.Time数据类型来处理挂钟时间和time.Duration来处理单调时间。 第一个基本方法是 time.Now(),它返回当前日期和时间,精确到纳秒。返回的值具有数据类型 time.Time,它是一个结构。根据 Golang 的官方文档,“A Time 代表具有纳秒精度的瞬间”。
-
-**time.Duration**有一个基本类型 int64。持续时间表示两个瞬间之间经过的时间,以 int64 纳秒计数”。最大可能的纳秒表示可达 290 年。
-
-## 指针,传参
-
-- Go 中函数**传参仅有值传递**一种方式;
-- **slice**、**map**、**channel**都是引用类型,但是跟c++的不同;
-- **slice**能够通过函数传参后,修改对应的数组值,是因为 slice 内部保存了引用数组的指针,并不是因为引用传递。
-
-```
-type slice struct {
- array unsafe.Pointer
- len int
- cap int
-}
-```
-
-slice本质上指的就是这个结构(data+len+cap,不包括底层的数组)。在参数传递过程中,这个结构拷贝了一份,data指向的还是原来的底层数组。当我们对slice中的元素进行修改时,还是会通过拷贝之后的data,直接对底层数组进行修改。
-
-> go 中,slice、map、channel都是引用类型,所以都会有如上的特性。
->
-
-在默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。
-
-注意1:无论是值传递,还是引用类型传递,传递给函数的都是变量的副本,不过,值传递是值的拷贝。引用传递是地址的拷贝,一般来说,地址拷贝更为高效。而值拷贝取决于拷贝的对象大小,对象越大,则性能越低。
-
-注意2:map、slice、chan、指针、interface默认以引用的方式传递。
-
-不定参数传值 就是函数的参数不是固定的,后面的类型是固定的。(可变参数)
-
-匿名函数的定义就是没有名字的普通函数定义。
-
-### map
-
-Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
-
-Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的**返回顺序,这是因为 Map 是使用 hash 表**来实现的。
-
-`[mapstructure](https://github.com/mitchellh/mapstructure)`
-用于将通用的`map[string]interface{}`解码到对应的 Go 结构体中,或者执行相反的操作。很多时候,解析来自多种源头的数据流时,我们一般事先**并不知道他们对应的具体类型**。只有读取到一些字段之后才能做出判断。这时,**我们可以先使用标准的`encoding/json`库将数据解码为`map[string]interface{}`类型,然后根据标识字段利用`mapstructure`
-库转为相应的 Go 结构体以便使用**
-
-指针的一个高级应用是你可以传递一个变量的引用(如函数的参数),这样不会传递变量的拷贝。指针传递是很廉价的,只占用 4 个或 8 个字节。当程序在工作中需要占用大量的内存,或很多变量,或者两者都有,使用指针会减少内存占用和提高效率。被指向的变量也保存在内存中,直到没有任何指针指向它们,所以从它们被创建开始就具有相互独立的生命周期。
-
-nil 指针也称为空指针。
-
-```perl
-var a int= 20 /* 声明实际变量 */
- var ip *int /* 声明指针变量 */
-
- ip = &a /* 指针变量的存储地址 */
-
- fmt.Printf("a 变量的地址是: %x\n", &a )
-
- /* 指针变量的存储地址 */
- fmt.Printf("ip 变量储存的指针地址: %x\n", ip )
- /* 使用指针访问值 */
- fmt.Printf("*ip 变量的值: %d\n", *ip )
-普通占位符
-占位符 说明 举例 输出
-%v 相应值的默认格式。 Printf("%v", people) {zhangsan},
-%+v 打印结构体时,会添加字段名 Printf("%+v", people) {Name:zhangsan}
-%#v 相应值的Go语法表示 Printf("#v", people) main.Human{Name:"zhangsan"}
-%T 相应值的类型的Go语法表示 Printf("%T", people) main.Human
-%% 字面上的百分号,并非值的占位符 Printf("%%") %
-布尔占位符
-占位符 说明 举例 输出
-%t true 或 false。 Printf("%t", true) true
-整数占位符
-占位符 说明 举例 输出
-%b 二进制表示 Printf("%b", 5) 101
-%c 相应Unicode码点所表示的字符 Printf("%c", 0x4E2D) 中
-%d 十进制表示 Printf("%d", 0x12) 18
-%o 八进制表示 Printf("%d", 10) 12
-%q 单引号围绕的字符字面值,由Go语法安全地转义 Printf("%q", 0x4E2D) '中'
-%x 十六进制表示,字母形式为小写 a-f Printf("%x", 13) d
-%X 十六进制表示,字母形式为大写 A-F Printf("%x", 13) D
-%U Unicode格式:U+1234,等同于 "U+%04X" Printf("%U", 0x4E2D) U+4E2D
-```
-
-## function方法
-
-A method is on an object or is static in class.A function is independent of any object (and outside of any class).
-
-For Java and C#, there are only methods.
-
-For C, there are only functions.
-
-For C++ and Python it would depend on whether or not you're in a class.
-
-### 1) 在定义时调用匿名函数
-
-匿名函数lambda:是指**一类无需定义标识符(函数名)的函数或子程序**
-。 所谓匿名函数,通俗地说就是没有名字的函数,lambda函数没有名字,是一种简单的、在同一行中定义函数的方法。 lambda函数一般功能简单:单行expression决定了lambda函数不可能完成复杂的逻辑,只能完成非常简单的功能。
-
-匿名函数可以在声明后调用,例如:
-`1. func(data int) {
-2. fmt.Println("hello", data)
-3. }(100)`
-
-表示对匿名函数进行调用,传递参数为 100。
-
-2) 将匿名函数赋值给变量
-
-匿名函数可以被赋值,例如:
-`1. // 将匿名函数体保存到f()中
-2. f := func(data int) {
-3. fmt.Println("hello", data)
-4. }
-5.
-6. // 使用f()调用
-7. f(100)`
-
-匿名函数的用途非常广泛,它本身就是一种值,可以方便地保存在各种容器中实现回调函数和操作封装。
-
-What is () before a function Golang?
-
-it's called receiver argument, 函数名前的括号规定了接收到的object,就像面向对象的类,想要调用Person类中的eat方法首先需要创建一个Person对象
-
-The parenthesis before the function name is **the Go way of defining the object on which these functions will operate**.
-
-```jsx
-type Vertex struct {
- X, Y float64
-}
-
-func (v Vertex) Abs() float64 {
- return math.Sqrt(v.X*v.X + v.Y*v.Y)
-}
-
-func main() {
- v := Vertex{3, 4}
- fmt.Println(v.Abs())
-}
-```
-
-### 闭包
-
-闭包使得Javascript的垃圾回收机制GC不会收回a()所占用的资源,因为a()的内部函数b()的执行需要依赖a()中的变量i
-
-defer `这些调用直到 return 前才被执。因此,可以用来做资源清理。`
-
-```
-package main
-import "fmt"
-type Test struct {
- name string
-}
-
-func (t *Test) Close() {
- fmt.Println(t.name, " closed")
-}
-func Close(t Test) {
- t.Close()
-}
-func main() {
- ts := []Test{{"a"}, {"b"}, {"c"}}
- for _, t := range ts {
- defer Close(t)
- }
-}
-```
-
-defer后面的语句在执行的时候,函数调用的参数会被保存起来,但是不执行。也就是复制了一份。但是并没有说struct这里的this指针如何处理,通过这个例子可以看出go语言并没有把这个明确写出来的this指针当作参数来看待。
-
-多个 defer 注册,按 FILO 次序执行 ( 先进后出 )。哪怕函数或某个延迟调用发生错误,这些调用依旧会被执行。
-
-**尽可能不要在goroutine中使用闭包!**
-
-## Exception
-
-Golang 没有结构化异常,使用 panic 抛出错误,recover 捕获错误。
-
-异常的使用场景简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。
-
-Python&Java try-catch 机制正是提案试图避免的那种事情。 Panic 和 recover 不是通常意义的异常机制。通常的方式是将 exception 和一个控制结构相关联,鼓励细粒度的 exception 处理,导致代码往往不易阅读。在 error 和调用一个 panic 之间确实存在差异,而且我们希望这个差异很重要。在 Java 中打开一个文件会抛出异常。在我的经验中,打开文件失败是最平常不过的事。而且还需要我写许多代码来处理这样的 exception。
-
-## Reflect
-
-反射是指在程序运行期**对程序本身进行访问和修改的能力**。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息。
-
-支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。C/[C++](http://c.biancheng.net/cplus/)语言没有支持反射功能,只能通过 typeid 提供非常弱化的程序运行时类型信息;Java、[C#](http://c.biancheng.net/csharp/) 等语言都支持完整的反射功能;Lua、[JavaScript](http://c.biancheng.net/js/)类动态语言,由于其**本身的语法特性就可以让代码在运行期访问程序自身的值和类型信息,因此不需要反射系统**。
-
-Go语言程序中的类型(Type)指的是系统原生数据类型,如 int、string、bool、float32 等类型,以及使用 type 关键字定义的类型,这些类型的名称就是其类型本身的名称。例如使用 type A struct{} 定义结构体时,A 就是 struct{} 的类型。
-
-Map、Slice、Chan 属于引用类型,使用起来类似于指针
-
-**结构体标签(Struct Tag)**
-
-通过 reflect.Type 获取结构体成员信息 reflect.StructField 结构中的 Tag 被称为结构体标签(StructTag)。结构体标签是对结构体字段的额外信息标签。结构体标签(Struct Tag)类似于 [C#](http://c.biancheng.net/csharp/)
- 中的特性(Attribute)。C# 允许在类、字段、方法等前面添加 Attribute,然后在反射系统中可以获取到这个属性系统。例如:
-
-Tag 在结构体字段后方书写的格式如下:`key1:"value1" key2:"value2"`
-
-key会指定**反射的解析方式**,如下: json(JSON标签) orm(Beego标签)、gorm(GORM标签)、bson(MongoDB标签)、form(表单标签)、binding(表单验证标签)
-
-结构体标签由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。键值对之间使用一个空格分隔。
-
-指定映射的字段名。为了做到这一点,我们需要为字段设置`mapstructure`标签。例如下面使用`username`代替上例中的`name`:
-
-```
-type Person struct {
- Namestring `mapstructure:"username"`
-}
-```
-
-## JSON
-
-Substring 从以连续顺序放置在两个指定索引之间的字符串中取出字符。另一方面, **子序列**可以通过删除中间的一些元素或不删除元素从另一个序列导出,但始终保持原始序列中元素的相对顺序。
-
-key 必须是字符串,value 可以是合法的 JSON 数据类型(字符串, 数字, 对象, 数组, 布尔值或 null)。
-
-key 和 value 中使用冒号 **:** 分割。 每个 key/value 对使用逗号 **,** 分割。
-
-Both JSON and XML can be used to receive data from a web server.
-
-parse to JS object: const obj = JSON.parse('{"name":"John", "age":30, "city":"New York"}');
-
-```jsx
-**Json Marshal:将数据编码成json字符串
-jsonstu,err := json.Marshal(stu)
-if err!=nil{
- fmt.Println("生成json字符串错误")
- }
-
-{"name":"张三","Age":18,"HIgh":true,"class":{"Name":"1班","Grade":3}}**
-```
-
-JSON、BSON 等格式进行序列化及对象关系映射(Object Relational Mapping,简称 ORM)系统都会用到结构体标签,这些系统使用标签设定字段在处理时应该具备的特殊属性和可能发生的行为。这些信息都是静态的,无须实例化结构体,可以通过反射获取到。
-
-```perl
-Type int `json: "type" id:"100"` //ERror:json后多了个空格,无法解析
-{//reflect 获取字段tag
-var u User
- t:=reflect.TypeOf(u)
- for i:=0;iServeHTTP函数->ServeMux的Handler方法->Index函数
-```
-
-这就是整个一条请求处理链,现在我们明白了`net/http`里对HTTP请求的原理。
-
-`net/http`的默认路径处理HTTP请求的时候,会发现很多不足,比如:
-
-1. 不能单独的对请求方法(POST,GET等)注册特定的处理函数
-2. 不支持Path变量参数
-3. 不能自动对Path进行校准
-
-所以我们得自己写一个处理请求的router
-
-## **GIN**
-
-Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin.只要你的路由带有参数,并且这个项目的API数目超过了10,就尽量不要使用`net/http`中默认的路由。在Go开源界应用最广泛的router是httpRouter,很多开源的router框架都是基于httpRouter进行一定程度的改造的成果。关于httpRouter路由的原理,会在本章节的router一节中进行详细的阐释。
-
-**Features**
-
-**Fast**
-
-Radix tree based routing, small memory foot print. No reflection. Predictable API performance.
-
-**Middleware support**
-
-An incoming HTTP request can be handled by a chain of middlewares and the final action. For example: Logger, Authorization, GZIP and finally post a message in the DB.
-
-**Crash-free**
-
-Gin can catch a panic occurred during a HTTP request and recover it. This way, your server will be always available. As an example - it’s also possible to report this panic to Sentry!
-
-**JSON validation**
-
-Gin can parse and validate the JSON of a request - for example, checking the existence of required values.
-
-**Routes grouping**
-
-Organize your routes better. Authorization required vs non required, different API versions… In addition, the groups can be nested unlimitedly without degrading performance.
-
-**Error management**
-
-Gin provides a convenient way to collect all the errors occurred during a HTTP request. Eventually, a middleware can write them to a log file, to a database and send them through the network.
-
-**Rendering built-in**
-
-Gin provides an easy to use API for JSON, XML and HTML rendering.
-
-再来回顾一下文章开头说的,开源界有这么几种框架,第一种是对httpRouter进行简单的封装,然后提供定制的中间件和一些简单的小工具集成比如gin,主打轻量,易学,高性能。第二种是借鉴其它语言的编程风格的一些MVC类框架,例如beego,方便从其它语言迁移过来的程序员快速上手,快速开发。还有一些框架功能更为强大,除了数据库schema设计,大部分代码直接生成,例如goa。不管哪种框架,适合开发者背景的就是最好的
-
-• routes group是为了管理一些相同的URL
-
-`Gin`提供了`Any`方法,可以一次性注册以上这些`HTTP Method`方法。如果你只想注册其中某两个、或者三个方法,`Gin`就没有这样的便捷方法了,不过`Gin`为我们提供了通用的`Handle`方法,我们可以包装一下使用。
-
-```
-funcHandle(r*gin.Engine, httpMethods []string, relativePathstring, handlers...gin.HandlerFunc) gin.IRoutes {
-var routes gin.IRoutes
-for _, httpMethod:=range httpMethods {
- routes = r.Handle(httpMethod, relativePath, handlers...)
- }
-return routes
-}
-```
-
-如果对于这些请求的URL我们一个个去注册,比如张三用户和李四用户,分别注册一个对应的GET方法,是很繁琐的,所以Gin为我们提供了URL路由的**模糊匹配**,比如URL路径中的参数
-
-/users/*id 表示模糊匹配id
-
-`/users/:id`这种匹配模式是精确匹配的,只能匹配一个k
-
-重定向的根本原因在于`/users`没有匹配的路由,但是有匹配`/users/`的路由,所以就会被重定向到`/users/`。得益于`gin.RedirectTrailingSlash`
- 等于`true`
-
-URL**获取查询参数**
-
-`GetQuery`来代替`Query`方法。
-
-`GetQuery`方法的底层实现其实是`c.Request.URL.Query().Get(key)`,通过`url.URL.Query()`来获取所有的参数键值对。
-
-```
-unc (c*Context)GetQueryArray(keystring) ([]string,bool) {
- c.getQueryCache()//缓存所有的键值对
-if values, ok:= c.queryCache[key]; ok&& len(values) > 0 {
-return values,true
- }
-return []string{},false
-}
-
-func (c*Context)getQueryCache() {
-if c.queryCache==nil {
- c.queryCache = c.Request.URL.Query()
- }
-}
-
-```
-
-从以上的实现代码中,可以看到最终的实现都在`GetQueryArray`方法中,找到对应的`key`就返回对应的`[]string`,返回就返回空数组。
-
-这里`Gin`进行了优化,通过缓存所有的键值对,提升代码的查询效率。这里缓存的`queryCache`本质上是`url.Values`,也是一个`map[string][]string`。提高了GIN的性能
-
-`GetQuery`方法的底层实现其实是`c.Request.URL.Query().Get(key)`,通过`url.URL.Query()`
-来获取所有的参数键值对
-
-![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ab28a5ec-65a2-4ed5-bfc7-f4c972dc4c8a/Untitled.png)
-
-通过`gin.Default()`生成的`gin.Engine`其实包含一个`RouterGroup`(嵌套组合),所以它可以用`RouterGroup`的方法。
-
-`Group`方法又生成了一个`*RouterGroup`,这里最重要的就是`basePath`,它的值是`group.calculateAbsolutePath(relativePath)`
-
-1.****gin 数据解析和绑定****
-
-客户端传参,后端接收并解析到结构体。
-
-### Lifecycle
-
-Gin-Context 实现了对request和response的封装,是Gin的核心实现之一,学习使用gin框架就是学习使用Context包的过程。内部封装了request 和response 过程中的数据。
-
-**框架启动过程**
-
-**1. New 一个Engine实例**
-
-`app **:=** gin.**Default**()`
-
-**2. 注册添加路由、中间件**
-
-你可能会疑惑,为什么这里的路由处理函数要接受一个 gin.Context 类型的参数,是在何时传入的?
-
-**Engine结构体本身发挥的核心功能就是路由处理。**
-
-`app.**GET**("/ping", **func**(c *****gin.Context) {
- c.**JSON**(200, gin.H{
- "message": "pong",
- })
- })`
-
-### 中间件
-
-非业务的需求都是在http请求处理前做一些事情,并且在响应完成之后做一些事情。我们有没有办法使用一些重构思路把这些公共的非业务功能代码剥离出去呢?回到刚开头的例子,我们需要给我们的`helloHandler()`
-增加超时时间统计,我们可以使用一种叫`function adapter`的方法来对`helloHandler()`
-进行包装:
-
-中间件是一种业务无关的,在正常的的业务handler处理前后的,独立的逻辑处理片段,嵌入在 HTTP 的请求和响应之间。它可以获得 `Echo#Context`
- 对象用来进行一些特殊的操作, 比如记录每个请求或者统计请求数。
-
-eg. 一个http请求过程来窥视一番。
-
-当你在浏览器中输入一个网址时,它会通过 DNS 解析到目标服务注册的公网IP地址
-
-请求到达目标服务的 web 反向代理服务器 Tengine 之后,经过一定的过滤转发到目标服务A上
-
-服务A通过 RPC框架 Dubbo 请求服务B的结果做中间计算,并且从 Tair 缓存中读取计算因子,计算结果
-
-服务A接着使用 Druid 通过 TDDL 写入计算结果到 MySQL Master 节点然后返回结果
-
-异步过程中 Canal 通过模拟 Binlog 主从复制的原理,迅速将这条 Binlog 消费并下发到消息队列 RocketMQ
-
-服务C通过 RocketMQ 消费到事件之后,通过配置中心 ConfigServer 拉取到的策略进行对应策略的事件处理。
-
-这个过程中我们使用了一系列的中间件来协同各个微服务完成整个流程,如web反向代理服务器 Tengine、RPC框架 Dubbo、缓存 Tair、[连接池](https://www.zhihu.com/search?q=%E8%BF%9E%E6%8E%A5%E6%B1%A0&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A1663627873%7D) Driud、数据库代理层 TDDL、Binlog 同步工具 Canal、消息队列 RocketMQ、配置中心 ConfigServer。
-
-中间件可以理解为洋葱穿透。
-
-ZooKeeper是一个[分布式](https://so.csdn.net/so/search?q=%E5%88%86%E5%B8%83%E5%BC%8F&spm=1001.2101.3001.7020)协调服务,它的主要作用是为分布式系统提供一致性服务,提供的功能包括:配置维护、命名服务、分布式同步、组服务等。Kafka的运行依赖ZooKeeper。
-
-*Apache Flink 是一个框架和分布式处理引擎,用于对无界和有界*数据流进行状态计算。Flink 被设计为在*所有常见的集群环境中运行,以内存中的速度*和*任何规模*执行计算。
-
-### kafka
-
-- Kafka 结合了三个关键功能,因此您可以通过一个经过实战考验的解决方案实现端到端的事件流用例:
-发布(写入)和订阅(读取)事件流,包括从其他系统持续导入/导出数据。
-根据需要持久可靠地存储事件流。
-在事件发生时或回顾性地处理事件流。
-
-所有这些功能都以分布式、高度可扩展、弹性、容错和安全的方式提供。 Kafka 可以部署在裸机硬件、虚拟机和容器上,也可以部署在本地和云端。您可以在自行管理 Kafka 环境和使用各种供应商提供的完全托管服务之间进行选择。
-
-服务器:Kafka 作为一个或多个服务器的集群运行,可以跨越多个数据中心或云区域。其中一些服务器形成存储层,称为代理。其他服务器运行 Kafka Connect 以将数据作为事件流持续导入和导出,以将 Kafka 与您现有的系统(如关系数据库以及其他 Kafka 集群)集成。为了让您实现关键任务用例,Kafka 集群具有高度可扩展性和容错性:如果其中任何一个服务器出现故障,其他服务器将接管它们的工作,以确保持续运行而不会丢失任何数据。
-
-客户端:它们允许您编写分布式应用程序和微服务,以并行、大规模和容错方式读取、写入和处理事件流,即使在网络问题或机器故障的情况下也是如此。 Kafka 附带了一些这样的客户端,这些客户端由 Kafka 社区提供的数十个客户端进行了扩充:客户端可用于 Java 和 Scala,包括更高级别的 Kafka Streams 库,用于 Go、Python、C/C++ 和许多其他编程语言以及 REST API。
-
-Events are organized and durably stored in **topics**. Very simplified, a topic is similar to a folder in a filesystem, and the events are the files in that folder. An example topic name could be "payments". Topics in Kafka are always multi-producer and multi-subscriber: a topic can have zero, one, or many producers that write events to it, as well as zero, one, or many consumers that subscribe to these events. Events in a topic can be read as often as needed—unlike traditional messaging systems, events are not deleted after consumption. Kafka's performance is effectively constant with respect to data size, so storing data for a long time is perfectly fine.
-
-Topics are **partitioned**, meaning a topic is spread over a number of "buckets" located on different Kafka brokers. This distributed placement of your data is very important for scalability because it allows client applications to both read and write the data from/to many brokers at the same time. When a new event is published to a topic, it is actually appended to one of the topic's partitions. Events with the same event key (e.g., a customer or vehicle ID) are written to the same partition, and Kafka [guarantees](https://kafka.apache.org/documentation/#semantics) that any consumer of a given topic-partition will always read that partition's events in exactly the same order as they were written.
-
-broker, cluster
-
-c.Next() 之前的操作是在 Handler 执行之前就执行(Authetication;c.Next() 之后的操作是在 Handler 执行之后再执行(总结处理,比如格式化输出、响应结束时间,响应时长计算之类的. if 想要计算这个请求到底花费了多久,就需要先执行下面的中间件和handler,等他们都完成后,再回到 `PrintResponse`
- 这个中间件里,进行计时就ok了。 当你打印返回的response body的时候,也是一样的道理,中间件都会比handler先执行,但是没有handler,中间件怎么拿到 response body,这时候就要用到 c.Next() 先去 执行 余下的中间件和 handler,然后再回到我这个中间件里面
-
-**3. 启动Gin框架**
-
-**Run 本质就是将 注册的路由信息engine 绑定到一个 http.Server,然后开始开始监听并处理请求**
-
-`app.**Run**()
-
-**func** (engine *****Engine) **Run**(addr **...string**) (err **error**) {
- **defer** **func**() { **debugPrintError**(err) }()
-
- address **:=** **resolveAddress**(addr)
- **debugPrint**("Listening and serving HTTP on %s\n", address)
- err = http.**ListenAndServe**(address, engine)
- **return**}`
-
-进入到 golang net/http包 (ListenandServe) ,Hanlder 是一个实现了ServeHTTP方法的类型
-
-API调用过程
-
-**当监听到请求时,gin框架就会衍生一个Context,为它添加上请求相关参数。开一个go程进行处理。**
-
-**1. net\http 创建连接,握手**
-
-****2. gin 框架处理请求核心:ServeHTTP****
-
-```
-func (engine*Engine)ServeHTTP(w http.ResponseWriter, req*http.Request) {
- c:= engine.pool.Get().(*Context)
- c.writermem.reset(w)
- c.Request = req
- c.reset()
- engine.handleHTTPRequest(c)
- engine.pool.Put(c)
-}
-```
-
-## SEO
-
- - Search engine optimization: the process of making your site better for search engines.
-
-如果您的网站不在 Google 的索引中
-
-虽然 Google 可抓取数十亿个网页,但难免也会遗漏部分网站。造成抓取工具遗漏网站的常见原因如下:
-
-- 此网站未与网络上的其他网站紧密关联
-- 您刚刚推出新的网站,Google 还没来得及抓取
-- 网站的设计致使 Google 难以有效抓取其中的内容
-- Google 在尝试抓取网站时收到了错误消息
-- 您的政策阻止 Google 抓取网站
-
-元描述标记很重要,因为 Google 可能会在搜索结果中将其用作网页的摘要。请注意,我们说的是“可能”,因为如果网页中有一段可见文本能很好地匹配用户查询,那么 Google 也可能会选择使用这段文本。最好为每个网页添加元描述标记,以防 Google 找不到要在摘要中使用的恰当文本。
-
- provider: a function that can produce a value. These functions are ordinary Go code.
->
-
-通过提供`provider`函数,让`wire`知道如何产生这些依赖对象。`wire`根据我们定义的`injector`函数签名,生成完整的`injector`函数,`injector`函数是最终我们需要的函数,它将按**依赖顺序调**用`provider`。
-
-injector中声明wire.build,`wire_gen.go`创建后,您可以通过运行重新生成它`[go generate](https://blog.golang.org/generate)`。****
-
-Bind 函数的作用是为了让接口类型参与 wire 的构建过程。wire 的构建依靠的是参数的类型来组织代码,所以接口类型天然是不支持的。Bind 函数通过将接口类型和实现类型绑定,来达到依赖注入的目的。
-
-eg.
-
-`type Fooer interface{
- HelloWorld()
-}
-type Foo struct{}
-func (f Foo)HelloWorld(){}
-
-var bind = wire.Bind(new(Fooer),new(Foo))`
-
-这样将 bind 传入 NewSet 或 Build 中就可以将 **Fooer 接口和 Foo 类型绑定**。
-
-这里需要特别注意,如果是 *Foo 实现了 Fooer 接口,需要将最后的 new(Foo) 改成 new(*Foo)
-
-```
-var Set = wire.NewSet(
- provideMyFooer,
- wire.Bind(new(Fooer), new(*MyFooer)),
- provideBar)
-```
-
-第一个参数`wire.Bind`是指向所需接口类型的值的指针,第二个参数是指向实现接口的类型的值的指针。任何包含接口绑定的集合也必须在提供具体类型的同一集合中具有提供者。
-
-***属性自动注入***
-
-`wire.Struct`函数构造一个结构类型并告诉注入器应该注入哪些字段。注入器将使用字段类型的提供程序填充每个字段。对于生成的结构类型`S`,`wire.Struct`同时提供`S`和`*S`
- 提供一项额外的灵活性: 它能适应指针与非指针类型,根据需要自动调整生成的代码。
-
-有时我们不需什么特定的初始化工作, 只是简单地创建一个对象实例, 为其指定属性赋值,然后返回。当属性多的时候,这种工作会很无聊。
-
-`wire.Struct` 可以简化此类工作, 指定属性名来注入特定属性:
-
-```
-//. type S struct {
-// MyFoo *Foo
-// MyBar *Bar
-// }
-// var Set = wire.NewSet(wire.Struct(new(S), "MyFoo")) -> inject only S.MyFoo
-// var Set = wire.NewSet(wire.Struct(new(S), "*")) -> inject all fields
-```
-
-依赖注入的过程。
-
-### **1. 定义 Injector**
-
-创建`wire.go`文件,定义下你最终想用的实例初始化函数例如`initApp`(即 Injector),定好它返回的东西`*App`,在方法里用`panic(wire.Build(NewRedis, SomeProviderSet, NewApp))`罗列出它依赖哪些实例的初始化方法(即 Provider)/或者哪些组初始化方法(ProviderSet)
-
-### **2. 定义 ProviderSet(如果有的话)**
-
-ProviderSet 就是一组初始化函数,是为了少写一些代码,能够更清晰的组织各个模块的依赖才出现的。也可以不用,但 Injector 里面的东西就需要写一堆。 像这样 `var SomeProviderSet = wire.NewSet(NewES,NewDB)`定义 ProviderSet 里面包含哪些 Provider
-
-### **3. 实现各个 Provider**
-
-Provider 就是初始化方法,你需要自己实现,比如 NewApp,NewRedis,NewMySQL,GetConfig 等,注意他们们各自的输入输出
-
-### **4. 生成代码**
-
-执行 wire 命令生成代码,工具会扫描你的代码,依照你的 Injector 定义来组织各个 Provider 的执行顺序,并自动按照 Provider 们的类型需求来按照顺序执行和安排参数传递,如果有哪些 Provider 的要求没有满足,会在终端报出来,持续修复执行 wire,直到成功生成`wire_gen.go`文件。接下来就可以正常使用`initApp`来写你后续的代码了。
-
-## 协程**Coroutine**
-
-在目前的绝大多数语言中,都是通过加锁等线程同步方案来解决这一困难问题,Go语言却另辟蹊径,它将共享的值通过Channel传递(实际上多个独立执行的线程很少主动共享资源)。在任意给定的时刻,最好只有一个Goroutine能够拥有该资源。数据竞争从设计层面上就被杜绝了。
-
-```
-//go 关键字放在方法调用前新建一个 goroutine 并让他执行方法体
-go GetThingDone(param1, param2);
-
-//上例的变种,新建一个匿名方法并执行
-go func(param1, param2) {
-}(val1, val2)
-
-//直接新建一个 goroutine 并在 goroutine 中执行代码块
-go {
- //do someting...
-}
-```
-
-**因为 goroutine 在多核 cpu 环境下是并行的。如果代码块在多个 goroutine 中执行,我们就实现了代码并行。那么问题来了,怎么拿到并行的结果呢?这就得用 channel 了。**
-
-> goroutine(go协程)是由Go runtime管理的轻量级线程。
->
-
-这句话表明了协程是用户态,因为是由Go runtime管理,而非OS内核管理
-
-并发编程中最常见的例子就是生产者消费者模式,该模式主要通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。简单地说,就是生产者生产一些数据,然后放到成果队列中,同时消费者从成果队列中来取这些数据。这样就让生产消费变成了异步的两个过程。当成果队列中没有数据时,消费者就进入饥饿的等待中;而当成果队列中数据已满时,生产者则面临因产品挤压导致CPU被剥夺的下岗问题。
-
-### Channel
-
-它包括三种类型的定义。可选的`<-`
-代表channel的方向。如果没有指定方向,那么Channel就是双向的,既可以接收数据,也可以发送数据。
-
-它包括三种类型的定义。可选的`<-`
-代表channel的方向。如果没有指定方向,那么Channel就是双向的,既可以接收数据,也可以发送数据。
-
-chan T // 可以接收和发送类型为 T 的数据
-chan<- float64 // 只可以用来发送 float64 类型的数据
-<-chan int // 只可以用来接收 int 类型的数据
-
-`<-`总是优先和最左边的类型结合。(The <- operator associates with the leftmost chan possible)
-
-创建管道`c := make(chan int)`
-
-默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。
-
-发动和接收数据应当在并行线上,而不能是串行的,因为发送和接收都会阻塞,如果串行,就会死锁(就是一个一直阻塞在那等对端),但不用为此操心,因为go在执行时候(编译会通过)会报错
-
-select和case的组合可以使哪个管道就绪(对端已阻塞),就读取该管道数据并执行相应case的代码块。
-
-官网译: select 会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。当多个都准备好的时候,会随机选择一个。
-
-- 碰着I/O访问,阻塞了后面所有的计算。空着也是空着,老大就直接把CPU切换到其他进程,让人家先用着。当然除了I\O阻塞,还有时钟阻塞等等。一开始大家都这样弄,后来发现不成,太慢了。为啥呀,一切换进程得反复进入内核,置换掉一大堆状态。进程数一高,大部分系统资源就被进程切换给吃掉了。后来搞出**线程**的概念,大致意思就是,这个地方阻塞了,但我还有其他地方的逻辑流可以计算,这些逻辑流是共享一个地址空间的,不用特别麻烦的切换页表、刷新TLB,只要把寄存器刷新一遍就行,能比切换进程开销少点。
-- 如果连时钟阻塞、 线程切换这些功能我们都不需要了,自己在进程里面写一个逻辑流调度的东西。那么我们即可以利用到并发优势,又可以避免反复系统调用,还有进程切换造成的开销,分分钟给你上几千个逻辑流不费力。这就是**用户态线程**。
-- 从上面可以看到,实现一个用户态线程有两个必须要处理的问题:一是碰着阻塞式I\O会导致整个进程被挂起;二是由于缺乏时钟阻塞,进程需要自己拥有调度线程的能力。如果一种实现使得每个线程需要自己通过调用某个方法,主动交出控制权。那么我们就称这种用户态线程是协作式的,即是。**协程**
-
-![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/103c64c8-8d67-412b-b4ee-32e84ba00440/Untitled.png)
-
-非对称加密是为了解决对称加密无法解决的问题。例如,怎么才能保证使用密钥的人是可信的呢?
diff --git a/source/_posts/Programming language.md b/source/_posts/Programming language.md
index 3b8b11f..b915748 100644
--- a/source/_posts/Programming language.md
+++ b/source/_posts/Programming language.md
@@ -1218,7 +1218,6 @@ ProTable 在 antd 的 Table 上进行了一层封装,支持了一些预设,
`request` 是 ProTable 最重要的 API,`request` 会接收一个对象。对象中必须要有 `data` 和 `success`,如果需要手动分页 `total` 也是必需的。`request` 会接管 `loading` 的设置,同时在查询表单查询和 `params` 参数发生修改时重新执行。同时 查询表单的值和 `params` 参数也会带入。
-iauth result success不符合param对successs的要求
### Vue