2024
2024 耳鸣差不多已经控制住了 开始测试乳糖牛奶 ENx 已经能满足自己日常使用 开始有计划的每天读书 每天能清空当天的 todo list 开港卡投资美股 完成了一个比较复杂的模块的重写, 1w+ 行单体测试, 第一次大规模使用单体测试, 平稳上线.
2024 耳鸣差不多已经控制住了 开始测试乳糖牛奶 ENx 已经能满足自己日常使用 开始有计划的每天读书 每天能清空当天的 todo list 开港卡投资美股 完成了一个比较复杂的模块的重写, 1w+ 行单体测试, 第一次大规模使用单体测试, 平稳上线.
accord, grant https://www.tjxz.cc/11067 give、accord、award、confer、grant、endow、present与bestow这组动词均含有“给予、赠给”之意。 give 最普通用词,泛指将自己的东西给予他人,所给予的东西,物质或非物质的均可。 She gave us a set of saucepans as a wedding present. 她送我的结婚礼物是一套深平底锅。 Can you give me a date for another appointment? 我能再和您约个时间吗? They never gave me a chance/choice. 他们从来就没给过我机会/选择余地。 Has the director given you permission to do that? 主任允许你那样做了吗? accord 侧重所给予的是应得的或是恰当的,或是因某种原因而适于接受的。 The massed crowds of supporters accorded him a hero’s welcome. 蜂拥云集的支持者们给予他英雄般的欢迎。 Certainly in our society teachers don’t enjoy the respect that is accorded to doctors and lawyers. 的确,在我们这个社会中,老师没有得到像医生和律师所受到的那种尊敬。 ...
gdb GDB是Linux下非常好用且强大的调试工具。GDB可以调试C、C++、Go、java、 objective-c、PHP等语言。对于一名Linux下工作的c/c++程序员,GDB是必不可少的工具 readelf -S main|grep debug # 查看寄存器的值 i registers # 以上输出不包括浮点寄存器和向量寄存器的内容。使用“i all-registers”命令,可以输出所有寄存器的内容 i all-registers # 打印单个寄存器的值,可以使用“i registers regname”或者“p $regname”,例如: i registers rsi # 用 gdb 调试 hello gdb ./hello # 在 _start 函数处添加一个断点 (gdb) b _start # run (gdb) r # 显示汇编代码, => 表示下一步要执行的行 (gdb) disassemble /m _start # 逐指令往后运行 (gdb) stepi # 查看寄存器的值 (gdb) i registers
godaddy 买域名 在Godaddy搜索某个关键字, 比如 wiloon 在列表里找一个喜欢的或者价格低的, 有很多首年1.x$的, 比如 wiloon.online 购买并支付, 支付方式可以用 paypal. cloudflare> add site> free> 添加解析记录 type: A name: @ ipv4 address: proxy: false (DNS only) Save (保存设置然后等 DNS 生效) 配置域名解析, 配置到某一个 vps, DNS> Nameservers> Change Nameservers
sketch, 素描 素描的学习过程: 1石膏几何体. 2素描静物 3人物 学习石膏几何体和静物会解决的问题: 基本的绘画技巧、比例、构图、黑白灰、空间、虚实等,建立绘画思维和观察方法。 三:为什么要先学素描? 搞清楚素描学习的目地 素描学习是对绘画意识和能力的培养,不要只是去学习套路的技法,不然就失去了素描学习的意义。 说的简单点,素描会解决形,色,空间,体积,基本的透视关系,画面关系等问题。所有的视觉艺术(摄影,电影,设计,绘画)都会涉及到这些,重要性显而易见。 有很多学生一开始就会给老师说,我想学水彩,想学油画,不学素描太枯燥了。 如果你是想真学到东西,不了解素描你可能永远都是外行,除了我们的中国画,西方历史上的名家们谁没画过素描呢?只是有些艺术流派,有些艺术创作风格,特别是现代艺术,可能有些不需要很强的素描的基本功。但是他们都是画过大量的素描。 https://zhuanlan.zhihu.com/p/321783268
ubuntu install k8s ubuntu 24.04 安装 单节点 k8s sudo swapoff -a sudo sed -i '/swap/s/^/#/' /etc/fstab sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl # 安装 containerd, 我之前 已经 安装 了, 跳过这步 K8S_VER="v1.33.4" ARCH="amd64" # 下载二进制文件 curl -LO "https://dl.k8s.io/release/${K8S_VER}/bin/linux/${ARCH}/kubectl" curl -LO "https://dl.k8s.io/release/${K8S_VER}/bin/linux/${ARCH}/kubeadm" curl -LO "https://dl.k8s.io/release/${K8S_VER}/bin/linux/${ARCH}/kubelet" # 添加可执行权限 chmod +x kubectl kubeadm kubelet # 移动到系统 PATH 目录 sudo mv kubectl kubeadm kubelet /usr/local/bin/ /etc/systemd/system/kubelet.service [Unit] Description=kubelet: The Kubernetes Node Agent Documentation=https://kubernetes.io/docs/ After=containerd.service Wants=containerd.service [Service] ExecStart=/usr/local/bin/kubelet Restart=always StartLimitInterval=0 RestartSec=10 [Install] WantedBy=multi-user.target sudo systemctl daemon-reload sudo systemctl enable kubelet VERSION="v1.34.0" curl -LO https://github.com/kubernetes-sigs/cri-tools/releases/download/${VERSION}/crictl-${VERSION}-linux-amd64.tar.gz sudo tar -C /usr/local/bin -xzvf crictl-${VERSION}-linux-amd64.tar.gz crictl --version # 如果之前 有过 初始化 sudo kubeadm reset -f sudo systemctl stop kubelet # 初始化集群, kubeadm 会启动 kubelet 服务 sudo kubeadm init --pod-network-cidr=10.244.0.0/16 install Kubernetes 1.28.2 on Ubuntu 22.04 ...
无状态服务, stateless service https://blog.csdn.net/xiangxizhishi/article/details/79434749 一、定义 无状态服务(stateless service)对单次请求的处理,不依赖其他请求,也就是说,处理一次请求所需的全部信息,要么都包含在这个请求里,要么可以从外部获取到(比如说数据库),服务器本身不存储任何信息 有状态服务(stateful service)则相反,它会在自身保存一些数据,先后的请求是有关联的 二、优劣 有状态服务常常用于实现事务(并不是唯一办法,下文有另外的方案)。举一个常见的例子,在商城里购买一件商品。需要经过放入购物车、确认订单、付款等多个步骤。由于HTTP协议本身是无状态的,所以为了实现有状态服务,就需要通过一些额外的方案。比如最常见的session,将用户挑选的商品(购物车),保存到session中,当付款的时候,再从购物车里取出商品信息 有状态服务可以很容易地实现事务,所以也是有价值的。但是经常听到一种说法,即server要设计为无状态的,这主要是从可伸缩性来考虑的。如果server是无状态的,那么对于客户端来说,就可以将请求发送到任意一台server上,然后就可以通过负载均衡等手段,实现水平扩展。如果server是有状态的,那么就无法很容易地实现了,因为客户端需要始终把请求发到同一台server才行,所谓“session迁移”等方案,也就是为了解决这个问题 三、session和cookie 基于session和cookie都可以实现事务,可以认为,session是有状态的,而cookie是无状态的 四、无状态实现事务的方法 并不是一定要用有状态服务才能实现事务,本文提供另外的几种方案作为参考 举一个多次提交的场景作为例子:用户需要提交很多数据,分为2个页面提交 这里就涉及到2次http请求,第一次提交字段1、2、3,第二次提交字段4、5、6 用session很容易实现这个需求,server只需要将第一次提交的数据,保存在session里,然后返回第2个表单作为相应;然后取出第一次提交的数据,和第二次提交的数据汇聚以后,一起存入数据库即可 不用session同样也可以实现,server接收到第一次请求以后,将数据作为隐藏元素,放在第2个表单里返回;这样用户第2次提交的时候,就隐含地再次提交了第一次的数据;server将所有数据存入数据库 用HTML5,则还可以进一步优化,client可以将第一次提交的数据,保存在sessionStorage里 用cookie也是类似的道理,同样可以实现,但是不太好 总的来说,3种替代方案(隐藏表单元素、sessionStorage、cookie)都避免了在server端暂存数据,从而实现了stateless service。本质上,这3种方案的请求里,都包含了所有必须的数据,符合本文一开始的定义 五、将有状态服务转换成无状态服务 根据本文一开始的定义,除了将所有信息都放在请求里之外,还有另外一种方法可以实现无状态服务,即将信息放在一个单独可共享的地方,独立于server存在 比如,同样还是采取session的方式,在服务端保存数据,减少每次client请求传输的数据量(节省流量);但是将session集中存放,比如放在单独的session层里。这种情况下,server同样是无状态的,可以做水平扩展 六、无状态类 引申一下,JAVA里有一种类的设计,可以称为无状态类。这种类的特征是只有方法没有字段,在三层架构(展现层、逻辑层、持久层)里,逻辑层经常可以看到这种类 我觉得无状态类和stateless server在思想上是一样的,这个类本身是没有状态的,所以当外部要调用它的方法时,需要在方法参数中传来所需的所有信息,不依赖该类自身的状态(字段值),在并发环境下,可以避免多线程带来的副作用 七、总结 有状态服务可以比较容易地实现事务,在不需要考虑水平扩展时,是比较好的选择 无状态服务的优势在于可以很方便地水平伸缩,但是在实现事务时,需要做一些额外的动作 可以通过剥离session等方法,将一个有状态服务,转换成无状态服务 关于这个话题,下面这个链接也不错: http://stackoverflow.com/questions/4495950/how-do-stateless-servers-work
`— title: Ansible author: “-” date: 2015-10-14T07:14:28+00:00 url: Ansible categories: inbox tags: reprint Ansible https://linuxtoy.org/archives/hands-on-with-ansible.html 最近纠结于在 Puppet、Chef、SaltStack、Ansible 等一干配置管理工具中如何选择。考虑到一旦开始没有选好,以后更改又是一堆麻烦事,所以就稍微有些慎重。 Puppet 和 SaltStack 我曾用过,但不是十分符合预期,所以先行排除。至于 Chef,虽然老早就听说过,但却一直没有找到机会尝试。翻了翻文档,Chef 跟 Puppet 及 SaltStack 也是一样采用服务端/客户端模式,对于在现有一定数量的机器上部署仍然有 些麻烦。最后落单到 Ansible 上。经过对 Ansible 的把玩,我感觉 Ansible 于我比较相投。我喜欢 Ansible 的方面包括: 充分利用现有设施。使用 Ansible 无需安装服务端和客户端,只要 SSH 即可。这意味着,任何一台装有 Ansible 的机器都可以成为强大的管理端。我觉得,这种去中心化的思路显得更为灵活。可能有人会担心 SSH 的效率,Ansible 的并行执行及加速模式或许可以打消你的顾虑。 使用简单,快速上手相当容易。我在用 Puppet 之前,就没少花时间钻研它。想想吧,我们使用这类自动化管理工具不就是想把自己从重复的、复杂的事情中解放出来么?为了简化一件事,而沉入另一件复杂的事,是不是有些不划算?从我的体验来看,Ansible 上手十分快,用 Ad-Hoc 可以应付简单的管理任务,麻烦点的也可以定义 Playbook 文件来搞定。 采用人类易读的格式。Ansible 的主机定义文件使用 INI 格式,支持分组,能够指定模式;此外也能动态生成,这对管理云主机应当很有用。而 Playbook 则是 YAML 格式,我觉得它比 Puppet 的 DSL 要易读易写多了。 能够使用你熟悉的语言来编写模块。虽然 Ansible 是使用 Python 开发的,但它不会将你限制到某种具体的编程语言,Bash、Python、Perl、Ruby 等等都可以,你擅长什么就用什么。 一言以蔽之,Ansible 背后的简单化哲学深得我心。这也比较符合我选择软件的一贯原则。可能还有人会比较关心目前 Ansible 都有谁在用。毕竟,榜样的力量是无穷。Puppet 不正是因为 Google 在用而吸引了不少眼球么?据我所知,当前使用 Ansible 较为知名的用户包括 Fedora、Rackspace、Evernote 等等。 ...
分组 首先我们来了解下分组的概念。所谓分组,就是将一个数据包分成一个个更小的数据包。例如对于一个10GB的数据包,总不可以一次性发送过去吧,而是把它分成若干个小的数据包发送过去。 文件头一般是一些说明性数据,例如源地址和目标地址,数据类型等。数据部分就是真正要传达给对象的内容 电路交换 所谓交换,指的就是服务器与服务器之间的数据交换。数据传输交换的方式有几种,而电路交换便是其中的一种。 假如A和B之间要进行通信,我们就假设A要和E打个电话吧。当A输入E的电话号码,开始拨号之后,那么服务器要做的第一件事就是根据E的电话号码找到E在哪里,由于A通往E的路径有多条,会根据某种算法找到E之后,建立一条通路,然后进行数据的传输。 我们假设选的路径是A–>D—>E 找到一条通往E的路径并建立会话的过程中,我们称之为电路交换的第一阶段—建立连接。之后A和E在通话的过程中会始终霸占着这条路径,数据传输的过程称为电路交换的第二阶段—数据传输。 电路交换的第三阶段,也就是最后一个阶段—释放连接。A和B只要有一方挂了电话,那便了开始释放连接。 传输例题图: 在这个过程中,新建连接需要花销一定的额外时间 (想象你打电话的时候是不是出现正在拨号的字眼) ,释放连接也会花销一些额外的时间。 那么,电话交换的过程中,数据需要分组来传送吗? 答是不用的,因为电话交换的过程中,A和B两个人始终霸占着一条通信电路,他们每说一句话,都会实时被对方获取,因此数据是不用分组的。 从这也可以看出,电路交换的方式,在数据的传输上是比较高效、实时的,只要A一发出数据,E立马就能收到了,这也是为什么我们的电话通信使用的是电路交换的方式。 但由于一直霸占着这条路径,假如霸占的过程中A与E都在沉默不说话,那么将是对这条路径的极大浪费。因此,电路连接的方式资源的利用率是比较低的。 而且,如果你通话的时间超级短,可能花在新建连接的时间比通话的时间还要长,这就更加难受了。 稍微总结一下 电路连接的三个阶段: 建立连接。 数据传输。 释放连接。 优点: 传输速度快、高效。 实时。 缺点: 资源利用率低。 新建连接需要占据一定的时间,甚至比通话的时间还长。 分组交换 从名字分组字眼,我们就可以知道,这种方式数据包是分组成更小的数据包进行传输的。分组交换的数据传输过程和电路交换不一样,分组交换采取存储转发传输的机制。我们下面还是以A给E传输数据作为例子来讲解。 假如A要给E发送一个数据包P,但这个数据包有点大,需要分成三组,例如分成p1,p2,p3三个更小的数据包。 这时A给E传输数据不需要新建连接这个过程,即不需要寻找一个通往E的路径。而且A直接把小的数据包丢给附近的路由器,然后A就不管了,例如A把p1丢给了B,这个时候A就不在去管p1的,当B收到p1这个完整的小数据包之后,B再丢给E。 但是A不一定都会把剩下的数据包都丢给B,有可能会把其他的数据包p2丢给C,之后再把p3丢给D,然后C和D在转发丢给E。这些都是不确定的,会根据某种算法的选择路由器。 这里有一个关键词存储,就是说,B必须收到完整的p1数据包后才能进行转发,这也不难理解,因为p1数据包包含E的地址,如果不是完整的数据包,B也不知道该发给谁啊。 示例图: 从电路交换的机制我们可以看出如下的一些问题: 由于A把数据包丢给B之后就不管了,B什么时候会把p1转发出去,谁也不知道,而且可能B会绕几个圈子再发给E也是有可能的。因为,电路交换的机制在数据传输方面不具有实时性。 而且,很有可能会有很多路由器把数据包丢给B,这个时候就会造成通信阻塞,这时可能p1只能排队等待B来发送。 由于B路由器的容量是有限的,如果有太多的数据包丢给它,它可能会容纳不下,这时候就可能会出现丢包的情况。 再者,由于p1,p2,p3数据包都有文件头,里面都包含了A和E的一些信息,当然还有其他的信息。可以说这些文件头有很多重复的数据,因此分组交换发送的数据具有很多的重复无用数据。 当然,分组交换还包括时延的缺点,因为B必须收到一个完整的p1才能把p1转发出去,因为这个接受存储的过程中存在时延,这种时延也成为传输时延,当然还存在传播时延和处理时延等。所谓处理时延就是每次都得检查这个数据包的文件头和决定将该数据包传输给谁。 说了电路交换的这么多缺点,那总得有优点吧? 实际上,上面的那些缺点,其实都不是什么大问题的。电路交换最主要的优点就是设计简单,资源利用率高了。 总结下分组交换 分组交换采用把一个个小的数据包存储转发传输的机制。 主要的一些缺点: 不具有实时性。 存在延时。 会造成通信阻塞。 存在无用的重复数据。 会出现丢包的情况。 致命的优点: 设计简单。 资源利用率很高。 生活中的通信选择 两种交换传输的特点决定了我们平时的电话通信使用的是电路交换,像互联网中的微信等这种不要求实时的通信用分组交换。 这也就是为什么急事的时候会打电话,因为比较实时嘛。像微信这些,有时你发个信息,可能网络不好的话,或者太多人在同时使用的话,可能你的信息要过一阵子对方才能收到。 这里可能有些人会说,分组交换为何要把数据包分成一小个来存储转发呢?一个大的数据包发过去不好吗? 假如你的一个数据包100GB,那B这个路由器就得能存100GB的容量,可是发100GB的概率是极少数的,那把路由器设计成100GB不是很浪费?这也是为什么要分组成小数据包的原因之一。 当然,还有一种报文交换的方式,就是一整个数据包存储转发的,不过这种方式使用的比较少,再此就不详细展开了。 https://zhuanlan.zhihu.com/p/44316491
“apisix” 启动etcd apisix podman run -d \ --name apisix \ -v apisix-logs:/usr/local/apisix/logs \ -v apisix-conf:/usr/local/apisix/conf \ -v /etc/letsencrypt/live/wiloon.com-0001:/usr/local/apisix/cert \ -p 9180:9180 \ -p 9080:9080 \ -p 443:9443 \ apache/apisix allow admin allow_admin: # http://nginx.org/en/docs/http/ngx_http_access_module.html#allow - 192.168.50.116/24 # If we don’t set any IP list, then any IP access is allowed by default. 修改etcd 地址 etcd: host: - "http://192.168.50.101:2379" test curl "http://192.168.50.101:9080/apisix/admin/services/" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' build apisix-dashboard buildah bud -f Dockerfile -t apisix-dashboard:1.5 . run apisix dashboard podman run -d \ --name apisix-dashboard \ -p 80:80 \ -v apisix-dashboard-config:/etc/nginx/conf.d/ \ -v /etc/localtime:/etc/localtime:ro \ nginx 设置 Upstream 创建 id 为 50 的上游信息 ...
coupling relationship, 耦合关系 耦合 不同模块之间的关系就是耦合,根据耦合程度可以分为 7 种,耦合度依次变低。 内容耦合 公共耦合 外部耦合 控制耦合 标记耦合 数据耦合, Data Coupling 非直接耦合 内容耦合 内容耦合是最紧的耦合程度,一个模块直接访问另一模块的内容,则称这两个模块为内容耦合。 公共耦合 一组模块都访问同一个全局数据结构,则称之为公共耦合。 外部耦合 一组模块都访问同一全局简单变量,而且不通过参数表传递该全局变量的信息,则称之为外部耦合。外部耦合和公共耦合很像,区别就是一个是简单变量,一个是复杂数据结构。 控制耦合 模块之间传递的不是数据信息,而是控制信息例如标志、开关量等,一个模块控制了另一个模块的功能。 从控制耦合开始,模块的数据就放在自己内部了,不同模块之间通过接口互相调用。 标记耦合 调用模块和被调用模块之间传递数据结构而不是简单数据,同时也称作特征耦合。 数据耦合, Data Coupling 调用模块和被调用模块之间只传递简单的数据项参数。相当于高级语言中的值传递。 非直接耦合 两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。耦合度最弱,模块独立性最强。 子模块无需知道对方的存在,子模块之间的联系,全部变成子模块和主模块之间的联系。 内聚 偶然内聚 逻辑内聚 时间内聚 通信内聚 顺序内聚 功能内聚 https://yanhaijing.com/program/2016/09/01/about-coupling/
dependency-library-service, 依赖 库, 服务 依赖管理: 究竟该用库还是服务? https://www.zybuluo.com/sambodhi/note/642554 http://blog.jessitron.com/2017/01/dependencies.html 依赖管理:究竟该用库还是服务? 当一个系统变得越来越复杂的时候,它必然大量的依赖外部系统和内部其他系统的服务或者库才能达成业务目标,因此,这个时候,对依赖进行有效的管理才能提升业务故障容忍度,这也是大系统小做的核心思路。 在现实生活中,要创造一个没有任何外部依赖的应用并非不可能,但也是极具挑战的。这也是为什么依赖管理对于每个软件项目都是至关重要的一部分。 通常来说,软件中的依赖关系通常包括编译时依赖、测试时依赖和运行时依赖。而从依赖形式上可以分为库依赖和服务依赖。那么问题来了,依赖管理,究竟该用库还是服务的形式呢?如何取舍? Atomist的软件工程师Jessitron Kerr,日前写了一篇文章,阐述了她对依赖管理究竟用库还是服务的看法,分析了库和服务的对比、利弊、权衡等等方面,她的思路非常清晰,经作者Jessitron Kerr授权,InfoQ翻译并整理成本文,相信对各位读者有所帮助。 以下是正文。 依赖 依赖管理,没有人愿意去想这个问题。我们只是想让这些东西能够运行。它是软件中令人讨厌、悬而未决的问题之一。 每种语言系统都声称它们有包管理器和构建工具。这关系到依赖如何工作。有些系统确实做的比其他更好一些,但是没有一个是完整的。我们真的有点“一叶障目”。 依赖很重要。图像分析中的边界,边界总是罕有很多信息;但是比起图像中的节点,边界却容易被忽视。 依赖可以是相对显式的,比如在pom.xml或package.json中声明。它们可能很难发现,例如HTTP从配置、代码、输入构成的URL调用。 依赖描述了我们如何将事物联系在一起。这意味着,它们也决定我们如何选择分解。将事务分解是我们如何扩展软件系统:在我们自己头脑中调整,也就是说,根据复杂性做出调整,而非体积。 如果仔细看看依赖,希望它们不要再这么麻烦了,也许我们可以让它更接近正确。这个话题与本文相比,还有更多内容,但它只是一个开头。 库和服务的对比 两者最大的区别:定义。 库是编译的。它们在不同的存储库(或庞大的doom(也就是monorepo)库中的目录)中分为不同的模块,它们可能由不同的公司(至少是团队)维护。通过将相同的代码编译成多个应用程序(也称为服务)来实现代码复用。 作者在此处讨论的是编译范围(compile-scope)库。至于Provided-scope,以及像.dll(姑且这么称呼)是另外一个事物,应该可能是单独的类别,但本文不讨论这些。 服务:一个应用通过网络(或者通过同一台机器上的通信接口)调用另一个应用;代码在不同进程中运行。在如何找到对方有些繁琐费时:服务发现完全是自己的事,使用DNS是最常见的解决方案。 权衡 能见度 库是显式声明的,尽管并不总是特地声明。有些东西实际上会将代码引入我的代码中,无论是作为jar还是显式地作为代码。 如果有的话,服务依赖被非正式地声明。它们可能在日志记录中被发现。如果您选择允许哪些应用访问其他应用,则可能会从安全日志中看到它们。 部署 在我看来,这个差异很重要。 库:你可以发布它要求人们升级。如果你的库是内部的,你甚至可以升级其他团队的代码中的库版本。但是,只有用户能够决定何时正式应用这个新版本。当用户选择部署升级代码时,你的新代码才会随之升级。 服务:何时升级由你决定。你将旧代码转为新代码来部署,就这样。每个使用你的服务的人,都会随时使用新代码。就这样,突然间你有了这个权利。这也意味着,你可以选择一次性运行多少服务。这点是人们感到兴奋的独立可扩展性。 如果你的库或者服务有数据支撑,那么控制代码部署意味着数据格式有很多。如果数据库仅由服务来访问,那么可以将任何必要的翻译到代码中去。如果数据库被其他人合并的库访问,那么最好保持模式兼容。 版本控制 Rich Hickey曾经说过关于库的版本控制,其中大部分,也适用于服务。这里是我的笔记。 如果将接口更改为库,那么你就拥有了不同的库。如果你为它用同一个名称,并称为新版本,那么就有了一个不同的库,它拒绝与同一个库一起编译,并为阻碍大打出手。然后你就遇到版本冲突的问题。不同的语言系统的解决方式不同。当应用声明对两个库的依赖时,会发生版本冲突,每个库都声明对同名第四个库的依赖。在JavaScript中,无论如何,我们都可以在两者中编译,它不过是代码复制。而在Java中,你很可能在给定的ClassLoader中每个类名只有一个定义,所以工具选择最新版本,希望每个人都能应付。 服务,你可以弄得很复杂,做成某种版本的路由选择;也可以在生产中,同时运行多个版本的服务。明白了吗?你将同一个服务成为两个版本,但实际上是两个不同的服务,与库相同。或者,你可以在在统一代码中支持API的多个版本。向后兼容性,是你和所有实际可工作软件的痛点。 API更改和向后兼容性 因此,你要改变用户和你的代码的交互方式。这里有一个很重要的区别:更改代码(重构、错误修复、完全重写)与要求客户为正确应用更改代码而更改是非常不同的,这是一个严重的影响。 服务:谁使用它?也许它是一个内部服务,你有通过grep来查看所有公司代码中使用你的URL的念头,对吧?你可以选择与这些团队来更改你的服务的使用。或者采取面向公众的服务。不要改变。你永远不知道谁在使用它。这确实令人沮丧。否则,你需要永久的向后兼容性。没错,你的代码将会变得令人厌恶。 库:如果你的包管理器相当好(意即不可变,如果它提供了某种库的版本,就永远将继续提供相同的下载),那么,旧版本的库仍然存在,他们可以留在生产环境中使用。你不能取消这个代码。但是,你可以令那些对版本号没有明确具体的用户心碎。这就是语义版本控制的所在。除了主版本的API的任何改变是鲁莽的,人们应该谨慎使用库的新的主版本。但如果你很友善,为它们取不同的名字,而不是假装同一个东西的不同版本号。 隔离 关于库的一个窍门:很难知道“什么是API更改?” 有了服务,事情就变得明了了:我们辨别出某些请求,并提供某种回应。 而使用库,就意味着所用的都是公用方法和公共类还有包……嗯,就像Java/Scala编码器那样,我甚至都没有特别注意我是否公开了。但是库的作者需要的话,他们会安全地变更库的内容。 服务是隔离的:你不能依赖我内部,因为你主体无法访问它们。为了显示出任何外部使用,我必须作出明确的决定。这是更为强大的模块化。这也意味着你可以用不同的语言来写它们。这是一个优势。 有几家公司在销售库。那些都是严肃的专业人士。他们必须从历史版本开始测试,在每个操作系统确保它们可以运行。他们必须清楚意识到暴露了什么,还要在很多情况下测试新版本。将它们扔在那里实用得多:即时向后兼容性是一个巨大的痛苦,但至少你知道它在哪里。 失败 库:一旦它失败,你的代码就会随之失败。它耗尽内存、进程中断。故障被同步传送,如果它失败,应用会感知到。 服务:如果它失效,或者没有回应,你真的不会知道它已经失效了。部分故障、不确定的故障,查找出来很难。就算在通过套接字协调的同一台机器上,也不能保证响应时间,或者响应是否传递。这是所有的使用这种模块化机制的主要代价。 结论 我认为选择是使用库还是服务来分配工作/模块化的最大考虑是选择哪种以及决定什么时候部署。谁在给定时间在生产环境中控制那些代码。 库更高效,更容易处理故障。这是一个事物。进程间通信更快,故障更容易处理,并且有可能达到一致性。 实际上,服务是去耦的。让一个团队负责自己的软件,编写并运营它。让团队于给定时间在生产中进行选择:这意味着不断变化的数据源或模式的希望。一般来说,我认为,由于在数据中存在的惯性,数据有很大的价值。在讨论软件架构时,重视不够。如果你有可靠的服务接口来保护你的数据访问,你可以(与棘手的工作)将其移动到不同的数据库或格式。否则,数据迁移就无从谈起。 随着组织的日益壮大,部署时解耦对于保持发展的势头至关重要。功能和语言系统的解耦、版本、工具都有所帮助。要求每个人都使用相同的工具(或但愿不会如此,存储库)是以可避免的方式将每个团队耦合到另一个团队。 总体来说,库更快,直到协调成为瓶颈。而服务能打开更多的瓶颈,它会使你的瓶颈更为难以理解。 其实依赖管理还有很多问题。本文探讨的是依赖管理部分的重要一点。基于实际情况做出何种选择都是可行的。选择好了之后,要“含泪保持微笑”地钻研。 Dependencies Dependency management. Nobody wants to think about it. We just want this stuff to work. It is one of the nasty sneaky unsolved problems in software. ...
“embedded basic” MCU 微控制单元MCU (Microcontroller Unit) 又叫单片机、微处理器,是集成电路的一种。MCU类似于CPU,是可以执行嵌入式程序的一种集成电路。 MCU执行的程序叫嵌入式程序。嵌入式程序可以存储在MCU上,也可以存储在外面的存储器上。比如Flash就是存储器的一种。 模组: 芯片必须配合一些外围设备才能工作。为了方便厂家使用,模组厂家会集成一些外围部件,并写入嵌入式程序,整体打包后作为一个解决方案,给设备厂家使用。 MCU可以算是CPU+RAM+ROM+UART+TIMER+RTC等简单外设。 SoC (System on Chip) ,中文名是片上系统。SoC含义很多,有一种定义是一个有专用目标的集成电路,是一个包含嵌入式软件的完整系统。SoC方案中,对设备所有智能化操作都是通过模组来实现的,设备无需另外增加MCU。此类控制通常而言比较简单,例如开关,灯之类的产品,只需要几个IO口,就可以控制产品。 JTAG JTAG (Joint Test Action Group,联合测试行动小组) 是一种国际标准测试协议 (IEEE 1149.1兼容) ,主要用于芯片内部测试。现在多数的高级器件都支持JTAG协议,如ARM、DSP、FPGA器件等。标准的JTAG接口是4线: TMS、 TCK、TDI、TDO,分别为模式选择、时钟、数据输入和数据输出线。 相关JTAG引脚的定义为: TMS: 测试模式选择,TMS用来设置JTAG接口处于某种特定的测试模式; TCK: 测试时钟输入; TDI: 测试数据输入,数据通过TDI引脚输入JTAG接口; TDO: 测试数据输出,数据通过TDO引 脚从JTAG接口输出; SWD接口 串行调试 (Serial Wire Debug) ,应该可以算是一种和JTAG不同的调试模式,使用的调试协议也应该不一样,所以最直接的体现在调试接口上,与JTAG的20个引脚相比,SWD只需要4个 (或者5个) 引脚,结构简单,但是使用范围没有JTAG广泛,主流调试器上也是后来才加的SWD调试模式。 SWD和传统的调试方式区别: SWD模式比JTAG在高速模式下面更加可靠。在大数据量的情况下面JTAG下载程序会失败,但是SWD发生的几率会小很多。基本使用JTAG仿真模式的情况下是可以直接使用SWD模式的,只要你的仿真器支持,所以推荐大家使用这个模式。 在大家GPIO刚好缺一个的时候,可以使用SWD仿真,这种模式支持更少的引脚。 在大家板子的体积有限的时候推荐使用SWD模式,它需要的引脚少,当然需要的PCB空间就小啦!比如你可以选择一个很小的2.54间距的5芯端子做仿真接口。 RDI 接口 远程调试接口 (Remote Debug Interface) ,是ARM公司提出的标准调试接口,主要用于ARM芯片的仿真,由于各个IDE厂商使用的调试接口各自独立,硬件无法进行跨平台的调试。现在众多的IDE厂家都逐步采用标准RDI作为ARM仿真器的调试接口,因此使跨平台的硬件调试成为可能。EasyJTAG由于使用标准RDI调试接口,因此在任何使用标准RDI接口的IDE调试环境中都可以使用,例如ARM公司的ADS1.2/IAR公司的EWARM 3.30 。 J-Link J-Link是最著名的ARM开发调试工具,J-Link由SEGGER公司生产。提供对市面上几乎所有ARM内核芯片的支持。目前最新版本的J-Link产品为V8,支持JTAG和SWD模式。并且对主要的IDE环境如KEIL、IAR都有良好的支持。优点很多,因此也是首选的调试工具。 所以JLINK就是怎么个作用呢? 起到一个中间转换桥梁,芯片测试用的是JTAG,而JTAG另一端在早起是并口,而现在大多都是USB口,中间过度的重担就交给了Jlink。 ULink仿真器 ULINK是ARM/KEIL公司推出的仿真器,目前网上可找到的是其升级版本,ULINK2和ULINK Pro仿真器。ULINK/ULINK2可以配合Keil软件实现仿真功能,并且仅可以在Keil软件上使用,增加了串行调试 (SWD) 支持,返回时钟支持和实时代理等功能。开发工程师通过结合使用RealView MDK的调试器和ULINK2,可以方便的在目标硬件上进行片上调试 (使用on-chip JTAG,SWD和OCDS) 、Flash编程。 ...
“equipment, device” equipment “Equipment” 通常指 “成套的,不只一件的设备”。而且,与 “device” 和 “appliance” 不同的是,“equipment” 不光包括 “电子设备”,还包含相关的工具。比如,上文提到的 “cycling and diving equipment 自行车和潜水装备” 中可能包括服装、自行车灯、潜水眼镜、呼吸器等物品;再如,“医疗设备” 的说法是 “medical equipment”。比如,虽然 “听诊器 stethoscope” 是一种 “medical equipment”,但它并不是一个 “medical device”,因为听诊器不是电子的。 device device" 通常指 “小巧的电子装置或设备”,比如 “移动设备 mobile device”、“听力装置 listening device”。“Device” 是可数名词。来听一个例句。
“esp,ebp,栈指针,帧指针” (1) ESP: 栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。 (2) EBP: 基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。 栈帧每一个都对应一个过程,而且每一个帧指针+4的位置都存储着函数的返回地址,每一个帧指针指向的存储器位置当中都备份着调用者的帧指针。各位需要知道的是,每一个栈帧都建立在调用者的下方 (也就是地址递减的方向) ,当被调用者执行完毕时,这一段栈帧会被释放。还有一点很重要的是,%ebp和%esp的值指示着栈帧的两端,而栈指针会在运行时移动,所以大部分时候,在访问存储器的时候会基于帧指针访问,因为在一直移动的栈指针无法根据偏移量准确的定位一个存储器位置。 还有一点比较重要的内容,就是栈帧当中内存的分配和释放。由于栈帧是向地址递减的方向延伸,因此如果我们将栈指针减去一定的值,就相当于给栈帧分配了一定空间的内存。这个理解起来很简单,因为在栈指针向下移动以后 (也就是变小了) ,帧指针和栈指针中间的区域会变长,这就是给栈帧分配了更多的内存。相反,如果将栈指针加上一定的值,也就是向上移动,那么就相当于压缩了栈帧的长度,也就是说内存被释放了。需要注意的是,上面的一切内容,都基于一个前提,那就是帧指针在过程调用当中是不会移动的。 函数调用的故事 调用者的各种参数入栈 调用者的返回地址入栈,这个是用 前指令地址++ 作返回地址的 调用者的帧指针入栈保护 (本次调用的基址指针就指向这里) 调用者的寄存器入栈保护 被调用的函数的局部变量的分配 https://blog.csdn.net/u011822516/article/details/20001765 https://www.tenouk.com/Bufferoverflowc/Bufferoverflow2a.html https://zhuanlan.zhihu.com/p/37537046
“etcd” etcd etcd是CoreOS团队于2013年6月发起的开源项目,它的目标是构建一个高可用的分布式键值(key-value)数据库。etcd内部采用raft协议作为一致性算法, etcd基于Go语言实现。 本文的主角是 etcd。名称 “etcd” 源自两个想法,即 unix “/etc” 文件夹 和 “d” 分布式系统。“/etc” 文件夹是用于存储单个系统的配置数据的位置,而 etcd 用于存储大规模分布式的配置信息。因此,分配了 “d” 的 “/etc” 就是 “etcd”。 etcd 被设计为大型分布式系统的通用基板。这些大型系统需要避免裂脑操作,并且愿意牺牲可用性来实现此目的。 etcd 以一致且容错的方式存储元数据。 etcd 集群旨在提供具有稳定性、可靠性、可伸缩性和性能的键值存储。 分布式系统将 etcd 用作配置管理、服务发现和协调分布式工作的一致键值存储组件。许多组织在生产系统上使用 etcd,例如容器调度程序、 服务发现服务和分布式数据存储。使用 etcd 的常见分布式模式包括领导者选举、分布式锁和监视机器活动状态等。 此外,etcd 开箱即用地支持多种语言和框架。Zookeeper 拥有自己的自定义Jute RPC 协议,该协议对于 Zookeeper 而言是完全唯一的,并限制了其受支持的语言绑定,而 etcd 的客户端协议则是基于 gRPC 构建的,gRP 是一种流行的 RPC 框架,具有 go,C ++,Java 等语言支持。同样,gRPC 可以通过 HTTP 序列化为 JSON,因此即使是通用命令行实用程序 (例如curl) 也可以与之通信。由于系统可以从多种选择中进行选择,因此它们是基于具有本机工具的 etcd 构建的,而不是基于一组固定的技术围绕 etcd 构建的。 在考虑功能,支持和稳定性时,etcd 相比于 Zookeeper,更加适合用作一致性的键值存储的组件。 etcd的特点 简单: 安装配置简单,而且提供了HTTP API进行交互,使用也很简单 安全: 支持SSL证书验证 快速: 根据官方提供的benchmark数据,单实例支持每秒2k+读操作 可靠: 采用raft算法,实现分布式系统数据的可用性和一致性 概念术语 Raft: etcd 所采用的保证分布式系统强一致性的算法。 Node: 一个Raft状态机实例。 Member: 一个etcd实例。它管理着一个Node,并且可以为客户端请求提供服务。 Cluster: 由多个Member构成可以协同工作的etcd集群。 Peer: 对同一个etcd集群中另外一个Member的称呼。 Client: 向etcd集群发送HTTP请求的客户端。 WAL: 预写式日志,etcd用于持久化存储的日志格式。 snapshot: etcd防止WAL文件过多而设置的快照,存储etcd数据状态。 Proxy: etcd的一种模式,为etcd集群提供反向代理服务。 Leader: Raft算法中通过竞选而产生的处理所有数据提交的节点。 Follower: 竞选失败的节点作为Raft中的从属节点,为算法提供强一致性保证。 Candidate: 当Follower超过一定时间接收不到Leader的心跳时转变为Candidate开始竞选。 Term: 某个节点成为Leader到下一次竞选时间,称为一个Term。 Index: 数据项编号。Raft中通过Term和Index来定位数据。 数据读写顺序 为了保证数据的强一致性,etcd集群中所有的数据流向都是一个方向,从 Leader (主节点) 流向 Follower,也就是所有 Follower 的数据必须与 Leader 保持一致,如果不一致会被覆盖。 ...
“etcd basic” 单节点的etcd podman run -d \ -p 2379:2379 \ -v etcd-data:/etcd-data \ --add-host=localhost:127.0.0.1 \ -e ETCD_DATA_DIR="/etcd-data" \ -e ETCD_ENABLE_V2="true" \ -e ALLOW_NONE_AUTHENTICATION="yes" \ -e ETCD_ADVERTISE_CLIENT_URLS="http://0.0.0.0:2379" \ -e ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379" \ --name etcd bitnami/etcd:latest set, get etcdctl ls etcdctl set /testdir/testkey "foo" etcdctl get /testdir/testkey etcdctl update /testdir/testkey "Hello" etcdctl rm /testdir/testkey etcdctl mk /testdir/testkey "Hello world" etcdctl mkdir testdir2 https://etcd.io/docs/v3.4.0/op-guide/container/ https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/container.md
“flutter” install yay -S flutter flutter doctor https://flutter.dev/docs/get-started/install/linux https://storage.googleapis.com/flutter_infra/releases/stable/linux/flutter_linux_2.0.5-stable.tar.xz export export FLUTTER_STORAGE_BASE_URL=“https://mirrors.tuna.tsinghua.edu.cn/flutter" export PUB_HOSTED_URL=“https://mirrors.tuna.tsinghua.edu.cn/dart-pub"
“futex” futex (fast userspace mutex) 是Linux的一个基础构件,可以用来构建各种更高级别的同步机制,比如锁或者信号量等等,POSIX信号量就是基于futex构建的。大多数时候编写应用程序并不需要直接使用futex,一般用基于它所实现的系统库就够了。 futex的性能非常优异,它是怎样做到的呢?这要从它的设计思想谈起。传统的SystemV IPC(inter process communication)进程间同步机制都是通过内核对象来实现的,以 semaphore 为例,当进程间要同步的时候,必须通过系统调用semop(2)进入内核进行PV操作。系统调用的缺点是开销很大,需要从user mode切换到kernel mode、保存寄存器状态、从user stack切换到kernel stack、等等,通常要消耗上百条指令。事实上,有一部分系统调用是可以避免的,因为现实中很多同步操作进行的时候根本不存在竞争,即某个进程从持有semaphore直至释放semaphore的这段时间内,常常没有其它进程对同一semaphore有需求,在这种情况下,内核的参与本来是不必要的,可是在传统机制下,持有semaphore必须先调用semop(2)进入内核去看看有没有人和它竞争,释放semaphore也必须调用semop(2)进入内核去看看有没有人在等待同一semaphore,这些不必要的系统调用造成了大量的性能损耗。futex就为了解决这个问题而生的,它的办法是: 在无竞争的情况下,futex的操作完全在user space进行,不需要系统调用,仅在发生竞争的时候进入内核去完成相应的处理(wait 或者 wake up)。所以说,futex是一种user mode和kernel mode混合的同步机制,需要两种模式合作才能完成,futex变量必须位于user space,而不是内核对象,futex的代码也分为user mode和kernel mode两部分,无竞争的情况下在user mode,发生竞争时则通过sys_futex系统调用进入kernel mode进行处理,具体来说: futex 变量是位于 user space 的一个整数,支持原子操作。futex 同步操作都是从user space开始的: 当要求持有futex的时候,对futex变量执行”down”操作,即原子递减,如果变量变为0,则意味着没有竞争发生,进程成功持有futex 并继续在user mode运行;如果变量变为负数,则意味着有竞争发生,需要通过sys_fute x系统调用进入内核执行futex_wait操作,让进程进入休眠等待。 当释放futex的时候,对futex变量进行”up”操作,即原子递增,如果变量变成1,则意味着没有竞争发生,进程成功释放futex并继续在user mode执行;否则意味着有竞争,需要通过sys_futex 系统调用进入内核执行 futex_wake 操作,唤醒正在等待的进程。 如果需要在多个进程之间共享futex,那就必须把futex变量放在共享内存中,并确保这些进程都有访问共享内存的权限;如果仅需在线程之间使用futex的话,那么futex变量可以位于进程的私有内存中,比如普通的全局变量即可。 更详细的信息请参阅futex作者的论文: Fuss, Futexes and Furwocks: Fast Userlevel Locking in Linux http://linuxperf.com/?p=23 什么是 Futex Futex,作为linux下的一种快速同步 (互斥) 机制 Futex 是Fast Userspace muTexes的缩写,由Hubertus Franke, Matthew Kirkwood, Ingo Molnar and Rusty Russell共同设计完成。几位都是linux领域的专家,其中可能Ingo Molnar大家更熟悉一些,毕竟是O(1)调度器和CFS的实现者。 ...
“github action” Deploy to vm - name: Deploy to vm uses: easingthemes/ssh-deploy@v2.1.2 env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} ARGS: "-avz --delete" SOURCE: "public/" REMOTE_HOST: "192.168.0.xxx" REMOTE_PORT: 38785 REMOTE_USER: "blog" TARGET: "/home/blog/public/" secrets.SSH_PRIVATE_KEY 粘贴私钥时后面加一个换行,否则会报错,invalid format