把一些前后端概念性比较强的理念放在这里,本篇设计CI、CD、RPC、PWA等
https://segmentfault.com/a/1190000038774393?utm_source=sf-related
CDN,即Content Delivery Network,内容分发网络,其搭建的思路是尽可能避开网上有可能影响数据传输速度和稳定性的瓶颈和环节,尽量使内容传输的更快更稳定。
CDN通过在网络边缘部署边缘服务器,依靠CDN中心平台的负载均衡、内容分发以及调度等功能,使用户就近获取所需的内容,降低网络延迟,提高用户响应速度和命中率。所以CDN就是广泛采用各种缓存服务器,使得用户的请求直接由这些缓存服务器响应,加快了响应速度。只有在用户请求的资源在缓存服务器上没有找到或者请求访问的资源在源站点服务器上已经修改过的情况下,缓存服务器才会去访问源站点服务器以获取最新的资源
在CDN环境下,web访问数据通常会经历客户端本地缓存和CDN边缘节点缓存两个阶段。如果这两个阶段均无法响应客户端请求,那么最后会由CDN节点向源站点发出回源请求,进而从源站点获取最新的数据,更新CDN节点的本地缓存,最后将新的数据返回给客户端。
CDN节点的缓存机制也是遵循http协议,也会收到cache-control等字段的影响, 与此同时,CDN的缓存时间的长短对回源率产生直接影响。若CDN缓存时间较短,CDN边缘节点的数据经常失效,导致频繁回源,增加了源站的负载,同时也增大访问延时,若CDN缓存时间太长,会带来数据更新时间慢的问题。因此开发者需要针对特定的业务,做特定的时间缓存管理
回源/回源比
回源是指浏览器在发送请求报文时,响应请求报文的是源站点的服务器,而不是各节点上的缓存服务器,这个过程称为回源
回源的请求或者流量太多,有可能会让源站点的服务器承载着过大的访问压力,进而影响服务的正常访问
回源请求数比例:回源请求数/(回源请求数+用户发送的请求数)
回源流量比:回源流量/(回源流量+用户请求访问的流量)
OOP的中心原则是数据和对它的操作紧密结合:对象拥有它的数据,并且它拥有对数据的实现。它通过接口、响应方法和消息的集合来隐藏对象,因此抽象中心是数据本身,以接口的形式隐藏在API后面
OOP的中心活动是组成新对象并通过向现有对象添加新方法来拓展现有对象
fp的中心原则是数据只是松散地耦合到函数上。您可以在同一个数据结构上编写不同的操作,抽象的中心模型是函数,而不是数据结构。函数隐藏了它们的实现,语言的抽象以及它们组合或表达的方式
fp的中心活动是编写新函数
PWA(progressing web app),渐进式网页应用程序,是Google在2016年GoogleI/O大会上提出的下一代web应用模型,并在随后的日子里迅速发展。 一个 PWA 应用首先是一个网页, 可以通过 Web 技术编写出一个网页应用. 随后借助于 App Manifest 和 Service Worker 来实现 PWA 的安装和离线等功能。
PWA的特点:
渐进式:适用于选用任何浏览器的所有用户,因为它是以渐进式增强作为核心宗旨来开发的。
自适应:适合任何机型:桌面设备、移动设备、平板电脑或任何未来设备。
连接无关性:能够借助于服务工作线程在离线或低质量网络状况下工作。
离线推送:使用推送消息通知,能够让我们的应用像 Native App 一样,提升用户体验。
及时更新:在服务工作线程更新进程的作用下时刻保持最新状态。
安全性:通过 HTTPS 提供,以防止窥探和确保内容不被篡改。
基于这些特点,对于移动端h5页面和app,增加了可以添加至屏幕的功能,点击主屏幕图标可以实现启动动画以及隐藏地址栏实现离线缓存功能,即使用户手机没有网络,依然可以使用一些离线功能。 这些特点和功能不正是我们目前针对移动web的优化方向吗,有了这些特性将使得 Web 应用渐进式接近原生 App,真正实现秒开优化。
Service Worker 是一个 基于HTML5 API ,也是PWA技术栈中最重要的特性, 它在 Web Worker 的基础上加上了持久离线缓存和网络代理能力,结合Cache API面向提供了JavaScript来操作浏览器缓存的能力,这使得Service Worker和PWA密不可分。
Service Worker的特点
一个独立的执行线程,单独的作用域范围,单独的运行环境,有自己独立的context上下文。
一旦被 install,就永远存在,除非被手动 unregister。即使Chrome(浏览器)关闭也会在后台运行。利用这个特性可以实现离线消息推送功能。
处于安全性考虑,必须在 HTTPS 环境下才能工作。当然在本地调试时,使用localhost则不受HTTPS限制。
提供拦截浏览器请求的接口,可以控制打开的作用域范围下所有的页面请求。需要注意的是一旦请求被Service Worker接管,意味着任何请求都由你来控制,一定要做好容错机制,保证页面的正常运行。
由于是独立线程,Service Worker不能直接操作页面 DOM。但可以通过事件机制来处理。例如使用postMessage。
Service Worker的生命周期
注册(register):在浏览器解析到JavaScript需要先注册Service Worker时的逻辑,即调用navigator.serviceWorker.register()时所处理的事情。
安装中( installing ):发生在 Service Worker 注册之后,表示开始安装。
安装后( installed/waiting ):Service Worker 已经完成了安装,这时会触发install事件,在这里一般会做一些静态资源的离线缓存。如果还有旧的Service Worker正在运行,会进入waiting状态,如果你关闭当前浏览器,或者调用self.skipWaiting(),方法表示强制当前处在 waiting 状态的 Service Worker 进入 activate 状态。
激活中( activating ):表示正在进入activate状态,调用self.clients.claim())会来强制控制未受控制的客户端,例如你的浏览器开了多个含有Service Worker的窗口,会在不切的情况下,替换旧的 Service Worker 脚本不再控制着这些页面,之后会被停止。此时会触发activate事件。
激活后( activated ):Service Worker激活成功,在activate事件回调中,一般会清除上一个版本的静态资源缓存,或者其他更新缓存的策略。这代表Service Worker已经可以处理功能性的事件fetch (请求)、sync (后台同步)、push (推送),message(操作dom)。
废弃状态 ( redundant ):Service Worker 的生命周期结束。
利用web
Web Push是一个基于客户端,服务端和推送服务器三者组成的一种流程规范,可以分为三个步骤:
实例(js)
//在页面代码里面监听onload事件,使用sw的配置文件注册一个service worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('serviceWorker.js')
.then(function (registration) {
// 注册成功
console.log('ServiceWorker registration successful with scope: ', registration.scope);
})
.catch(function (err) {
// 注册失败
console.log('ServiceWorker registration failed: ', err);
});
});
}
//serviceWorker.js
var CACHE_NAME = 'my-first-sw';
var urlsToCache = [
'/',
'/styles/main.css',
'/script/main.js'
];
self.addEventListener('install', function(event) {
// 在install阶段里可以预缓存一些资源
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
//在fetch事件里能拦截网络请求,进行一些处理
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.match(event.request).then(function (response) {
// 如果匹配到缓存里的资源,则直接返回
if (response) {
return response;
}
// 匹配失败则继续请求
var request = event.request.clone(); // 把原始请求拷过来
//默认情况下,从不支持 CORS 的第三方网址中获取资源将会失败。
// 您可以向请求中添加 no-CORS 选项来克服此问题,不过这可能会导致“不透明”的响应,这意味着您无法辨别响应是否成功。
if (request.mode !== 'navigate' && request.url.indexOf(request.referrer) === -1) {
request = new Request(request, { mode: 'no-cors' })
}
return fetch(request).then(function (httpRes) {
//拿到了http请求返回的数据,进行一些操作
//请求失败了则直接返回、对于post请求也直接返回,sw不能缓存post请求
if (!httpRes || ( httpRes.status !== 200 && httpRes.status !== 304 && httpRes.type !== 'opaque') || request.method === 'POST') {
return httpRes;
}
// 请求成功的话,将请求缓存起来。
var responseClone = httpRes.clone();
caches.open('my-first-sw').then(function (cache) {
cache.put(event.request, responseClone);
});
return httpRes;
});
})
);
});
Service Worker必须是https协议的,但本地环境下http://localhost或者http://127.0.0.1也可以的。
浏览器兼容性:部分浏览器不支持
http缓存是涉及到cache-control, Etag, last-modified
等字段,断网的时候,都不能访问,缓存不能编程,不能精细地对需要缓存的内容增删改查,
serviceworker缓存:基于 Web Worker ,一个独立的 worker 线程,独立于当前网页进程;让缓存做到优雅和极致
Service Worker为PWA赋能离线可用性以及push消息:
和 CacheStorage 搭配,可以做离线应用
和 Notification 搭配,可以做像 Native APP 那样的消息推送
再加上 Manifest 等,就差不多成了 PWA 了
webworker是现代浏览器提供的一个js多线程方案,可以将一些大计算量的代码交给web worker运行。js语言执行的是单线程语言,也就是说所有任务排成一个队列,而有了webworker就不一样
兼容性:
手机浏览器不支持,ie10、火狐3.6、safari4.0、chrome和opera11
支持api:
结束worker:主页面worker.terminal() 或者worker自销毁self.close;或者关闭页面
service worker
service worker是基于webworker的事件驱动的,他们执行的机制都是新开一个线程去处理额外的、以前不能处理的任务
对于web worker,可以用来进行复杂的计算,因为它不阻塞浏览器主线程的渲染,而service worker可以用来进行本地缓存或者请求转发,相当于浏览器本地的proxy
总结
(1)webworker现在可以在浏览器端做多线程操作,但是比较限制
(2)serviceworker是在webworker基础上实现的,可以认为是使用webworker技术来处理网络请求、响应等问题
(3)两者的浏览器兼容性都不是很好,尤其是serviceworker,草案在更新,特性没有完整
(4)serviceworker可以带来更多
https://juejin.im/post/6844903896893095949#heading-5
https://juejin.im/post/6844904182839771143
目前webapp缓存方案:
1.基于头部信息的缓存方式。使用浏览器头部信息缓存主要有两种,一种是判断静态资源http头部的etag和last-modified是否修改,修改则重新请求,否则忽略;还有一种是根据expire过期时间定的,原理一样,但是两种方法都会产生大量http请求,即返回304,离线后就没有办法请求。
2.使用app cache。将要缓存的静态文件声明在一个manifest的文件清单里,然后在要缓存的html里通过manifest属性关联清单文件即可在下次载入html入优先加载缓存清单中的静态文件。
缺点:
manifest一次更新就全部更新
可以在打开app之前预缓存,提前下载文件或者更新manifest文件
还是有全部的304请求
3.localstorage存储。对于cookie确实丰富了很多,也扩大了容量,方便易用的api也被广泛接受,但是同样无法对静态文件进行存储,二是大小限制,虽然每个浏览器的localstorage大小不一样,但是最大也只有ie的7mb,对于离线应用显然是不够的
4.indexDB缓存:indexedDB的做法是把一些数据存储到浏览器的indexedDB中,当网络断开时,可以从浏览器中读取数据,用来做一些离线应用,而且不需要去写特定的sql语句,它是nosql 的,数据形式使用的是json。这种存储数据的方式似乎无懈可击,容量足够,存储自由,但是对于静态文件及极其麻烦。
serviceworker相对于上述的优点:
它可以是浏览器提供给用户的上网代理,通过fetch连接到用户的所有请求,可以不向服务器发送,并可以将请求转向本地缓存或者其他资源文件的加载,无论数据还是静态文件,可以通过js的操作进行增量更新,同事不阻塞渲染进程,这就很好地解决了问题
单点登录简称为SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
SSO一般都需要一个独立的认证中心,子系统的登录均得通过passport,子系统本身将不参与登录操作,
CAS是中央认证服务的简称,是耶鲁大学发起的开源项目,旨在为Web应用系统提供一种可靠的单点登录方法。CAS包括两个部分:CAS server和CAS client,CAS server需要独立部署,主要负责对用户的认证工作,CAS Client负责处理对客户端受保护资源的访问请求,需要登录时重定向到CAS Server。
CAS与OAuth2的区别
1 CAS的单点登录时保障客户端的用户资源的安全,OAuth2则是保障服务端用户资源的安全
2 CAS的单点登录资源都在客户端这边,不在CAS的服务器那一方,用户在CAS服务端提供了用户名密码之后作为CAS客户端并不知道这件事,随便给客户端一个ST,那么客户端是不能确定这个ST是用户伪造还是真的有效,拿着这个ST去服务端再问一下,这个用户给我的是有效的ST还是无效的ST,OAuth2认证资源都在OAuth2服务提供者那一方,客户端
3 CAS登录和OAuth2在流程上的最大区别就是,通过ST或者code去认证时,需不需要预先商量好的密码
延迟与用户反应
0-16ms 人们特别擅长跟踪运动,如果动画不流畅,他们就会对运动心生反感。用户可以感知每秒 60帧的平滑动画转场,也就是每帧16ms,留给应用大约10ms的时间来生成一帧
0-100ms 在此时间内窗口响应用户操作,用户会觉得立即获得结果,时间再长,操作与反应之间的 连接就会中断
100-300ms 用户会遇到轻微可觉察的延迟
300-1000ms 在此窗口内延迟感觉像任务自然和持续发展的一部分,对于网络上的大多数用户,加 载页面或者更改视图代表着一个任务
1000+ms 超过1s,用户的注意力将离开他们正在执行的任务
10000+ms 用户将感动失望,可能会放弃任务,之后或许他们不会再回来
埋点,是网站分析的一种常用的数据采集方法。我们主要用来采集用户行为数据(例如页面访问路径,点击了什么元素)进行数据分析,从而让运营同学更加合理的安排运营计划。
为了更精准地触达用户痛点,定位转化低点,提升业务赋能,基于数据分析的优化策略势在必行。
数据埋点分析系统的构成:
1.埋点采集JSSDK:收集用户行为数据,并进行上报
2.数据处理服务:接收上报数据并存储,筛取所需数据,进行数据处理并透出
3.数据可视化平台:汇总展示详细数据,支持自定义,打通业务
4.Chrome插件工具:在页面上直观展示坑位数据,提供场景更友好的可视化服务
基本的流程是:用户进入平台任意一个已埋点的Web页面,进行一系列地操作,都会由JSSDK进行分类并将数据上报至服务端进行存储,再由站点/插件发起查询,服务端将处理后的数据返回,再通过数据可视化平台进行透出展示。
数据采集一般分为三种:
1.无埋点(全埋点):零埋点成本,抓取用户行为全量数据,任何操作都会被上传,数据量大,噪音多。手动代码埋点比较常见,需要调用埋点的业务方在需要采集数据的地方调用埋点的方法。优点是流量可控,业务方可以根据需要在任意地点任意场景进行数据采集,采集信息也完全由业务方来控制。这样的有点也带来了一些弊端,需要业务方来写死方法,如果采集方案变了,业务方也需要重新修改代码,重新发布。
2.可视化埋点:在页面中操作,选择埋点位置/模块,非开发人员也能埋点。可是化埋点是近今年的埋点趋势,很多大厂自己的数据埋点部门也都开始做这块。优点是业务方工作量少,缺点则是技术上推广和实现起来有点难(业务方前端代码规范是个大前提)。阿里的活动页很多都是运营通过可视化的界面拖拽配置实现,这些活动控件元素都带有唯一标识。通过埋点配置后台,将元素与要采集事件关联起来,可以自动生成埋点代码嵌入到页面中。
3.侵入式埋点(手动埋点):埋点时需要将数据采集代码写入业务代码中,埋点成本高,准确度也更高。手动代码埋点比较常见,需要调用埋点的业务方在需要采集数据的地方调用埋点的方法。优点是流量可控,业务方可以根据需要在任意地点任意场景进行数据采集,采集信息也完全由业务方来控制。这样的有点也带来了一些弊端,需要业务方来写死方法,如果采集方案变了,业务方也需要重新修改代码,重新发布。
有限状态机,英文翻译是 Finite State Machine,缩写为 FSM,简称为状态机。状态机有 3 个组成部分:状态(State)、事件(Event)、动作(Action)。其中,事件也称为转移条件(Transition Condition)。事件触发状态的转移及动作的执行。不过,动作不是必须的,也可能只转移状态,不执行任何动作。
自动化测试的最大优势就是可以快速而且反复地进行测试。
前端自动化测试有个金字塔理论,把测试从上到下分为UI测试(用户界面测试)/Service(服务测试)/Unit(单元测试)
越往金字塔底层测试的效率越高,测试质量保障程度越高,测试的成本越低。也就是说,前端项目通常UI变化频繁,一旦发生变化,UI测试就无法执行且难以维护,所以UI自动化测试的成本高收益小,相比UI测试,Service测试更加简单直接且变化不会很频繁。单元测试主要对公共函数、方法进行测试,测试用例复用度高且更能保证代码质量。
我们开发项目时通常会选择使用框架和库,使用框架的好处是约束我们代码的风格,保证代码的可维护性和扩展性,使用工具库可以提高开发效率,同理,在实施自动化测试时我们也会选择使用测试框架和库,目前市面上比较流行的前端测试框架有Mocha、QUnit、Jasmine、Jest等。
Mocha:适用于Nodejs和浏览器、简易、灵活、有趣的JavaScript测试框架
Jest:由Facebook开源的JavaScript测试框架,应用于脸书系以及ReactJs系
Jasmine:BDD(行为驱动开发)测试框架,不依赖于browser、DOM以及js,适合website和nodejs项目
Qunit:一个易于使用的Javascript单元测试框架,由开发jQuery的团队开发,所以多用于jQuery相关的项目
以Mocha为例
Mocha的四项核心能力
测试用例分组、生命周期函数、兼容不同风格断言、同步异步测试框架
测试框架;Mocha
断言库:chai
测试覆盖率:Istanbul
测试浏览器:chrome
浏览器驱动:selenium-webdriver/chrome
接口测试http请求断言:supertest
react组件测试:enzyme
基准测试:benchmark
WebRTC(Web Real-Time Communication)是一种实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器点对点的连接,实现视频流和音频流或其他任意数据的传输,WebRTC包含的这些标准使用户在无需安装任何插件或者第三方软件的情况下,创建点对点的数据分享和电话会议成为可能。
它并不是单一的协议,包含了媒体、加密、传输层等在内的多个协议标准,以及一套基于JavaScript的API,通过简单易用的JavaScript API,在不安装任何插件的情况下,使浏览器拥有P2P音视频和数据分享的能力。同时WebRTC并不是一个孤立的协议,它拥有灵活的指令,可以便捷地实现对接现有SIP和电话网络的系统。
WebRTC有三个模块:
Voice Engine(音频引擎):
Video Engine(视频引擎):
Transport:
WebRTC的核心组件:
音视频引擎:OPUS、VP8/VP9、H264
传输层协议:底层传输协议为UDP
媒体协议:SRTP/SRTCP
数据协议:DTLS/SCTP
P2P内网穿越:STUN/TURN/ICE/Trickle ICE
信令与SDP协商:HTTP/WebSocket/SIP、Offer Answer 模型
持续集成(Continuous integration,简称CI),
持续集成指的是,频繁地(一天多次)将代码集成到主干。 它的好处主要有两个:
1.快速发现错误。每完成一点更新,就集成到主干,可以快速发现错误,定位错误也比较容易。
2.防止分支大幅偏离主干。如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。
持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。
持续交付(Continuous delivery),
持续交付指的是,频繁地将软件的新版本,交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。 持续交付可以看作持续集成的下一步。它强调的是,不管怎么更新,软件是随时随地可以交付的。
持续部署(continuous deployment)
持续部署是持续交付的下一步,指的是代码通过评审以后,自动部署到生产环境。
持续部署的目标是,代码在任何时刻都是可部署的,可以进入生产阶段。
根据持续集成的设计,代码从提交到生产,整个过程有以下几步。
(1)提交。流程的第一步,是开发者向代码仓库提交代码。所有后面的步骤都始于本地代码的一次提交
(2)测试(第一轮)。代码仓库对commit操作配置了钩子(hook),只要提交代码或者合并进主干,就会跑自动化测试。 测试有好几种。
(3) 构建。通过第一轮测试,代码就可以合并进主干,就算可以交付了。 交付后,就先进行构建(build),再进入第二轮测试。所谓构建,指的是将源码转换为可以运行的实际代码,比如安装依赖,配置各种资源(样式表、JS脚本、图片)等等。 常用的构建工具如下。
(4) 测试(第二轮)。构建完成,就要进行第二轮测试。如果第一轮已经涵盖了所有测试内容,第二轮可以省略,当然,这时构建步骤也要移到第一轮测试前面。
第二轮是全面测试,单元测试和集成测试都会跑,有条件的话,也要做端对端测试。所有测试以自动化为主,少数无法自动化的测试用例,就要人工跑。
需要强调的是,新版本的每一个更新点都必须测试到。如果测试的覆盖率不高,进入后面的部署阶段后,很可能会出现严重的问题。
(5)部署。通过了第二轮测试,当前代码就是一个可以直接部署的版本(artifact)。将这个版本的所有文件打包( tar filename.tar * )存档,发到生产服务器。
(6)回滚。一旦当前版本发生问题,就要回滚到上一个版本的构建结果。最简单的做法就是修改一下符号链接,指向上一个版本的目录。
视频直播服务目前支持三种直播协议,分别是RTMP、HLS、FLV
实时消息传输协议,由Adobe公司研发,但当前还没有收入国际标准(wikipedia)
。协议比较全能,既可以用来推送又可以用来直播,其核心理念是将大块的视频帧和音频帧“剁碎”,然后以小数据包的形式在互联网上进行传输,且支持加密,因此隐私性相对比较理想,但拆包组包的过程比较复杂,所以在海量并发时容易出现一些不可预期的稳定性问题。基于HTTP协议的流直播(wikipedia)
。苹果推出的解决方案,将视频分成 5-10 秒的视频小分片,然后用 m3u8 索引表进行管理。由于客户端下载到的视频都是 5-10 秒的完整数据,故视频的流畅性很好,但也同样引入了很大的延迟(HLS 的一般延迟在 10-30s 左右)。相比于 FLV, HLS 在iPhone 和大部分 Android 手机浏览器上的支持非常给力,所以常用于 QQ 和微信朋友圈的 URL 分享。https://html.form.guide/email-form/email-form-mailto/
RPC(Remote Procedure Call),远程过程调用,是一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序直接携带信息数据。
RPC实现了服务消费调用方Client与服务提供实现方Server之间的点对点调用,即包括stub、通信、数据的序列化/反序列化,且client与server一般采用直连的方式
RPC一般是用来实现部署在不同机器上的系统之间的方法调用,使得程序能够像访问本地系统资源一样,通过网络传输去访问远端资源。一般来说,RPC框架的实现原理都是类似的
RPC调用的过程:
客户端调用:负责发起RPC调用,为调用方用户提供使用API
服务端响应:主要是服务端业务逻辑实现
序列化/反序列化:负责对RPC调用通过网络传输的内容进行序列化和反序列化,不同的RPC框架有不同的实现机制,一般分为文本(xml,json)与二进制(java原生的,Hessian、protobuf、Thrift、Avro、Kryo、MessagePack)。需要注意的是,不同的序列化方式在可读性、码流大小、支持的数据类型及性能方面都有较大差距,我们可以根据需要自行选择
Stub:我们看成代理对象,它会屏蔽RPC调用过程中的复杂的网络处理逻辑,使其透明简单,且能够保持与本地调用一样的代码风格
通信传输:即RPC的底层通信传输模块,一般通过Socket在客户端与服务端传递请求和应答消息。
Grpc
Grpc是Google的rpc框架
分布式服务框架除了包含RPC的特性,还包括多台server提供服务的负载均衡、策略及实现,服务的注册、发布与引入,以及服务的高可用策略,服务治理等等。
测试时用