istio 是通过给每个生产 pod 注入一个 envoy sidecar 进行流量劫持的。注入后,流入 pod 的流量和 pod 请求的流量都会经过 sidecar 进行路由。 这样的流量劫持是通过 sidecar pod 和生产 pod 共享一个网络命名空间,然后设置 iptable 规则实现的。 PS1:下面的步骤前提是在一个 k8s 集群内操作,并且已经安装了 istio) PS2:实验步骤是基于宋大的博文上实验的,可以先看看宋大的两篇博文:用 vagrant 和 virtualbox 搭建一个 k8s 集群 理解 Istio Service Mesh 中 Envoy 代理 Sidecar 注入及流量劫持 1.sidecar 是如何注入的呢? 比如我们要部署一个服务,首先要编写一个关于 service 和 deployment 的 yaml 文件(拿 istio 官网的 bookinfo 为例子): --- apiVersion: v1 kind: Service metadata: name: productpage labels: app: productpage spec: ports: - port: 9080 name: http selector: app: productpage --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: productpage-v1 spec: replicas: 1 template: metadata: labels: app: productpage version: v1 spec: containers: - name: productpage image: istio/examples-bookinfo-productpage-v1:1.
本文翻译自:https://cloudnativelabs.github.io/post/2017-05-10-kube-network-service-proxy/ 服务是 Kubernetes 的一个抽象概念。服务将一组逻辑上的、提供相同功能的 pod 组合在一起。一个在 Kubernetes 内的服务可以是多种类型的,比如 ‘ClusterIP’ 和 ‘NodePort’ 类型实现服务发现和负载均衡。这些服务的类型的实现都需要一个运行在各个 Kubernetes 集群节点的服务代理。Kubernetes 有一种名为 ‘kube-proxy’ 的代理,是基于 iptable 实现的。虽然,kube-proxy 提供了一种开箱即用的代理解决方案,它并不是一种最优的方案。在这篇文章里,我们会深入研究另一种 Kubernetes 里网络服务代理的实现:kube-router,它是基于 Liunx IPVS 的。我们还会讨论基于 iptables 的 kube-proxy 的好处与不足,以及将它与基于 IPVS/LVS 实现的 Kubernetes 网络服务代理进行对比。 请看下面的 demo,感受 IPVS 实现的 kube-router 作为 Kubernetes 服务代理的效果。 (此处查看原文可以看到 asciinema demo,如果不能访问,asciinema 地址在这里) 下面我们来深入研究一下细节。 ClusterIP 和 NodeProt 类型的服务 在服务的生命周期里,每个 ‘ClusterIP’ 和 ‘NodePort’ 服务会被分配一个唯一的 IP 地址(称为 ClusterIP)。Kubernetes 会配置 Pods 通过 ClusterIP 与服务进行交互,而且 pods 发送至 service 的请求会被自动地负载均衡到 service 的成员 pods 上。另外,Kubernetes master 节点会为 ‘NodePort’ 类 service 分配一个标志配置范围(默认是 30000 至 23767)的端口,然后每个 node 节点会将该 service 的请求代理到这个端口上。
本文简单概括了 kubernetes 里有什么组件,以及它们是如何工作的。 一、kubernetes 的组件和架构 一个 kubernetes 集群包含两部分:master 节点和 node 节点。master 节点负责接收管理员对于 kubernetes 资源对象的修改,并根据这些修改,对 node 节点进行配置和调度。node 节点是 kubernetes 集群内的计算节点,负责给部署在上面的业务应用提供计算以及储存能力。 kubernetes 里有以下组件:Kubernetes API Server、Controll Manager、Scheduler、kubelet 和 kube-proxy。 其中,Kubernetes API Server、Controll Manager、Scheduler 运行在 master节点上。而 kubelet 和 kube-proxy 运行在 node 节点上。 这些组件分别负责 kubernetes 运行的不同职责: Kubernetes API Server:kubernetes 集群的管理入口,它提供 HTTP REST 接口给 kubernetes 用户进行集群的配置,这些配置将会保存在 master 节点运行的 etcd 里面,供其他组件查阅。 Controll Manager:负责监听 etcd 变化并作出对应的动作。Controll Manager 是一系列 manager 的统称,其中有以下这些manager: Replication Manager:负责 node 节点的 pod 管理。 Node Controller:负责 node 节点管理。 Namespace Controller:负责管理集群内的命名空间。 ResourceQuota Controller:负责针对 namespace、pod、容器这三个维度的资源限制管理。 ServiceAccount Controller:负责管理集群内访问的账号。 Token Controller:负责允许访问 kubernetes 集群的 token。 Service Controller:负责管理集群内的 Service。 Endpoint Controller:负责管理集群内的 Endpoint。 Scheduler:负责接收 Replication Controller 的指挥,对要部署在 node 上的 pod 进行调度。Scheduler 会根据调度算法以及调度策略,调度 pod 在哪个 node 上部署。
@(PHP) ###PHP扩展分享 ###什么时候需要写php扩展 有需要使用php调用C/C++函数库 实现php没有的功能 优化php性能 ###php扩展简介 php提供一个工具用来生成php扩展的代码,类似代码生成器,工具目录source/to/php_source_code/ext/ext_skel,这个是供类unix平台调用的脚本,php源码也提供了一个供windows平台调用的脚本,用来生成php扩展代码.该脚本是个php脚本,在source/to/php_source_code/ext/ext_skel_win32.php php将扩展代码统一放到source/to/php_source_code/ext/下,这样当开发者按照工具生成对应格式的php扩展源码,并统一放到这个目录下,php源码就会自动识别,并可以选在在编译时加上开发者的扩展. ###先找个题目 总所周知php在连接字符串会进行多次初始化临时变量,和内存复制.假设有一个变态的需求需要重复一个字符串’a' n次,并且在系统中多次出现,由于n已知,为了提高性能,可以考虑用扩展实现 ###动手 ####1. 编写扩展原型文件,使用工具生成扩展代码 php没有规定原型文件的文件后缀,但有规定原型文件的格式. 如果你的扩展需要暴露出接口函数供php代码调用,原型文件只需包含接口函数的原型.接口函数原型包括函数输入参数的类型和名字,还有函数返回的类型.其中类型支持php的变量类型.如bool,int,string,float等等.. 根据上面的需求,创建一个名为repeatString的函数,返回类型为string,接受一个string类型的$inputString字符串,和一个int类型的$repeatCount,表示输出$inputString被重复$repeatCount次的字符串. 首先进入ext目录 cd path/to/php_source_code/ext && \ vim repeatString.proto 编写内容 string repeatString(string inputString, int repeatCount) 保存,使用ext_skel工具生成扩展框架,可以指定扩展名和扩展的原型文件 ./ext_skel --extname=repeatString --proto=repeatString.proto 你会看到ext目录下会多了一个repeatString的文件夹,里面放的就是你扩展的所有所需文件 需要留意的有几个文件: config.m4 – *unix平台下配置文件,这里是官方解释: 扩展的 config.m4 文件告诉 UNIX 构建系统哪些扩展 configure 选项是支持的,你需要哪些扩展库,以及哪些源文件要编译成它的一部分。对所有经常使用的 autoconf 宏,包括 PHP 特定的及 autoconf 内建的,请查看 Zend Engine 2 API 参考 章节。 config.w32 – windows平台下扩展的配置文件 php_repeatString.h – 扩展头文件 repeatString.
1. 想法 前些天在研究分布式锁的方案,突发奇想,在云原生时代,像 php、c、go、java 这种原来用特定编程语言开发的程序,已经通过打包、编排,变成了云原生操作系统里的一个工作进程。而像是网络这种原本的操作系统基础设施,也在云原生操作系统里面有了实现(从微服务发展到 service mesh)。 那么,锁是不是也可以看作是基础设施,提供给云原生操作系统的工作进程使用呢? 云原生开发者在编写工作进程逻辑的时候,无需考虑引入哪个分布式锁的包/库,就像是调用系统调用一样,将资源锁住,读写资源,然后再调用系统调用进行解锁。而云原生操作系统负责调度容器进行资源的访问,防止容器争抢资源。这样做的好处是,可以屏蔽掉不同语言的分布式锁之间的实现差异,让业务逻辑开发者专注业务逻辑的优化、思考业务应该使用哪种锁,以及锁的粒度等业务相关的工作。 其实不止是锁,也许还可以思考一下,原生操作系统的基础设施有哪些可以对应到云原生操作系统上的。 且不论叫它 lock mesh 是否合理吧:) 有这个想法后,我在 github、google 和 CNCF landscape 上搜索了一番,没有找到相关的项目或者资料。后续我会再进行一些搜索,确定我是不是想别人已经想到的东西。 如果你也觉得这个想法可行,不妨跟我联系哦:) 2. 怎么办 有了想法,然后呢? 我列了一些 todo 项: 再搜索一下是否已经存在相关的资料、项目 思考这个方案是否有必要,是否可行 有没有必要做这一层抽象,编写容器逻辑的开发者,其实是不是按照原来的方式,调用库就好了 如果要做,怎么基于云原生平台(像是 kubernetes)去实现这种基础设施 如果可行,根据具体方案,给出架构图 立项,做项目规划 写demo 开源出去,让更多的开发者接触到项目,并参与代码贡献 建立社区,完善文档(微信群、钉钉群(国内)、slack(国外))
这周花了点时间看了 skynet 关于定时器功能的源码,以及扩展的了解了一下定时器实现的其他方案,写一遍博客总结一下.内容会分为下面几部分: skynet的定时器结构图 skynet定时器执行过程 关于skynet定时器的一些思考 其他定时器方案 linux定时器方案 总结 1. skynet 的定时器结构图 2. skynet 定时器执行过程 首先确定定时器里面的几个概念: tick: 指 skynet timer 线程每一次处理事件的循环.由于这个循环每隔 0.00025 秒会执行一次,很像时钟滴答滴答的走,很多书籍都称为 tick skynet 启动时间戳: 顾名思义,比如 1528613417 时间戳(北京时间 2018/6/10 14:50:17)代表 skynet 是这个时间点启动的,而且这个时间是不会变的.这个比较容易理解 系统启动时间数: 单位 0.01 秒. 顾名思义,但是为什么说是时间数呢?因为这是一个时间的量,比如300,表示系统启动了3秒. skynet timer 线程在每一次 tick 都会去同步这个时间量 skynet 启动时间数: 单位 0.01 秒. 与系统启动时间数类似,如 300,表示 skynet 启动了 3 秒,每次 tick skynet timer 线程也会去更新这个时间量 首先需要说明的是, skynet 定时器的时间刻度是百分秒(0.01秒).意味着 skynet 最小可以定义事件 0.01 秒后执行.这个精度对于游戏领域可以说是够用了. skynet 初始化时会创建5个定时器的事件槽,分别是 Near,level0-3.
这个星期以来,由于工作需要使用skynet,于是看了5天skynet的源码。收获很多,于是周末写篇博文总结一下这周以来的学习。 这5天的学习过程可以概括为5句话:(因为5天嘛) 代码结构,从main函数开始 skynet启动过程,worker线程工作过程 skynet启动过程,skynet数据结构熟悉, worker回调函数注册,worker的c层回调lua逻辑原理, lua层逻辑协程的使用熟悉,服务之间通过消息交互的过程 看了5天后回过头来发现,其实了解的还停留在skynet的核心部分,还没有走出核心。因为skynet还包含很多其他内容,比如他的消息机制的各种api、socket支持、loginserver gateserver、datacenter、harbor等等。。。 不过问题也不大,毕竟核心比较重要,后续还要继续学习。 那下面就来总结一下这5天都看到来什么内容。有可能因为我了解尚浅,写出来的和实际的skynet有出入。不过没关系,后续会回过头来再看看。 下面的总结会按照这个结构: 源码目录结构 skynet的结构图以及工作过程 那么,开始吧。 1. 源码目录结构 可以用一个段落来说明 $ ll drwxr-xr-x 26 root staff 884 6 1 06:30 . drwxr-xr-x 5 root staff 170 6 2 07:07 .. drwxr-xr-x 12 root staff 408 6 1 07:10 .git -rw-r--r-- 1 root staff 94 6 1 05:36 .gitignore -rw-r--r-- 1 root staff 96 6 1 05:36 .gitmodules drwxr-xr-x 8 root staff 272 6 1 22:25 .
Spring与SpringBoot的IoC和DI 前言 spring的内容有很多,而springBoot对于spring的优化的内容也有非常多方面可讲。这次主要分享的是spring和springBoot中的IoC和DI,以及我所看到的在我们java项目中的体现。 Ioc(控制反转) 控制反转即反转控制类实例的方式。 从控制反转为被控制 从自己生成/销毁反转为别人生成/销毁 控制反转的好处在于: 业务逻辑中不用考虑实例的生成/销毁,可以专注于业务逻辑(使用类实例) 对测试更加友好(mock实例行为) DI(依赖注入) 依赖注入是控制反转的一种实现方法。“别人"管理类实例的生成/销毁,并将业务逻辑需要用到的类实例注入进来。 Spring的IoC 一个Spring应用有一个应用上下文(ApplicationContext),充当类实例的容器,管理着类实例的生成和销毁,并在应用需要时返回/注入对应的类实例。 Spring按类实例的种类管理容器中的类实例(种类有唯一的种类id:beanId)。每一种类实例称为Bean。 与很多IoC框架类似,类实例的注入方式有很多种(单例/非单例),Spring中类实例存在方式可以是: 单例 (singleton) 每次注入返回新的实例 (prototype) 每次请求返回同一的实例 (request) 每次回话返回同一实例 (session/global session) 以下是spring注册Bean的其中某些特性: 1. xml可以配置BeanId,默认以类名为beanId。 2. 注解为bean的时候默认以类名为beanId,也可以指定beanId 3. 在获取bean时,如果无法查找到beanId,会查找对应的类是否有注册,如果不存在,会查找对应类的子类是否有注册 另外,spring的IoC和DI利用了java的注解实现,也是面向切面编程的一个体现。在加载应用的时候,切换到应用bean管理切面,扫描类并注册Bean,在需要注入时,再次切换到bean管理切面,返回需要的bean。 SpringBoot的IoC springBoot简化了spring的配置部分 spring通过xml配置文件扫描/注解方式注册类实例 springBoot默认通过注解方式注册类实例,并默认扫描application包以及子包的所有类实例。默认以注解方式标记类实例。 springBoot可以沿用spring注解配置Bean时的规则
一 为什么需要知道异常控制流 异常控制流发生在程序执行的各个阶段 异常控制流使程序更加简洁和优雅,而且它为程序并行执行提供了条件 了解异常控制流机制对了解计算机程序的执行原理有帮助 二 知道了异常控制流能干什么 了解计算机程序执行原理 设计更优雅的程序 三 什么是异常控制流 1. 两个小定义 异常控制流是用户程序和系统程序交互的基础 计算机系统供一种用户级的异常控制流,让开发者可以在代码的异常逻辑里面,无需解开嵌套多层的调用栈,直接跳转到异常处理的代码里面 2. 异常控制流是用户程序和系统程序交互&软硬件交互的基础 要说明这点,先要说一下什么是用户程序,什么是系统程序。 为了限制用户程序的权限,和将一些系统操作抽象出来,计算机系统将一些高等操作称为系统程序,如申请内存,读取内存信息到寄存器/读取磁盘信息到内存,从一个外部设备读取信息到磁盘,等。 同时这样分离还有一个好处,就是提高系统的吞吐率。众所周知,cpu寄存器-一级缓存-二级缓存-内存-磁盘-网络存储设备,越往后读取的速度越慢,而让cpu盲目的等待IO的数据到位,然后再执行后续的程序,是极其浪费cpu计算资源的。而另一方面,由于计算机系统的多进程特性,一个用户程序在一个进程调用系统程序,进行耗时的IO时,完全可以对这个进程进行挂起,让cpu资源去处理其他进程的逻辑,待IO完毕,给挂起的程序一个通知,让挂起的程序继续进行IO后的逻辑。 这种通知进程的行为,就是异常,而这个通知,就是后面要讲到的信号。 计算机系统的异常有4种:中断,陷阱,故障,终止 中断 中断通常发生在用户程序和硬件交互。中断的过程是异步的,即发生中断时用户程序被挂起(被中断了),待硬件返回后,用户程序继续发生中断代码后续的命令。 陷阱 陷阱和中断类似,只不过陷阱是同步的。陷阱像是一种故意的异常,发生在软件程序上的调用,如用户程序向系统程序申请fock一个子进程,或者执行另外一个程序。所以实现系统程序的封装和系统程序和用户程序的隔离,接口化,使用的也是陷阱这种异常。 故障 故障是由于错误引起的。故障发生时,程序的控制转移到故障处理程序,待故障处理程序逻辑结束后,程序计数器会尝试回到故障出现的行命令。一个典型的故障就是内存缺页故障。当程序读取内存的某一个虚拟地址,发现读取的内存页面不在内存里面,于是引起缺页故障,处理程序将数据从磁盘读取到内存对应的页面,故障处理结束,程序再次回到读取内存页面的那行,这时候,读取的代码再次执行,就可以正常运行了。 终止 终止发生在程序出现致命错误的时候,系统在执行完故障处理程序后,会停止该程序。如:内存损坏时。 3. 信号 信号有很多种类型,系统给不同的信号定义了不同的意义,其中也保留了用户可以自定义的信号 (图出自<<Computer Systems Third edition») 比如在当前进程按下ctrl+c..实际上就是键盘通过操作系统给当前进程发送了一个2信号 4. 除了系统程序可以给用户程序发送信号,用户程序也可以给用户程序发送信号 通过kill命令,传入信号和进程id,可以给进程发送一个信号。常见的nginx通过kill命令重启nginx,平滑重新加载配置,就是用了这个特性。 在程序逻辑里,可以调用c的kill函数给进程id传入信号。如果进程id大于0,向进程id发送信号;如果进程id为0,则为自己和自己所在的进程组每个进程发送信号;如果进程id小于0,则为进程id的绝对值以及进程id绝对值所在的进程组所有的进程都发送信号。 (图出自<<Computer Systems Third edition») 在程序逻辑里面,用户也可以注册信号的handler函数,当进程被传入信号时,程序控制就会交给handler函数。处理完后,控制交还给故障发生时的后一句命令,程序继续执行。 (图出自<<Computer Systems Third edition») 5. 计算机系统的用户级异常控制流 c语言提供setjump和longjump方法。setjmp方法保存当前进程的运行状态(包括程序计数器,栈指针和通用目的寄存器),而longjmp根据之前保存的状态还原程序执行的状态,继续执行setjmp处后面的逻辑。
一. 信息在计算机里面的存储 无论是保存在硬盘里面作永久储存,还是保存在内存/寄存器里面作为计算的临时储存,信息在计算机里面需要一个表示方法,或者叫做保存方法. 首先,可以先来看一下c语言都有哪些信息的类型,或者说我们编程时候都用到哪些信息类型: 布尔型,表示是与否 整型,表示整数 浮点型,表示小数 字符串,表示一系列字符的集合 数组,一系列上述类型的集合 对象,一系列上述类型的索引集合 其中,布尔型可以用简单的 整型0&整型1 表示,就不单独说明. 由于一些基本的概念,比如字符串用ASCII码/UTF-8编码表示,整型根据不同的长度有,是否有符号,会有不同的范围,下面只挑选不同信息类型(或者叫数据类型)的值得提一下的点作说明. 整型: 整型无符号形式不用多说,而有符号的表示形式除了简单的使用最高位作为符号位(1000 0001 表示-1),还有两种表现形式:补码表示和反码表示. 实际上,补码相对于原码和反码,对于计算机的计算是更优化的.大多数现代计算机都用补码表示有符号数. 补码中, 如 1000 0001,将最高有效位解释成负权,而后, 所以 1000 1111 = **-**2^7 **+**2^3 **+**2^2 **+**2^1 **+**2^0 = -113 而反码就与补码类似,不过补码的负权为 - 2 ^(w-1), 反码的负权为 -2^(w-1) - 1, 其中w为整型长度 补码相比反码和原码(即最高位表示符号位的形式)的差别在于: 补码的0000 0000表示0,没有-0表示,而反码和原码都存在一个-0表示 (反码为1111 1111, 原码为 1000 0000) 补码的加减乘除运算,都等价于将补码转换为无符号数的加减乘除,再转换回补码,而反码和原码都要先作相应的转换.(至于为什么反码和补码不能直接运算,可以参考这个文章: 原码, 反码, 补码 详解) 浮点型 书中介绍了一种浮点型表示的标准: IEEE浮点表示.