首页 文章 精选 留言 我的

精选列表

搜索[架构师],共5862篇文章
优秀的个人博客,低调大师

珠峰前端架构师培养计划2021

2 --> 爱分享 爱生活 加油 2021 一、了解Vue.js 1.1.1 Vue.js是什么? 简单小巧、渐进式、功能强大的技术栈 1.1.2 为什么学习Vue.js? 学习曲线平缓、易上手、功能强大、轻便 目前最流行的三大框架之一,适用范围广 升职加薪 ------ 哈哈哈哈哈哈哈哈 1.1.3 Vue.js的模式 MVVM模式,视图层和数据层的双向绑定,让我们无需再去关系DOM操作的事情,更过的精力放在数据和业务逻辑上去 1.1.4 Vue.js环境搭建 script vue脚手架工具vue-cli搭建。 二、数据绑定,指令,事件 2.1.1 vue实例和数据绑定 一、 <script src="http://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> 通过构造函数Vue就可以创建一个Vue的根实例,并启动Vue应用---入口 var app = new Vue({ el: '', data: { } }) 二、其中必不可少的一个选项就是el,el用于指定一个页面中已存在的DOM元素来挂载Vue实例 三、通过Vue实例的data选项,可以声明应用内需要双向绑定的数据。建议所有会用到的数据都预先在data内声明,这样不至于将数据散落在业务逻辑中,难以维护。也可以指向一个已经有的变量 四、挂载成功后,我们可以通过app.$el来访问该元素。Vue提供了很多常用的实例属性与方法,都已 $开头,比如 $el,Vue实例本身也代理了data对象里所有属性,所以可以这样访问 五、如果是访问data里的属性,用app.属性名 2.1.2 created: 实例穿件完成后调用,此阶段完成了数据的观测等,但尚未挂载,$el还不可用。需要初始化处理一些数据时会比较有用。------还未挂载 mounted: el挂载到实例上后调用,一般我们的第一个业务逻辑会从这里开始。------刚刚挂载 beforeDestroy: 实例销毁之前调用。主要解绑一些使用addEventListener监听的事件等。 2.1.3 文本插值和表达式 语法: 使用双大括号(Mustache语法)"{{ }}"是最基本的文本插值方法,他会自动将我们双向绑定的数据实时显示出来 用法: 在{{ }}中,处了简单的绑定属性值外,还可以使用JavaScript表达式进行简单的运算、三元运算等 Vue.js只支持单个表达式,不支持语句和流控制 2.2.1 过滤器 Vue支持在{{ }}插值的尾部添加一小管道符"|"对数据进行过滤,经常用于格式化文本,比如字母全部大写、货币千位使用逗号分隔等。过滤的规则是自定义的,通过给Vue实例添加选项filters来设置过滤器:{{data | filter1 | filter2}}{{data | formatData(1,2)}}中的第一个和第二个参数,分别对应过滤器的第二个和第三个参数 2.2.2 指令和事件 指令( Directives )是 Vue 模板中最常用的一项功能,它带有前缀 v-,能帮我们快速完成DOM操作。循环渲染。显示和隐藏 v-­text:—————­解析文本 和{{ }} 作用一样 v­-html:————— 解析html v­-bind—————–v­bind 的基本用途是动态更新 HTML 元素上的属性,比如 id 、class 等,本节只介绍基本章节,后面章节会更加深入详细讲述 v­-on——————它用来绑定事件监听器v­-on具体介绍在普通元素上, v­on 可以监听原生的 DOM 事件,除了 click 外,还有dblclick、 keyup, mousemove 等。表达式可以是一个方法名,这些方法都写在 Vue 实例的 methods属性内,并且是函数的形式,函数内的 this 指向的是当前 Vue 实例本身,因此可以直接使用 this.xxx 的形式来访问或修改数据vue中用 到的所有方法都定义在methods中 2.2.3 语法糖 语法糖是指在不影响功能的情况下 , 添加某种简洁方法实现同样的效果 , 从而更加方便程序开发。 v-bind ——> : (冒号) v-on ——> @ 运用以上知识点,总和一个小demo: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>demo</title> <style> .data { background: red; height: 18px; } </style> </head> <body> <div id="app"> {{name}} <br> {{name | formatDate}} <br> <div v-html="html"></div> <span v-text="weather"></span> <br> <div v-bind:class="className"></div> <button v-on:click="click">{{countnum}}</button> </div> <script src="http://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script> var plusDate = function(value){ return value < 10 ? '0' + value : value } var app = new Vue({ el : '#app', data : { name: new Date(), html: '<div>你好</div>', weather: '今天天气不错', className: 'data', countnum: 0 }, filters:{ formatDate: function(value) { var date = new Date(value) var year = date.getFullYear() var month = plusDate(date.getMonth()+1) var day = plusDate(date.getDate()) var hours = plusDate(date.getHours()) var min = plusDate(date.getMinutes()) var sec = plusDate(date.getSeconds()) return year + '--' + month + '--' + day + ' ' + hours + ':' + min + ':' + sec } }, mounted: function(){ var _this = this this.timer = setInterval(function(){ _this.name = new Date() },1000) }, methods: { click: function(){ this.countnum = this.countnum + 1 } }, beforeDestroy: function(){ clearInterval(this.timer) } }) </script> </body> </html> 三、 计算属性 3.1 什么是计算属性 我们己经可以搭建出一个简单的 Vue 应用,在模板中双向绑定一些数据或表达式了。但是表达式如果过长,或逻辑更为复杂时,就会变得雕肿甚至难以阅读和维护 <div> {{ text.split ( ’,’ ) •reverse () . join (’,’)}} </div> 这里的表达式包含 3 个操作,并不是很清晰,所以在遇到复杂的逻辑时应该使用 计算属性 所有的计算属性都以函数的形式写在 Vue 实例内的computed 选项内,最终返回计算后的结果。 3.2 计算属性用法 在一个计算属性里可以完成各种复杂的逻辑,包括运算、函数调用等,只要最终返回一个结果就可以。除了上例简单的用法 计算属性还可以依赖多个 Vue 实例的数据,只要其中任一数据变化,计算属性就会重新执行,视图也会更新 getter和setter 每一个计算属性都包含一个 getter 和一个 setter,我们上面的两个示例都是计算属性的默认用法 , 只是利用了 getter来读取。在你需要时,也可以提供一个 setter 函数 , 当手动修改计算属性的值就像修改一个普通数据那样时,就会触发 setter函数,执行一些自定义的操作 计算属性除了上述简单的文本插值外,还经常用于动态地设置元素的样式名称 class 和内联样式 style 小技巧: 计算属性还有两个很实用的小技巧容易被忽略: 一、是计算属性可以依赖其他计算属性: 二、是计算属性不仅可以依赖当前 Vue 实例的数据,还可以依赖其他实例的数据 3.3计算属性缓存 调用 methods 里的方法也可以与计算属性起到同样的作用 页面中的方法: 如果是调用方法,只要页面重新渲染。方法就会重新执行,不需要渲染,则不需要重新执行 计算属性:不管渲染不渲染,只要计算属性依赖的数据未发生变化,就永远不变 结论: 没有使用计算属性,在 methods 里定义了一个方法实现了相同的效果,甚至该方法还可以接受参数,使用起来更灵活。既然使用 methods 就可以实现,那么为什么还需要计算属性呢?原因就是计算属性是基于它的依赖缓存的。 一个计算属性所依赖的数据发生变化时,它才会重新取值,所以text 只要不改变,计算属性也就不更新 何时使用: -----------使用计算属性还是 methods 取决于你是否需要缓存,当遍历大数组和做大量计算时,应当使用计算属性,除非你不希望得到缓存。 计算属性demo <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>计算属性</title> </head> <body> <div id="data"> {{fullName}} <br> {{watch()}} </div> <script src="http://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script> var app = new Vue({ el: '#data', data: { firstName:'Cai', lastName: 'hua' }, computed: { fullName: function(){ return this.firstName + ' ' + this.lastName } }, methods: { watch: function(){ return this.firstName + ' ' + this.lastName } } }) </script> </body> </html> 四、 v-­bind以及class与style的绑定 应用场景: DOM 元素经常会动态地绑定一些 class 类名或 style 样式 4.1 了解bind指令 v-­bind的复习: 链接的 href 属性和图片的 src 属性都被动态设置了,当数据变化时,就会重新渲染。 在数据绑定中,最常见的两个需求就是元素的样式名称 class 和内联样式 style 的动态绑定,它们也是 HTML的属性,因此可以使用 v­-bind 指令。 我们只需要用 v­-bind计算出表达式最终的字符串就可以,不过有时候表达式的逻辑较复杂,使用字符串拼接方法较难阅读和维护,所以 Vue.js 增强了对 class 和 style 的绑定。 4.2 绑定 class 的几种方式 4.2.1 对象语法 给 v-­bind:class 设置一个对象,可以动态地切换 class,值对应true ,false 当 class 的表达式过长或逻辑复杂时,还可以绑定一个计算属性,这是一种很友好和常见的用法,一般当条件多于两个时, 都可以使用 data 或 computed 4.2.2 数组语法 当需要应用多个 class 时, 可以使用数组语法 , 给:class 绑定一个数组,应用一个 class列表: 数组成员直接对应className--类名 可以用三目运算实现,对象和数组混用 4.2.3 在组件上使用 : 暂时不考虑—­挖坑 4.3 绑定内联样式 使用 v­-bind:style (即:style ) 可以给元素绑定内联样式,方法与 :class 类似,也有对象语法和数组语法,看起来很像直接在元素上写 CSS 注意 : css 属性名称使用驼峰命名( came!Case )或短横分隔命名( kebab­case),应用多个样式对象时 , 可以使用数组语法,在实际业务 中,style 的数组语法并不常用 , 因为往往可以写在一个对象里面, 而较为常用 的应当是计算属性 使用 :style 时, Vue .js 会自动给特殊的 css 属性名称增加前缀, 比如 transform 。 无需再加前缀属性!!!! v-bind绑定demo: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>v-bind绑定</title> <style> .divStyle{ width: 88px; height: 88px; background: red; } .borderStyle{ border: 8px solid black; } .color{ color: red; } .size{ font-size: 28px; } .blueClass{ color: blue; } .font{ font-size: 36px; } </style> </head> <body> <div id="demo"> //对象语法 <br> <div v-bind:class="{divStyle : isActive, borderStyle : isBorder}"></div> <hr> //数组语法<br> <div v-bind:class="[colorClass,sizeClass]">Hello word</div> <hr> //对象和数组混用 <div v-bind:class="[{blueClass : isBlue},fontClass]">你好!</div> <hr> //绑定内联样式 <div v-bind:style="{'background':background,'fontSize':fontSize + 'px'}">真好!</div> </div> <script src="http://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script> var app = new Vue({ el:'#demo', data:{ isActive: true, isBorder: true, colorClass:"color", sizeClass: "size", isBlue:true, fontClass:"font", background: 'red', fontSize:'56' } }) </script> </body> </html> 五、vueJS中的内置指令 5.1 基本指令 5.1.1 v-­cloak一般与display:none进行结合使用 作用:解决初始化慢导致页面闪动的最佳实践 5.1.2 v-­once 定义:它的元素和组件只渲染一次 5.2 条件渲染指令 5.2.1 v-­if, v-­eles-­if ,v-­else 用法: 必须跟着屁股走 v-if的弊端 :Vue 在渲染元素时 ,出于效率考虑,会尽可能地复用已有的元素而非重新渲染, 因此会出现乌龙,只会渲染变化的元素,也就是说,input元素被复用了 解决方法: 加key,唯一,提供key值可以来决定是否复用该元素 5.2.2 v-­show 只改变了css属性display v­-if和v­-show的比较 v-­if: 实时渲染:页面显示就渲染,不显示。我就给你移除 v-­show: v-­show的元素永远存在也页面中,只是改变了css的display的属性 5.3 列表渲染指令v­-for 用法: 当需要将一个数组遍历或枚举一个对象属性的时候循环显示时,就会用到列表渲染指令 v­-for。 两种使用场景: 遍历多个对象 遍历一个对象的多个属性 v-for demo <body> <div id="demo"> //遍历多个对象一定是遍历的数组 //带索引的写法:括号的第一个变量,代表item,第二个代表index <ul> <li v-for="vuestu in vueStudy">{{vuestu.name}}</li> </ul> <br> //遍历一个对象的多个属性 //拿到value,key,index的写法 v-k-i--外开 <div v-for="(value,key,index) in women">{{index}}-----{{key}}------{{value}}</div> </div> <script src="http://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script> var app = new Vue({ el:'#demo', data:{ vueStudy:[ //每个对象对应一个li {name:'敲代码'}, {name:'看资料'}, {name:'看蔡华鹏博客'} ], women:{ grid1: '张柏芝', grid2: '迪丽热巴', grid3: '高圆圆' } } }) </script> </body> 5.4 数组更新,过滤与排序 改变数组的一系列方法: push() 在末尾添加元素 pop() 将数组的最后一个元素移除 shift() 删除数组的第一个元素 unshift():在数组的第一个元素位置添加一个元素 splice() :可以添加或者删除函数—返回删除的元素三个参数: 第一个参数 表示开始操作的位置 第二个参数表示:要操作的长度 第三个为可选参数: sort():排序 reverse() 两个数组变动vue检测不到: 改变数组的指定项 改变数组长度 改变指定项: Vue.set(app.arr,1,”car”);app.arr.splice(1): 改变数组长度过滤:filter 解决方法: 1. set2. splice 5.5 方法和事件 [object MouseEvent] 5.5.1 基本用法 v-­on绑定的事件类似于原生 的onclick等写法: methods:{ handle:function (count) { count = count || 1; this.count += count; } } 如果方法中带有参数,但是你没有加括号,默认传原生事件对象event 5.5.2 修饰符 在vue中传入event对象用 $event 向上冒泡stop:阻止单击事件向上冒泡prevent:提交事件并且不重载页面self:只是作用在元素本身而非子元素的时候调用once: 只执行一次的方法 可以监听键盘事件: <input @keyup.13 ="submitMe"> ——­指定的keyCode vueJS为我们提供了: .enter .tab .delete 等等、、、、、、 六、 表单与v-­model 6.1 基本用法 v­-model: VUE提供了v­model指令, 用于在表单类元素上双向绑定事件 input和textarea 可以用于input框,以及textarea等 注意: 所显示的值只依赖于所绑定的数据,不再关心初始化时的插入的value 单选按钮: 单个单选按钮,直接用v­-bind绑定一个布尔值,用v-­model是不可以的 如果是组合使用,就需要v­-model来配合value使用,绑定选中的单选框的value值,此处所绑定的初始值可以随意给 复选框: 单个复选框,直接用定一个布尔值,可以用v­-model可以用v-­bind 多个复选框– 如果是组合使用,就需要v­-model来配合value使用,v-model绑定一个数组—如果绑定的是字符串,则会转化为true。false,与所有绑定的复选框的checked属性相对应 下拉框: 如果是单选,所绑定的value值初始化可以为数组,也可以为字符串,有value直接优先匹配一个value值,没有value就匹配一个text值 如果是多选,就需要v­-model来配合value使用,v­-model绑定一个数组,与复选框类似 v-­model一定是绑定在select标签上 总结一下:如果是单选,初始化最好给定字符串,因为v­model此时绑定的是静态字符串或者布尔值如果是多选,初始化最好给定一个数组 6.2 绑定值 单选按钮只需要用v­-bind给单个单选框绑定一个value值,此时,v­-model绑定的就是他的value值 复选框 下拉框在select标签上绑定value值对option并没有影响 6.3 修饰符 lazyv-model默认是在input输入时实时同步输入框的数据,而lazy修饰符,可以使其在失去焦点或者敲回车键之后在更新 number将输入 的字符串转化为number类型 trimtrim自动过滤输入过程中收尾输入的空格 七、 可复用性的组件详解 7.1 使用组件的原因 作用:提高代码的复用性 7.2 组件的使用方法 1. 全局注册 Vue.component('my-component',{ template:'<div>我是组件的内容</div>' }) 优点:所有的vue实例都可以用 缺点:权限太大,容错率降低 2. 局部注册 var app = new Vue({ el:'#app', components:{ 'my-component':{ template: '<div>我是组件的内容</div>' } } }) 3. vue组件的模板在某些情况下会受到html标签的限制,比如 <table> 中只能还有 <tr> , <td> 这些元素,所以直接在table中使用组件是无效的,此时可以使用is属性来挂载组件 <table> <tbody is="my-component"></tbody> </table> 7.3 组件使用的奇淫技巧 推荐使用小写字母加­-进行命名(必须) child, my-­componnet命名组件 template中的内容必须被一个DOM元素包括 ,也可以嵌套 在组件的定义中,除了template之外的其他选项---data,computed,methods data必须是一个方法 7.4 使用props传递数据 父亲向儿子传递数据 在组件中使用props来从父亲组件接收参数,注意,在props中定义的属性,都可以在组件中直接使用 props来自父级,而组件中data return的数据就是组件自己的数据,两种情况作用域就是组件本身,可以在template,computed,methods中直接使用 props的值有两种,一种是字符串数组,一种是对象 可以使用v-­bind动态绑定父组件来的内容 7.5 单向数据流 解释 : 通过 props 传递数据 是单向的了, 也就是父组件数据变化时会传递给子组件,但是反过来不行。 目的 :是尽可能将父子组件解稿,避免子组件无意中修改了父组件的状态。 应用场景: 业务中会经常遇到两种需要改变 props 的情况 一种是父组件传递初始值进来,子组件将它作为初始值保存起来,在自己的作用域下可以随意使用和修改。这种情况可以在组件 data 内再声明一个数据,引用父组件的 props步骤一:注册组件步骤二:将父组件的数据传递进来,并在子组件中用props接收步骤三:将传递进来的数据通过初始值保存起来 <div id="app"> <my-comp init-count="666"></my-comp> </div> <script> var app = new Vue({ el:'#app', components:{ 'my-comp':{ props:['init-count'], template:'<div>{{count}}</div>', data:function () { return{ count:this.initCount } } } } }) </script> 另一种情况就是 prop 作为需要被转变的原始值传入。这种情况用计算属性就可以了步骤一:注册组件步骤二:将父组件的数据传递进来,并在子组件中用props接收步骤三:将传递进来的数据通过计算属性进行重新计算 <body> <div id="data"> <input type="text" v-model="width"> <my-conponent :width="width"></my-conponent> </div> <script src="http://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script> var app = new Vue({ el:'#data', data:{ width:'' }, components:{ 'my-conponent':{ props:['width'], template:'<div :style="style"></div>', computed:{ style:function(){ return{ width:this.width+'px', background:'red', height:'30px' } } } } } }) </script> </body> 7.6 数据验证 @ vue组件中camelCased (驼峰式) 命名与 kebab­case(短横线命名) @ 在html中, myMessage 和 mymessage 是一致的,,因此在组件中的html中使用必须使用kebab-­case(短横线)命名方式。在html中不允许使用驼峰!!!!!!@ 在组件中, 父组件给子组件传递数据必须用短横线。在template中,必须使用驼峰命名方式,若为短横线的命名方式。则会直接保错。@ 在组件的data中,用this.XXX引用时,只能是驼峰命名方式。若为短横线的命名方式,则会报错。 验证的 type 类型可以是: String Number Boolean Object Array Function Vue.component ( ’ my-compopent ’, { props : { //必须是数字类型 propA : Number , //必须是字符串或数字类型 propB : [String , Number] , //布尔值,如果没有定义,默认值就是 true propC: { type : Boolean , default : true }, //数字,而且是必传 propD: { type: Number , required : true }, //如果是数组或对象,默认值必须是一个函数来返回 propE: { type : Array , default : function () { return [] ; } }, //自定义一个验证函数 propF: { validator : function (value) { return value > 10; } } } }); 7.7 组件通信 组件关系可分为父子组件通信、兄弟组件通信、跨级组件通信 7.7.1 自定义事件—子组件给父组件传递数据 使用v-­on 除了监昕 DOM 事件外,还可以用于组件之间的自定义事件。JavaScript 的设计模式 一一观察者模式, dispatchEvent 和 addEventListener这两个方法。 Vue 组件也有与之类似的一套模式,子组件用$emit()来 触发事件 ,父组件用$on()来 监听子组件的事件。直接来代码 第一步:自定义事件 第二步: 在子组件中用$emit触发事件,第一个参数是事件名,后边的参数是要传递的数据 第三步:在自定义事件中用一个参数来接受 <body> <div id="app"> <p>您好,您现在的银行余额是{{total}}元</p> <btn-compnent @change="handleTotal"></btn-compnent> <!-- <button-component @change="money"></button-component> --> </div> <script src="http://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script> var app = new Vue({ el: '#app', data: { total: 0 }, components: { 'btn-compnent': { template: '<div>\ <button @click="handleincrease">+10000</button> \ <button @click="handlereduce">-10000</button>\ </div>', data: function () { return { count: 0 } }, methods: { handleincrease: function () { this.count = this.count + 10000; this.$emit('change', this.count); }, handlereduce: function () { this.count = this.count - 10000; this.$emit('change', this.count); } } } }, methods: { handleTotal: function (total) { this.total = total; } } }) </script> </body> 7.7.2 在组件中使用v-­model $emit的代码,这行代码实际上会触发一个 input事件, ‘input’后的参数就是传递给v-­model绑定的属性的值 v-­model 其实是一个语法糖,这背后其实做了两个操作: v­-bind 绑定一个 value 属性 v­-on 指令给当前元素绑定 input 事件 要使用v-­model,要做到: 接收一个 value 属性。 在有新的 value 时触发 input 事件 <body> <div id="app"> <p>您好,您现在的银行余额是{{total}}元</p> <btn-compnent v-model="total"></btn-compnent> </div> <script src="http://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script> //关于 var app = new Vue({ el: '#app', data: { total: 0 }, components: { 'btn-compnent': { template: '<div>\ <button @click="handleincrease">+1</button> \ <button @click="handlereduce">-1</button>\ </div>', data: function () { return { count: 0 } }, methods: { handleincrease: function () { this.count++; ----------------------注意观察.这一行,emit的是input事件---------------- this.$emit('input', this.count); }, handlereduce: function () { this.count--; this.$emit('input', this.count); } } } }, methods: { /* handleTotal:function (total) { this.total = total; }*/ } }) </script> </body> 7.7.3 非父组件之间的通信 官网描述:

优秀的个人博客,低调大师

架构师的选择,Pulsar还是Kafka?

介绍 最近,我一直在研究Pulsar及其与Kafka的比较。快速搜索将显示两个最著名的开源消息传递系统之间存在当前的"战争"。 作为Kafka的用户,我确实对Kafka的某些问题感到困惑,并且我对Pulsar感到非常失望。所以最后,我设法花了一些时间进行研究,并且做了很多研究。在本文中,我将重点介绍Pulsar的优势,并为您提供一些理由,使您对比Kafka来考虑它。但是,请在产品使用,支持,社区,文档等方面明确一点;Kafka显然超过了Pulsar,并且只有在本文中讨论的大多数优点都适合您的用例的情况下,才考虑使用Pulsar。让我们开始! Kafka基础知识 Kafka是消息传递系统之王。它由LinkedIn于2011年创建,并在Confluent的支持下得到了广泛的传播。Confluent已向开源社区发布了许多新功能和附加组件,例如用于模式演化的Schema Registry,用于从其他数据源轻松流式传输的Kafka Connect等。数据库到Kafka,Kafka Streams进行分布式流处理,最近使用KSQL对Kafka主题执行类似SQL的查询等等。它还具有用于许多系统的许多连接器,有关更多详细信息,请查看Confluent Platform。 Kafka快速,易于安装,非常受欢迎,可用于广泛的范围或用例。从开发人员的角度来看,尽管Apache Kafka一直很友好,但在操作上却是一团糟。因此,让我们回顾一下Kafka的一些痛点。 > Kafka example. Source: https://talks.rmoff.net/pZC6Za/slides Kafka的问题 · 扩展Kafka十分棘手,这是由于代理还存储数据的耦合体系结构所致。剥离另一个代理意味着它必须复制主题分区和副本,这非常耗时。 · 没有与租户完全隔离的本地多租户。 · 存储可能会变得非常昂贵,尽管可以长时间存储数据,但是由于成本问题,很少使用它。 · 万一副本不同步,有可能丢失消息。 · 必须提前计划和计算代理,主题,分区和副本的数量(以适应计划的未来使用量增长),以避免扩展问题,这非常困难。 · 如果仅需要消息传递系统,则使用偏移量可能会很复杂。 · 集群重新平衡会影响相连的生产者和消费者的性能。 · MirrorMaker Geo复制机制存在问题。像Uber这样的公司已经创建了自己的解决方案来克服这些问题。 如您所见,大多数问题与操作方面有关。尽管安装起来相对容易,但Kafka难以管理和调整。而且,它还没有像它可能的那样灵活和有弹性。 Pulsar基础知识 Pulsar由Yahoo在2013年创建,并于2016年捐赠给Apache基金会。Pulsar现在是Apache的顶级项目。Yahoo,Verizon,Twitter等公司在生产中使用它来处理数百万条消息。它具有许多功能,并且非常灵活。它声称比Kafka更快,因此运行成本更低。它旨在解决Kafka的大部分难题,使其更易于扩展。 Pulsar非常灵活;它可以像Kafka这样的分布式日志,也可以像RabbitMQ这样的纯消息传递系统。它具有多种类型的订阅,几种交付保证,保留策略以及几种处理模式演变的方法。它还有很多功能…… > Pulsar architecture: https://pulsar.apache.org/docs/en/concepts-architecture-overview/ Pulsar的特性 · 由于内置了多租户,因此不同的团队可以使用相同的集群并将其隔离。这解决了许多管理难题。它支持隔离,身份验证,授权和配额。 · 多层体系结构:Pulsar将所有主题数据存储在由Apache BookKeeper支持的专业数据层中,作为数据分类帐。存储和消息传递的分离解决了扩展,重新平衡和维护集群的许多问题。它还提高了可靠性,几乎不可能丢失数据。另外,在读取数据时,可以直接连接到Bookeeper,而不会影响实时摄取。例如,可以使用Presto对主题执行SQL查询,类似于KSQL,但请放心,这不会影响实时数据处理。 · 虚拟主题。由于采用n层体系结构,因此对主题的数量没有限制,主题及其存储是分离的。还可以创建非持久性主题。 · N层存储。Kafka的一个问题是,存储可能变得昂贵。因此,它很少用于存储"冷"数据,并且消息经常被删除。并且仍然向客户展示透明视图;客户端可以从时间开始读取,就像所有消息都存在于日志中一样。 ·Pulsar函数。易于部署,轻量级计算过程,对开发人员友好的API,无需运行自己的流处理引擎(如Kafka)。 · 安全性:它具有内置的代理,多租户安全性,可插入身份验证等等。 · 快速重新平衡。分区分为易于重新平衡的段。 · 服务器端重复数据删除和无效字段。无需在客户端中执行此操作,也可以在压缩期间执行重复数据删除。 · 内置架构注册表。支持多种策略,非常易于使用。 · 地理复制和内置发现。将群集复制到多个区域非常容易。 · 集成的负载均衡器和Prometheus指标。 · 多重集成:Kafka,RabbitMQ等。 · 支持许多编程语言,例如GoLang,Java,Scala,Node,Python… · 客户端不需要知道分片和数据分区,这是在服务器端透明进行的。 > List of features: https://pulsar.apache.org/ 如您所见,Pulsar具有许多有趣的功能。 Pulsar 动手 开始使用Pulsar非常容易。确保已安装JDK! · 下载Pulsar并解压缩: $ wget https://archive.apache.org/dist/pulsar/pulsar-2.6.1/apache-pulsar-2.6.1-bin.tar.gz 2.下载连接器(可选): $ wget https://archive.apache.org/dist/pulsar/pulsar-2.6.1/connectors/{connector}-2.6.1.nar 3.下载nar文件后,将文件复制到pulsar目录中的connectors目录 4.启动Pulsar! $ bin/pulsar standalone Pulsar提供了一个称为pulsar-client的CLI工具,我们可以使用它与集群进行交互。 产生消息: $ bin/pulsar-client produce my-topic --messages "hello-pulsar" 阅读消息: $ bin/pulsar-client consume my-topic -s "first-subscription" Akka流示例 作为一个客户示例,让我们在Akka上使用Pulsar4s! 首先,我们需要创建一个Source来使用数据流,所需要的只是一个函数,该函数将按需创建使用者并查找消息ID: val topic = Topic("persistent://standalone/mytopic")val consumerFn = () => client.consumer(ConsumerConfig(topic, subscription)) 然后,我们传递consumerFn函数来创建源: import com.sksamuel.pulsar4s.akka.streams._val pulsarSource = source(consumerFn, Some(MessageId.earliest)) Akka源的物化值是Control的一个实例,该对象提供了一种"关闭"方法,可用于停止使用消息。现在,我们可以像往常一样使用Akka Streams处理数据。 要创建一个接收器: val topic = Topic("persistent://standalone/mytopic")val producerFn = () => client.producer(ProducerConfig(topic))import com.sksamuel.pulsar4s.akka.streams._val pulsarSink = sink(producerFn) 完整示例摘自Pulsar4s: Pulsar函数示例 Pulsar函数处理来自一个或多个主题的消息,对其进行转换并将结果输出到另一个主题: > Pulsar Functions. Source: https://pulsar.apache.org/docs/en/functions-overview/ 可以在两个接口之间进行选择以编写函数: · 语言本机界面:不需要特定于Pulsar的库或特殊的依赖项。无法访问上下文。仅支持Java和Python。 · Pulsar Function SDK:可用于Java / Python / Go,并提供更多功能,包括访问上下文对象。 使用语言本机接口非常容易,您只需编写一个简单的函数即可转换消息: def process(input):return "{}!".format(input) 用Python编写的这个简单函数只是向所有传入的字符串添加一个感叹号,并将结果字符串发布到主题。 要使用SDK,您需要导入依赖项,例如在Go中,我们将编写: package mainimport ("context""fmt""github.com/apache/pulsar/pulsar-function-go/pf")func HandleRequest(ctx context.Context, in []byte) error {fmt.Println(string(in) + "!")return nil}func main() {pf.Start(HandleRequest)} 要发布无服务器功能并将其部署到集群,我们使用pulsar-admin CLI,如果使用Python,我们将使用: $ bin/pulsar-admin functions create \--py ~/router.py \--classname router.RoutingFunction \--tenant public \--namespace default \--name route-fruit-veg \--inputs persistent://public/default/basket-itemsPulsar Functions的一个重要功能是您可以在发布该函数时设置交付保证:$ bin/pulsar-admin functions create \--name my-effectively-once-function \--processing-guarantees EFFECTIVELY_ONCE 有以下选择: Pulsar的优势 让我们回顾一下与Kafka相比的主要优势: · 更多功能:Pulsar函数,多租户,架构注册表,n层存储,多种使用模式和持久性模式等。 · 更大的灵活性:3种订阅类型(独占,共享和故障转移),您可以在一个订阅上收听多个主题。持久性选项:非持久(快速),持久,压缩(每个消息仅最后一个键)。可以选择交付保证,它具有服务器端重复数据删除和无效字样。许多保留政策和TTL。 · 无需提前定义扩展需求。 · 支持排队和流媒体。因此它可以像RabbitMQ或Kafka。 · 由于存储与代理分离,因此扩展性更好。重新平衡更快,更可靠。 · 易于操作:得益于去耦和n层存储。管理员REST API也很棒。 · 与Presto的SQL集成,可直接查询存储而不会影响代理。 · 借助n层自动存储选项,可以更便宜地存储。 · 更快:许多基准测试在各种情况下都表现出更好的性能。Pulsar声称具有较低的延迟和更好的扩展功能。但是,这正受到Confluent的挑战,因此,请带着盐味做,并自己制定基准。 · Pulsar Functions将无服务器计算带到您的消息传递平台。 · 集成架构注册表支持轻松的架构演变 · 集成的负载平衡器和Prometheus指标。 · 地理复制效果更好,更易于设置。Pulsar还内置了发现能力。 · 可以创建的主题数量没有限制。 · 与Kafka兼容,易于集成。 Pulsar的问题 Pulsar并不完美,Kafka之所以流行是有原因的,它做一件事并且做得很好。Pulsar试图解决太多领域,但没有超越任何一个领域。让我们总结一下Pulsar的一些问题: · 受欢迎程度:Pulsar不那么受欢迎。它缺乏支持,文档和实际使用情况。对于大型组织而言,这是一个主要问题。 · 由于n层体系结构,它需要更多组件:Bookkeeper。 · 在平台内没有对流应用程序的适当支持。Pulsar函数与Kafka Streams不同,它们更简单,并且不用于实时流处理。您无法进行有状态处理。 · 与Kafka相比,插件和客户端更少。此外,掌握Pulsar技能的人较少,因此需要在内部学习。 · 它在云中的支持较少。Confluent具有托管云产品。 Confluent在Pulsar和Kafka之间进行了比较,可以在其中进行更多的详细说明。该博客还回答了有关Kafka与Pulsar的一些问题,但请注意,这些问题可能有偏见。 Pulsar使用案例 Pulsar可用于广泛的用例: · 发布/订阅队列消息传递 · 分布式日志 · 事件源壁架,用于永久性事件存储 · 微服务 · SQL分析 · 无服务器功能 什么时候应该考虑Pulsar · 需要像RabbitMQ这样的队列,也需要像Kafka这样的流处理程序。 · 需要简单的地理复制。 · 多租户是必须具备的,并且想确保每个团队的访问权限。 · 需要将所有消息保留很长时间,并且不想将其卸载到另一个存储中。 · 性能对你至关重要,基准测试表明Pulsar提供了更低的延迟和更高的吞吐量。 · 在本地运行,没有设置Kafka的经验,但具有Hadoop经验。 请注意,如果在云中,请考虑基于云的解决方案。云提供商拥有涵盖某些用例的不同服务。例如,对于队列消息传递,云提供商提供了许多服务,例如Google pub / sub。对于分布式日志,有Confluent云或AWS Kinesis。云提供商还提供了非常好的安全性。Pulsar的优势在于可以在一个平台上提供许多功能。一些团队可能将其用作微服务的消息传递系统,而另一些团队则将其用作数据处理的分布式日志。 结论 我是Kafka的忠实粉丝,这就是为什么我对Pulsar如此感兴趣。竞争是好的,它驱动创新。 Kafka是一种成熟,富有弹性且经过战斗考验的产品,在世界范围内获得了巨大成功。没有它,我无法想象任何公司。但是,我确实看到Kafka成为其自身成功的受害者,巨大的增长减慢了功能开发的速度,因为它们需要支持这么多大型公司。删除ZooKeeper依赖项等重要功能花费的时间太长。这为诸如Pulsar等工具蓬勃发展创造了空间。解决Kafka的一些问题并添加更多功能。 但是,Pulsar仍然很不成熟,在投入生产之前,我会格外小心。在将Pulsar纳入你的组织之前,进行分析,进行基准测试,研究并编写概念证明。从小处着手,在从Kafka迁移之前进行概念验证,并在决定进行完全迁移之前先评估影响。 作者:闻数起舞链接:https://0x9.me/cYZFN 本文分享自微信公众号 - JAVA高级架构(gaojijiagou)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

架构师》反思:系统可靠性

最近系统学习了一个系统可靠性及其相关知识,今天在这总结一下。 首先,什么是系统的可靠性呢?系统的可靠性是指在规定的时间内及规定的环境下完成规定功能的能力,也就是系统的无故障运行概率。 我会从以下几个方面来归纳主要内容: 1. 故障模型 2. 可靠性模型 3. 可靠性指标 4. 可靠性设计 故障模型 系统故障是指硬件或者软件的错误状态,一般引进故障的原因是这些:部件的失效、环境的物理干扰、操作错误或不正确的设计。 按照时间的长短,故障可以分为:永久性、间歇性、瞬时性。 故障的级别有:逻辑级故障、数据结构级故障、软件故障和差错故障、系统级故障。 可靠性模型 与故障模型想对应的,就是系统的可靠性模型。常用的有以下三种:时间模型、故障植入模型和数据模型。 这三种模型暂时还没有看懂(晕)。 可靠性指标 可靠性指标,主要有以下几个: 平均无故障时间(MTTF-Mean Time To Failure) 它表示一个系统平均情况下,正常运行的时间。 与它相关的指标是“失效率”U,关系: U = 1 / MTTF。 平均故障修复时间(MTTR-Mean Time To Fix/Repire) 平均每次修复所需要的时间 平均故障间隔时间(MTBF-Mean Time Between Failure) 一看就知道,MTBF = MTTF + MTTR。 在实际情况下,一般MTTR都会比较小,所以我们近似地认为MTBF = MTTF。 MTTF是用来说明一个软件系统能够正常运行的时间的指标。它越大,说明该系统越可靠。计算方法很简单, 可靠性计算 一个系统的可靠性计算往往不能直接得出。这是因为计算机系统是一个复杂的系统,影响其可靠性的因素也非常复杂。所以我们需要为其建立适当的数据模型,把大系统划分为若干子系统,然后再根据一定原则进行组合计算。 这种计算方法,可以简化分析的过程。 对于系统的划分,我们可以把它分为:串联系统、并联系统、模冗余系统、混联系统。(其中模冗余系统是M个并联的子系统中,需要有N个以上的子系统能正常工作,整个系统才能正常工作。这种系统,常在并联后加上一个表决器。) 计算这些系统可靠性时,我们需要计算出每个子系统的失效率,然后根据概率的加法原则(串联系统)和乘法原则(并联系统)进行综合运算,最后得出整个系统的可靠性。 可靠性设计 本小节是整单的重点。 提高系统可靠性的方法,主要是两种:避错和容错。避错主要是指提前做一些措施,避免系统在运行中出现错误。而容错则是指系统在运行中部分组件出现错误,仍然不失效,可以继续运行;或者当数据、文件损坏或丢失后,系统可以自动将这些数据恢复到以前的状态,使系统能够继续正常运行。 测试就是最常用的一种避错技术。而容错则一般使用冗余来实现。 冗余技术 冗余技术是容错的主要手段。主是通过对资源的冗余,包括硬件、软件、信息、时间等,可以使系统的容错性得到较大的提高。 结构冗余 这里又分静态冗余和动态冗余。 静态冗余一般是指增加同样功能的部件,同时运行,最后由表决器对结果进行表决,以多数结果作为系统的最终结果。 动态冗余则是做一些多重的设备储备,当系统检测到某一部件失效时,启用相应的新部件代替它进行工作。这里有检测、切换和恢复的过程,所以称之为动态冗余。这些多余的设备储备,可与主模块一起工作,也可以不工作,分别称为热备份和冷备份。冷备份缺点是当主模块失效时,备份系统可能无法及时衔接上,因为备份机无法获取到原来机器上所有的数据。 其实,我们还可以结合以上两种冗余的优缺点,使用混合冗余的方式,对系统进行结构性冗余设计。 信息冗余 添加一些额外的信息用于保证其正确性。例如:纠错码。 时间冗余 类似结构冗余,不过这里是在同一设备上执行重复计算。 故障恢复策略 如果故障已经发生,则需要一定的方法来恢复故障。一般有两种恢复策略:向前和向后。 向前恢复是指不停止当前的计算,而把系统从不连贯的状态恢复为连贯的正确状态,需要有错误的详细说明。例如我们可以在系统发生故障时,把异常信息都捕获到并存储起来备案,然后尽量让系统继续执行。这也是平常最常用的策略。 后向恢复是把系统恢复到之前的一个状态,然后继续执行。这种方法比较简单,但是却造成程序运行的不连贯性,不适应一些高要求系统,如实时系统。 软件容错 主要有以下几种方式: 恢复块方法 这种方法是一种动态的故障屏蔽技术,采用的是后向恢复策略。它提供相同功能的主模块和多个备用模块,当主功能计算完成后需要进行验证测试,如果测试没有通过,则会使用备用模块进行计算,如果还是没有通过,则继续使用下一个备用模块。 设计时应该保证实现主块和备用块之间的独立性,使其不会相互影响。 N版本程序设计 此法是一种静态故障屏蔽技术,采用前向恢复策略。 采用多个相同功能的N份程序同时运行,使用表决器进行最后结果的表决。 重点在于: N版本的程序设计应该使用不同的方法,如不同的设计语言、不同的开发环境和工具。 同时,由于N个程序同时运行,最后同时表决,所以需要解决多个程序间的并发性。 防卫式程序设计 此法的基本思想是在程序中包含错误检测代码。一旦错误发生,程序能撤销错误状态,恢复到一个已知和正确状态中去。包括错误检测、破坏估计和错误恢复三个方面。 这种方式主要是以软件的形式来容错,也就是说软件自身有较强的容错性,较为常用。 集群 集群是由两个以上的节点机(一般是服务器)构成的一种松散耦合的计算节点集合,为用户提供网络服务或应用程序(包括数据库、Web服务和文件服务等)的单一客户视图,同时提供接近容错机的故障恢复能力。 一说到集群,一般会想到使用它来为应用程序提供一种可扩展的高性能设计。但是集群同时还可以为应用程序提供较高的容错能力。以下是集群的分类: 高性能计算科学集群、负载均衡集群、高可用性集群 在实际应用中,这三种基本类型经常会混合使用。 硬件配置 (1)镜像服务器双机 使用两台单独的服务器做镜像服务器,之间使用镜像软件通过网络同步数据。镜像服务器的性能比单一服务器的性能要低,适用对集群系统要求不高的用户, 特点:简单、价格最低廉、可靠性较低、占用网络资源、性能较低。 (2)双机和磁盘阵列柜 此方式同样使用双服务器,同时后端的数据存储使用磁盘阵列柜。阵列柜为双机提供逻辑盘阵访问,并不随意扩展新的物理磁盘。 此方式不需要进行数据的同步,所以性能较镜像服务器要高出很多。但是可能会导致“单点错”,即系统中某一部件或某个应用程序发生故障时,导致所有系统全部宕机。如磁盘阵列如果出错,可能会导致存储的数据全部丢失。 特点:性能较高、可能导致单点错误。 (3)光纤通道双机双控集群系统 使用光纤来组建通道进行连接。允许镜像配置。 特点:扩展性强、费用较高。 随着硬件和网络操作系统的发展。集群技术将会在系统可用性、高可靠性和系统冗余方面逐步提高。 (如以后的集群可以依靠集群文件系统实现对系统中所有文件、设备和网络资源的全局访问,并且生成一个完整的系统映像。) 论文阅读总结 可靠性工程 原文链接:http://wenku.baidu.com/view/98b021225901020207409c76.html 该文从工程学的角度来说明了可靠性工程如何开展,并举例说明如何在软件开发过程中应用可靠性工程。 概念及发展 简单的定义:基于软件产品的可靠性进行预测、建模、估计、度量及管理。 其目标是提高软件系统的可靠性。为达到这个目的,我们需要明白失效产生的原因。 核心问题:如何开发出高可靠性的软件;另一问题:如何评估已有系统的可靠性。 在软件开发中的应用 可靠性工程贯穿于软件开发生命周期的各个阶段。 项目开发计划及需求分析阶段 本阶段中,主要是要明确可靠性需求,建立系统的可靠指标。一般情况下,可靠性工作可如下安排: 1)确定功能概图 功能概图主要描述系统中各功能及其使用环境和被使用的概率。 2)对失效进行定义和分类 3)确实用户的可靠性需求 4)平衡性研究 5)建立可靠性指标 软件设计和功能实现阶段 该阶段主要工作: 1)在模块间分配可靠性指标 分解系统为多模块,各模块间分配指标,使得最后计算出的总指标满足需求。 2)按可靠性指标进行设计 有关可靠性设计的内容,参见在上文中内容。 3)根据功能概图集中资源配置 4)控制错误的引入和传播 软件审查(代码审核)、软件测试(单元测试和集成测试)。 5)测试现成软件的可靠性 系统测试和现场试运行阶段 该阶段是保证可靠性的最后阶段。主要工作: 1)确实操作概图 操作概图主要描述系统最后可以使用的各操作(命令)及其使用环境和被使用的概率。 2)可靠性增强测试 系统测试、交付测试。 按照操作概图中的概率执行测试用例,模仿用户的应用方式测试。 3)根据测试来证明是否已经达到可靠性指标 收集失效数据,规划室额外的测试。 4)现场可靠性评估 分析数据,分析差异原因。 维护阶段 主要工作: 1)规划交付使用后的人员需求。 2)监视现场可靠性,并做出适当的调整。 3)监视并维护新功能引起的失效。 4)分析软件交付后失效的产生原因,指导工程改进,降低引入类似错误的可能性。 成功案例 文中以一交换机的研发做为例子,说明可靠性工程的应用,给产品带来了惊人的好处: 问题数下降、维护费用下降、测试件间隔缩短、引入新产品的间隔缩短、客户满意度提升。 原因如下: ⑴把可靠性作为确定是否发行的标准,可避免用户在使用中反映过多问题和进行相应的维护工作。 ⑵采用“操作概图驱动”的测试方法,提高了测试效率;20%的操作覆盖了95%的应用,20%的错误导致了95%的实效;先测试20%的使用最频繁的操作可以加速可靠性的提高。 结束语 国内外还未能有系统化的可靠性工程学理论。我们需要不断结合实践进行研究和总结,为使可靠性工作成为有计划、有组织和有目标的研究工作而努力。 高可靠性测试 原文链接:http://tech.it168.com/a2008/0829/202/000000202483.shtml 该文以作者参与的CraftGS系统为例,讲述了如何在系统中应用测试技术保证软件的高可靠性,这些技术包括:软件验证、软件确认、软件测试管理。 综述 高可靠性软件泛指一类软件:该类软件运行过程中若出现故障会引发重大灾难性事故或经济损失。通常航天型号软件、银行系统软件、医疗行业软件、通讯行业软件等均属此范畴。 作者的CraftGS系统就是可靠性要求较高的一个软件系统,其中各子系统的可靠性指标都在0.95以上。 方案:软件验证技术 + 软件确认技术 + 软件测试管理。 验证技术主要是人工完成,方法有:面对面质询、文档抽查、非正式会议、同行评审等等。 软件确认技术则主要着眼于排除程序代码中的错误。目前支持很好的自动化。 工程质量的把控,主要依靠测试管理,分为:“软件测试团队组织管理、软件测试计划管理、软件缺陷(错误)跟踪管理以及软件测试件管理”四大部分。 软件验证技术 主要包含以下方面: 需求规格说明验证 保证用户的所有需求(功能、业务、非功能、约束)都已经被分配到软件需求规格说明的各需求项中。 设计规格说明验证 主要是逐步检查概要设计和详细是否全部分配了之前的分析成果。其中,还要进行数据库设计的验证。 代码验证 包括:代码规范审查、代码审查和代码静态分析。 交付验证 在测试完成后,系统交付客户前,需要进行交付验证和测试。交付验证包括安装验证和使用验证两部分,以确保软件和用户手册匹配。 软件确认技术 其实这里是测试技术。有: 单元测试(白盒) 建构桩模块和驱动模块以驱动被测单元(函数、类、模块)运行,使用设计好的测试用例对各单元进行测试。 集成测试(灰盒) 验证各模块组装后的软件是否能达到概要设计规格说明中模块的设计目标;各模块内部是否存在冲突,保模块能否正常工作。一般采用自底向上按集成度由小到大进行集成测试。 系统测试(黑盒) 检测系统是否满足软件需求规格说明中的各需求项,包括:业务需求、功能需求、非功能需求(质量属性)及约束。虽然不涉及代码,但是由于需求项涉及的领域较广,所以测试方法多而杂,如: 功能测试、执行路径测试、可靠性测试、压力测试、可恢复性测试、可移植性测试……等。 这些测试的特点:在一定环境条件下(如:模拟现场或极端条件),设计并运行各种测试用例,根据测试结果数据,评估软件系统是否符合软件需求项的各类要求。 交付测试 交付测试的主要参与者是目标客户,客户参与越多越好。主要进行:安装测试、可用性测试、alpha测试、beta测试等。 软件测试管理 软件测试团队组织管理 是否能组建一个合适的测试团队,直接影响到测试工作的进展和质量。作者的CraftGS系统中的测试团队,有资深测试专家、测试人员、兼职人员(同行评审)、测试新手。 软件测试计划管理 其实就是安排好测试的流程。 主要有:软件测试策划、软件测试技术剪裁、测试进度管理、成本管理。 软件缺陷(错误)跟踪管理 跟踪一个错误的全生命周期,确保每一个错误都能及时纠正及不引入新的错误。当测试人员提交错误后,需要督促开发团队及时修正,并在修正完成后进行回归测试。一般使用BUG管理系统即可。 软件测试件管理 努力建设好测试团队的财富库并对测试团队成员进行技能培训以帮助他们能使用好这个财富库。 测试件(Testware)是指测试工作形成的产品,包括:实践中积累的经验教训、测试技巧、测试工具、规格文档及一些通用脚本。 测试件管理工作主要是:建设与培训。 结语 目前对高可靠性软件如何实话软件测试技术仍是一个颇不成熟的领域,缺少一种体系化的方法。 本文转自BloodyAngel博客园博客,原文链接:http://www.cnblogs.com/zgynhqf/archive/2010/08/16/1800767.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

架构师必看 京东咚咚架构演进

咚咚是什么?咚咚之于京东相当于旺旺之于淘宝,它们都是服务于买家和卖家的沟通。 自从京东开始为第三方卖家提供入驻平台服务后,咚咚也就随之诞生了。 我们首先看看它诞生之初是什么样的。 1.0 诞生(2010 – 2011) 为了业务的快速上线,1.0 版本的技术架构实现是非常直接且简单粗暴的。 如何简单粗暴法?请看架构图,如下。 1.0 的功能十分简单,实现了一个 IM 的基本功能,接入、互通消息和状态。 另外还有客服功能,就是顾客接入咨询时的客服分配,按轮询方式把顾客分配给在线的客服接待。 用开源 Mina 框架实现了 TCP 的长连接接入,用 Tomcat Comet 机制实现了 HTTP 的长轮询服务。 而消息投递的实现是一端发送的消息临时存放在 Redis 中,另一端拉取的生产消费模型。 这个模型的做法导致需要以一种高频率的方式来轮询 Redis 遍历属于自己连接的关联会话消息。 这个模型很简单,简单包括多个层面的意思:理解起来简单;开发起来简单;部署起来也简单。 只需要一个 Tomcat 应用依赖一个共享的 Redis,简单的实现核心业务功能,并支持业务快速上线。 但这个简单的模型也有些严重的缺陷,主要是效率和扩展问题。 轮询的频率间隔大小基本决定了消息的延时,轮询越快延时越低,但轮询越快消耗也越高。 这个模型实际上是一个高功耗低效能的模型,因为不活跃的连接在那做高频率的无意义轮询。 高频有多高呢,基本在 100 ms 以内,你不能让轮询太慢,比如超过 2 秒轮一次,人就会在聊天过程中感受到明显的会话延迟。 随着在线人数增加,轮询的耗时也线性增长,因此这个模型导致了扩展能力和承载能力都不好,一定会随着在线人数的增长碰到性能瓶颈。 1.0 的时代背景正是京东技术平台从 .NET 向 Java 转型的年代,我也正是在这期间加入京东并参与了京东主站技术转型架构升级的过程。 之后开始接手了京东咚咚,并持续完善这个产品,进行了三次技术架构演进。 2.0 成长(2012) 我们刚接手时 1.0 已在线上运行并支持京东 POP(开放平台)业务,之后京东打算组建自营在线客服团队并落地在成都。 不管是自营还是 POP 客服咨询业务当时都起步不久,1.0 架构中的性能和效率缺陷问题还没有达到引爆的业务量级。 而自营客服当时还处于起步阶段,客服人数不足,服务能力不够,顾客咨询量远远超过客服的服务能力。 超出服务能力的顾客咨询,当时我们的系统统一返回提示客服繁忙,请稍后咨询。 这种状况导致高峰期大量顾客无论怎么刷新请求,都很可能无法接入客服,体验很差。 所以 2.0 重点放在了业务功能体验的提升上,如下图所示。 针对无法及时提供服务的顾客,可以排队或者留言。 针对纯文字沟通,提供了文件和图片等更丰富的表达方式。 另外支持了客服转接和快捷回复等方式来提升客服的接待效率。 总之,整个 2.0 就是围绕提升客服效率和用户体验。 而我们担心的效率问题在 2.0 高速发展业务的时期还没有出现,但业务量正在逐渐积累,我们知道它快要爆了。 到 2012 年末,度过双十一后开始了 3.0 的一次重大架构升级。 3.0 爆发(2013 – 2014) 经历了 2.0 时代一整年的业务高速发展,实际上代码规模膨胀的很快。 与代码一块膨胀的还有团队,从最初的 4 个人到近 30 人。 团队大了后,一个系统多人开发,开发人员层次不一,规范难统一,系统模块耦合重,改动沟通和依赖多,上线风险难以控制。 一个单独 tomcat 应用多实例部署模型终于走到头了,这个版本架构升级的主题就是服务化。 服务化的第一个问题如何把一个大的应用系统切分成子服务系统。 当时的背景是京东的部署还在半自动化年代,自动部署系统刚起步,子服务系统若按业务划分太细太多,部署工作量很大且难管理。 所以当时我们不是按业务功能分区服务的,而是按业务重要性级别划分了 0、1、2 三个级别不同的子业务服务系统。 另外就是独立了一组接入服务,针对不同渠道和通信方式的接入端,见下图。 更细化的应用服务和架构分层方式可见下图。 这次大的架构升级,主要考虑了三个方面:稳定性、效率和容量。 做了下面这些事情: 业务分级、核心、非核心业务隔离 多机房部署,流量分流、容灾冗余、峰值应对冗余 读库多源,失败自动转移 写库主备,短暂有损服务容忍下的快速切换 外部接口,失败转移或快速断路 Redis 主备,失败转移 大表迁移,MongoDB 取代 MySQL 存储消息记录 改进消息投递模型 前 6 条基本属于考虑系统稳定性、可用性方面的改进升级。 这一块属于陆续迭代完成的,承载很多失败转移的配置和控制功能在上面图中是由管控中心提供的。 第 7 条主要是随着业务量的上升,单日消息量越来越大后,使用了 MongoDB 来单独存储量最大的聊天记录。 第 8 条是针对 1.0 版本消息轮询效率低的改进,改进后的投递方式如下图所示: 不再是轮询了,而是让终端每次建立连接后注册接入点位置,消息投递前定位连接所在接入点位置再推送过去。 这样投递效率就是恒定的了,而且很容易扩展,在线人数越多则连接数越多,只需要扩展接入点即可。 其实,这个模型依然还有些小问题,主要出在离线消息的处理上,可以先思考下,我们最后再讲。 3.0 经过了两年的迭代式升级,单纯从业务量上来说还可以继续支撑很长时间的增长。 但实际上到 2014 年底我们面对的不再是业务量的问题,而是业务模式的变化。 这直接导致了一个全新时代的到来。 4.0 涅槃(2015 至今 ) 2014 年京东的组织架构发生了很大变化,从一个公司变成了一个集团,下设多个子公司。 原来的商城成为了其中一个子公司,新成立的子公司包括京东金融、京东智能、京东到家、拍拍、海外事业部等。 各自业务范围不同,业务模式也不同,但不管什么业务总是需要客服服务。 如何复用原来为商城量身订做的咚咚客服系统并支持其他子公司业务快速接入成为我们新的课题。 最早要求接入的是拍拍网,它是从腾讯收购的,所以是完全不同的账户和订单交易体系。 由于时间紧迫,我们把为商城订做的部分剥离,基于 3.0 架构对接拍拍又单独订做了一套,并独立部署,像下面这样。 虽然在业务要求的时间点前完成了上线,但这样做也带来了明显的问题: 复制工程,定制业务开发,多套源码维护成本高 独立部署,至少双机房主备外加一个灰度集群,资源浪费大 以前我们都是面向业务去架构系统,如今新的业务变化形势下我们开始考虑面向平台去架构,在统一平台上跑多套业务,统一源码,统一部署,统一维护。 把业务服务继续拆分,剥离出最基础的 IM 服务,IM 通用服务,客服通用服务,而针对不同的业务特殊需求做最小化的定制服务开发。 部署方式则以平台形式部署,不同的业务方的服务跑在同一个平台上,但数据互相隔离。 服务继续被拆分的更微粒化,形成了一组服务矩阵(见下图)。 而部署方式,只需要在双机房建立两套对等集群,并另外建一个较小的灰度发布集群即可,所有不同业务都运行在统一平台集群上,如下图。 更细粒度的服务意味着每个服务的开发更简单,代码量更小,依赖更少,隔离稳定性更高。 但更细粒度的服务也意味着更繁琐的运维监控管理,直到今年公司内部弹性私有云、缓存云、消息队列、部署、监控、日志等基础系统日趋完善, 使得实施这类细粒度划分的微服务架构成为可能,运维成本可控。 而从当初 1.0 的 1 种应用进程,到 3.0 的 6、7 种应用进程,再到 4.0 的 50+ 更细粒度的不同种应用进程。 每种进程再根据承载业务流量不同分配不同的实例数,真正的实例进程数会过千。 为了更好的监控和管理这些进程,为此专门定制了一套面向服务的运维管理系统,见下图。 统一服务运维提供了实用的内部工具和库来帮助开发更健壮的微服务。 包括中心配置管理,流量埋点监控,数据库和缓存访问,运行时隔离,如下图所示是一个运行隔离的图示: 细粒度的微服务做到了进程间隔离,严格的开发规范和工具库帮助实现了异步消息和异步 HTTP 来避免多个跨进程的同步长调用链。 进程内部通过切面方式引入了服务增强容器 Armor 来隔离线程, 并支持进程内的单独业务降级和同步转异步化执行。而所有这些工具和库服务都是为了两个目标: 让服务进程运行时状态可见 让服务进程运行时状态可被管理和改变 最后我们回到前文留下的一个悬念,就是关于消息投递模型的缺陷。 一开始我们在接入层检测到终端连接断开后,消息无法投递,再将消息缓存下来,等终端重连接上来再拉取离线消息。 这个模型在移动时代表现的很不好,因为移动网络的不稳定性,导致经常断链后重连。 而准确的检测网络连接断开是依赖一个网络超时的,导致检测可能不准确,引发消息假投递成功。 新的模型如下图所示,它不再依赖准确的网络连接检测,投递前待确认消息 id 被缓存,而消息体被持久存储。 等到终端接收确认返回后,该消息才算投妥,未确认的消息 id 再重新登陆后或重连接后作为离线消息推送。 这个模型不会产生消息假投妥导致的丢失,但可能导致消息重复,只需由客户终端按消息 id 去重即可。 京东咚咚诞生之初正是京东技术转型到 Java 之时,经历这些年的发展,取得了很大的进步。 从草根走向专业,从弱小走向规模,从分散走向统一,从杂乱走向规范。 本文主要重心放在了几年来咚咚架构演进的过程,技术架构单独拿出来看我认为没有绝对的好与不好, 技术架构总是要放在彼时的背景下来看,要考虑业务的时效价值、团队的规模和能力、环境基础设施等等方面。 架构演进的生命周期适时匹配好业务的生命周期,才可能发挥最好的效果。 来源:51CTO

优秀的个人博客,低调大师

DDD落地实践-架构师眼中的餐厅

本文以餐厅场景为叙事主线,以领域驱动为核心思想,结合架构设计与功能设计方法论。是从领域分析到落地的全过程案例,内容偏重于落地,因此不乏一些探讨,欢迎指正。 文章较长、全程干货、耐心读完、必有收获。 本文不针对餐厅的实现细节,重在探讨设计思想和方法。 1、领域设计 让我们抛开技术人员的本能技术视角、站在纯业务视角来分析领域问题。 领域设计的核心是分而治之,目的是实现业务领域的自治性。 就像你平时不会将枕头和被子放在厨房或卫生间一样,你的床上不会放着大米白面,否则你想睡觉是一件很复杂的事情,软件系统也是如此,这就是我们要解决的问题。 1.1 宏观流程 假如我要设计一个餐厅,由于分而治之的需要,我会首先从宏观流程去分析,可以帮我们迅速找到重要的区域。  因此会得到几个明确的行为区域,我将餐厅划分为“菜品域”,“订单域”,“厨房域”,“用餐域”,这是业务级别的领域划分,后续应该针对每个区域单独分析。 产出物是:宏观流程和参与角色 1.2 统一语言 语言贯穿于整个开发过程,从需求分析到设计、从设计到编码,因此好的语言非常重要,好的语言体现了清晰的业务概念。 在这个阶段,我们需要通过梳理,找到业务中都有哪些实体与行为,对其做一些归纳。我们的核心问题是:“谁”通过什么“行为”影响了“谁”,其中的三个要素分别是:角色、行为、实体。我的建议是先找到 “角色”、“实体”、“行为”,并对其归类,我常常关注角色以及具体身份、实体以及实体实例,功能以及包含的重要步骤。 角色:是施事主语、是名词,是主动发起行为的一类实体。 行为:是动词、是做了什么事情,是行为本身。 实体:是名词,是除“角色”之外的其他实体。 推荐使用脑图画出来,我认为归纳后的脑图有助于我们识别根本要素,有利于抽象。 产出物是:名词、概念定义、相关脑图。  1.3 用例分析 在这一步、我们使用相对宏观的分析,不需要进入用例的细节分析,掌握角色与行为之间的关系,理清谁在做什么,角色的职责差异是什么。 产出物:用例图 以做菜为例,如图 1.4 领域划分 我们在分析宏观流程时,划分了几个行为区域,但那是业务级别的。在那基础之上,我们需要拉进某个区域的视角,再结合之前的用例分析,按照“功能相关性”、“角色相关性”进一步划分领域。 功能相关性:是用例与领域之间的关系,任何业务的领域都是由一套用例组成的,所以领域划分以功能相关性为主,例如与做菜相关的用例都应该归属于厨房,所以我们确认了厨房域,确认了厨房域包含的用例,这是很自然的事。 角色相关性:其次是角色,常用于划分子域,某个区域涉及多个角色参与,可以按照角色的分工,拆分为多个子域,从而满足不同角色的个性化需要。例如厨房的采购人员负责买菜、刀工负责切菜、大厨负责烹饪。我们就会考虑将厨房划分为“采购域”、“加工域”、“烹饪域”。 通常来说,子域不具备独立的问题空间,不会作为独立的领域存在。 产出物:领域、子域 以厨房域为例,如图  1.5 领域建模 这是大家比较熟知的阶段,重点分析实体与领域之间关系(领域聚合),实体与实体的关系(OO聚合)。 领域模型是实现功能的基石、需要有对功能的本质理解,才能找到最核心的实体,实体之间的OO聚合关系决定了功能的扩展性,OO聚合是最重要的核心点。  组合、聚合 聚合(aggregation):聚合关系是一种弱的关系,整体和部分可以相互独立。 组合(composition):组合关系是一种强的整体和部分的关系,整体和部分具有相同的生命周期。 可以使用如下案例,既能表达领域聚合,又能表达OO聚合的关系。   产出物:聚合、实体、值对象、实体的属性 (领域服务和事件在后续的功能设计中提供) 1.6 领域上下游 领域上下游关系,不是领域的依赖关系,依赖关系指的是能力的依赖,是共用了某些能力,依赖关系是固定的。领域上下游关系,也不是调用关系,调用关系是与用例相关的,并非描述领域处境的。 领域上下游关系指的是影响力的关系,上游影响下游,影响力分为“逻辑影响”和“数据影响”,一般说来我们更应该关注“数据影响”,所以领域上下游关系是一种数据流向的限定,是业务发生的顺序限定,用于规定该领域所使用的数据,是下游领域依赖上游领域“准备就绪”的体现。合理的上下游限定,有助于减少领域之间的不必要依赖,有利于数据的复用并减少重复计算。 领域上下游是与场景相关的,并不是一成不变的,不同的场景存在不同的上下游,各场景应该独立说明。 产出物:各场景的上下游说明 例:在【菜品管理】场景下 如果厨房的某些食材不足了,或者某个厨师休假了,就会影响到菜品的展示,从而影响到客户的订单。 例:在【客户消费】场景下  客户的订单、影响厨房生产的菜,从而影响刀工的行为,也影响到了采购。 请对比下面两个图,用于理解领域的上下游  实际上,厨师不应该依赖采购人员的采购功能,也不依赖刀工的切菜功能,他只是依赖“初加工食材”而已,而“初加工食材”就是被处理好的数据,厨师在做饭时,“初加工食材”就已经被处理好了,上面的图例只是为了说明一个关于领域上下游的问题,这是业务发生顺序以及数据来源的问题。 我们常常使用领域事件串联业务流程,在使用领域事件时,不止要关注点对点的解耦,更应该使业务流程符合领域上下游限定,让各个领域独立运行,减少领域之间的功能依赖,降低领域之间的耦合,减少业务变化带来的影响。 2、架构设计 架构设计是为了解决软件系统复杂度带来的问题,找到系统中的元素并搞清楚他们之间关系。 架构的目标是用于管理复杂性、易变性和不确定性,以确保在长期的系统演化过程中,一部分架构的变化不会对其它部分产生不必要的负面影响。这样做可以确保业务和研发效率的敏捷,让应用的易变部分能够频繁地变化,对应用的其它部分的影响尽可能地小。 架构设计三原则:合适原则、简单原则、演化原则 2.1 分层架构 我们需要按照 接口层、领域层(领域用例层、领域模型层)、依赖层、基础层 构建架构模型。 接口层:为外部提供服务的入口,是适配层的北向网关。不实现任何业务逻辑,也不处理事务,是跨领域的,是流程编排层,是门面服务。 领域用例层:是领域服务层,是领域用例的实现层、隶属于某个领域、是业务逻辑层,是事务层,业务逻辑应该在这层完整体现,不要分散到其他层级。 领域模型层:是领域模型(实体、值对象、聚合)的所在位置,专注于领域模型自身的能力,不包含业务功能,可以处理事务,是原子化的能力,是领域对象的自我实现。 依赖层:是连接外部服务的出口,是适配层的南向网关。包括仓储,端点、RPC等,主要作用是领域和外部解耦,用于保持领域的独立性,是跨领域的。 基础层:与业务无关的,与领域无关的,通用的技术能力,技术组件等。 2.2 架构映射 架构的视角,从大到小依次是:系统->应用(微服务)->模块(包)->子模块 这样的从大到小的层级。 业务领域映射:我们将划分好的领域,按照对应的视角映射为对应的元素,领域模型映射到架构模型时,应该是视角对等的,如果餐厅是系统、那么厨房就是应用,如果餐厅是应用、那么厨房就是模块。也应该层级匹配的,将用例的实现映射到用例层,将领域模型的实现映射到领域模型层。 技术和抽象问题:有时候、业务领域分析不能体现那些共性的技术问题,所以需要适当结合技术视角,可能需要对领域模型微调。同时、我们需要找到共同需要的基础能力,例如“水”、“电”、“煤气”等等,将这些作为额外的考虑因素,要做到业务问题与技术问题解耦,不要将技术问题和业务逻辑揉成一团。 领域设计,类似餐厅设计师,他设计餐厅有几个区域,区域的用途是什么。 架构设计,类似建筑设计师,他设计如何走水电煤气、如何施工等。 产出物:分层架构图 以厨房为视角,其架构如下  以餐厅为视角,其架构如下  分层架构图,体现逻辑上的层级分布,而不是代表组件的具体含义,组件是应用还是模块、需要结合实际情况而定。 2.3 必要的约束 1、分层架构越往下层就越是稳定的:下层是被上层依赖的,下层不可以反向依赖上层(扩展点除外)。因为分层架构的核心原则是将容易变化的逻辑上浮,将共性的、原子化的、通用的逻辑下沉,被依赖的下层应该是稳定的,这要求上层承接更多业务变化。下层离开上层应该是可以独立存在的,例如在接口层定义的DTO不可以在下层被使用,但领域层定义的实体可以被上层使用。 2、在使用充血模型时,应该符合面向对象编程原则:不要随意的将一些能力都充到领域实体模型中。以“菜”为例,重量和规格是“菜”的自身的属性,激发味蕾是“菜”的能力,“菜”可以维护自身的持久化状态。但是、请注意、“菜”不可以“炒菜”,因为“炒菜”的时候,“菜”还没有出现呢,“菜”不是自己的上帝,“菜”需要被做出来,所以“菜”被做出来之前是没有“菜”的,这是个时间上的概念,不要错把“炒菜”的能力放在“菜”的身上。“炒菜”用到的“水+电+气+食材+调料+厨具”不应该是“菜”的属性范围,这些元素都在“厨房”的范围中,不要让领域的模型包含不属于自身的元素,领域的实体模型只是领域的一部分,只用于实现通用的模型能力。 3、接口层和依赖层是与领域无关的:他们是与技术相关的层级,不属于任何领域,这两层不能包含业务逻辑。有时候我们可以把接口层拆为两层(接口层+应用层),也可以把依赖层拆分为两个(模型依赖、服务依赖)。 4、领域层是与环境无关的:无论某个领域是应用还是模块,都应该具备独立的用例层和独立的模型层,即使多个领域在同一个应用当中,也要按照他们是分别独立去看待,无论某个领域是应用还是模块,领域对外部的交互,不可以绕过依赖层和接口层。 5、领域应该是最小完备的:把一个领域拆分为子域、子子域、子子子...... 无限拆分,拆分到一定程度之后,某个子域就不完整了,不完整的子域是不可以独立存在的。拆分不不够或者过度拆分,都是不符合低耦合高内聚原则的。当一个领域的内部子域不具备独立性时,他们之间不必严格解耦,不需要通过依赖层访问本领域的其他子域,他们之间可以直接调用。 6、领域服务层就是领域用例层:他们俩是同一回事儿,都是用于实现领域内的用例的。不要将领域服务与领域用例视为两个独立的层,也不要将领域服务与领域模型视为同一层,否则会导致逻辑的分散(一部分在领域服务层、一部分在领域模型层、还有一部分可能在用例层),也会导致每个层的职责不明确,容易搞乱。如果将业务逻辑写在领域模型中,会导致业务逻辑进一步下沉,业务逻辑的不确定性太大,是不适合下沉的,是违反分层架构原则的。领域模型对应的是实体、领域服务对应的是用例。 7、领域用例层只能承接符合自身领域的用例:我们划分出领域的目的,就是为了区分每个领域的职责所在,因此他们必须严格按照职责办事,我们在之前已明确了用例和领域之间的关系,需要严格遵守。 8、领域模型层遵循最小依赖原则:只可以依赖必要的资源,必要资源指的是领域模型实现自身能力需要的资源,不包括实现业务逻辑包含的资源。例如领域模型需要依赖DB完成持久化,可以依赖数据访问资源,但不应该依赖其他领域资源、不可以依赖RPC资源等。 2.4 微服务划分 服务划分以领域划分为参考,主要看我们要拆分到什么粒度,这 应该符合低耦合高内聚原则,不破坏领域实体的聚合关系。 产出物:微服务 例如餐厅:是有必要拆分的,餐厅的“菜品域”,“订单域”,“厨房域”有独立的问题空间。 例如厨房:是没有必要拆分的,厨师与刀工的耦合非常高,他们都在做饭,分开之后是不完整的,分开就是没有必要的。 所以餐厅被拆分为:厨房(Kitchen)、菜品(Category)、订单(Order)三个微服务。 基于此、我们单独拿出餐厅门面服务作为接口层应用,再单独拿出餐厅基础服务作为水电煤气的应用。 一般情况下,依赖层不会作为单独的服务提供,会被以组件的形式嵌入到其他服务中。 3、功能设计(用例实现) 如果说领域设计是餐厅的设计师、架构设计是餐厅的建筑师、那么功能设计就是餐厅的厨师或服务员。 任何设计都要落地到功能设计,如果厨师不守规则,偏偏要去洗手间洗菜,最后的结果依然是一团乱,最终会导致设计无法落地。 功能设计是实现 “面向扩展开放、面向修改关闭” 的途径,是指导研发落地必备环节。 3.1 功能的概念 功能迭代时,功能会发生一些变化,所以他的含义是可能变化的,所以我们需要再次审视功能的概念,及时加以调整。 例如、我们实现了一个“做蛋炒饭”的功能,后来又实现了一个“做辣椒炒蛋”的功能,那么我们应该将功能升级为“炒菜”,甚至是“制作菜品”等。 明确功能的概念,是功能设计的前提。 产出物:更新语言库,更新脑图 3.2 用例的位置 我们在领域分析章节,已明确了用例与角色的关系,用例与领域的关系。 然而一个新功能的加入,我们仍然要再次评估,以确保他处于正确的位置。 产出物:更新用例图 3.3 事件风暴 我们需要深入功能的细节,首推的方法是事件风暴,适用于解构复杂功能。 事件风暴的作用并不限于功能分析,只是我觉得很适用于功能分析,事件风暴的一张图包含很多内容,正好是功能设计所需要的。 将功能拆分为多个子功能(步骤)。(在后续使用) 确认参与该步骤的角色和领域。(在后续的3.6章节落地) 确认步骤的串联流程和领域事件。(在后续的3.6章节落地) 确认参与该步骤的领域实体。(在后续的3.7章节落地) 产出物:事件风暴模型 3.4 用例分析 我们暂且收回思路,首先要关注共性和差异问题,以确保功能的扩展性。 确认用例的泛化+差异点,实现功能的扩展。 寻找共同包含的步骤,实现逻辑的复用。 产出物:用例分析图 例:制作菜品(做大拌菜、做铁锅炖、做炒鸡蛋、做蒸米饭、做炒米饭) 3.5 用例实现类(领域服务类)结构图 专注于用例层的类设计,实现“面相修改关闭,面相扩展开放”。 用例的类结构图是用例分析图的一种映射。 出物:用例层的类结构图  3.6 用例流程图 我们接回思路,更进一步,将事件风暴模型落实到代码层面。 我们将步骤分配到实现类中、步骤就是该类的一个方法,进一步明确由哪个类和方法来实现该步骤,从而就规定了步骤所在的领域。 我们将步骤和领域事件串联起来,规定了业务实现流程。推荐使用泳道图表达上述内容。泳道的纵向组件是用例的实现类。 这是真实业务流程的映射。 产出物:用例流程图 以炒鸡蛋为例,其用例流程图如下  3.7 活动图(时序图) 我们进一步将事件风暴模型落实到代码层面,我们使用时序图,体现依赖和调用关系,规定了步骤与领域实体模型的关系,进一步说明用例是如何实现的。 这时候,为了简便、我们可以收起领域服务类(用例层)的泳道。 产出物:时序图、活动图  试想一下、假如把业务逻辑放在领域模型当中(例如聚合),如何实现“面相扩展开放、面相修改关闭”呢? 4、编码实现 编码实现...... 我决定还是...... 偷个懒吧...... 哈哈哈。 但是我们回顾一下之前的内容,是否足够了?不同的研发人员依照设计去编码,是否会写出不一样的代码? 最后、我们的目标是“解决软件复杂度带来的问题”,而实现这个目标的途径是“设计指导研发落地”。 本文分享自微信公众号 - 京东云开发者(JDT_Developers)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册