Meteor的工作原理及优势与不足 - 博文视点

mikel阅读(1131)

来源: Meteor的工作原理及优势与不足 – 博文视点

小编说:Meteor作为开源的全栈JavaScript开发平台,在工作方式上进行了较大创新,和传统Web 应用区别较大,对于任何一项技术,都有其擅长的领域,也有其不擅长的地方,Meteor也不例外。

本文选自《Meteor全栈开发》一书,我们在前面的文章中向您介绍了Meteor为何这么“快”,在本文中我们将深入探讨Meteor的工作原理以及优势弱势。

1  Meteor 的工作原理

1.1  工作流程

Meteor 在工作方式上进行了较大创新,和传统Web 应用区别较大。下面先回顾一下传统应用的工作流程,如图所示。

客户端(Client)负责向服务器请求所需的数据、资源,然后渲染显示;服务器端(Server)负责业务处理、数据库操作、构造响应内容、资源管理,服务器端的责任大、任务重。其各自职责关系如图所示。

Meteor的工作方式更像是手机APP。客户端首次访问 Meteor应用时,会从服务器把需要用到的资源都加载到客户端,如 JS、CSS、字体、图片,并创建一个mini数据库。然后和服务器端建立好数据通信的通道。之后,用户操作应用过程中涉及业务操作时,也是在客户端进行处理;进行数据库操作时,也是操作客户端的mini 数据库。服务器端只负责向客户端传输数据、数据的安全写入,以及执行一些只能在服务器端进行的操作,例如发送email,如图所示。

Meteor 应用的客户端包含了应用所需的静态资源、业务处理代码、一个简化的数据库。如手机APP 一样,很多操作直接在本地完成,需要执行特定动作和需要数据时才请求服务器端。

所以相比较于传统Web 应用,Meteor 选择了重客户端、轻服务器端的模式,充分利用现代客户端强大的运算能力,减轻服务器端的压力。

1.2  核心技术

Meteor 的工作方式必然需要一些特定的技术来支持,让我们来了解一下Meteor 的几个核心技术。

1. mini 数据库(mini-database)

Meteor 的底层技术中首先吸引我的就是客户端的 mini 数据库。Meteor 目前支持的数据库是 MongoDB,所以客户端的mini 数据库就是 miniMongo。

对于开发人员来讲,miniMongo 就像是一个真实 MongoDB 数据库,可以进行各种增删改查的操作,和MongoDB 的 API 完全一致。

miniMongo 的主要作用是缓存数据,相当于服务器端数据库的局部镜像,它不会缓存全部数据,只是缓存当前客户端用到的数据。

使用 miniMongo 的效果就是应用运行非常快,而且提供了更好的用户体验。例如用户保存了一条数据,Meteor会先保存到 miniMongo,保存成功后立即反馈给用户,体验极其顺畅;同时 Meteor会把数据同步到服务器端的真实数据库中,这个过程对于用户和开发者都是透明的。

那么如果网络出现问题,或者后台数据库操作时出现问题时,数据没有同步成功怎么办?

当客户端发现没有同步成功后,会通知用户出现了问题,页面执行相应的错误处理逻辑。例如用户保存了一条数据,数据先被写入 miniMongo,然后反馈用户操作成功,同时后台进行数据库同步。万一服务器端操作失败,会通知客户端,客户端会告知用户之前的操作有问题,并执行相应的错误处理流程。

2. Tracker

Tracker提供了响应式应用的基础功能。下面先简单了解一下什么是响应式。以之前创建的项目为例,页面中有一个按钮,单击按钮后,页面中显示的那一个数字自动加1。通过查看代码,代码的逻辑如图所示。

{{ counter }} 通过函数关联了 val 变量,按钮单击事件的处理函数中修改了变量 val 的值,并没有更新页面中的内容,但{{ counter }} 自动更新了,这就是响应式。

响应式的背后技术基础就是 Tracker。Tracker会跟踪目标数据,当其有任何变化后,都会重新计算使用到目标数据的地方。

在上面的示例中,变量 val 是一个响应式变量,会被 Tracker 跟踪,{{ counter }} 是变量 val 的消费者,当 val 被修改后,Tracker 便通知它的消费者进行更新。

3. DDP

DDP是一个数据传输协议。Web应用通常会使用HTTP,为什么还要使用 DDP呢?因为 HTTP 适合传输document,而 Meteor 中主要是传输数据,HTTP 在这方面就不太适合了,所以需要使用专门用于传输数据的 DDP。

DDP 基于 websockets,实现了全双工的数据传输,这一点也优于HTTP。如果使用 HTTP,则只能是客户端请求服务器获取数据,服务器端无法主动向客户端发送数据,而 DDP 的双向机制使数据传输更加主动、灵活。

DDP 使用 JSON 格式封装数据。因为 MongoDB 存储的文档结构是 JSON,客户端的JS 对JSON 的处理也是非常方便的,所以 DDP 协议使客户端和服务器端的数据沟通变得极其自然。

DDP 协议也是响应式功能的基础。因为通过 DDP,服务器端可以主动向客户端发送数据,所以当数据库中有任何变化时,都可以立即通知客户端,客户端便可以进行更新操作,以快速响应。

2  优势与不足

对于任何一项技术,都有其擅长的领域,也有其不擅长的地方。下面就看一下Meteor 的优势和劣势。

2.1  优势

Meteor 作为一站式的全栈开发平台,使用一种开发语言就可以贯穿前后端的开发,具有方便的数据交换协议、繁荣的生态等特质,使Meteor 自然地具备了很多优势,如下所示。

易学

使用Meteor 可以快速看到效果,这对学习者来说是一个很大的激励。

Meteor 提供了一套通用JavaScript API,开发者无须深入研究某个特别的前端库,或者某个后端框架,了解基础的JavaScript 就足以起步了。

偏向客户端

现在的应用都非常注重用户端的体验,为了提升客户端的智能效果,就需要客户端与服务器能够双向沟通,需要服务器可以推送数据给客户端。

Meteor 使用的DDP 协议就可以自动实现全双工通信,开发者无须为此费心。

响应式

在目前很多应用的开发中,处理事件(用户单击了某些元素后触发某动作, 如更新数据库,或者更新当前视图)的代码是一个重要部分。

在响应式编程中,这类事件处理函数的工作就减少了。

响应式是Meteor 的主要特征,所以Meteor 非常适合如实时聊天或者在线游戏类的应用。

代码高度重用

与Java 一样:写一次,到处运行。

基于Meteor 的同构特性,相同的代码可以运行于客户端,也可以运行在服务器端,运行在手机移动端也没问题。

强大的 CLI 命令行工具

Meteor 提供了一个命令控制台工具,用来辅助整个开发过程(具体功能上面有描述)。

健康的生态系统

Meteor还是一个生态系统,拥有大量的扩展包,提供了非常丰富的功能。这减少了开发者的很多工作量,而且众多开发者还在不断分享更多的扩展包。

2.2  弱势

虽然使用 Meteor可以开发很多类型的应用,但在有些情况下,还是建议选择其他的开发平台。毕竟 Meteor不是全能的,有其自身的弱项,在以下一些方面存在不足。

运算密集型应用

Meteor是基于Node.js的,Node.js本质上是单线程处理模式,不能很好地利用多处理器,所以 Meteor不能提供很强的计算能力。

成熟度

Meteor毕竟还很年轻,在大型应用方面还没有成熟的案例,Meteor在大型部署和处理高请求压力方面还需证明自己。

在社区方面,尽管Node.js的社区已经非常成熟,对大家帮助很大,但它还是没法和老牌语言的社区相比,如PHP、Java。

在主机环境方面,支持Meteor的主机仍大大少于支持PHP、Python等语言的主机。

约束少

在Meteor中,对于项目的结构方面没有严格的规定。其好处是很自由,但同时也是缺点。在一个人开发时,没有约束意味着开发速度快;但是在团队中,还是有清晰、固定的结构比较好,便于协作开发。

SQL

如果你的项目一定要使用SQL数据库,那么目前Meteor还无法满足此需求。

现在Meteor官方支持的数据库只有MongoDB。虽然已经计划支持SQL数据库,还有社区成员修改为支持SQL的成功案例,但毕竟尚未成熟支持。

静态化内容

类似新闻类型的网站,很多内容都已经生成为静态化的文件。客户端发送请求给服务器,服务器返回静态化HTML内容,这个场景更适合使用传统Web 平台—可以充分利用服务器的静态内容缓存—用户请求一个新闻页面,服务器端从缓存获取静态化文件,直接返回给用户,速度非常快。

而使用 Meteor 则利用不到 Meteor 的任何优势。因为Meteor 的优势是响应式和强大的交互通信协议,静态类型的网站自然不需要这些特质。

初次加载时间

如果对于加载时间有较高要求,就不适合使用Meteor。因为Meteor 初次加载慢、后期访问快,初始访问时会相对耗时,需要加载很多静态资源。

2.3  关于质疑

Meteor 的快速发展过程中也伴随着不少的质疑,例如,Meteor 不适合大型项目的开发,Meteor 的实时机制以及长连接会占用很多系统资源导致Meteor 的性能很差, 等等。

对于这些质疑,如何回应本身没那么重要,最关键的是我们面对这些质疑的心态。因为质疑是源自他人的自身感受,并不是非常客观的定论。这就需要我们有正确的思维角度,而不是简单否定或肯定。

例如,面对“Meteor 不适合大型项目的开发”这个结论,我们可能需要考虑, 是还没有大型项目真正去使用Meteor,还是很多大型项目使用Meteor 后遇到了很多问题;如果是真正遇到了麻烦的问题,那么这些问题是Meteor 自身机制导致的, 还是由于使用者对Meteor 不够熟悉而没有找到好的解决办法。

再比如性能问题。Meteor 把很多逻辑移到了前端执行,利用了更多的客户端处理能力,减轻了服务器端的压力;同时,实时机制也的确增加了服务器端的压力。那么此类机制具体增加了服务器的多少性能消耗?这两方面相比较的话,具体是好处更多还是负面影响更大?

很多问题需要我们根据自己的实际情况来分析,根据利弊的分析与总结来下结论。即使同一个项目,在不同的发展阶段也会根据不同的需求和面临的不同问题, 而使用不同的技术。例如,京东初期使用ASP.NET,随着规模的不断壮大,逐渐改为Java ;Facebook 初期使用PHP 开发,后来性能无法满足其要求,便自行研发PHP 虚机来提升性能。

Meteor 成长于创业孵化器。在这个环境下,Meteor 自然会更加关注创业团队的开发问题,希望创业项目能够快速迭代,尽可能快地根据用户反馈来改进。因此便形成了其自身的鲜明特性:开发速度快。

所以,应该根据自身项目的需求定位和发展阶段来分析技术,不能感觉Meteor有很多好处就贸然采用,也不要因为他人的质疑而轻易否定。

meteor weixin 开发中的坑 - ~ - CSDN博客

mikel阅读(853)

来源: meteor weixin 开发中的坑 – ~ – CSDN博客

近期开发微信中总是会遇到“invalid signature ”这样的错误
总结下文档中没有给出的几个坑:
1 由于不同页面的路径不尽相同,每个页面到要动态传入url到wx.config 中,因此每个页面都要进行config配置,传入要调用的url和wx接口列表,需要注意的是,wx.config() 不能多次配置,多次配置会出现“invalid signature”的错误,而且该错误只在ios设备上出现,安卓设备调用该接口可能是正常的。
2 签名算法中的几个参数和顺序

签名的实现中包含的参数构成了如下的示例
noncestr=Wm3WZYTPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
timestamp=1414587457
url=http://mp.weixin.qq.com?params=value

附录中给出的常见解决办法是:
invalid signature签名错误。建议按如下顺序检查:
1 确认签名算法正确,可用 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验。
2 确认config中nonceStr(js中驼峰标准大写S), timestamp与用以签名中的对应noncestr, timestamp一致。
3 确认url是页面完整的url(请在当前页面alert(location.href.split(‘#’)[0])确认),包括’http(s)://’部分,以及’?’后面的GET参数部分,但不包括’#’hash后面的部分。
4 确认 config 中的 appid 与用来获取 jsapi_ticket 的 appid 一致。
5 确保一定缓存access_token和jsapi_ticket。
6 确保你获取用来签名的url是动态获取的,动态页面可参见实例代码中php的实现方式。如果是html的静态页面在前端通过ajax将url传到后台签名,前端需要用js获取当前页面除去’#’hash部分的链接(可用location.href.split(‘#’)[0]获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。

对比这6条,通过签名计算工具得到的签名也是ok的,但是调用wx.getLocation 之类的接口时候提示“invalid signature ”,其中的access_token 已经确保是ok的,根据access_token 获取的JSAPI也是没问题的(ps:写微信开发提示的人给出‘invalid signature’这样的错误提示实在令人蛋疼,既然提示无效,干嘛不给个更加具体的错误,或者给个可以验证access_token/JSAPI的地方来进行一个对比)
最后的最后发现:传入参数的时间戳可能存在问题,后来发现是虚拟机时间和当前时间不一致导致的,遇到这样蛋疼的事情,也只好呵呵呵了,怪不得那么开发者都喜欢直接上mac,节约的时间就是money!

另外一个weixin+react中的坑,签名经常提示无效,最后刷新页面后重新调用就可以了。可能的原因:单页面应用,页面加载时候路径没有发生变化,导致超时签名出现错误。

基于ASP.NET MVC的热插拔模块式开发框架(OrchardNoCMS)--BootStrap - Nic Pei - 博客园

mikel阅读(1290)

来源: 基于ASP.NET MVC的热插拔模块式开发框架(OrchardNoCMS)–BootStrap – Nic Pei – 博客园

 

image

按照几个月之前的计划,也应该写一个使用Bootstrap作为OrchardNoCMS的UI库。之前这段时间都是在学习IOS开发,没顾得上写,也没顾得上维护OrchardNoCMS代码。看看我的活动轨迹就知道啦:

image

最近这两周快过年了,刚好写一篇博客来消磨时间,也顺便希望可以对大家有所帮助。

由于我精简后的版本中去掉了Orchard.JQuery模块,所以首先我建立了一个模块来存放我需要的所有JS和CSS文件。

image

创建一个ResourceManifest文件,它实现了接口IResourceManifestProvider。通过该类来把脚本和样式文件定义好。

image

可以看到主要用到了JQuery,bootstrap,knockoutjs等,需要注意的是amberme.js是我们封装好的一个脚本,通过它可以方便的绑定数据。

接下来我们需要在当前皮肤的Layout页面中加入我们定义的JS文件和CSS文件。Orchard本身提供了一套资源管理机制。所以对应的引用机制你如果不了解可以先看一下下面的代码:

image

对于JS文件的引用使用Script.Reuire()来获取JS文件,Style.Require()获取CSS文件。AtHead()方法表示该脚本需要放到Head节点,另外还有几种方法可以参考代码,或者自己试试。

接下来还是使用Orchard.Car模块作为例子也说明如何使用我们的UI库。

之前的Car模块有个CarController,我们对应的为它的Index这个Action创建一个Index.cshtml,对应的Add这个Action对应的添加一个Add.cshtml页面。

在Index页面中,首先我们需要种一排按钮,HTML如下:

image

绑定数据的列表HTML如下:

image

 

 

 

后台只负责传过来数据,具体的列名自己在HTML种定义,有很大的灵活性。

页面 加载完成后,绑定数据的初始化如下:

image

 

对应的还提供了很多功能,例如点击按钮后的回调,绑定数据前或者绑定完成后的函数调用等等。可以自己看看代码(Amberme.js)。

image

运行界面如下:

image

在上面种增删改的按钮时,我们已经看到buttony有个属性是data-template-url,新增按钮的这个属性为/car/add;所以当点击该按钮时,会弹出car控制器对应的add这个Action的View页面:

image

 

 

弹出的页面必须是个Form,这样我们在提交表单时,使用jQuery.form的ajax提交,会有更好的体验,而所有这些提交和验证操作都已经在amberme.js中封装好。你只需要配置正则表达式或者是属性就行啦。

新增界面如下:

image

以上就是OrchardNoCMS的Bootstrap示例,可以到https://github.com/nicholaspei/OrchardNoCMS 下载完整代码。

EasyUI中那些不容易被发现的坑——EasyUI重复请求2次的问题 - 西安王磊 - 博客园

mikel阅读(1562)

来源: EasyUI中那些不容易被发现的坑——EasyUI重复请求2次的问题 – 西安王磊 – 博客园

问题控件:datagrid、combobox、所有能设置url属性的控件

问题版本:1.4.4、1.4.5(之前的版本没测)

问题如图:

重复请求2次,错误代码如图:

 

 

错误问题分析:html加载的时候回请求url加载数据,紧接着js执行代码的时候,又给datagrid绑定事件的时候,datagrid又会去请求一次数据源,导致请求2次的问题;

 

解决方法:html代码不要设置url属性,在绑定datagrid事件的时候,设置url属性值,就可以解决此问题;

 

解决代码如图:

 

 

 

其他控件解决方法相同。

当然,如果不使用js绑定控件的事件,就不会出现重复提交的问题,可以根据实际情况使用html设置url.

easyui datagrid 重复加载解决办法 - zhaobao110的专栏 - CSDN博客

mikel阅读(1924)

来源: easyui datagrid 重复加载解决办法 – zhaobao110的专栏 – CSDN博客

用了一段时间easyui 发现每次加载的时候,后台的调用方法加载了2次,经过查资料及摸索后,有2种解决办法

1   去掉 table 中的class=easyui-datagrid  大概原因是渲染的时候,根据class加载了一次 然后调用js的时间又初始化了一遍

2 不去掉 table 中的class=easyui-datagrid   把所有参数写在url后面,不要用queryParams: { “json”: $.toJSON(parm) },

例如

var parm = { Class: “XxglWorker”, Method: “ywt_xxglDalList”, For: 1, Options: {} };

$(‘#list’).datagrid({
method: “post”,
queryParams: { “json”: $.toJSON(parm) },
url: top.PubUrl,
pagination: true});

改为

$(‘#list’).datagrid({
method: “post”,
url: top.PubUrl+’json=’+$.toJSON(parm),
pagination: true});

实践证明以上2中方法均可

如果是。net的朋友对easyui的后台封装的话,不建议使用反射,性能不好,建议使用接口代替反射操作

关于jquery easyui datagrid执行多次的问题

mikel阅读(1580)

关于JQuery easyui datagrid执行多次的问题

今天终于解决了一个困扰了我很久的问题,大概情况如下代码所示:

复制代码
var gridHelper = {
    isInit : false,//是否已经初始化datagrid标志
    isRefresh : true,//是否需要刷新datagrid数据的标志
    initGrid : function(){
        if(!gridHelper.isInit){
            $("#datagrid").datagrid({
                //options,这里不定义URL和queryParams
            });
            gridHelper.isInit = true;
        }
        if(gridHelper.isRefresh){
            var $grid = $("#datagrid");
            $.extend($grid.datagrid("options"),{
                url : "",//这里定义url
                queryParams : {}//根据实际情况定义参数
            });
            $grid.datagrid("load");//这行代码执行后发送了两次请求
            gridHelper.isRefresh = false;
        }
    }
};
复制代码

当initGrid方法第一次执行时,发现后台获取数据的方法执行了两次,但我初始化datagrid的时候并没有定义URL属性,肯定也不会发送请求;往下代码能发送请求的就只有$grid.datagrid(“load”)这一句,最多也只会发送一次,但事实是两次,这是为什么呢,难道这是easyui的一个BUG吗?经过断点调试与思考,我发现datagrid在初始化的时候,即使不定义URL属性,也会有“处理中,请稍候。。。”这样的加载中效果(持续那么一瞬间),但是肯定没有发送请求的,估计源码中通过setTimeout来实现。问题就出在这里了,就是在setTimeout的过程中,我下面的代码定义了URL属性,setTimeout后发现URL被定义了,所以照样发送了一个请求,然后我load了一次,又重复发送了一个请求。了解了这样的原理后,我通过加入setTimeout解决了这个问题:

复制代码
        if(gridHelper.isRefresh){
            window.setTimeout(function(){
                var $grid = $("#datagrid");
                $.extend($grid.datagrid("options"),{
                    url : "",//这里定义url
                    queryParams : {}//根据实际情况定义参数
                });
                $grid.datagrid("load");
                gridHelper.isRefresh = false;
            },100);//延迟100毫秒执行,时间可以更短
        }
复制代码

这样,以上代码就只会发送一次请求了!纯属个人见解,有问题请提出,大家一起交流!

如何转换SQL Server 2008数据库到SQL Server 2005 - 潇湘隐者 - 博客园

mikel阅读(965)

来源: 如何转换SQL Server 2008数据库到SQL Server 2005 – 潇湘隐者 – 博客园

    背景介绍: 公司一套系统使用的是SQL SERVER 2008数据库,突然一天收到邮件,需要将这套系统部署到各个不同地方(海外)的工厂,需要在各个工厂部署该数据库,等我将准备工作做好,整理文档的时候,坑爹的事情发生了,居然发现有两三个工厂使用的还是SQL SERVER 2005数据库,要命的是这几个工厂没有SQL SERVER 2008的数据库服务器。而其中两个正准备做服务器的迁移升级,但是IBM的存储还没有到,没办法,这么”反人类,阻挡历史进程“的事情就发生了,我以为这种小概率的事情永远都不会发生在我身上。好在这个数据库不大,才200M。于是才有下面文章的总结。

        由于SQL Server 2008数据库转换到SQL Server 2005上,是不能通过备份还原、分离附加等操作来实现的,只能通过数据的导入导出实现。方法大致有几种:

    1:通过Generate Scripts(包括数据) ,这个方法呢,如果对于很小的数据库,倒是一个非常方便的方法。注意前提是数据库很小,如果数据库很大,那你千万别选这个方法,SQL导入数据的性能会折磨死你。

    2:通过Generate Scripts(生成表、视图等),然后使用SQL Server Import and Export Tool导入数据

    3: Red Gate SQL Compare 工具

     关于性能对比呢,博客园这位老兄的这篇文章“不同版本的SQL Server之间数据导出导入的方法及性能比较 “ 已经做了详细的测试,下面只是介绍、总结转换SQL Server 2008数据库到SQL Server 2005的操作步骤。

Generate Scripts

Step 1:  单击某个需要转换的数据库,然后选择任务(Tasks)——生成脚本(Generate Scripts)

clipboard

Step 2: 弹出生成脚本指导界面

clipboard[1]

Step 3:选择需要导出的数据库对象,有导出整个数据库对象和选择指定数据库对象两个选项

clipboard[2]

Step 4:  设置生成脚本选项,例如指定脚本位置,脚本文件的编码等等选择,最重要的是高级选项”Advanced“

clipboard[3]

如下所示,最好在”Script for Server Version“ 里面选择”SQL Server 2005“等选项

clipboard[4]

测试服务器这个版本里面没有’Script Data”(生成数据的脚本)选项,如下所示,如果你要通过脚本插入数据,那么就必须选择这个选项。

clipboard[5]

Step 5: Summray your selections, 点击Next继续

clipboard[6]

Step 6:  执行过程。完成后即生成了相应脚本文件。

clipboard[7]

将生成的脚本在另外一台测试服务器执行,创建相应的数据库以及数据库对象。然后点击”SQL Server Import and Export“工具进行数据导入,操作步骤如下所示:

SQL Server Import and Export

Step 1: 欢迎界面,点击Next跳过。

clipboard[8]

Step 2: 选择数据源(Data Source),我们需要选择“SQL Server Native Client 10”, 输入身份认证信息,我选择Windows 身份认证。选择

需要导出数据的数据库,例如,我选择一个测试数据库TSL

clipboard[9]

clipboard[10]

Step 3:配置需要导入的Destination,选择对应的服务器和数据库

clipboard[11]

Step 4:两个选择,选项1是用于整个表或视图导入导出,选项2用于书写查询SQL或导入导出

clipboard[12]

Step 5: 勾选你需要导数的脚本,单击Source旁边的选项,即可选择全部对象。有时候需要编辑“Edit Mappings”选项,勾选”Enable identity Insert“,否则会导致后面导入导出报错

clipboard[13]

Step 6: 勾选立即执行,点击Next执行下一步

clipboard[14]

Step 7 : 执行过程,需要检查是否执行成功,查看相关出错信息。

clipboard[15]

执行完成后,然后在测试服务器将该数据库备份压缩后,通过FTP上传到各个工厂,进行还原。关于 Red Gate SQL Compare的操作,也非常方便简单,由于测试过程中没有截图,就此略过。

    由于测试用的数据库比较小,不知道比较大的数据库,例如几百G的数据,操作起来是否会遇到性能等杂七杂八的问题。不过相信很多人是不会遇到这么奇葩的案例的。例如使用导入导出工具,其实就会遇到一些小问题(以前遇到的):

   1:在验证时出现错误,错误信息如下所示:

clipboard[16]

 

– Validating (Error)

       Messages

         * Error 0xc02020f4: Data Flow Task 1: The column “xxxx” cannot be processed because more than one code page (936 and 1252) are specified for it.

          (SQL Server Import and Export Wizard)

        * Error 0xc02020f4: Data Flow Task 1: The column “xxxx” cannot be processed because more than one code page (936 and 1252) are specified for it.

          (SQL Server Import and Export Wizard)

    解决:检查你源数据库和目标数据库的排序规则是否不一致,如果不一致,就会出现大量上面错误。

  2:在验证时出现下面错误信息

– Validating (Error)

            Messages

                  * Error 0xc0202049: Data Flow Task 1: Failure inserting into the read-only column “xxxxx”.

                  (SQL Server Import and Export Wizard)

                  * Error 0xc0202045: Data Flow Task 1: Column metadata validation failed.

                  (SQL Server Import and Export Wizard)

    解决:出现这个错误,是因为这一列是自动增长,需要在”Edite Mappings” 选项里面勾选“Enable Identity Insert”选项,如果数据库里面有大量这类列,是个头疼的问题。

sqlserver 中的GUID 全局唯一标识

mikel阅读(1135)

–简单实用全局唯一标识

 

DECLARE @myid uniqueidentifier
SET @myid = NEWID()
PRINT ‘Value of @myid is: ‘+ CONVERT(varchar(255), @myid)

 

–结果集

Value of @myid is: 6F9619FF-8B86-D011-B42D-00C04FC964FF

很多时候我们创建表需要唯一标识的主键
CREATE TABLE cust
(
 CustomerID uniqueidentifier NOT NULL --由于newid()函数得到的是uniqueidentifier 类型的值
   DEFAULT newid(),
 Company varchar(30) NOT NULL,
 ContactName varchar(60) NOT NULL, 
 Address varchar(30) NOT NULL, 
 City varchar(30) NOT NULL,
 StateProvince varchar(10) NULL,
 PostalCode varchar(10) NOT NULL, 
 CountryRegion varchar(20) NOT NULL, 
 Telephone varchar(15) NOT NULL,
 Fax varchar(15) NULL
)
GO

微服务从设计到部署(一)微服务简介 - oopsguy - 博客园

mikel阅读(1102)

来源: 微服务从设计到部署(一)微服务简介 – oopsguy – 博客园

链接https://github.com/oopsguy/microservices-from-design-to-deployment-chinese
译者Oopsguy

目前微服务受到很多关注:文章、博客、社交媒体上的讨论和会议演讲。他们正在迅速走向加德纳技术成熟度曲线(Gartner Hype cycle)的高峰。与此同时,也有持怀疑态度的软件社区人员认为微服务没什么新鲜可言。反对者声称它的思想只是面向服务架构(SOA)的重塑。然而,无论是炒作还是怀疑,不可否认微服务架构模式具有非常明显的优势——特别是在实施敏捷开发和复杂的企业应用交付方面。

本章节是七章之中介绍关于的设计、构建和部署微服务方面的内容。您将了解微服务的由来和与传统单体应用模式的比较。这本电子书描述了许多关于微服务架构方面的内容。无论是对您的项目的意义,或者是如何去应用它,您都将了解到微服务架构模式的优点和缺点。

我们先来看看为什么要考虑使用微服务。

1.1、构建单体应用

我们想一下,您开始制作一个新的打车应用,打算与 Uber 和 Hailo 竞争。经过初步交流与需求收集,您将手动或者使用类似 Rails、Spring Boot、Play或者Maven等平台来生成一个新项目。

这个新应用是一个模块化的六边形架构,如图 1-1 所示:

图 1-1、一个简单的打车应用

应用程序的核心是由模块实现的业务逻辑,它定义了服务、领域对象和事件。围绕核心的是与外部世界接口对接的适配器。适配器示例包括了数据库访问组件、生产和消费消息的消息组件和 web 组件,它们暴露了 API 或者实现了一个 UI。

尽管有一个逻辑模块化的架构,但应用程序被作为一个整体进行打包和部署。实际格式取决于应用程序的语言和框架。例如,许多 Java 应用程序被打包成 WAR 文件并部署在如 Tomcat 或者 Jetty 之类的应用服务器上。其他 Java 应用程序是打包成自包含(self-contained)的可执行 JAR。类似地,Rails 和 Node.js 应用程序被打包为目录层次结构。

以这种风格编写的应用是很常见的。他们很容易开发,因为我们的 IDE 和其他工具专注于构建单个应用程序。这些应用程序也很容易测试。您可以通过简单地启动并使用如 Selenium 测试包来测试 UI 以轻松地实现端到端(end-to-end)测试。单体应用同样易于部署。你只需拷贝打包好的应用程序到服务器。您还可以通过运行多个副本和结合负载均衡器来扩展应用。在项目的早期阶段,它运作良好。

1.2、走向单体地狱

不幸的是,这种简单的方法有很大的局限性。成功的应用有一个趋势,随着时间推移而变得越来越臃肿。在每个冲刺时期,您的开发团队实现了更多的用户需求,这意味着添加了许多行代码。几年之后,您的小而简单的应用将会逐渐成长为庞然大物似的单体。为了给出一个极端的示例,我最近和一位开发者做了交谈,他正在编写一个工具用于从他们的数百万行代码(lines of code,LOC)应用中分析数千个 JAR 之间的依赖。我相信这是大量开发者在多年齐心协力下创造了这样的野兽。

一旦您的应用程序成为了一个庞大、复杂的单体,您的开发组织可能陷入了一个令人痛苦的世界。敏捷开发和交付的任何一次尝试都将原地徘徊。一个主要问题是应用程序实在是非常复杂。对于任何一个开发人员来说,这是在太大了,这是可以理解的。最终,正确地修复 bug 和实现新功能变得非常困难且耗时。此外,这个趋势就像是往下的螺旋。如果基本代码难以理解,那么改变将不会变得正确。您最终得到的是一个巨大且不可思议的大泥球

应用程序的规模也将减缓发展。应用程序越大,启动的时间越长。我调查过开发者们的单体应用的大小和性能,一些报告的启动时间为 12 分钟。我也听说过应用程序启动需要 40 分钟以上的怪事。如果开发人员经常要重启应用服务器,那么很大一部分时间都是在等待中度过,他们的生产力将受到限制。

另一个大问题是,复杂的单体应用本身就是持续部署的障碍。如今,SaaS 应用发展到了每天可将变更推送到生产中多次。这对于复杂的单体非常困难,因为您必须重新部署整个应用程序才能更新其中任何一部分。这对我之前提到的漫长的启动时间也不会有什么帮助。此外,因为变化的影响通常不是很清楚,您很可能需要做大量的手工测试。因此,持续部署是不可能做到的。

当不同模块存在资源需求冲突时,单体应用也可能难以扩展。例如,一个模块可能会执行 CPU 密集型图像处理逻辑,理想情况下是部署在 Amazon EC2 Compute Optimized 实例中。另一个模块可能是内存数据库,最适合 EC2 Memory-optimized 实例。然而,因为这些模块被部署在一起,您必须在硬件选择上做出妥协。

单体应用的另一个问题是可靠性。因为所有模块运行在同一进程。任何模块的一个 bug,比如内存泄漏,可能会拖垮整个进程。此外,由于应用程序的所有的实例都是相同的,该错误将影响到整个应用的可用性。

最后但并非是最不重要的,单体应用使得采用新的框架和语言变得非常困难。例如,我们假设您有 200 万行代码使用了 XYZ 框架编写。使用较新的 ABC 框架来重写整个应用,这将是非常昂贵的(在时间和成本方面),即使那个框架非常好。因此,这对于采用新技术是一个非常大的障碍。您在项目开始时无论选择何种新技术都将会感到困扰。

总结一下:您有一个成功的关键业务应用程序,它已经发展成为一个只有少数开发人员(如果有的话)能够理解的巨大单体。它使用了过时、非生产性技术编写的,这使得招聘优秀开发人员变得困难。应用程序难以扩展,不可靠。因此敏捷开发和应用交付是不可能的。

那么您能做些什么呢?

1.3、微服务-解决复杂问题

许多组织,如 Amazon、eBay 和 Netflix,已经采用现在所谓的微服务架构模式解决了这个问题,而不是构建一个臃肿的单体应用。它的思路是将您的应用程序分解成一套较小的互连服务。

一个服务通常实现了一组不同的特性或者功能,例如订单管理、客户管理等。每一个微服务都是一个迷你应用,它自己的六边形架构包括业务逻辑以及多个适配器。

一些微服务会暴露一个供其他微服务或应用客户端消费的 API。其他微服务可能实现了一个 web UI。在运行时,每个实例通常是一个云虚拟机(virtual machine,VM)或者一个 Docker 容器。

例如,前面描述的系统可能分解成如图 1-2 所示:

图 1-2、一个单体应用分解成微服务

应用程序的每个功能区域现在都由自己的微服务实现。此外,Web 应用程序被分为一组更简单的 Web 应用程序。例如,以我们的出粗车为例,一个专门是乘客的,一个专门是司机的。这使得它更容易地为特定的用户、司机、设备或者专门的用例部署不同的场景。每个后端服务暴露一个 REST API,大部分的服务消费由其他服务提供的 API。例如,司机管理使用了通知服务器来告知一个可用司机关于一个潜在路程。UI 服务调用了其他服务来渲染页面。服务也可以使用异步、基于消息的通信。本电子书后面将会详细介绍服务间的通信。

一些 REST API 也暴露给移动端应用供司机和乘客使用。然而,应用不能直接访问后端服务。相反,他们之间的通信是由一个称为 API 网关(API Cateway)的中介负责。API 网关负责负载均衡、缓存、访问控制、API计量和监控,可以通过使用 NGINX 来实现。第 2 章详细讨论 API 网关。

图 1-3、开发和交付中的缩放立方(Scale Cube)

微服务架构模式相当于此缩放立方的 Y 轴坐标,此立方是一个来自《架构即未来》的三维伸缩模型。另外两个坐标轴是由运行多个相同应用程序副本的负载均衡器组成的 X 轴缩放和 Z 轴坐标(或者数据分区),其中请求的属性(例如,一行记录的主键或者客户标识)用于将请求路由到特定的服务器。

应用程序通常将这三种类型的坐标方式一起使用。Y 轴坐标将应用分解成微服务,如图 1-2 所示。

在运行时,X 坐标轴上运行着服务的多个实例,每一个服务配合负载均衡器以满足吞吐量和可用性。某些应用程序也有可能使用 Z 坐标轴来进行分区服务。图 1-4 展示了如何用 Docker 将旅途管理(Trip Management)服务部署到 Amazon EC2 上运行。

图 1-4、使用 Docker 部署旅途管理服务

在运行时,旅途管理服务由多个服务实例组成,每个服务实例是一个 Docker 容器。为了实现高可用,容器是在多个云虚拟机上运行的。服务实例的前方是一个如 NGINX 的负载均衡器,用于跨实例分发请求。负载均衡器也可以处理其他问题,如缓存访问控制API 度量监控

微服务架构模式明显影响到了应用程序与数据库之间的关系。与与其他共享单个数据库模式(schema)服务不同,每一个服务都有自己的数据库模式。一方面,这种做法是与企业级数据库数据模型的想法不符,此外,它经常导致一些数据冗余。然而,如果您想从微服务中受益,每一个服务都应该有自己的数据库模式。因为它做到了松耦合。图 1-5 展示了数据库架构示例应用程序。

每个服务都有自己的数据库。而且,服务可以使用一种最适合其需求、号称多语言持久架构(polyglot persistence architecture)的数据库。例如,司机管理,找到与潜在乘客接近的司机必须使用支持高效地理查询的数据库。

图 1-5、打车应用的数据库架构

从表面上看,微服务架构模式类似于 SOA。微服务是由一组服务组成。然而,换另一种方式去思考微服务架构模式,它是没有商业化的 SOA,没有集成 Web 服务规范(WS-*)和企业服务总线(Enterprise Service Bus,ESB)。基于微服务的应用支持更简单、轻量级的协议,例如,REST,而不是 WS-*。他们也尽量避免使用 ESB,而是实现微服务本身具有类似 ESB 的功能。微服务架构也拒绝了 SOA 的其他部分,例如,数据访问规范模式概念。

1.4、微服务的优点

微服务架构模式有许多非常好的地方。第一,它解决了复杂问题。它把可能会变得庞大的单体应用程序分解成一套服务。虽然功能数量不变,但是应用程序已经被分解成可管理的块或者服务。每个服务都有一个以远程过程调用(RPC)驱动或者消息驱动的 API 明确定义的边界。微服务架构模式强制一定程度的模块化,实际上,使用单体基础代码来实现是极其困难的。因此,个体服务能被更快地开发,并更容易理解与维护。

第二,这种架构使得每个服务都可以由一个团队独立专注开发。开发者可以自由选择任何符合服务 API 契约的技术。当然,更多的组织是希望通过技术选型限制来避免完全混乱的状态。然而,这种自由意味着开发人员不再有可能在这种自由的新项目开始时使用过时的技术。当编写一个新服务时,他们可以选择当前的技术。此外,由于服务比较小,使用当前技术重写旧服务将变得更加可行。

第三,微服务架构模式可以实现每一个微服务独立部署。开发人员根本不需要去协调部署本地变更到它们的服务。这些变更一经测试即可立即部署。比如,UI 团队可以执行 A|B 测试,并快速迭代 UI 变更。微服务架构模式使得持续部署成为可能。

最后,微服务架构模式使得每个服务能够独立扩展。您可以仅部署满足每个服务的容量和可用性约束的实例数目。此外,您可以使用与服务资源要求最匹配的硬件。例如,您可以在 EC2 Compute Optimized 实例上部署一个 CPU 密集型图像处理服务,并且在 EC2 Memory-optimized 实例上部署一个内存数据库服务。

1.5、微服务的缺点

就像 Fred Brooks 近 30 年前写的《人月神话》说的,没有银弹。像其他技术一样,微服务架构模式也是如此,存在着缺点。其中一个缺点就是名称本身。微服务这个术语的重点过多偏向于服务的规模。事实上,有些开发者主张构建极细粒度的 10-100 LOC(代码行) 服务虽然小型服务可能比较好,但重要的是要记住,小型服务只是一种手段,而不是主要目标。微服务的目标在于充分分解应用程序以方便应用敏捷开发和部署。

微服务另一个主要的缺点是由于微服务是一个分布式系统而变得复杂。开发者需要选择和实现基于消息或者 RPC 的进程间通信机制。此外,由于目标请求可能很慢或者不可用,他们还必须编写代码来处理部分故障。虽然这些都不是很复杂高深的事,但模块间通过语言级方法/过程调用相互调用,这比单体应用要复杂得多。

微服务的另一个挑战是分区数据库架构。更多多个业务实体的业务事务是相当普遍的。这些事务在单体应用中的实现显得微不足道,因为只有一个单独的数据库。在基于微服务的应用程序中,您需要更新不同服务所有用的数据库。通常不会选择分布式事务,不仅仅是因为 CAP 定理。他们根本不支持如今高度可扩展的 NoSQL 数据库和消息代理。您最后不得不使用最终基于一致性的方法,这对于开发人员来说更具挑战性。

测试微服务应用程序也很复杂。例如,使用现代的框架如 Sprig Boot,只需要编写一个测试类来启动一个单体 web 应用程序并测试其 REST API。相比之下,一个类似的测试类对于微服务来说需要启动该服务及其所依赖的任何服务,或者至少为这些服务配置存根。再次声明,虽然这不是一件高深的事情,但不要低估了这样做的复杂性。

微服务架构模式的另一个主要的挑战是实现了跨越多服务变更。例如,我们假设您正在实现一个变更服务 A、B 和 C 的需求,其中 A 依赖于 B,并且 B 依赖于 C。在单体应用程序中,您可以简单地修改相应的模块、整合变更并一次性部署他们。相反,在微服务中您需要仔细规划和协调出现的变更到每一个服务。例如,您需要更新服务 C,然后更新服务 B,最后更新服务 A。幸运的是,大多数变更只会影响一个服务;需要协调的多服务变更相对较少。

部署基于微服务的应用程序也是非常地复杂。一个单体应用可以很容易地部署到基于传统负载均衡器的一组相同的服务器上。每个应用程序实例都配置有基础设施服务的位置(主机和端口),比如数据库和消息代理。相比之下,微服务应用程序通常由大量的服务组成。例如,据 Adrian Cockcroft,Hailo 拥有 160 个不同的服务,Netflix 拥有超过 600 个服务。

每个服务都有多个运行时实例。还有更多的移动部件需要配置、部署、扩展和监控。此外,您还需要实现服务发现机制,使得服务能够发现需要与之通信的任何其他服务的位置(主机和端口)。传统比较麻烦的基于票据(ticket-based)和手动操作方式无法扩展到如此复杂程度。因此,成功部署微服务应用程序要求开发人员能高度控制部署方式和高度自动化。

一种自动化的方式是使用现成的平台即服务(PaaS),如 Cloud Foundry。PaaS 为开发人员提供了一种简单的方式来部署和管理他们的微服务。它让开发人员避开了诸如采购和配置 IT 资源等烦恼。同时,配置 PaaS 的系统与网络专业人员可以确保最佳实践和落实公司策略。

自动化微服务部署的另一个方式是开发自己的 PaaS。一个普遍的起点是使用集群方案,如 Kubernetes,与 Docker 等容器技术相结合。在本书最后我们将看到基于软件的应用交付方式如 NGINX 是如何在微服务级别处理缓存、访问控制、API 计量和监控,可以帮助解决这个问题。

1.6、总结

构建复杂的微服务应用程序本质上是困难的。单体架构模式只适用于简单、轻量级的应用程序,如果您使用它来构建复杂应用,您最终会陷入一个痛苦的世界。微服务架构模式是复杂、持续发展应用的一个更好的选择。尽管它存在着缺点和实现挑战。

在后面的章节中,我将介绍微服务架构的方方面面并探讨诸如服务发现、服务部署方案以及将单体应用重构为服务的策略。

微服务实战:NGINX Plus 作为反向代理服务器

By Floyd Smith

10000 个网站中有超过 50% 使用 NGINX,这主要是因为它具有作为反向代理服务器的能力。您可以 NGINX 放在当前应用程序前面甚至是数据库服务器以获取各种功能 —— 更高的性能、更高的安全性、可扩展性、灵活性等。你现有的应用程序只需要配置代码和作出很少或无需改变。对于存在性能压力的站点,或者预计未来存在高负荷,效果看起来似乎没那么神奇。

那么这与微服务有什么关系呢?实现一个反向代理服务器,并使用 NGINX 的其他功能来为您提供架构灵活性。反向代理服务器、静态和应用文件缓存、SSL/TLS 和 HTTP/2 都会从您的应用程序剔除。让应用程序只做它该做的事,NGINX 还可作为负载均衡器,微服务实施过程中的一个关键角色。先进的 NGINX Plus 的功能包含了复杂的负载均衡算法、多种方式的会话持久和管理监控,这些对微服务尤其有用(NGINX 最近还增加了使用 DNS SRV 记录的服务发现支持,这是一个顶尖的功能)。而且,如本章所述,NGINX 可以自动化部署微服务。

此外,NGINX 还提供了必要的功能来支撑 NGINX 微服务参考架构中的三大模型。代理模型使用 NGINX 作为 API 网关;网格路由模型使用了一个额外的 NGINX 作为进程间通信中枢;Fabric 模型中每个微服务使用一个 NGINX 来控制 HTTP 流量,在微服务之间实现 SSL/TLS,这非常具有突破性。

这 11 款 Chrome 扩展,让网页看起来更舒服 - 少数派

mikel阅读(1249)

来源: 这 11 款 Chrome 扩展,让网页看起来更舒服 – 少数派

Chrome 已经成为越来越多人的默认浏览器。其丰富多样的扩展程序可以大幅度提高网页浏览体验。这篇文章,为你推荐几款让网页浏览更加舒适的扩展程序。

为什么你们就不能加个空格呢?

这款扩展专门治疗由于汉字与英文之间没有空格导致的腰酸,背痛以及神情恍惚。其内置的空格之神,可以为一切中文字和半形的英文、数字、符号之间插入空白。

相关文章:为什么你们就是不能加个空格呢?

下载:为什么你们就不能加个空格呢?(Chrome 网上应用店)

维基百科视觉现代化:Wikiwand

多年来,维基百科网页的视觉风格坚定不移地停留在 Web2.0 时代。Wikiwand 可以让你看到一个属于这个时代的维基百科。

除了视觉上的提升,Wikiwand 也添加了一些人性化的交互:比如超链接悬浮窗,类似 Kindle 的书签功能,搜索联想,黑色模式等等…

相关文章:轻、快、美,浏览维基百科的魔杖:Wikiwand

下载:Wikiwand(Chrome 网上应用店)

微博界面改造:眼不见心不烦

或许你也像我一样,面对微博右侧的「主播红人榜」全是陌生面孔,怀疑自己一定是老了。为了麻痹自己,我开始使用「眼不见心不烦」。这款扩展不仅可以屏蔽主播红人榜,亚洲新歌榜,会员专区等一切右边栏;还可以无限制地屏蔽关键词、用户、来源…

下载:眼不见心不烦(Chrome 网上应用店)

Safari 的阅读体验:阅读模式

「阅读模式」这款扩展可以为 Chrome 带来了类似 Safari 的阅读体验。点击图标即可进入,再次点击退出,完全的无痛体验。有一种 Chrome 原生阅读模式的错觉。

下载:阅读模式(Chrome 网上应用店)

悬浮大图:Imagus

嫌弃「查看大图」永远不够大?有了 Imagus 这款扩展程序,只要鼠标在图片上悬浮三秒,图片就会以原始尺寸显示出来。

下载:Imagus(Chrome 网上应用店)

关灯看视频

视频页面关灯是一个老生常谈的问题了,许多网站并没有支持这个功能,如果你有这个需要,这款「关灯看视频」可以提供比较广泛的支持。亲测兼容优酷,Youtube,腾讯视频以及 Freestyle。

下载:关灯看视频

Darkness

连 iOS11 都开始以「智能反转颜色」变相支持夜间模式了,众网页支持夜间模式还只是个美好愿景。

Darkness 可以为 Google,Facebook,YouTube,Reddit 等不存在的网站提供完美的夜间模式支持,不会有图片反转,文字颜色奇怪等尴尬问题。当然了,Darkness适配的网站还很少,如果你需要广泛适用的,可能需要下面这款神级扩展。

下载:Darkness(Chrome 网上应用店)

Stylish

许多网页的视觉风格是用 CSS 文件进行管理的,替换网页本身的 CSS 可以达到美化的目的了。通过安装 CSS,可以把知乎「拍扁」,让微博网页版变得简洁无比,让 Feedly 用上 Material Design 等等等…… Stylish 作为一个 CSS 管理器,可以让替换 CSS 变得简单。

至于前文提到的「暴力夜间模式」以及更多好用的 CSS,可以阅读这篇文章详细了解:不喜欢某个网站的样子?用 Stylish 给它一键「换肤」

下载:Stylish(Chrome 网上应用店)

油猴脚本:Tampermonkey

在介绍这款扩展之前,我们来了解一下「用户脚本」的用处:用户脚本可以通过修改网页为特定网站增加功能,美化等等。Tampermonkey 类似 Stylish,是一个用户脚本管理器,由于用户脚本比 CSS 更为神通广大,其应用面比 Stylish 更广。有了这个管理器,就可以轻松的安装和管理脚本了。先安装 Tampermonkey,再去寻找和安装脚本即可。(此时你的 Chrome 会有一种扩展里套扩展的感觉)

关联阅读:用 Chrome 的人都需要知道的「神器」扩展:「油猴」使用详解

下载:Tampermonkey(Chrome 网上应用店)

这里为大家推荐两个用户脚本分享网站,相当于脚本商店:

OpenUserJS

Greasyfork.org

除了以上两个网站,GitHub 上也可以找到用户脚本。为了展示用户脚本的能力,这里推荐几个:

Yet Another Weibo Filter

可以实现前文中「眼不见心不烦」的功效,几乎无异。

下载:Yet Another Weibo Filter(Greasy Fork)

视频站启用 HTML5 播放器

用 HTML5 播放器替代优酷土豆、腾讯、新浪、微博、搜狐、乐视等视频网站的 Flash 播放器,免受 MacBook 风扇声音洗礼。

下载:视频站启用 HTML5 播放器(Greasy Fork)

跳过网站等待、验证码及登录

移除登录,下载,验证码等无法跳过的倒计时。

下载:跳过网站等待、验证码及登录(Greasy Fork)

克制

对于扩展与脚本的使用,这里有句忠告:在接下来的探索过程中,你可能会发现油猴脚本更加强大额功能,但冲突与混沌一定不是你想要的效果,在添加和使用的过程中,建议谨慎添加,勤于管理。