实时开发框架Meteor API解读系列 Meteor.methods - CSDN博客

mikel阅读(890)

来源: 实时开发框架Meteor API解读系列 Meteor.methods – CSDN博客

写在前面的话

  1. 该系列是零散的。 写到哪个是哪个 不会有个顺序。
  2. 尽量一篇博客一个API
  3. 尽量搭配一个代码实例 (因为时间比较零碎不一定能够保证)
  4. 每篇博文我会标记所使用的Meteor版本,防止因为API变化,导致博客的内容的正确性问题
  5. 能够看懂官方API就尽量看官方的,官方的才是最准确的。我这里只是提供一个参考

V 0.7.01

本篇文章涉及到的API:Meteor.methods,Meteor.call,Meteor.apply,Meteor.starup


Meteor.methods — Anywhere

关于 Meteor.methods 先看一下官方的简单描述

Defines functions that can be invoked over the network by clients.

大概意思就是 定义一些方法可以被客户端通过网络进行调用。注意下面的解释都是以这一条为基础来解释的。

ps:因为我初次看官方文档就糊涂了。不知道大家注意了没有,官方文档在每个API 后面有个标记,标记api的使用范围是在客户端(Client)使用还是服务端(Server)使用,亦或是同时都可以使用(Anywhere)。

Meteor.methods后面的标识是Anywhere我的第一反应是服务端通过Meteor.methods定义的方法客户端可以调用,客户端通过Meteor.methods定义的方法服务端可以调用。其实如果稍微思考一下就会知道这样想是错误的。因为客户端有那么多,Meteor怎么知道到底使哪个客户端来执行这个方法呢? 但是这里的Anywhere是什么意思呢?稍后解释。

    *强调 API的作用也可以说它的主要目的是实现: 在服务端定义一组方法,使客户端能够调用。换句话说,通过这个API,客户端可以命令服务端做一些我们允许做的事情。
client 端定义的Meteor.methods叫做 'stub'(官方用的是这个单词) ,至于怎么翻译,看个人理解吧。这是下文需要用的一个描述。[我翻译成'存根',理解为 对服务端方法调用的客户端补充动作。]

参数:Object
函数Meteor.methods的参数是一个object对象,这个对象里面的属性名就是供客户端调用的函数名,该属性对应的值就是共调用的函数。
例如:

{
    "hello":function(){..},
    "welcome":function(){..},
    "bey":function(){..}
}

如下使用:(服务端)

 Meteor.methods({
        "say":function(name){
            //do somthing
            console.log("Hello " + name);
            return "Hello boy";
        } ,
        "draw":function(...){
            .....
        },
        .....
 });

返回值:JSON对象(官方原文是: EJSON-able value)或者一个Meteor.Error对象

Meteor.Error 当后台处理到异常时,应该主动抛出的出错信息。这是一个Meteor自定义的一个异常对象。这个对象与本篇主题不相关,暂不做解释。可能一篇博客介绍它。

在被调用函数的内部 (如say对应函数的内部)存在一个上下对象 this。这个对象组成如下:

{
    isSimulation: Boolean// a boolean value, true if this invocation is a stub.
    unblock: Function,//when called, allows the next method from this client to begin running.
    userId: String,//the id of the current user.
    setUserId:  Function//a function that associates the current client with a user.
    connection: Object//on the server, the connection this method call was received on.
}

根据官方的解释可能不是很i清楚,后面我们通过代码进行测试。

现在Meteor.methods的参数类型,和其中的函数应用已经讲,那么接着讲 如何来调用这些在Server端定义的内容。

有两个方法可以使用

1. Meteor.call
2. Meteor.apply

(个人认为:这两个方法的区别有点类似js的call与apply的区别)

Meteor.call — Anywhere

具体形式如下:

Meteor.call("function name",arg1,arg2,..,function(error,result){
    //do something
});

function nanme 表示 在Meteor.methods中定义的对象的属性名,通过这个属性名来表示调用服务端对应的这个方法。

arg1,arg2,.. 是任意个参数,这个参数将作为服务端函数的参数。

注意这些参数仅为json或者一般数据类型,不能传递包含function对象

最后一个是回调函数。如果服务端函数抛出异常则 将赋值给error这个参数。
如果没有异常抛出,则服务端函数的返回值将赋值给result这个参数。

接下来看具体实现和代码。

meteor create api-001 #创建meteor应用
cd api-001 #进入应用目录
rm *.html *.css *.js #删除自动生成的.html,css,js文件
mkdir server #创建server文件夹
mkdir client #创建client文件夹

(关于文件夹的作用请参考其他人博客,或官方文档,我暂时还没有写。)
在server文件夹中新建一个js文件,如 main.js 内容如下:

Meteor.methods({
    "test":function(){
        console.log("Hello  I'm Server");
    }
});

现在代码已经完成了,运行这个工程。
用浏览器(建议使用Chrome,该系列博客都将以此浏览器进行)打开http://localhost:3000 按F12 进入开发者模式 找到console控制台,输入:

Meteor.call("test")

这时在meteor启动的命令窗口将看到字符串输出。这就是Meteor.call的使用了。客户端调用服务端的方法。

(这里使用chrome控制台来完成相关测试是为了尽量保持博客的简洁性。同样的语句当然也可以写到客户端的js文件中,然后在html中引用,调用。效果是一样的,但是对于这篇博客来说 代码量就显的繁琐了。关于chrome的开发者模式的使用,请查找资料,或者查看我的博客如果你在看这篇博客时我已经写了的话)

最简单的demo已经了解过了,接下来看下它的返回值和参数的传递。
改动一下 main.js:

Meteor.methods({
    //这里的参数可以是N个
    "test":function(a,b){
        console.log(a + " " + b);
        //这里使用的返回类型是json对象,当然可以使用基本数据类型,如string,number,bool等
        return {
          a:a,
          b:b,
          c:a+b
        }
    }
});

meteor自动重启。同样在chrome控制台输入:

Meteor.call("test",1,2,function(error,result){
    //服务端抛出的异常将被这个回调函数的地一个参数接收,也就是error
    //当服务端无异常抛出时,error为undefined
    if(error){ 
        console.log("服务端异常");
        console.log(error);
        return;
    }
    //服务端函数的返回值将被第二个参数接受,也就是result
    console.log(result);
});

打印结果很明确就是服务端的返回值。

注意,因为Meteor.call可以在ServerClient同时使用,
    Server端运行Meteor.call时,最后一个参数不是回调函数的话,在可能的情况下服务端将同步执行这个方法体)验证代码如下  main.js

var fs = Npm.require('fs');
Meteor.methods({
    "test":function(a){
        return a;
    }
});

Meteor.startup(function(){
  var s = Meteor.call("test",5);
  console.log(s);
});

    强调!这仅是在Server可以这样用,在Client如果要取到Server的返回值,必须使用回调函数才能获取到。
------------------------------------
    现在,下面的这个结论如果不能理解或者对你造成了迷惑请忽略它,因为它的实际作用不大。只是因为API中对它有个简单的提及,那么就用代码代码验证一下。
    文档原文:
    "On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method." 
    原文就是上面这样了。不知道你思考了没有我上面提及到的这句话伸展:"在Client如果要取到Server的返回值" 什么伸展呢?因为Meteor.methods在客户端也可以使用,那么如果Client Meteor.call调用ClientMeteor.methods中的函数 是不是也需要用回调函数呢? 结论是:不一定需要。
    但是根据原文文档(原文没有直接提及,是根据"you do not pass a callback and you are not inside a stub"推断出来的,所以说这个结论实际作用不大),你得满足一个条件才行。条件是:你的Meteor.call必须在stub[前文已经解释过]中。(当然Client Metoer.methods本身不能涉及到异步调用。这个和Server端的同步是同样的样要求),而且在sub中使用Method.call 只会调用Client Meteor.methods中定义的方法,而ServerMeteor.methods不会执行。
官方文档提到了这个上面的这个情况。原文如下:
 "Finally, if you are inside a stub on the client and call another method, the other method is not executed (no RPC is generated, nothing 'real' happens). If that other method has a stub, that stub stands in for the method and is executed."

通过以下代码的运行就能看到:
Server/main.js

Meteor.methods({
  hello:function(){
    console.log("server");
    return 10;
  }
});

Client/client.js

Meteor.methods({
  hello:function(){
    console.log("clent");
    return 12;
  },
  say:function(){
    console.log('say');
    var hello = Meteor.call("hello");//Server下的hello 不会执行。
    console.log(hello);//打印值是12
  }
});


Meteor.apply — Anywhere

服务端代码不需要改动,在浏览器控制台输入:

Meteor.apply("test",[1,2],function(errror,result){
    if(error){
        //do something;
        return;
    }
    // do somthing 
    console.log(result);
});

可以看到 Meteor.apply和Meteor.call 在一般用法 上基本没区别,仅是传递参数的方式不同而已,一个使用参数列表,一个使用参数数组。其实对于大多数 开发过程而言 掌握上述两种的使用已经能够满足开发需要了。因此对无更多要求的人而言,本篇博客可以到此为止。前文提到的 服务端函数 内部的上下文对象的使用 和 关于Meteor.methods的Anywhere 这两个还没解释的地方 可以忽略了。

Meteor.methods的Anywhere

大家细心一点可能发现Meteor.call,Meteor.apply同样是”Anywhere”.

先解释一下Server的call,apply和Client的call,apply的区别。

区别很简单:

    Server
        仅能调用服务端的Meteor.methods中定义的函数
    Client
        同时调用服务端和客户端的Meteor.methods中定义的函数。这个涉及到了Meteor.methodsAnywhere了。具体请往下看。

因为前面已经介绍了methods,call,apply的简单用法了。那么下面的代码就尽量简化不再描述这些了。

首先修改server的main.js。内容如下:

Meteor.methods({
    "test":function(a){
      console.log("server");
      return  a+1;
    }
});
//Meteor加载完所有文件后将执行这个方法。具体使用请参考官方文档,比较简单。
Meteor.startup(function(){
    //然meteor启动后执行test方法
    Meteor.call("test",1,function(err,result){
        console.log(result);
    });
});

另外,最开始的时候就新建了一个client文件,在该文件夹下 新建一个js文件,如client.js
内容如下:

Meteor.methods({
    "test":function(a){
        console.log("Client");
        return a+"aaa";
    }
});

代码基本就这样 然后重新运行meteor.

可以看到在Meteor.startup调用的Meteor.call只执行了Server下定义的test函数。

接着打开Chrome的控制台,输入:

 Meteor.call("test",1,function(err,result){
        console.log(result);
 });

可以发现,客户端Meteor.methods和服务端Meteor.methods定义的test方法都执行了。而且在这个call函数的里回调函数的返回值 是服务端test的返回值,客户端test函数的返回值自动被舍弃了。

如果ClientServer同时定义了Meteor.methods 函数,在进行调用时,server 执行Meteor.callMeteor.apply 只会使Server端的Meteor.methods中定义的函数执行
,而Client端执行的callapply 会同调用客户端和服务端中Meteor.methods定义的函数。
且返回值都是以Server端的函数返回值,客户端的返回值将自动舍弃。其实这个结论自己多写几个代码就能够了解。

到这里 Meteor.methods的Anywhere不用再解释,应该也明白了。

Meteor.methods中函数的上下文对象 this

this中包含五个属性如下 (这些当需要用到时,能够想起有怎么个属性存在就可以了。到时候会自动知道该怎么使用)

{
    isSimulation: Boolean// a boolean value, true if this invocation is a stub.
    unblock: Function,//when called, allows the next method from this client to begin running.
    userId: String,//the id of the current user.
    setUserId:  Function//a function that associates the current client with a user.
    connection: Object//on the server, the connection this method call was received on.
}
  1. this.userId
    调用这个函数的用户的id ,类型:string。如果用户没有登录,值为null。 该属性在 Server的Meteor.methods和Client的Meteor.methods都存在。用官方的描述就是Anywhere。 这个属性依赖Meteor的accounts 模块。这里不赘述。
  2. this.setUseId 类型:Function,该setUerId函数的接收一个字符串或者null。使用范围:Server 设置当前链接的 用户。也就是说可以改变登录用户,或者使用户登录[如果this.userId为null],注销登录用户[this.setUserId(null)] .同样需要 Meteor的accounts模块的支持。
  3. this.isSimulation 类型:Boolean 。使用范围:Anywhere
    这个属性 我解释不好(如有好的理解请在博客下方留言),官方的解释是这样的:
Access inside a method invocation. Boolean value, true if this invocation is a stub.

[我理解意思是在Client的Meteor.methods中该值为true,Server中为false]。而且经过代码如下代码测试貌似和我理解的意思差不多 :

1. Servercall apply调用时,在ServerMeteor.methods```isSimulation```false

2. Clientcallapply调用时,
    ServerMeteor.methods```isSimulation```false
    ClientMeteor.methods```isSimulation```true

测试代码:
client.js:

Meteor.methods({
    "test":function(a){
        console.log("Client");
        console.log(this.isSimulation); //false
        return a+"aaa";
    }
});

main.js:

Meteor.methods({
    "test":function(a){
      console.log("server");
      console.log(this.isSimulation);//true
      return  a+1;
    }
});

Meteor.startup(function(){
    Meteor.call("test",1,function(err,result){
        console.log(result);
    });
});

this.unblock()
解释这个之前,我刚才得到了一个结论,一直没有注意到(没有使用过this.unblick()也没注意过它是否是阻塞的,写博客确实也是一种自我学习过程,当然这是题外话了。)的结论。默认情况下在Client调用Server端的函数这种过程是阻塞式的。意识就是说,如果有多个客户端同时调用test这个函数,而test这个函数的执行需要耗费一段,那么这个调用会进行排队依次执行。而就是只要当前面N次调用都完成以后,后面的调用才会进行。可以经过一下代码验证:
清空client.js的内容,不需要。 然后main.js的内容是:

var fs = Npm.require('fs');
Meteor.methods({
    "test":function(a){
      if(a==1){
        //这里我通过多次读写文件的时间耗费进行阻塞
        //,注意文件大小最好是在10M-40M之间,太小的文件,或者一般的计算可能执行速度太快看不到效果
          for(var i=0;i<20;i++){
            var data = fs.readFileSync("/home/ec/download/NVIDIA");
            fs.writeFileSync("/home/ec/download/test/NVIDIA"+i, data);
          }
      }
      console.log(a);
    }
});

在单个浏览器端,或者同时多个浏览器运行:(把命令写在一行)

 Meteor.call("test",1); Meteor.call("test",2);

这样就可以看到阻塞的效果。[我猜大概这样是为了实现类似资源锁之类操作]

那么现在,我们不想它阻塞在这里也是很容易做的。没错!就是在函数的第一行执行:
(这里貌似存在一个bug 这个this.unblock()没有起作用 ,具体原因可参考 详情 和 解决方法

this.unblock();

这是main.js内容如下:(参考解决过后的代码)

var fs = Npm.require('fs');
var readFile = Meteor._wrapAsync(fs.readFile.bind(fs));
var writeFile = Meteor._wrapAsync(fs.writeFile.bind(fs));
Meteor.methods({
    "test":function(a){
      this.unblock();
      if(a==1){
          //这里我通过多次读写文件的时间耗费进行阻塞
          //,注意文件大小最好是在10M-40M之间,太小的文件,或者一般的计算可能执行速度太快看不到效果
          var data = readFile("/home/ec/download/NVIDIA");
         for(var i=0;i<5;i++){
            writeFile("/home/ec/download/test/NVIDIA"+i, data);
            console.log(a+"-"+i);
        }
      }
      console.log(a);   
      return a;
    }
});

好了,this.unblock()的使用是这篇博客多耗费了2天时间。总算跳过去了。现在就剩最后一个点了。

this.connection Server

这是一个Connection对象,具体可以参考Server connections和这个部分的Meteor.onConnection(callback) 此属性不再本篇博客的讨论范围内。既然不讨论,那么就抄个官方文档的解释放在这里吧。

Access inside a method invocation. The connection this method was received on. null if the method is not associated with a connection, eg. a server initiated method call.

其中一个表达的大概意思是 如果没有通过http(非Client-Server或DDP)而是直接在Server中来调用这个methods里面的方法,那么connection是一个null。

本篇博客结束了。

对了最后其实还落下了一点点。那就是关于Meteor.apply(name, params [, options] [, asyncCallback])第三个可选参数options的解释了。
应该比较好理解 。同样贴上一个官方文档。

Options #仅在client端才有此参数
wait Boolean
(Client only) If true, don’t send this method until all previous method calls have completed, and don’t send any subsequent method calls until this one is completed.
onResultReceived Function
解释:如果为true,除非前面的方法调用都已经完成回调了,才会开始执行本次的函数调用。并且在本次函数调用完成之前 不会在执行其他任何的函数调用了。
(Client only) This callback is invoked with the error or result of the method (just like asyncCallback) as soon as the error or result is available. The local cache may not yet reflect the writes performed by the method.

这个的验证代码可以自己尝试写一下。就当是思考题吧。

转载请注明出处。谢谢!

Ubuntu14.04防火墙配置_限制指定ip访问

mikel阅读(2445)

安装ufw
sudo apt-get install ufw

启用ufw
sudo ufw enable

阻止所有外部对本机的访问
sudo ufw default deny

允许所有的外部IP访问本机的22/tcp (ssh)端口
sudo ufw allow 22/tcp

允许此IP访问所有的本机端口
sudo ufw allow from 10.0.0.163

sudo ufw allow from 10.0.0.162

sudo ufw allow from 10.0.0.161

查看防火墙状态
sudo ufw status

删除上面建立的某条规则
sudo ufw delete allow smtp

防火墙规则文件路径
/lib/ufw/user.rules

关闭防火墙
sudo ufw disable

 

【使用流程】

有台Ubuntu14.04服务器需要禁止一些IP来访问,本想使用/etc/hosts.allow和/etc/hosts.deny来做限制,后来还是使用utw防火墙搞定了。

vi /etc/hosts.allow
ALL:10.0.0.163,10.0.0.162,10.0.0.161

vi /etc/hosts.deny
ALL:ALL

设置好后
/etc/init.d/networking restart

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阅读(854)

来源: 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阅读(1291)

来源: 基于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阅读(1564)

来源: 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阅读(1583)

关于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阅读(966)

来源: 如何转换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阅读(1137)

–简单实用全局唯一标识

 

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