Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15843 articles
Browse latest View live

前端开发技术的发展

$
0
0

前端开发技术,从狭义的定义来看,是指围绕HTML、JavaScript、CSS这样一套体系的开发技术,它的运行宿主是浏览器。从广义的定义来看,包括了:

  • 专门为手持终端设计的类似WML这样的类HTML语言,类似WMLScript这样的类JavaScript语言。
  • VML和SVG等基于XML的描述图形的语言。
  • 从属于XML体系的XML,XPath,DTD等技术。
  • 用于支撑后端的ASP,JSP,ASP.net,PHP,nodejs等语言或者技术。
  • 被第三方程序打包的一种类似浏览器的宿主环境,比如Adobe AIR和使用HyBird方式的一些开发技术,如PhoneGap(它使用Android中的WebView等技术,让开发人员使用传统Web开发技术来开发本地应用)
  • Adobe Flash,Flex,Microsoft Silverlight,Java Applet,JavaFx等RIA开发技术。

本文从狭义的前端定义出发,探讨一下这方面开发技术的发展过程。

从前端开发技术的发展来看,大致可以分为以下几个阶段:

一. 刀耕火种

1. 静态页面

最早期的Web界面基本都是在互联网上使用,人们浏览某些内容,填写几个表单,并且提交。当时的界面以浏览为主,基本都是HTML代码,有时候穿插一些JavaScript,作为客户端校验这样的基础功能。代码的组织比较简单,而且CSS的运用也是比较少的。

最简单的是这样一个文件:

<html><head><title>测试一</title></head><body><h1>主标题</h1><p>段落内容</p></body></html>

2. 带有简单逻辑的界面

这个界面带有一段JavaScript代码,用于拼接两个输入框中的字符串,并且弹出窗口显示。

<html><head><title>测试二</title></head><body><input id="firstNameInput" type="text" /> <input id="lastNameInput" type="text" /> <input type="button" onclick="greet()" /><script language="JavaScript">
        function greet() {
            var firstName = document.getElementById_x("firstNameInput").value;
            var lastName = document.getElementById_x("lastNameInput").value;
            alert("Hello, " + firstName + "." + lastName);
        }</script> </body></html>

3. 结合了服务端技术的混合编程

由于静态界面不能实现保存数据等功能,出现了很多服务端技术,早期的有CGI(Common Gateway Interface,多数用C语言或者Perl实现的),ASP(使用VBScript或者JScript),JSP(使用Java),PHP等等,Python和Ruby等语言也常被用于这类用途。

有了这类技术,在HTML中就可以使用表单的post功能提交数据了,比如:

<form method="post" action="username.asp"><p>First Name: <input type="text" name="firstName" /></p><p>Last Name: <input type="text" name="lastName" /></p><input type="submit" value="Submit" /></form>

在这个阶段,由于客户端和服务端的职责未作明确的划分,比如生成一个字符串,可以由前端的JavaScript做,也可以由服务端语言做,所以通常在一个界面里,会有两种语言混杂在一起,用<%和%>标记的部分会在服务端执行,输出结果,甚至经常有把数据库连接的代码跟页面代码混杂在一起的情况,给维护带来较大的不便。

<html><body><p>Hello world!</p><p><%
            response.write("Hello world from server!")
        %></p></body></html>

4.组件化的萌芽

这个时代,也逐渐出现了组件化的萌芽。比较常见的有服务端的组件化,比如把某一类服务端功能单独做成片段,然后其他需要的地方来include进来,典型的有:ASP里面数据库连接的地方,把数据源连接的部分写成conn.asp,然后其他每个需要操作数据库的asp文件包含它。

上面所说的是在服务端做的,浏览器端通常有针对JavaScript的,把某一类的Javascript代码写到单独的js文件中,界面根据需要,引用不同的js文件。针对界面的组件方式,通常利用frameset和iframe这两个标签。某一大块有独立功能的界面写到一个html文件,然后在主界面里面把它当作一个frame来载入,一般的B/S系统集成菜单的方式都是这样的。

此外,还出现了一些基于特定浏览器的客户端组件技术,比如IE浏览器的HTC(HTML Component)。这种技术最初是为了对已有的常用元素附加行为的,后来有些场合也用它来实现控件。微软ASP.net的一些版本里,使用这种技术提供了树形列表,日历,选项卡等功能。HTC的优点是允许用户自行扩展HTML标签,可以在自己的命名空间里定义元素,然后,使用 HTML,JavaScript和CSS来实现它的布局、行为和观感。这种技术因为是微软的私有技术,所以逐渐变得不那么流行。

Firefox浏览器里面推出过一种叫XUL的技术,也没有流行起来。

二. 铁器时代

这个时代的典型特征是Ajax的出现。

1. AJAX

AJAX其实是一系列已有技术的组合,早在这个名词出现之前,这些技术的使用就已经比较广泛了,GMail因为恰当地应用了这些技术,获得了很好的用户体验。

由于Ajax的出现,规模更大,效果更好的Web程序逐渐出现,在这些程序中,JavaScript代码的数量迅速增加。出于代码组织的需要,“JavaScript框架”这个概念逐步形成,当时的主流是prototype和mootools,这两者各有千秋,提供了各自方式的面向对象组织思路。

2. JavaScript基础库

Prototype框架主要是为JavaScript代码提供了一种组织方式,对一些原生的JavaScript类型提供了一些扩展,比如数组、字符串,又额外提供了一些实用的数据结构,如:枚举,Hash等,除此之外,还对dom操作,事件,表单和Ajax做了一些封装。

Mootools框架的思路跟Prototype很接近,它对JavaScript类型扩展的方式别具一格,所以在这类框架中,经常被称作“最优雅的”对象扩展体系。

从这两个框架的所提供的功能来看,它们的定位是核心库,在使用的时候一般需要配合一些外围的库来完成。

jQuery与这两者有所不同,它着眼于简化DOM相关的代码。例如:

  • DOM的选择

jQuery提供了一系列选择器用于选取界面元素,在其他一些框架中也有类似功能,但是一般没有它的简洁、强大。

$("*")                //选取所有元素
$("#lastname")        //选取id为lastname的元素
$(".intro")            //选取所有class="intro"的元素
$("p")                //选取所有&lt;p&gt;元素
$(".intro.demo")    //选取所有 class="intro"且class="demo"的元素
  • 链式表达式:

在jQuery中,可以使用链式表达式来连续操作dom,比如下面这个例子:

如果不使用链式表达式,可能我们需要这么写:

var neat = $("p.neat");
neat.addClass("ohmy");
neat.show("slow");

但是有了链式表达式,我们只需要这么一行代码就可以完成这些:

$("p.neat").addClass("ohmy").show("slow");

除此之外,jQuery还提供了一些动画方面的特效代码,也有大量的外围库,比如jQuery UI这样的控件库,jQuery mobile这样的移动开发库等等。

3. 模块代码加载方式

以上这些框架提供了代码的组织能力,但是未能提供代码的动态加载能力。动态加载JavaScript为什么重要呢?因为随着Ajax的普及,jQuery等辅助库的出现,Web上可以做很复杂的功能,因此,单页面应用程序(SPA,Single Page Application)也逐渐多了起来。

单个的界面想要做很多功能,需要写的代码是会比较多的,但是,并非所有的功能都需要在界面加载的时候就全部引入,如果能够在需要的时候才加载那些代码,就把加载的压力分担了,在这个背景下,出现了一些用于动态加载JavaScript的框架,也出现了一些定义这类可被动态加载代码的规范。

在这些框架里,知名度比较高的是RequireJS,它遵循一种称为AMD(Asynchronous Module Definition)的规范。

比如下面这段,定义了一个动态的匿名模块,它依赖math模块

define(["math"], function(math) {
    return {
        addTen : function(x) {
            return math.add(x, 10);
        }
    };
}); 

假设上面的代码存放于adder.js中,当需要使用这个模块的时候,通过如下代码来引入adder:

<script src="require.js"></script><script>
    require(["adder"], function(adder) {
        //使用这个adder
    }); 
</script>

RequireJS除了提供异步加载方式,也可以使用同步方式加载模块代码。AMD规范除了使用在前端浏览器环境中,也可以运行于nodejs等服务端环境,nodejs的模块就是基于这套规范定义的。(修订,这里弄错了,nodejs是基于类似的CMD规范的)

三. 工业革命

这个时期,随着Web端功能的日益复杂,人们开始考虑这样一些问题:

  • 如何更好地模块化开发
  • 业务数据如何组织
  • 界面和业务数据之间通过何种方式进行交互

在这种背景下,出现了一些前端MVC、MVP、MVVM框架,我们把这些框架统称为MV*框架。这些框架的出现,都是为了解决上面这些问题,具体的实现思路各有不同,主流的有Backbone,AngularJS,Ember,Spine等等,本文主要选用Backbone和AngularJS来讲述以下场景。

1. 数据模型

在这些框架里,定义数据模型的方式与以往有些差异,主要在于数据的get和set更加有意义了,比如说,可以把某个实体的get和set绑定到 RESTful的服务上,这样,对某个实体的读写可以更新到数据库中。另外一个特点是,它们一般都提供一个事件,用于监控数据的变化,这个机制使得数据绑定成为可能。

在一些框架中,数据模型需要在原生的JavaScript类型上做一层封装,比如Backbone的方式是这样:

var Todo = Backbone.Model.extend({
    // Default attributes for the todo item.
    defaults : function() {
        return {
            title : "empty todo...",
            order : Todos.nextOrder(),
            done : false
        };
    },

    // Ensure that each todo created has `title`.
    initialize : function() {
        if (!this.get("title")) {
            this.set({"title" : this.defaults().title
            });
        }
    },

    // Toggle the 'done' state of this todo item.
    toggle : function() {
        this.save({
            done : !this.get("done")
        });
    }
});

上述例子中,defaults方法用于提供模型的默认值,initialize方法用于做一些初始化工作,这两个都是约定的方法,toggle是自定义的,用于保存todo的选中状态。

除了对象,Backbone也支持集合类型,集合类型在定义的时候要通过model属性指定其中的元素类型。

// The collection of todos is backed by *localStorage* instead of a remote server.
var TodoList = Backbone.Collection.extend({
    // Reference to this collection's model.
    model : Todo,

    // Save all of the todo items under the '"todos-backbone"' namespace.
    localStorage : new Backbone.LocalStorage("todos-backbone"),

    // Filter down the list of all todo items that are finished.
    done : function() {
        return this.filter(function(todo) {
            return todo.get('done');
        });
    },

    // Filter down the list to only todo items that are still not finished.
    remaining : function() {
        return this.without.apply(this, this.done());
    },

    // We keep the Todos in sequential order, despite being saved by unordered 
    //GUID in the database. This generates the next order number for new items.
    nextOrder : function() {
        if (!this.length)
            return 1;
        return this.last().get('order') + 1;
    },

    // Todos are sorted by their original insertion order.
    comparator : function(todo) {
        return todo.get('order');
    }
});

数据模型也可以包含一些方法,比如自身的校验,或者跟后端的通讯、数据的存取等等,在上面两个例子中,也都有体现。

AngularJS的模型定义方式与Backbone不同,可以不需要经过一层封装,直接使用原生的JavaScript简单数据、对象、数组,相对来说比较简便。

2. 控制器

在Backbone中,是没有独立的控制器的,它的一些控制的职责都放在了视图里,所以其实这是一种MVP(Model View Presentation)模式,而AngularJS有很清晰的控制器层。

还是以这个todo为例,在AngularJS中,会有一些约定的注入,比如$scope,它是控制器、模型和视图之间的桥梁。在控制器定义的时候,将$scope作为参数,然后,就可以在控制器里面为它添加模型的支持。

function TodoCtrl($scope) {
    $scope.todos = [{
        text : 'learn angular',
        done : true
    }, {
        text : 'build an angular app',
        done : false
    }];

    $scope.addTodo = function() {
        $scope.todos.push({
            text : $scope.todoText,
            done : false
        });
        $scope.todoText = '';
    };

    $scope.remaining = function() {
        var count = 0;
        angular.forEach($scope.todos, function(todo) {
            count += todo.done ? 0 : 1;
        });
        return count;
    };

    $scope.archive = function() {
        var oldTodos = $scope.todos;
        $scope.todos = [];
        angular.forEach(oldTodos, function(todo) {
            if (!todo.done)
                $scope.todos.push(todo);
        });
    };
}

本例中为$scope添加了todos这个数组,addTodo,remaining和archive三个方法,然后,可以在视图中对他们进行绑定。

3. 视图

在这些主流的MV*框架中,一般都提供了定义视图的功能。在Backbone中,是这样定义视图的:

// The DOM element for a todo item...
var TodoView = Backbone.View.extend({
    //... is a list tag.
    tagName : "li",

    // Cache the template function for a single item.
    template : _.template($('#item-template').html()),

    // The DOM events specific to an item.
    events : {
        "click .toggle" : "toggleDone","dblclick .view" : "edit","click a.destroy" : "clear","keypress .edit" : "updateOnEnter","blur .edit" : "close"
    },

    // The TodoView listens for changes to its model, re-rendering. Since there's
    // a one-to-one correspondence between a **Todo** and a **TodoView** in this
    // app, we set a direct reference on the model for convenience.
    initialize : function() {
        this.listenTo(this.model, 'change', this.render);
        this.listenTo(this.model, 'destroy', this.remove);
    },

    // Re-render the titles of the todo item.
    render : function() {
        this.$el.html(this.template(this.model.toJSON()));
        this.$el.toggleClass('done', this.model.get('done'));
        this.input = this.$('.edit');
        return this;
    },

    //......

    // Remove the item, destroy the model.
    clear : function() {
        this.model.destroy();
    }
});

上面这个例子是一个典型的“部件”视图,它对于界面上的已有元素没有依赖。也有那么一些视图,需要依赖于界面上的已有元素,比如下面这个,它通过 el属性,指定了HTML中id为todoapp的元素,并且还在initialize方法中引用了另外一些元素,通常,需要直接放置到界面的顶层试图会采用这种方式,而“部件”视图一般由主视图来创建、布局。

// Our overall **AppView** is the top-level piece of UI.
var AppView = Backbone.View.extend({
    // Instead of generating a new element, bind to the existing skeleton of
    // the App already present in the HTML.
    el : $("#todoapp"),

    // Our template for the line of statistics at the bottom of the app.
    statsTemplate : _.template($('#stats-template').html()),

    // Delegated events for creating new items, and clearing completed ones.
    events : {
        "keypress #new-todo" : "createOnEnter","click #clear-completed" : "clearCompleted","click #toggle-all" : "toggleAllComplete"
    },

    // At initialization we bind to the relevant events on the `Todos`
    // collection, when items are added or changed. Kick things off by
    // loading any preexisting todos that might be saved in *localStorage*.
    initialize : function() {
        this.input = this.$("#new-todo");
        this.allCheckbox = this.$("#toggle-all")[0];

        this.listenTo(Todos, 'add', this.addOne);
        this.listenTo(Todos, 'reset', this.addAll);
        this.listenTo(Todos, 'all', this.render);

        this.footer = this.$('footer');
        this.main = $('#main');

        Todos.fetch();
    },

    // Re-rendering the App just means refreshing the statistics -- the rest
    // of the app doesn't change.
    render : function() {
        var done = Todos.done().length;
        var remaining = Todos.remaining().length;

        if (Todos.length) {
            this.main.show();
            this.footer.show();
            this.footer.html(this.statsTemplate({
                done : done,
                remaining : remaining
            }));
        } else {
            this.main.hide();
            this.footer.hide();
        }

        this.allCheckbox.checked = !remaining;
    },

    //......
});

对于AngularJS来说,基本不需要有额外的视图定义,它采用的是直接定义在HTML上的方式,比如:

<div ng-controller="TodoCtrl"><span>{{remaining()}} of {{todos.length}} remaining</span><a href="" ng-click="archive()">archive</a><ul class="unstyled"><li ng-repeat="todo in todos"><input type="checkbox" ng-model="todo.done"><span class="done-{{todo.done}}">{{todo.text}}</span></li></ul><form ng-submit="addTodo()"><input type="text" ng-model="todoText"  size="30"
        placeholder="add new todo here"><input class="btn-primary" type="submit" value="add"></form></div>

在这个例子中,使用ng-controller注入了一个TodoCtrl的实例,然后,在TodoCtrl的$scope中附加的那些变量和方法都可以直接访问了。注意到其中的ng-repeat部分,它遍历了todos数组,然后使用其中的单个todo对象创建了一些HTML元素,把相应的值填到里面。这种做法和ng-model一样,都创造了双向绑定,即:

  • 改变模型可以随时反映到界面上
  • 在界面上做的操作(输入,选择等等)可以实时反映到模型里。

而且,这种绑定都会自动忽略其中可能因为空数据而引起的异常情况。

4. 模板

模板是这个时期一种很典型的解决方案。我们常常有这样的场景:在一个界面上重复展示类似的DOM片段,例如微博。以传统的开发方式,也可以轻松实现出来,比如:

var feedsDiv = $("#feedsDiv");

for (var i = 0; i < 5; i++) {
    var feedDiv = $("<div class='post'></div>");

    var authorDiv = $("<div class='author'></div>");
    var authorLink = $("<a></a>")
        .attr("href", "/user.html?user='" + "Test" + "'")
        .html("@" + "Test")
        .appendTo(authorDiv);
    authorDiv.appendTo(feedDiv);

    var contentDiv = $("<div></div>")
        .html("Hello, world!")
        .appendTo(feedDiv);
    var dateDiv = $("<div></div>")
        .html("发布日期:" + new Date().toString())
        .appendTo(feedDiv);

    feedDiv.appendTo(feedsDiv);
}

但是使用模板技术,这一切可以更加优雅,以常用的模板框架UnderScore为例,实现这段功能的代码为:

var templateStr = '<div class="post">'
    +'<div class="author">'
    +    '<a href="/user.html?user={{creatorName}}">@{{creatorName}}</a>'
    +'</div>'
    +'<div>{{content}}</div>'
    +'<div>{{postedDate}}</div>'
    +'</div>';
var template = _.template(templateStr);
template({
    createName : "Xufei",
    content: "Hello, world",
    postedDate: new Date().toString()
});

也可以这么定义:

<script type="text/template" id="feedTemplate"><% _.each(feeds, function (item) { %><div class="post"><div class="author"><a href="/user.html?user=<%= item.creatorName %>">@<%= item.creatorName %></a></div><div><%= item.content %></div><div><%= item.postedData %></div></div><% }); %></script><script>
$('#feedsDiv').html( _.template($('#feedTemplate').html(), feeds));</script>

除此之外,UnderScore还提供了一些很方便的集合操作,使得模板的使用更加方便。如果你打算使用BackBone框架,并且需要用到模板功能,那么UnderScore是一个很好的选择,当然,也可以选用其它的模板库,比如Mustache等等。

如果使用AngularJS,可以不需要额外的模板库,它自身就提供了类似的功能,比如上面这个例子可以改写成这样:

<div class="post" ng-repeat="post in feeds"><div class="author"><a ng-href="/user.html?user={{post.creatorName}}">@{{post.creatorName}}</a></div><div>{{post.content}}</div><div>发布日期:{{post.postedTime | date:'medium'}}</div></div>

主流的模板技术都提供了一些特定的语法,有些功能很强。值得注意的是,他们虽然与JSP之类的代码写法类似甚至相同,但原理差别很大,这些模板框架都是在浏览器端执行的,不依赖任何服务端技术,即使界面文件是.html也可以,而传统比如JSP模板是需要后端支持的,执行时间是在服务端。

5. 路由

通常路由是定义在后端的,但是在这类MV*框架的帮助下,路由可以由前端来解析执行。比如下面这个Backbone的路由示例:

var Workspace = Backbone.Router.extend({
    routes: {"help":                 "help",    // #help"search/:query":        "search",  // #search/kiwis"search/:query/p:page": "search"   // #search/kiwis/p7
    },

    help: function() {
        ...
    },

    search: function(query, page) {
        ...
    }    
});

在上述例子中,定义了一些路由的映射关系,那么,在实际访问的时候,如果在地址栏输入"#search/obama/p2",就会匹配到"search/:query/p:page"这条路由,然后,把"obama"和"2"当作参数,传递给search方法。

AngularJS中定义路由的方式有些区别,它使用一个$routeProvider来提供路由的存取,每一个when表达式配置一条路由信息,otherwise配置默认路由,在配置路由的时候,可以指定一个额外的控制器,用于控制这条路由对应的html界面:

app.config(['$routeProvider',
function($routeProvider) {
    $routeProvider.when('/phones', {
        templateUrl : 'partials/phone-list.html',
        controller : PhoneListCtrl
    }).when('/phones/:phoneId', {
        templateUrl : 'partials/phone-detail.html',
        controller : PhoneDetailCtrl
    }).otherwise({
        redirectTo : '/phones'
    });
}]); 

注意,在AngularJS中,路由的template并非一个完整的html文件,而是其中的一段,文件的头尾都可以不要,也可以不要那些包含的外部样式和JavaScript文件,这些在主界面中载入就可以了。

6. 自定义标签

用过XAML或者MXML的人一定会对其中的可扩充标签印象深刻,对于前端开发人员而言,基于标签的组件定义方式一定是优于其他任何方式的,看下面这段HTML:

<div><input type="text" value="hello, world"/><button>test</button></div>

即使是刚刚接触这种东西的新手,也能够理解它的意思,并且能够照着做出类似的东西,如果使用传统的面向对象语言去描述界面,效率远远没有这么高,这就是在界面开发领域,声明式编程比命令式编程适合的最重要原因。

但是,HTML的标签是有限的,如果我们需要的功能不在其中,怎么办?在开发过程中,我们可能需要一个选项卡的功能,但是,HTML里面不提供选项卡标签,所以,一般来说,会使用一些li元素和div的组合,加上一些css,来实现选项卡的效果,也有的框架使用JavaScript来完成这些功能。总的来说,这些代码都不够简洁直观。

如果能够有一种技术,能够提供类似这样的方式,该多么好呢?

<tabs><tab name="Tab 1">content 1</tab><tab name="Tab 2">content 2</tab></tabs>

回忆一下,我们在章节1.4 组件化的萌芽 里面,提到过一种叫做HTC的技术,这种技术提供了类似的功能,而且使用起来也比较简便,问题是,它属于一种正在消亡的技术,于是我们的目光投向了更为现代的前端世界,AngularJS拯救了我们。

在AngularJS的首页,可以看到这么一个区块“Create Components”,在它的演示代码里,能够看到类似的一段:

<tabs><pane title="Localization">
        ...</pane><pane title="Pluralization">
        ...</pane></tabs>

那么,它是怎么做到的呢?秘密在这里:

angular.module('components', []).directive('tabs', function() {
    return {
        restrict : 'E',
        transclude : true,
        scope : {},
        controller : function($scope, $element) {
            var panes = $scope.panes = [];

            $scope.select = function(pane) {
                angular.forEach(panes, function(pane) {
                    pane.selected = false;
                });
                pane.selected = true;
            }

            this.addPane = function(pane) {
                if (panes.length == 0)
                    $scope.select(pane);
                panes.push(pane);
            }
        },
        template : '<div class="tabbable">'
            + '<ul class="nav nav-tabs">' 
            + '<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">' 
            + '<a href="" ng-click="select(pane)">{{pane.title}}</a>' 
            + '</li>' 
            + '</ul>' 
            + '<div class="tab-content" ng-transclude></div>' 
            + '</div>',
        replace : true
    };
}).directive('pane', function() {
    return {
        require : '^tabs',
        restrict : 'E',
        transclude : true,
        scope : {
            title : '@'
        },
        link : function(scope, element, attrs, tabsCtrl) {
            tabsCtrl.addPane(scope);
        },
        template : '<div class="tab-pane" ng-class="{active: selected}" ng-transclude>' + '</div>',
        replace : true
    };
})

这段代码里,定义了tabs和pane两个标签,并且限定了pane标签不能脱离tabs而单独存在,tabs的controller定义了它的行为,两者的template定义了实际生成的html,通过这种方式,开发者可以扩展出自己需要的新元素,对于使用者而言,这不会增加任何额外的负担。

四. 一些想说的话

关于ExtJS

注意到在本文中,并未提及这样一个比较流行的前端框架,主要是因为他自成一系,思路跟其他框架不同,所做的事情,层次介于文中的二和三之间,所以没有单独列出。

写作目的

在我10多年的Web开发生涯中,经历了Web相关技术的各种变革,从2003年开始,接触并使用到了HTC,VML,XMLHTTP等当时比较先进的技术,目睹了网景浏览器的衰落,IE的后来居上,Firefox和Chrome的逆袭,各类RIA技术的风起云涌,对JavaScript的模块化有过持续的思考。未来究竟是什么样子?我说不清楚,只能凭自己的一些认识,把这些年一些比较主流的发展过程总结一下,供有需要了解的朋友们作个参考,错漏在所难免,欢迎大家指教。

个人邮箱:xu.fei@outlook.com
新浪微博: http://weibo.com/sharpmaster

 

本文转载自: http://www.ituring.com.cn/article/25069


  青春就应该这样绽放   游戏测试:三国时期谁是你最好的兄弟!!   你不得不信的星座秘密

高手详解SQL性能优化十条经验

$
0
0
1.查询的模糊匹配

尽量避免在一个复杂查询里面使用 LIKE '%parm1%'—— 红色标识位置的百分号会导致相关列的索引无法使用,最好不要用.

解决办法:

其实只需要对该脚本略做改进,查询速度便会提高近百倍。改进方法如下:

a、修改前台程序——把查询条件的供应商名称一栏由原来的文本输入改为下拉列表,用户模糊输入供应商名称时,直接在前台就帮忙定位到具体的供应商,这样在调用后台程序时,这列就可以直接用等于来关联了。

b、直接修改后台——根据输入条件,先查出符合条件的供应商,并把相关记录保存在一个临时表里头,然后再用临时表去做复杂关联

2.索引问题

在做性能跟踪分析过程中,经常发现有不少后台程序的性能问题是因为缺少合适索引造成的,有些表甚至一个索引都没有。这种情况往往都是因为在设计表时,没去定义索引,而开发初期,由于表记录很少,索引创建与否,可能对性能没啥影响,开发人员因此也未多加重视。然一旦程序发布到生产环境,随着时间的推移,表记录越来越多

这时缺少索引,对性能的影响便会越来越大了。

这个问题需要数据库设计人员和开发人员共同关注

法则:不要在建立的索引的数据列上进行下列操作:

◆避免对索引字段进行计算操作

◆避免在索引字段上使用not,<>,!=

◆避免在索引列上使用IS NULL和IS NOT NULL

◆避免在索引列上出现数据类型转换

◆避免在索引字段上使用函数

◆避免建立索引的列中使用空值。

3.复杂操作

部分UPDATE、SELECT 语句 写得很复杂(经常嵌套多级子查询)——可以考虑适当拆成几步,先生成一些临时数据表,再进行关联操作

4.update

同一个表的修改在一个过程里出现好几十次,如:

update table1
set col1=...
where col2=...;
update table1
set col1=...
where col2=...
......
象这类脚本其实可以很简单就整合在一个UPDATE语句来完成(前些时候在协助xxx项目做性能问题分析时就发现存在这种情况)

5.在可以使用UNION ALL的语句里,使用了UNION

UNION 因为会将各查询子集的记录做比较,故比起UNION ALL ,通常速度都会慢上许多。一般来说,如果使用UNION ALL能满足要求的话,务必使用UNION ALL。还有一种情况大家可能会忽略掉,就是虽然要求几个子集的并集需要过滤掉重复记录,但由于脚本的特殊性,不可能存在重复记录,这时便应该使用UNION ALL,如xx模块的某个查询程序就曾经存在这种情况,见,由于语句的特殊性,在这个脚本中几个子集的记录绝对不可能重复,故可以改用UNION ALL)

6.在WHERE 语句中,尽量避免对索引字段进行计算操作

这个常识相信绝大部分开发人员都应该知道,但仍有不少人这么使用,我想其中一个最主要的原因可能是为了编写写简单而损害了性能,那就不可取了

9月份在对XX系统做性能分析时发现,有大量的后台程序存在类似用法,如:

......
where trunc(create_date)=trunc(:date1)
虽然已对create_date 字段建了索引,但由于加了TRUNC,使得索引无法用上。此处正确的写法应该是

where create_date>=trunc(:date1) and create_date
或者是

where create_date between trunc(:date1) and trunc(:date1)+1-1/(24*60*60)
注意:因between 的范围是个闭区间(greater than or equal to low value and less than or equal to high value.),

故严格意义上应该再减去一个趋于0的小数,这里暂且设置成减去1秒(1/(24*60*60)),如果不要求这么精确的话,可以略掉这步。

7.对Where 语句的法则

7.1 避免在WHERE子句中使用in,not  in,or 或者having。

可以使用 exist 和not exist代替 in和not in。

可以使用表链接代替 exist。Having可以用where代替,如果无法代替可以分两步处理。

例子

SELECT *  FROM ORDERS WHERE CUSTOMER_NAME NOT IN
(SELECT CUSTOMER_NAME FROM CUSTOMER)
优化

SELECT *  FROM ORDERS WHERE CUSTOMER_NAME not exist
(SELECT CUSTOMER_NAME FROM CUSTOMER)
7.2 不要以字符格式声明数字,要以数字格式声明字符值。(日期同样)否则会使索引无效,产生全表扫描。

例子使用:

SELECT emp.ename, emp.job FROM emp WHERE emp.empno = 7369;
不要使用:SELECT emp.ename, emp.job FROM emp WHERE emp.empno = ‘7369’
8.对Select语句的法则

在应用程序、包和过程中限制使用select * from table这种方式。看下面例子

使用SELECT empno,ename,category FROM emp WHERE empno = '7369‘
而不要使用SELECT * FROM emp WHERE empno = '7369'
9. 排序

避免使用耗费资源的操作,带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎 执行,耗费资源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序

10.临时表

慎重使用临时表可以极大的提高系统性能

已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



阿里巴巴首次披露六大重要信息

$
0
0

6月17日消息,阿里巴巴集团昨日向SEC提交了招股说明书的补充文件Form F-1/A。更新后的招股书首次披露了其27位合伙人,并发布了2014财年业绩。此外,招股书也显示了阿里的自我估值、天猫与淘宝成交额、需要偿还的外债等重要信息。招股书数据甚至暴露了UC的估值。

阿里巴巴更新招股书 首次披露六大重要信息

以下就是招股书更新后的重要信息:

2014财年总营收525.04亿元 净利润234.03亿元

阿里巴巴集团2014财年(截至2014年3月31日)的未经审计财务状况在招股书中获得披露。

文件显示,阿里巴巴集团2014财年的总营收为525.04亿元人民币(约合84.46亿美元),较上年增长52.1%;其中国内业务营收451.32亿元人民币(约合72.60亿美元),较上年增长54.7%,国际业务营收为48.51亿元人民币(约合7.8亿美元),较上年增长16.6%,云计算和网络基础设施营收7.73亿元人民币(约合1.24亿美元),较上年增长18.9%,其他业务营收为17.48亿元人民币(约合2.82亿美元),较上年增长223.7%。

阿里巴巴集团2014财年净利润为234.03亿元人民币(约合37.64亿美元),较上年增长170.6%。归于普通股东的基本和摊薄每股收益分别为10.61元人民币(约合1.71美元)和10.00元人民币(约合1.61美元),较上年增长189.9%和180.1%。

阿里集团自我估值1162亿美元

招股书显示,阿里巴巴集团给自我的估值为930亿美元到1162亿美元之间,低于外界估值的1600亿美元到2000亿美元。

阿里巴巴集团披露了自2011年6月30日以来阿里巴巴普通股估值的变化,在2011年时阿里集团股份每股为8美元一股,到2011年9月30日为10美元一股。

阿里巴巴集团股份在2011年12月31日到2013年3月31日一直维持在13.50美元到15.50美元一股,但自2013年6月以来,阿里巴巴集团每股估值出现上升。

首次分开披露天猫与淘宝成交额

根据最新招股书,天猫2013年成交额(GMV)达4410亿元人民币,占总交易额28.6%,较2012年公布的2000亿元成交额相比,天猫业务2013年同比增长120%。淘宝2013年成交额为1.1万亿元人民币,占总交易额71.4%,较2012年同比增长不足40%。

此外,截止2014年一季度,移动端贡献成交额已占总额27.4%,也就是达到1178亿元人民币。

首次披露了其27位合伙人

阿里巴巴集团在补充文件中披露了集团27名合伙人的名单,其中有7人是阿里巴巴集团的创始人,分别是马云、蔡崇信、吴咏铭、彭蕾、戴姗、金建航和蒋芳;

有8人是在2004年以前进入公司,由公司自己培养起来的合伙人,分别是陆兆禧、姜鹏、彭翼捷、童文红、王帅、吴敏芝、张建锋和张宇;

另外12人则在2004年之后进入公司,他们都是来自财务、法务、技术等各个领域的高级管理人才,分别是程立、樊路远、胡晓明、井贤栋、刘振飞、邵晓峰、Timothy A. STEINERT、王坚、武卫、俞思瑛、曾鸣、张勇。

在这27名合伙人之中,有将近五分之一是技术人员,女性成员占9人,21人为70后。

阿里巴巴集团还公布了公司董事会的构成情况。公司上市后的董事会将由9人组成,其中马云、蔡崇信、陆兆禧和张勇作为执行董事进入董事会,软银提名孙正义担任非执行董事,应邀作为独立非执行董事进入董事会的有杨致远、董建华、郭德明和迈克尔伊万斯(Michael Evans)。雅虎公司的代表杰奎琳李塞思(Jacqueline D. RESES)将在公司上市后退出董事会。

阿里巴巴的还款单:2017年需偿还266亿元

2013年以来疯狂并购的阿里巴巴集团并非是毫无外债的“款爷”,而是“负债累累”。招股书显示,从2015年到2019年阿里巴巴集团一共需要偿还外债325.86亿元。

其中,阿里巴巴集团2015年需要偿还11亿元、2016年需要偿还7000万元、2017年是还债的高峰——达到266.29亿元,2018年需偿还35.54亿元,2019年需偿还12.31亿元。

阿里巴巴招股书不小心暴露了UC估值

阿里巴巴集团已完成对UC的并购,不过,UC到底估值多少一直成谜。阿里巴巴集团今日更新的招股书,并披露阿里巴巴集团收购UC剩余股份的细节。

招股书显示,2014年6月,阿里巴巴集团收购UC剩余的34%股份代价是4.79亿美元+1230万股限制性股票,这部分限制性股票约占阿里巴巴总股本的0.5%。

招股书同时披露,到2014年6月16日,阿里巴巴集团股票已经达到40美元到50美元一股,这意味着UC所拥有的阿里巴巴集团这部分股票的价值为4.9亿美元到6.15亿美元之间。

以此推断,截止到2014年6月16日,UC剩余34%股份的价格在9.96亿美元到10.94亿美元之间,阿里巴巴集团对UC整体估值区间应在29.29亿美元到32.18亿美元之间。

 


© 推荐 for 互联网的那点事. | 猛击下载: iPhone客户端猛击下载: Android客户端

普林斯顿教授谈为何中国学生录取率仅4%:中国教育扭曲人性

$
0
0

美国普林斯顿大学分子生物学系教授康毅滨先生,负责该系在中国的招生工作,因此接触了形形色色的中国学生(其中绝大部分是清华、北大、复旦、中科大等国内知名学校的尖子生),真切地感受到了中国教育的扭曲给学生带来的困扰。

“中国学生聪明,勤奋,但也迷茫,功利心比较重,妨碍了他们的长远发展。”康毅滨在接受《星期日新闻晨报》访问时说。

什么样的学生被淘汰?每年一月底,康毅滨就要从系里抱回一大包资料仔细看——里面是所有申请普林斯顿生物分子系的中国学生的材料。

每年,该系每年大约招收25名本科学生攻读博士,系里给康毅滨的“中国额度”4个,而他收到的申请约有七八十份。 4%左右的“成功率”。每个“申请包”主要有这些材料:本科各科成绩单,托福和GRE的考分,个人陈述, 以及推荐信。康毅滨把它们分成“定量”和“非定量”两类。分数他看得很仔细,但那些“非定量”的东西却能告诉他更多。

问:“个人陈述”有什么用?

康毅滨:就是说说你为什么想成为一个分子生物学家,为什么想来普林斯顿。

问:你看过几百份“个人陈述”,从里面看到了些什么?

康毅滨:中国学生的GRE能考得很好,但我能看出来,他们写的“陈述”经常千篇一律,缺乏特点。

问:他们给你什么印象?

康毅滨:不清楚为什么要来普林斯顿,或者过分要求完美,不敢展示真实的自己。

问:真实鲜活的“陈述”是怎样的?”

康毅滨:有个学生是这么写的:他以前的专业是电子工程,后来才慢慢发现真正感兴趣的是生物。他申请转了系,尽管绝大多数人反对,因为没有基础,读得有些吃力,但他还是很高兴。因为每一学期都会比上一学期进步一些。他的“陈述”给我留下了比较深的印象,因为它展示了一个人在寻找和实现梦想过程中的困惑和欣喜。去年我们还录取了一名学生,她在“陈述”中坦率地指出了母校的问题:她很遗憾本科四年没有接受更为全面的教育。你可以看到她的渴望。第一轮筛选,从80份申请材料中挑出10-15名左右的“候选人”。

2月初,康毅滨开始电话面试。虽然并不直接和学生面对面,但大洋彼岸传来的声音,会告诉他对方是一个什么样的学生。

问:你会问些什么问题?

康毅滨:主要是看看英文口语能力、科研经历、随机应变的能力,以及学生的一些背景状况。

问:接到电话的学生,会很紧张吧?

康毅滨:电话面试大约一个小时,45分钟说英语,15分钟用中文。就算英语不是特别好,学生还是可以完整地表达自己的。但大部分中国学生会把它看作一个“考试”,而不是一个“对话”,所以有些会很紧张。

问:你听出了什么?

康毅滨:有些学生听得出来他(她)事先在纸上写好回答,照着念,或者是背出来。还有是“排练过度”,说得非常溜,像演讲一样,但并没有针对我的提问。

问:他们会给你留下什么印象?

康毅滨:那些答非所问的学生,我想可能没有自己做过真正独立的研究,或者对自己没对信心。我希望学生是展现一个真实的自我,而不是一个过度包装的、失去了真实性的“加工成品”。

问:或者是我们的教育没有告诉他们,说实话是最好的回答。

康毅滨:我们要挑选的,是真的热爱科学、而且诚实的人。去年,我几乎是在申请截止前的最后一刻才收到了一个学生的材料,条件很好,我就给他打电话。他老老实实,告诉 我,虽然他很早就进实验室,工作也很努力,但不知道为什么,实验总不是很顺利。但他可以很清楚地描述他在实验中遇到的问题,和为解决问题所作出的种种尝试。表面上看,他的科研并不成功,但我能感受他的认真、诚实、努力,这已经具备了一个科学家、一个人最重要的品质。

电话面试并不是最终的决定。之后,康毅滨要在候选人中反复地掂量、比较。在这个过程中,分数高低往往不是决定因素,而是从细微处看到的非智力因素。

问:分数不重要?

康毅滨:分数很重要,但不是一个绝对因素。申请普林斯顿的学生都是国内名牌大学的尖子,经过高度选择过的人,智力都没有问题。我会仔细看每一门的成绩,但并不是分数高就能入选,相反,我认为第一名和第七八名的实力并不相差太远。录取与否,智力以外的因素很重要。我们系录取过一个河南的学生,家在农村,初中就独自在县城 ,住校读书,吃过不少苦。在电话和电邮里,我感觉到她为人谦和,没有一些被宠惯的尖子生的趾高气昂。还有个学生,他会和老师“套瓷”,但不是恭维,套近乎,而是自己的确做过研究,对老师有真正的了解,提问很专业,很深入。这样的学生,不油嘴滑舌,让人感觉到懂得认真负责,尊重机遇。但有的学生过于自信,甚至有些傲慢,觉得自己不是去普林斯顿,就能去哈佛,一副唯我独尊的样子,很难给人留下好印象。

进入普林斯顿后,他们会遇到什么样的困扰?

来上海之前,康毅滨在福建老家待了十多天。每天陪父亲、侄子去爬山。读五年级的侄子告诉他,在他们学校,老师让每个学生都要在班上找一个“对手”。每次考试 下来,赢得多的同学受表扬,输得多的要被批评。在这样的氛围中,班上学习好的同学也不大愿意花时间帮助成绩不好的同学。让康毅滨震惊的是,目前中国基础教育到处可见这样的“激励”方式。普林斯顿是金字塔尖上的精英学校,但对最终被普林斯顿录取的中国学生来说,与其说已经攀登到了金字塔尖,不如说真正的竞争刚刚开始。

而这时候,中国教育从小学——不,从幼儿园——就开始灌输的狭隘的竞争意识,清楚地烙在这些留学生的身上,困扰着他们。

问:中国学生到了普林斯顿后,会遇到哪些问题?

康毅滨:有些学生进入普林斯顿后,心态急,享受不了科学研究的乐趣,而把实验看作“计件劳动”,急于求成,一旦不如意,就垂头丧气。有些学生进来后发现,自己辛辛苦苦读了那么多年书考上来,但这并不是自己喜欢的、擅长的,很迷茫。还有,他们往往在人际关系上会遇到问题,觉得不受欢迎,孤单。

问:为什么会这样?

康毅滨:普林斯顿相信学习是一个探索的过程,是一个认识自我,发现自我,找到自己想要什么、热爱什么的过程,但国内教育系统出来的学生,常常是另一种心态:最好一进 学校,就有人指定给他(她)一个课题,而且是一个保证可以做出来成果的课题。就像解一道数学题,一定会有答案,做出来了就能拿高分。然后呢,就想靠这个课题找到一份体面的工作。他们基础扎实,学习勤奋,上进心强,应试能力强,成绩优秀,但缺乏探索精神,独立思考和创新能力比较弱,功利心比较强。

问:其实这是成年人的典型心态。

康毅滨:我们系有过一个中国学生,来普林斯顿不久,我发现他并不真正喜欢研究。后来他告诉我,其实早就发现自己并不热爱科研,但从小学到大学,他都是第一名,所有的人都指望他考上美国一流大学。他是为了别人的期待考普林斯顿的。其实这个学生小时候对生物很有兴趣,只是后来成人世界把他的实验成功与否过早地和名利、和事业紧紧联系在一起。而在这一行业真正成功的人,往往并不以出人头地为目的(如果只是为了这些,还有其它很多更简捷的路可走),而是享受探索的过程,包括其中许许多多的失败和得来不易的成功。

问:你刚才说的中国学生在人际关系上的麻烦,也是功利心造成的?

康毅滨:你看,我小侄子从小就被这么“教育”,从这样的思维里出来的学生,对竞争的理解会很狭隘,认为把别人踩在脚下就是胜利,把别人压下去就是成功。实验室是一个团队工作,有人发表论文,本来是大家一起高兴的时候,但一些中国学生往往有些闷闷不乐,似乎别人的成功就意味着自己的失败。有时科研项目八字还没一瞥,就 想“分家”,好算作自己一个人的成果……有些中国学生特别想快速成名。这样的心态,学校和家庭教育要负很大的责任。在美国,成绩是一个人的隐私,不会公布出来,分数就不会造成那么大的压力。他们提倡团队的合作,互相帮助,共同提高。

问:两种意义上的竞争,就会产生矛盾。

康毅滨:这样的竞争意识过强,就会缺乏团队精神,以自我为中心,容易在工作和生活中造成和他人关系的紧张。比如,老生周末带新生开车购物,晚到了几分钟会被人埋怨,而新生却可以理所当然地在超市慢条斯理地货比三家,让老生在外面等几个钟头。还有学生问我:为什么去年邀请他去家里过节的那些美国人今年不再邀请他了呢……

有时候我会想,他们恐怕自己都没有意识到这是一个问题。从小到大,玩完的玩具,吃完饭的碗筷,换下来的脏衣服……

爸爸妈妈爷爷奶奶都帮他们处理好了,在这样的环境中长大,自然会觉得理所当然。而多数美国学生的确比较有“公共意识”。实验室的垃圾通常他们处理得比较多。生物系有许多做后勤工作的员工,包括老鼠房负责日常喂养的工人,运送实验用品的搬运工和收发员,打扫实验室的清洁工等。每年到了圣诞节,我实验室的美国学生会牵头一起凑份子,每人出个五块十块,买个小礼物送给那些工人,以表示对他们平时工作的感激。这种看似微不足道的事,往往反映出一个学生从小所受的教育,以及将来他在事业上能走多远。(

镜像链接: 谷歌镜像 | 亚马逊镜像

Shop Amazon Gift Cards - Perfect Gifts Anytime

相关日志

js记录页面的点击位置并在页面刷新后滚动到该位置

$
0
0
需求:
页面上的结果集列表有多条记录,选中某一行进行操作时,页面可能已经滚动过一段距离,当点击提交时希望能记录滚动过的长度,并在页面提交刷新后自动滚动到该位置。

实现思路:
使用jquery来操作dom,方便易用。
1、获取当前滚动过的距离;
2、在前后台之间传递;
3、在页面刷新后滚动到该位置。


代码:
1、获取当前滚动的距离:
var x = $(document).scrollLeft();
var y = $(document).scrollTop();

2、在前后台传递参数:
以struts2+velocity为例。
需要实现这样一个流程:
前台在点击某条记录修改时获取当前滚动的距离x、y,将x、y传给后台,后台处理完成后继续跳转页面(在本需求中即为当前页面),将x、y作为参数传给目的页面。
form表单的action为/test/book/modify.action,对应的action类为BookAction.java。该类增加两个属性并设置相应的get、set方法:
int scrollLeft=-1;
int scrollTop=-1;
public void setXxx(int value){
    xxx = value;
}
public int getXxx(){
    return xxx;
}

首先配置该action的相关配置:
<package name="book" namespace="/test/book" extends="default"><action name="*" class="com.test.book.BookAction" method="{1}"><result>/WEB-INF/vm/test/book/{1}.vm</result><result name="list4modify" type="chain"><param name="actionName">list</param></result>

以上配置可知,当提交的action为/test/book/modify.action时,会进入BookAction.modify()方法,该方法返回值为"list4modify"。
public class BookAction{
...
    public String modify(){
        ...
        return "list4modify";
    }
}

然后触发list.action,在页面list.vm中需要处理滚动的动作。
3、在页面刷新后滚动到该位置:
在list.vm中编写如下逻辑即可:
 $(document).ready(function() {
      var x = parseInt($!scrollX);
      var y = parseInt($!scrollY);
      if((x || y) && (x > 0 || y > 0)){
	    window.scroll(x,y);
      }
}


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



ActiveMQ的queue以及topic两种消息处理机制分析

$
0
0

        上一期介绍了我们项目要用到activeMQ来作为jms总线,并且给大家介绍了activeMQ的集群和高可用部署方案,本期给大家再介绍下,如何根据自己的项目需求,更好地使用activeMQ的两种消息处理模式。

       

1    queue与topic的技术特点对比

 

Topic

Queue

概要

Publish Subscribe messaging 发布订阅消息

Point-to-Point 点对点

有无状态

topic数据默认不落地,是无状态的。

Queue数据默认会在mq服务器上以文件形式保存,比如Active MQ一般保存在$AMQ_HOME\data\kr-store\data下面。也可以配置成DB存储。

完整性保障

并不保证publisher发布的每条数据,Subscriber都能接受到。

Queue保证每条数据都能被receiver接收。

消息是否会丢失

一般来说publisher发布消息到某一个topic时,只有正在监听该topic地址的sub能够接收到消息;如果没有sub在监听,该topic就丢失了。

Sender发送消息到目标Queue,receiver可以异步接收这个Queue上的消息。Queue上的消息如果暂时没有receiver来取,也不会丢失。

消息发布接收策略

一对多的消息发布接收策略,监听同一个topic地址的多个sub都能收到publisher发送的消息。Sub接收完通知mq服务器

一对一的消息发布接收策略,一个sender发送的消息,只能有一个receiver接收。receiver接收完后,通知mq服务器已接收,mq服务器对queue里的消息采取删除或其他操作。

          Topic和queue的最大区别在于topic是以广播的形式,通知所有在线监听的客户端有新的消息,没有监听的客户端将收不到消息;而queue则是以点对点的形式通知多个处于监听状态的客户端中的一个。

 

2    topic和queue方式的消息处理效率比较

        通过增加监听客户端的并发数来验证,topic的消息推送,是否会因为监听客户端的并发上升而出现明显的下降,测试环境的服务器为ci环境的ActiveMQ,客户端为我的本机。

        从实测的结果来看,topic方式发送的消息,发送和接收的效率,在一个订阅者和100个订阅者的前提下没有明显差异,但在500个订阅者(线程)并发的前提下,效率差异很明显(由于500线程并发的情况下,我本机的cpu占用率已高达70-90%,所以无法确认是我本机测试造成的性能瓶颈还是topic消息发送方式存在性能瓶颈,造成效率下降如此明显)。

        Topic方式发送的消息与queue方式发送的消息,发送和接收的效率,在一个订阅者和100个订阅者的前提下没有明显差异,但在500个订阅者并发的前提下,topic方式的效率明显低于queue。

        Queue方式发送的消息,在一个订阅者、100个订阅者和500个订阅者的前提下,发送和接收的效率没有明显变化。

Topic实测数据:

 

 

发送者发送的消息总数

所有订阅者接收到消息的总数

消息发送和接收平均耗时

单订阅者

100

100

101ms

100订阅者

100

10000

103ms

500订阅者

100

50000

14162ms

 

Queue实测数据:

 

 

发送者发送的消息总数

所有订阅者接收到消息的总数

消息发送和接收平均耗时

单订阅者

100

100

96ms

100订阅者

100

100

96ms

500订阅者

100

100

100ms

 

3     topic方式的消息处理示例
3.1     通过客户端代码调用来发送一个topic的消息:

import javax.jms.Connection;

import javax.jms.ConnectionFactory;

import javax.jms.DeliveryMode;

import javax.jms.Destination;

import javax.jms.MessageProducer;

import javax.jms.Session;

import javax.jms.TextMessage;

 

import org.apache.activemq.ActiveMQConnection;

import org.apache.activemq.ActiveMQConnectionFactory;

 

publicclass SendTopic {

    privatestaticfinalintSEND_NUMBER = 5;

    publicstaticvoid sendMessage(Session session, MessageProducer producer)

            throws Exception {

         for ( int i = 1; i <= SEND_NUMBER; i++) {

            TextMessage message = session

                    .createTextMessage("ActiveMq发送的消息" + i);

            //发送消息到目的地方

            System. out.println("发送消息:" + "ActiveMq 发送的消息" + i);

            producer.send(message);

        }

    }

   

    publicstaticvoid main(String[] args) {

        // ConnectionFactory:连接工厂,JMS用它创建连接

        ConnectionFactory connectionFactory;

        // Connection:JMS客户端到JMS Provider的连接

        Connection connection = null;

        // Session:一个发送或接收消息的线程

        Session session;

        // Destination:消息的目的地;消息发送给谁.

        Destination destination;

        // MessageProducer:消息发送者

        MessageProducer producer;

        // TextMessage message;

        //构造ConnectionFactory实例对象,此处采用ActiveMq的实现jar

        connectionFactory = new ActiveMQConnectionFactory(

                ActiveMQConnection. DEFAULT_USER,

                ActiveMQConnection. DEFAULT_PASSWORD,

                "tcp://10.20.8.198:61616");

        try {

            //构造从工厂得到连接对象

            connection = connectionFactory.createConnection();

            //启动

            connection.start();

            //获取操作连接

            session = connection.createSession( true, Session. AUTO_ACKNOWLEDGE);

            //获取session注意参数值FirstTopic是一个服务器的topic(与queue消息的发送相比,这里是唯一的不同)

            destination = session.createTopic("FirstTopic");

            //得到消息生成者【发送者】

            producer = session.createProducer(destination);

            //设置不持久化,此处学习,实际根据项目决定

            producer.setDeliveryMode(DeliveryMode. PERSISTENT);

            //构造消息,此处写死,项目就是参数,或者方法获取

            sendMessage(session, producer);

            session.commit();

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            try {

                if ( null != connection)

                    connection.close();

            } catch (Throwable ignore) {

            }

        }

    }

}

 

3.2     启动多个客户端监听来接收topic的消息:

publicclass ReceiveTopic implements Runnable {

      private StringthreadName;

 

      ReceiveTopic(String threadName) {

           this.threadName = threadName;

      }

 

      publicvoid run() {

           // ConnectionFactory:连接工厂,JMS用它创建连接

           ConnectionFactory connectionFactory;

           // Connection:JMS客户端到JMS Provider的连接

           Connection connection = null;

           // Session:一个发送或接收消息的线程

           Session session;

           // Destination:消息的目的地;消息发送给谁.

           Destination destination;

           //消费者,消息接收者

           MessageConsumer consumer;

           connectionFactory = new ActiveMQConnectionFactory(

                      ActiveMQConnection. DEFAULT_USER,

                      ActiveMQConnection. DEFAULT_PASSWORD,"tcp://10.20.8.198:61616");

           try {

                 //构造从工厂得到连接对象

                 connection = connectionFactory.createConnection();

                 //启动

                 connection.start();

                 //获取操作连接,默认自动向服务器发送接收成功的响应

                 session = connection.createSession( false, Session. AUTO_ACKNOWLEDGE);

                 //获取session注意参数值FirstTopic是一个服务器的topic

                 destination = session.createTopic("FirstTopic");

                 consumer = session.createConsumer(destination);

                 while ( true) {

                      //设置接收者接收消息的时间,为了便于测试,这里设定为100s

                      TextMessage message = (TextMessage) consumer

                                  .receive(100 * 1000);

                      if ( null != message) {

                            System. out.println("线程"+threadName+"收到消息:" + message.getText());

                      } else {

                            continue;

                      }

                 }

           } catch (Exception e) {

                 e.printStackTrace();

           } finally {

                 try {

                      if ( null != connection)

                            connection.close();

                 } catch (Throwable ignore) {

                 }

           }

      }

 

      publicstaticvoid main(String[] args) {

            //这里启动3个线程来监听FirstTopic的消息,与queue的方式不一样三个线程都能收到同样的消息

           ReceiveTopic receive1= new ReceiveTopic("thread1");

           ReceiveTopic receive2= new ReceiveTopic("thread2");

           ReceiveTopic receive3= new ReceiveTopic("thread3");

           Thread thread1= new Thread(receive1);

           Thread thread2= new Thread(receive2);

           Thread thread3= new Thread(receive3);

           thread1.start();

           thread2.start();

           thread3.start();

      }

}

 

4     queue方式的消息处理示例

         参考上一期文章:开源jms服务ActiveMQ的负载均衡+高可用部署方案探索。

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



什么是POP3、SMTP和IMAP

$
0
0

转自:http://help.163.com/09/1223/14/5R7P6CJ600753VB8.html

POP3

POP3是Post Office Protocol 3的简称,即邮局协议的第3个版本,它规定怎样将个人计算机连接到Internet的邮件服务器和下载电子邮件的电子协议。它是因特网电子邮件的第一个离 线协议标准,POP3允许用户从服务器上把邮件存储到本地主机(即自己的计算机)上,同时删除保存在邮件服务器上的邮件,而POP3服务器则是遵循 POP3协议的接收邮件服务器,用来接收电子邮件的。( 与IMAP有什么区别?)

SMTP

SMTP的全称是“Simple Mail Transfer Protocol”,即简单邮件传输协议。它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。SMTP 服务器就是遵循 SMTP 协议的发送邮件服务器。
  SMTP 认证,简单地说就是要求必须在提供了账户名和密码之后才可以登录 SMTP 服务器,这就使得那些垃圾邮件的散播者无可乘之机。
  增加 SMTP 认证的目的是为了使用户避免受到垃圾邮件的侵扰。

IMAP

IMAP全称是Internet Mail Access Protocol,即交互式邮件存取协议,它是跟POP3类似邮件访问标准协议之一。不同的是,开启了IMAP后,您在电子邮件客户端收取的邮件仍然保留 在服务器上,同时在客户端上的操作都会反馈到服务器上,如:删除邮件,标记已读等,服务器上的邮件也会做相应的动作。所以无论从浏览器登录邮箱或者客户端 软件登录邮箱,看到的邮件以及状态都是一致的。( 与POP3有什么区别?

网易163免费邮箱相关服务器信息:

163免费邮客户端设置的POP3、SMTP、IMAP地址
  

网易邮箱已经默认开启 POP3/SMTP/IMAP 服务,方便您可以通过电脑客户端软件更好地收发邮件,如果关闭可以通过以下方式开启:

请登录 163邮箱,点击页面正上方的“ 设置”,再点击左侧上“ POP3/SMTP/IMAP”,其中“开启SMTP服务”是系统默认勾选开启的。您可勾选图中另两个选项,点击确定,即可开启成功。不勾选图中两个选项,点击确定,可关闭成功。  

什么是POP3、SMTP和IMAP?



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Hadoop之MapReduce程序应用一

$
0
0

摘要:MapReduce程序处理专利数据集。

关键词:MapReduce程序   专利数据集

数据源:专利引用数据集cite75_99.txt。(该数据集可以从网址 http://www.nber.org/patents/下载)

问题描述:

读取专利引用数据集并对它进行倒排。对于每一个专利,找到那些引用它的专利并进行合并。top5输出结果如下:

1                                3964859, 4647229

10000                            4539112

100000                           5031388

1000006                          4714284

1000007                          4766693

解决方案:

1  开发工具:  VM10+Ubuntu12.04+hadoop1.1.2+eclipse

2  在eclipse中创建一个工程,并且在工程里添加一个java类。

程序清单如下:

package com.wangluqing;

import java.io.IOException; 
import java.util.Iterator; 
import org.apache.hadoop.conf.Configuration; 
import org.apache.hadoop.conf.Configured; 
import org.apache.hadoop.fs.Path; 
import org.apache.hadoop.io.Text; 
import org.apache.hadoop.mapred.FileInputFormat; 
import org.apache.hadoop.mapred.FileOutputFormat; 
import org.apache.hadoop.mapred.JobClient; 
import org.apache.hadoop.mapred.JobConf; 
import org.apache.hadoop.mapred.KeyValueTextInputFormat; 
import org.apache.hadoop.mapred.MapReduceBase; 
import org.apache.hadoop.mapred.Mapper; 
import org.apache.hadoop.mapred.OutputCollector; 
import org.apache.hadoop.mapred.Reducer; 
import org.apache.hadoop.mapred.Reporter; 
import org.apache.hadoop.mapred.TextOutputFormat; 
import org.apache.hadoop.util.Tool; 
import org.apache.hadoop.util.ToolRunner;

public class MyJob1 extends Configured implements Tool {
public static class MapClass extends MapReduceBase implements Mapper<Text,Text,Text,Text> {

@Override
public void map(Text key, Text value, OutputCollector<Text, Text> output,
Reporter reporter) throws IOException {
// TODO Auto-generated method stub

output.collect(value, key);
}

}

public static class Reduce extends MapReduceBase implements Reducer<Text,Text,Text,Text> {

@Override
public void reduce(Text key, Iterator<Text> values,
OutputCollector<Text, Text> output, Reporter reporter)
throws IOException {
// TODO Auto-generated method stub
String csv = "";
while(values.hasNext()) {
if(csv.length()>0) 
csv += ",";
csv += values.next().toString();
}

output.collect(key, new Text(csv));

}
}

public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String[] arg={"hdfs://hadoop:9000/user/root/input/cite75_99.txt","hdfs://hadoop:9000/user/root/output"};
int res = ToolRunner.run(new Configuration(),new MyJob1(), arg);
System.exit(res);
}

public int run(String[] args) throws Exception {
// TODO Auto-generated method stub
Configuration conf = getConf(); 
JobConf job = new JobConf(conf, MyJob1.class); 
Path in = new Path(args[0]); 
Path out = new Path(args[1]); 
FileInputFormat.setInputPaths(job, in); 
FileOutputFormat.setOutputPath(job, out);

job.setJobName("MyJob"); 
job.setMapperClass(MapClass.class); 
job.setReducerClass(Reduce.class); 
job.setInputFormat(KeyValueTextInputFormat.class); 
job.setOutputFormat(TextOutputFormat.class); 
job.setOutputKeyClass(Text.class); 
job.setOutputValueClass(Text.class); 
job.set("key.value.separator.in.input.line", ","); 
JobClient.runJob(job); 
return 0;

}

}

运行Run on hadoop,在Ubuntu 下执行命令

hadoop fs -cat  /usr/root/output/part-00000  |  head

可以查看到经过MapReduce程序处理后的结果。

总结:

第一:可以采用装有Hadoop版本对应插件的Eclipse集成开发工具进行MapReduce程序开发。

第二:根据数据流和问题域设计和编写MapReduce程序。

Resource:

http://www.wangluqing.com/2014/03/hadoop-mapreduce-programapp1/ 

2 参考《Hadoop实战》第四章 MapReduce基础程序

 

作者:wangloveall 发表于2014-6-18 6:20:36 原文链接
阅读:5 评论:0 查看评论

像我这样一个内向的亚裔程序员,如何在西方交到朋友?

$
0
0

最近在听高晓松的《晓说》时,听到他议论西方人和中国人的区别:西方人重公德不重私德,重国家而不重个人。中国人正好相反。他讲了很多例子,听起来都是很有道理,并引导我得出自己的判断:中国办不好开源社区也是这个原因。

今天在网上看到的一个事件也印证了这一点:一个亚裔程序员实习生在网上写了一篇求助信,得到了网友的大量热情洋溢的回复和支持,很多人写了大量的篇幅来开导这个程序员小伙。

就我的见识,中国人好像只对绯闻事件或中日开战有这么大的热情。像这样充满人文关怀的讨论并不多见。

这个程序员小伙的忧愁是:像我这样的一个内向的亚裔程序员,如何在西方交到朋友?

很明显,这个小伙很孤独,但他提出问题并不孤独。不说他在美国实习时感到孤独,很多想我这样在国内的程序员同样也会感到孤独,没有朋友。

如果你喜欢看英文,可以直接看 这里。下面是我草译的他的这篇文章。

像我这样的一个内向的亚裔程序员,如何交到朋友?

我知道这个问题听起来非常的不好回答,但我心里就是想的。

我是学软件开发的,现在在加利福尼亚的一个中等规模的公司里实习,我可以在那里实习到8月份。

我喜欢我的工作,我自认为也是一个聪明、感觉、和气的小伙。

然而,让我忧郁的是,我发现周末时我只能自己一个人独自发呆,或一个人做自己的事情。

我的实习生活2月份就开始了,每到周末,我都是独自吃饭、看电影、购物、散步,所有的事情。我连室友都没有。

大多时候这也没什么,但一旦遇到不顺、缺乏自信的时候,这极为痛苦。我甚至认为这样下去会很危险。

每当我看到街上一群人相伴而行,妒忌的心情就好从心底升起。我很好奇他们是如何相互结识的,他们是如何做到如此亲密的。

我26岁,我也希望认识一些不错的女孩。

我的主要问题——我自认为的——是:

1. 3年半的大学生活,我没有任何的社交,我内向的非常严重。我跟陌生人在一起时感到特别不舒服。

2.我以为我的英语还可以,但后来发现这只是跟那些其它亚裔学生比较时才是这样,现在我越来越认识到我的英语还不行,成为了跟其他说英语的人成为朋友的障碍。在公司里经常会遇到各种人,我试图表达清楚我的意思,但往往方式非常笨拙,甚至糟糕,当很多人在一起说话时,我很难参与到他们的交谈中。

3. 我没有车,虽然对于在旧金山湾区的很多人来说这不是问题,但对于我,它成为我社交的巨大障碍。试想一下,那个女孩愿意和一个很笨的、而且没有车的家伙约会!

我该怎么做?我经常想象我应该在咖啡店里或排队时和身边的人交谈….但我知道我无法像一个普通的美国人那样交谈,我退缩了。

我开始恐惧周末的到来。对我来说,它就是两天绝对孤独的日子,毫无建设性的、永远浪费掉的两天,让自己越来越痛恨自己的两天。

【完】

Hack News上网友的回复超过了400条,而且很多都是大段中肯的建议。不知道国内有没有这样让人暖心的社区,请告诉我,谢谢。

请阅读全文: 像我这样一个内向的亚裔程序员,如何在西方交到朋友?

本文由 外刊IT评论网( www.vaikan.com)原创发表
文章地址: 像我这样一个内向的亚裔程序员,如何在西方交到朋友?

你也许会喜欢这些文章:

  1. 我不是个内向的程序员,我只是很忙
  2. 软件开发中没有所谓正确的方法
  3. 你不是软件工程师!
  4. 相信我!
  5. 干掉你程序中的僵尸代码




jQuery库(noConflict)冲突解决机制

$
0
0

      许多的JS框架类库都选择使用$符号作为函数或变量名,而且在实际的项目开发中,使用模板语言的话有可能"$"符号即为该模板语言的关键字。例如Veclocity模板语言,$是关键字.与jQuery一起使用可能会存在冲突(页面中直接写jq代码,引入的js文件不存在该问题)。吐槽下为啥这么多js库喜欢用$($ is money?)。

      jQuery是使用$符号作为函数或变量名最为典型的一个。在jQuery中,$符号只是window.jQuery对象的一个引用,因此即使$被删除,jQuery依然能保证整个类库的完整性。

      jQuery的设计充分考虑了多框架之间的引用冲突。我们可以使用jQuery.noConflict方法来轻松实现控制权的转交。

      在论述如何解决jQuery冲突之前,我们有必要先对noConflict函数做一个了解,解决冲突的方法就藏在里面。

jQuery.noConflict

jQuery.noConflict([removeAll]);

       缺省参数情况下:

       运行这个函数将变量$的控制权让渡给第一个实现它的库。在运行完这个函数之后,就只能使用jQuery变量访问jQuery对象(函数不带参数),例如jQuery("div p")。不过需要注意的是该函数必须在你导入jQuery文件之后,并且在导入另一个导致冲突的库"之前"使用。当然也应该在其他冲突库被使用之前,除非jQuery是最后一个导入的。

       当参数为true时,执行noConflict则会将$和jQuery对象本省的控制权全部移交给第一个产生他们的库。

       不过具体的移交机制是如何实现的呢?查阅源码即可发现,在jQuery源码中定义了两个私有变量_jQuery,_$。具体如下截图:

QQ截图20140618134225

      容易理解的是,jQuery通过上面两个私有的变量映射了window环境下的jQuery和$两个对象,防止了变量被强行覆盖。一旦noConflict被调用,jquery可以通过_jQuery,_$,jQuery,$四者之间的差异,来决定控制权的移交方式,具体代码如下图:

QQ截图20140618134652

        接下来看看参数设定问题,如果deep没有设置,_$覆盖了window.$,此时jQuery的别名$失效了,但是jQuery变量未失效,仍可使用。此时如果有其他库或代码重新定义了$变量的话,其控制权就转交出去了。反之deep设置为true时,_jQuery进一步覆盖window.jQuery,此时$和jQuery都将失效。

        这种操作的好处是,不管是框架混用还是jQuery多版本共存这种高度冲突的执行环境,由于noConflict的控制权移交机制,以及本身返回违背覆盖的私有变量jQuery对象,完全能够通过变量映射的方式解决冲突。

示例

       了解了jQuery内部解决冲突的实现方式,接下来看看一些实际的情况吧。

       1、将$引用的对象映射回原始的对象。

jQuery.noConflict();
// 使用 jQuery
jQuery("div p").hide();
// 使用其他库的 $()
$("content").style.display = 'none';

       2、恢复使用别名$,然后创建并执行一个函数,在这个函数的作用域中仍然将$作为jQuery的别名来使用。在这个函数中,原来的$对象是无效的。这个函数对于大多数不依赖于其他库的插件都十分有效。

jQuery.noConflict();
(function($) { 
  $(function() {
    // 使用 $ 作为 jQuery 别名的代码
  });
})(jQuery);
// 其他用 $ 作为别名的库的代码

       3、创建一个新的别名用来在接下来的库中使用jQuery对象。

var j = jQuery.noConflict();
// 基于 jQuery 的代码
j("div p").hide();
// 基于其他库的 $() 代码
$("content").style.display = 'none';

         基于这种方式,所有的jQuery代码都通过j进行调用,避免了冲突的可能。

       4、完全将jQuery移到一个新的命名空间。

var dom = {};
dom.query = jQuery.noConflict(true);结果:
// 新 jQuery 的代码
dom.query("div p").hide();
// 另一个库 $() 的代码
$("content").style.display = 'none';
// 另一个版本 jQuery 的代码
jQuery("div > p").hide();

解决冲突方式

      冲突的方式无非3中情况:

 1、其他库先于jQuery引用($被占用).

     最简单的我们可以在任何地方调用jQuery.noConflict函数,之后使用jQuery()座位jQuery对象的制造工厂。

 jQuery.noConflict();       //将变量$的控制权移交给先导入的库
	 jQuery(function(){
		 jQuery("p").click(function(){   //使用jQuery变量 
			 
		 });
 });
 $("pp").style.display='none';   //其他库的调用

     此外,如果你想确保jQuery不会与其他库冲突,但又想使用一个类似"$"的快捷方式,可以使用如下代码:

var $mj=jQuery.noConflict();       //自定义快捷变量
	 $mj(function(){
		 $mj("p").click(function(){   //使用jQuery变量 
			 
		 });
 });
 $("pp").style.display='none';   //其他库的调用

       如果你不想给jQuery自定义名称,却想使用$,有不与其他库冲突.可以有两种解决方式:

       其一:

jQuery.noConflict();       
 jQuery(function($){
   $("p").click(function(){   //在函数内部继续使用$
			 
   });
 });
 $("pp").style.display='none';   //其他库的调用

       其二:

jQuery.noConflict();       //将变量$控制权让渡
(function($){               //定义匿名函数形参为$
	$(function(){      //匿名函数内部均为jQuery的$
           $("p").click(function(){
           });
        });
})(jQuery);                //执行匿名函数且传递实参jQuery
 $("pp").style.display='none';   //其他库的调用

         这是较为理想的方式,因为可以改变最少的代码来实现全面的兼容性

 2、其他库后于jQuery被引用

         你可以参考上述做一些冲突解决的方法,其实其根本就不冲突,你可以使用jQuery变量做一些jQuery的处理工作。同时可以使用$()方法作为其他库的快捷方式。

 3、不同版本jQuery、且有其他库

         可以参考上述示例,将jQuery完全移到另一个命名空间。

var dom = {};
dom.query = jQuery.noConflict(true);
// 新 jQuery 的代码
dom.query("div p").hide();
// 另一个库 $() 的代码
$("content").style.display = 'none';
// 另一个版本 jQuery 的代码
jQuery("div > p").hide();

 最后

         jQuery解决冲突的机制是十分灵活的,有了这些冲突解决方案,就可以在项目中安心的使用jQuery了。



作者:kiritor 发表于2014-6-18 16:01:11 原文链接
阅读:29 评论:0 查看评论

JVM内存溢出深度分析分析

$
0
0

今天,发现游戏逻辑服务器内存溢出问题,每隔一定时间就生成java_pidxxxxxx.hprof ,基本1G内存分配不够用了,导致FGC频繁发生。

工具:

MAT  Eclipse Memory Analyzer Tool(MAT)分析内存泄漏

MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速、功能丰富的JAVA heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。

2.为什么使用MAT?
    当服务器应用占用了过多内存的时候,会遇到OutOfMemoryError。如何快速定位问题呢?Eclipse MAT的出现使这个问题变得非常简单。它能够离线分析dump的文件数据。
首页: http://www.eclipse.org/mat/ 
插件更新地址:
http://download.eclipse.org/mat/1.0/update-site/


排查原因:

1.是因为策划引入了一张大地图(2W*3W,),导致瞬间内存膨胀了160M,

2.热加载的时候地图系统内存占用会额外扩大n倍。

左:热加载前,右,热加载后


3.冗余属性数据存储

处理:

1.冗余属性数据存储:

把PathInfoConfig的_2xNodes和_4xNodes去掉,基本省下了一半的地图内存占用,

(左边,优化前,右边,优化后 看a部分)


2.停掉地图数据部分的热加载:

左边:热加载前,右边,热加载后,基本内存占用维持不变.


3.对PathNode的key 类型换成 Integer,通过移位来生成key 
x << 8|y 

优化前后做个对比 

获取的堆栈分析文件

1.jmap -dump:format=b,file=/data/log/dump.dat 26822

2.通过JVM自动生成hprof 文件


参考文章: Shallow heap & Retained heap


作者:jiangguilong2000 发表于2014-6-18 14:38:45 原文链接
阅读:12 评论:0 查看评论

OpenWRT 路由系统上抓包

$
0
0


前言:


做路由器开发,难免会遇到抓包的情况,但是抓包需要有Hub或者无线抓包网卡,调试无线问题,且目前手中没有无线抓包网卡,怎么办?那不能分析无线连接建立的过程,我们就来分析下,其传输的数据,肯定是从ra0无线接口来进行了。


准备:


编译tcpdump.ipk, libpcap.ipk并且安装到OpenWRT系统中。

可选传输方式scp, wget等。

wget http://<web server>/target_file

opkg install <package_name>.ipk


抓包:


tcpdump tcp -i ra0 -s 0 -c 10000 and dst port ! 22 -w ./target2.cap

1)tcp: ip icmp arp rarp 和 tcp、udp、icmp这些选项等都要放到第一个参数的位置,用来过滤数据报的类型
(2)-i eth1 : 只抓经过接口eth1的包
(3)-t : 不显示时间戳
(4)-s 0 : 抓取数据包时默认抓取长度为68字节。加上-S 0 后可以抓到完整的数据包
(5)-c 100 : 只抓取100个数据包
(6)dst port ! 22 : 不抓取目标端口是22的数据包
(7)src net 192.168.1.0/24 : 数据包的源网络地址为192.168.1.0/24
(8)-w ./target.cap : 保存成cap文件,方便用ethereal(即wireshark)分析



分析数据:


通过WINSCP/scp/samba等方式把文件取出, 用wireshark分析。


参考:


http://www.cnblogs.com/ggjucheng/archive/2012/01/14/2322659.html


作者:qianguozheng 发表于2014-6-18 13:05:32 原文链接
阅读:107 评论:0 查看评论

使用Quartz和Obsidian来调度任务

$
0
0

在介绍使用到的Quartz和Obsidian的API之前,首先我得声明一下,一般来说使用API并不是调度任务的最佳方式。Quartz提供了一个通过XML来配置作业的机制,而Obsidian则为你提供了一套完整的管理和监控的WEB应用。

然而,有一些使用场景还是强烈推荐使用API的,我们来看一下吧。

Quartz

我们先来看下在Quartz里每半小时调度一次任务是怎样实现的:

// First, create the scheduler
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();

// Build up the job detail
JobDetail job = JobBuilder.newJob(HelloWorld.class)
    .withIdentity("HelloWorldJob", "HelloWorldGroup")
    .build();

// Add some configuration
job.getJobDataMap().put("planet", "Earth");

// Create the scheduler
CronScheduleBuilder schedule = CronScheduleBuilder.cronSchedule("* 0/30 * * * ?");

// Create the trigger
CronTrigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("HelloWorldTrigger", "HelloWorldGroup")
    .withSchedule(schedule)
    .build();

// Schedule the job with the created trigger.
scheduler.scheduleJob(job, trigger);

scheduler.start(); // This is how you start the scheduler itself if you need to

// Later, we can shut down the scheduler 
scheduler.shutdown();

可以看到,首先你得获取到一个调度器实例的句柄,然后创建JobDetail,CronScheduleBuilder,以及CronTrigger,最后你才能去调度任务。

这里有几步看起来有点多余,还有那些无关的属性比如说作业标识符,触发器名字,触发器组,等等,但这是你使用的一个基本的模板所必须的。

Obsidian

我们来看下Obsidian 是怎么做的。我们使用的是同一个Job类(假设它同时满足Quartz和Obsidian的要求),用的也是同样的半小时一次的策略。

// Create our configuration parameters
List<ConfigurationParameter> parameters = Arrays.asList(
         new ConfigurationParameter().withName("planet")
                                     .withType(ParameterType.STRING)
                                     .withValue("Earth")
);

// Set up the job configuration
JobCreationRequest request = new JobCreationRequest()
.withNickname("HelloWorld")
.withJobClass(HelloWorld.class.getName())
        .withState(JobStatus.ENABLED)
.withSchedule("* 0/30 * * *")
        .withRecoveryType(JobRecoveryType.LAST) // how to recover failed jobs
.withParameters(parameters);

// Now actually save this configurations, which becomes active on all schedulers.
JobDetail addedJob = new JobManager().addJob(request, "Audit User");
System.out.println("Added new job: " + addedJob );

// If you need to start an embedded scheduler, use this line to initialize it.
SchedulerStarter scheduler = SchedulerStarter.get(SchedulerMode.EMBEDDED);

// Later, we can gracefully shut down the scheduler 
scheduler.shutDown();

可以看到的是,Obsidian非常简单,它废弃了那些对你开发和管理任务没有帮助的无关属性。你只需要创建一个JobCreationRequest,带上需要的几个字段,包括ConfigurationParameters什么 的,然后调用 JobManager.addJob(),把作业属性和可选的一个审核用户传进去就好了,这个用户是用来跟踪任务的修改的。

调用这个接口会把你的配置存储到Obsidian的数据库中,因此它会广播到你集群中的所有调度器中。很多用户都体会到了它的便利性,他们只需执行一次作业调度的初始化配置,就可以通过这个功能强大的WEB应用来对它们进行修改了。

这个API是经过仔细设计的,它提供的特性都是用户所需的,不仅功能强大同时还易于使用。这个示例是一个使用Obsidian的简单的模板,如果你想深入了解一下或者需要用到别的一些扩展的特性的话,可以参考下我们 完整的API文档

原创文章转载请注明出处: 使用Quartz和Obsidian来调度任务英文原文链接

使用Spring跟踪应用异常(1)

$
0
0

几周以前,一个同事请我帮他一周的忙。他想要享受一个难得的假期,但是找不到其他人帮他。由于我刚完成一个特别复杂的编码工作,需要来点自我调节,所以就答应了他的请求。换换环境对我也是非常有益的。

那份工作的一部分是监控一些非常关键的后台运行进程,观察它们是否运行正常。

开发者花了很多时间和精力在应用中加入日志输出,以此表明应用正常运行。当异常发生时,可以知道究竟出现了什么错误。这些日志文件通常可以说明我们的应用每天运行究竟是正常的还是异常的。

在这里,我将忽略其它技术。比如为应用增加类似HTTP或JMX监视器。这些技术提供的是应用程序即时信息,而不是本文讨论的二级监视结果。

监控日志文件通常有三种方式:

  1. 从不检查;
  2. 被动检查;
  3. 主动检查。

从不检查意味着那些应用一直运行从不停止,而且也不需要检查。这一点我敢打赌。

被动检查是很常见的。举个例子,Smith太太打电话投诉当她想要买一双新鞋时,网站就瘫痪了。她已经支付了两次,但是从没收到过鞋子。在这个传统的公司中,开发者和运维工程师是被完全隔离的。必须从运维工程师获得该问题发生时的日志,开发者才能开始分析。但是从运维那里返回的往往是一些完全不相关的日志,接下来开发者不得不再次向他们索要日志,甚至如此反复多次。这个过程耗费了几周时间,Smith太太变得怒不可遏。最后,日志终于到了开发者手中,问题解决。

可能很多公司都像上面“被动检查”的场景中描述的那样,开发者不被信任或允许操作在线服务器。这种情况司空见惯,我们中的绝大部分人都遇到过这种情况。我们应当信任开发者去操作在线系统。然而,作为一个开发者,在操作在线系统之前,必须记住两条黄金定律:

  1. 不要停止任何东西;
  2. 如果停止某些东西,请确保有人会在身边提醒你。

“主动检查”意味着检查日志文件要成为一个定期任务:每天、每小时或其它间隔定期执行。即使你的应用包含了大量的JMX、http或其他监视器,也不能确保一定会能发现每个问题。监视器只能发现你设定问题,除此之外的其他任何问题都不会报告。

再回到要我帮忙的问题,可能是历史的或其他原因,通过手动检查日志文件来监控系统运行,通常要对一些文件进行剪切或粘贴操作。这些重复的剪切和粘贴操作,每周大概会占用一个人半天的工作时间。

我非常不喜欢这些工作。我不善于这些需要手动且易出错的重复无聊的工作。每周要消耗半天人力,所以将这项任务自动化显然更符合成本效益。只要时间不是花在追求完美的解决方案上。那么,有什么选择呢?

如果你看了日志的规模,你一定会喜欢上Splunk,它能够监控多个消息源(譬如系统的日志守护进程)的消息。

这就意味着向Splunk发送错误只是简单地建立一个Log4j的系统日志附加器,不过那不是这篇博文的范畴…

最后,你需要写一些shell脚本来完成一些类似于“grep”命令的功能,将结果写入文件,并将这些文件通过邮件发送给你。

开发一个基于Spring的app有多难?它需要包含一些尽可能通用的、可重用的类,能够定期检查错误日志文件并将结果通过邮件发送给你。当然,结果还是通过邮件发送比较好,因为你总是会习惯性地查看你的邮箱。

开始

在其他类似的工程当中,都面临着如何迈出第一步的问题。要想让这个工程发布,需要写哪些类。有很多方法来确定你需要写哪些类,譬如简单地凭直觉,或者利用类似UML的设计工具,或者用快速原型或 测试驱动开发。在每种情况下,你真正要做的就是让类名体现的功能满足一些要求。例如,在这里我需要:

  1. 搜索一个给定的路径和子路径,找出某种特定类型的文件。
  2. 检查找到文件的时间,判断是否有必要在这个文件中搜索错误。
  3. 时间满足要求,则在其中查找异常。
  4. 找到异常,则判断是否是我们关注的,还是需要忽略它。
  5. 如果异常是我们关注的,则将其加到报告中。
  6. 检查完所有文件后,形成待发布报告。
  7. 通过邮件或其他方式发布报告。
  8. 以上所有操作每天定时执行。

这些操作就形成了几个类名:FileLocator、FileValidator、RegexValidator、FileAgeValidator和Report。

上述几个类名中包含了多次的“Validator”,意味着我们可以用一个接口,叫做“Validator”。利用接口的几个实现来完成上面的验证工作。这些实现可以结合起来组成一个应用。

这只是一个初步的想法。如果你看了代码,会发现没有一个类名是Report,只有一个Results类和 重构的Formatter接口、TextFormatter和HtmlFormatter类、Publisher接口和EmailPublisher类。

为了让任务定期执行,有多种选择。首先,需要将java代码和调用它的脚本放在一起,然后放在Unix机器上执行。但是这就意味着该应用不能在Windows上运行,并且只能作为一个独立的应用。这是个大问题,所以我们可以利用Spring和Quartz调度,使得建立一个调度任务非常简单。

而且Spring提供了一个非常好的Java邮件类和Email模板,这对我们的报告邮件非常有用。

这些只是开始,关于类设计一些模糊的想法通过一种松耦合的方式连接在一起组成了我们的应用。如果在正式的工作中,你可能需要花时间把这些整理成文档,甚至需要画出类图,加在一个Doc文档中,然后多次复查直到都不在改变。但是,我不需要去管这些…

配置应用

和其他应用一样,我们需要指出系统建立必须的属性值以及它们是如何被使用的。这个应用由app.properties文件配置,路径为src/main/resources。

# The path to the log file directory to scan for errors 
scan.in=/Library/Tomcat/logs
# A regex defining what to look for - or what not to include
scan.for=^.*Exception.*
exclude=^.*IllegalStateException.*
# The number of following lines to add to the report
following.lines=10
# Where to email the report
email.to=info@captaindebug.com
# The max age of a file in days
max.days=1000

第一个我们感兴趣的属性是scan.in,它是Web服务器的日志路径,作为类FileLocator的输入参数。

搜索文件

在编写FileLocator类时,我在要求的范围之外做了一点提升。

提升真的不是一个好主意。你应该为了满足功能要求而编写代码,那是为了完成工作。

下面的代码不只是搜索指定路径下的log文件,还搜索所有的子目录。

@Service
public class FileLocator {

  private static final Logger logger = LoggerFactory.getLogger(FileLocator.class);

  @Value("${scan.in}")
  private String scanIn;

  @Autowired
  @Qualifier("fileValidator")
  private Validator validator;

  /** Search for the files requested */
  public void findFile() {

    logger.info("Searching in... {}", scanIn);
    File file = createFile(scanIn);
    search(file);
  }

  @VisibleForTesting
  File createFile(String name) {
    return new File(name);
  }

  private void search(File file) {

    if (file.isDirectory()) {
      logger.debug("Searching directory: {}", file.getName());
      File[] files = file.listFiles();
      searchFiles(files);
    } else {
      logger.debug("Validating file: {}", file.getName());
      validator.validate(file);
    }
  }

  private void searchFiles(File[] files) {
    for (File file : files) {
      search(file);
    }
  }
}

上面的代码用比较传统的递归方法来搜索日志文件,主要的入口函数是findFile()。它利用Spring标记@Value标记的scanIn实例变量创建一个File对象,并把其传给search()方法,由该方法检查这个File对象是不是一个目录。如果是目录,则对该目录下的每一个File对象循环调用search()。如果File对象是一个文件,则用文件验证类来处理。
到目前为止,利用应用的第一个类,我们能搜索一个指定的日志文件路径,找到该路径下的日志文件。如果你想知道搜索到文件后做了什么,就需要等待我的下一篇博文。

最后一个思考:是否需要关注系统中的每一个错误?有一个古老的哲学寓言:如果森林里的一棵树到了,但是没有其他任何一棵树听见,还能认为它发出了声音吗?同样道理,如果你的应用抛出了一个异常,但是用户没有受到影响,这还是一个错误吗?是否还需要花时间研究它?

以上代码请见Github: https://github.com/roghughe/captaindebug/tree/master/error-track

相关文章

猥琐男帮你分析男人,女同学都进来看看

$
0
0

站在男生的角度分析男人的心理,女生可以看看。免费关注微信公众号 jiarenorg,就能天天收到佳人精彩文章了,咱们微信里见!

猥琐男帮你分析男人,女同学都进来看看| jiaren.org

其实自称猥琐男非我本意,只是被八卦的女同学喷多了,干脆就先认了吧。这样,你们就不必再骂我猥琐男了——因为我已经TM的认了。

由于我年纪不是很老。所以这里的男人,不包括大叔。25岁往上的女同学可以点离开了。这帖子你们看了没意义。

首先你们要知道一点,男的只要他是单身——甚至他不是单身,他看女人多多少少都是带着有色眼镜在看。在这副有色眼镜之下,有几条大忌:

1、浓妆。
2、过于肥胖。
3、油腻。尤其是脸部的油腻。
4、头发不香——甚至有臭味。
5、过于明显浓烈的香水味——往往也是劣质香水的特点。
6、不自然的高傲的表情。俗称装逼。

至于其他的,什么脸蛋漂亮不漂亮,身材好不好,比例好不好,胸大不大,屁股翘不翘,说真的,和上面6条比,渣都不是。男的虽然嘴巴里经常说这些,但实际上——也许他们自己都不清楚——实际上这些,他们内心并不是那么看重。

什么东西能强烈的吸引男生呢?

1、笑脸。

不是指露牙肉的大笑,也不是故作羞涩的假笑。就是自然地笑。哪怕你是在莫名其妙的发笑——只要别让他感到你是在嘲笑他就行。积极的情绪是能感染人的。你和他可以都没有幽默感,但如果能多笑笑,自然相处的感觉会开心很多。

2、淡淡的香味。

尤其是头发的。注意这里不是香水味。都说男人是视觉动物,其实不仅仅,他们还是嗅觉动物。如果第一印象有臭味的话,那就非常糟糕了。如果你能保持自然的笑脸,和淡淡的香味,男生看到你不说心花怒放,但一定是比较愉悦的。

3、干净整洁。

一指皮肤。二指衣服。油腻的皮肤给男生带的潜在信号就是:此女内分泌有问题。可能他不会想的这么直接,只是一种不好的感觉。这是生物本能。干净整洁的衣服则是宣称着女生良好的个人卫生习惯——男生不管他自己多脏多邋遢,都肯定喜欢有着干净整洁的个人卫生习惯的女生。相比你优良的成绩,出色的工作能力,他更看重的是你贤惠的能力。而衣裳的整洁,是贤惠这个很抽象的词的具体表现。

4、傻、弱。

不是提倡女生去装。但事实情况就是男生有些惧怕哪些过于厉害的女生。尤其是比他们要厉害的。因为男性本身在细心,忍耐,以及在主动地位上就要劣于女生。他们仅仅的优势就是解决问题的能力比较强。

这里说的装,不是说看到虫子或两栖动物要害怕那种低级的装。这里的装,仅仅是指不去揭穿男生的失误,愚蠢,可笑,懦弱,局促,紧张。在同一个问题前面,你装做会或者可能会比他更失误,更愚蠢,更可笑,更懦弱,更局促,更紧张就行了。用一句话来说:就是不要表现出超越他解决问题的能力。因为这是他面对你仅存的优势。

去维护他这种心理优势,和男的甜言蜜语夸赞女生漂亮起的效果是一样的。

综3、4条可以解释为什么男的喜欢皮肤白的女生。因为皮肤黑是一种潜在的不干净卫生的提示——尽管这种提示是错误的。另外,皮肤黑是一种经常在室外活动的表现——他们潜意识害怕你能力比他强。

5、让他觉得他对你有戏,但又不是100%确定手到擒来。

如果你一直冷若冰霜,冰清玉洁到高傲——那么他内心可能会喜欢你,会敬重你,甚至爱上你——但他绝对不会追你。因为他还没追你你就已经拒绝他了。所谓风情万种,无非是让每一个接触的男人都觉得你对他有点那么点意思——但实际上你什么都没做。

而如果你先一步喜欢他,也不要去倒追——那是违反大自然规律的。你只要让他能感受到他追你会很有戏。表达出来以后不要着急,不要担心是不是自己做的不够——为什么他还没有什么表示?因为男的总是有一堆问题要去解决,可能最近比较忙,他想先把事情做完,再认真来追你也说不定。这种男人也是比较可取的。事业和感情分的清楚。当然,还有种情况是他已经喜欢其他人了,这只能水滴石穿,静观其变了。

如果你表现得过头了,效果可能是反的。因为他把你当备胎了。即是说,他还没追你,但已经追到手了。和第一种情况是反的。

拿捏好没追就已经追到手,和没追就已经拒绝他了,这两种极端情况的黄金中点,就是女生的艺术所在了。

至于怎么表现,非常简单。稍微有点超出一般朋友的尊重他的意见——但绝非言听计从;对他有一两处地方的特别——哪怕你对每个男人都有一些细节上的特别——这种情况他是看不到的睁眼瞎的,他只会注意到自己的。

大胆的看他,他看你以后你可以看别的地方——但不要从来不看他,尤其是他在干出风头的事情的时候。一些无伤大雅的小事上可以嘲弄他,但当他被多数人嘲弄的时候你要保持沉默和理解。等等,表现的方式太多太多了。

最后分析几个常见的问题。

1、感觉到他喜欢你,但他不来追你。

首先我要说的是,他肯定是不够喜欢你。不要找别的理由安慰自己了。

拿我自己举个例子,在路上看到女孩子,我会根据她美的程度进行注意。但美到让我惊心动魄不可多见那个程度的,我一定会把一切置之脑后去搭讪。如果没有,是因为美得不够——而不是我忙,我腼腆,我缩等等原因。因为到一定程度后,不止不会找借口,连真正有顾忌,也会把这些顾忌置之脑后。

当然,这个美是千人千面的,各有所好。不要因为那些世俗的标准去改变自己,做好自己就行了。比如我觉得美得惊天动地的,可能另一个男人只是觉得长得很特别甚至算不上好看。

如果你希望他来追你,多看他两眼就行了。如果已经经常接触,那么我前面已经说了一些表现方法了。

如果不希望他来追你,也不要去戏弄别人的感情——男人的报复心一样强烈,只是他经常忙得顾不上而已。

2、在一起时间久后,他就没以前那么疼你了。

首先这是个无法根治的问题。不朝三暮四的男人是没上进心的男人。不拈花惹草的男人是没野心的男人。他死心塌地的专门对你好——如果不是你真的那么好,那么就是他废。

话糙了点,但真的是这个理。

你能尽量做的是,不被他彻底征服,尽量不去依赖他——如果依赖,也只是满足他的自尊心。

婚姻中女性保持自己独立生活的经济能力,主要并不是不和社会脱节啊等乱七八糟的原因,更大的原因是让他保持对你感兴趣。

————————————————————

为什么我比较漂亮,却从来没有男生来搭讪我?

首先你看看自己,是不是没事就拿个手机整?

走路上的美女拿个手机狂整,给男生的第一印象是什么呢?她在和她男朋友发信息。

这样,他来和你搭讪的动力,直接削弱90%。其中包括缩,反感,正义感(不愿夺人所爱)等各种原因。

然后,你看看自己是不是在室外的时候总是一帮闺蜜一起?

一个男人面对单独的你可能有心里优势,但面对一群叫叫嚷嚷的鸭子的时候他肯定没勇气了。尤其是你那尖酸刻薄的女伴,或者过于强势的女伴。( 来源

猥琐男帮你分析男人,女同学都进来看看| jiaren.org小提示:如果文章有分页可能会被截断,请 点此阅读全文

佳人专注精品阅读6年,多谢佳友们的支持,请记得在QQ邮件底部评分处打5颗星噢~~


猥琐男帮你分析男人,女同学都进来看看| jiaren.org把佳人藏进QQ邮箱 | 人气热文 | 投稿 | 关注佳人QQ空间 | 小组 | 佳人首页 | 猥琐男帮你分析男人,女同学都进来看看| jiaren.org佳人微信

佳人推荐:

佳人猜您也喜欢:
猥琐男帮你分析男人,女同学都进来看看| jiaren.org
男人找剩女不如找失足妇女?
猥琐男帮你分析男人,女同学都进来看看| jiaren.org
文艺女青年的归宿不是男人,只能是自己
猥琐男帮你分析男人,女同学都进来看看| jiaren.org
男女之间为什么无法沟通?
猥琐男帮你分析男人,女同学都进来看看| jiaren.org
男女上床前后的反差
猥琐男帮你分析男人,女同学都进来看看| jiaren.org
解读男女关系的33个绝妙比喻
无觅

手机页面开发总结_ - 狐狸不会飞

$
0
0

1,图片像素不要用具体px,要用% px

2,左右边距也用%px

3,字体上,如果太小的px 手机上和电脑上是有区别的最好的情况是用em

4,对于get请求所带参数,参数之间不要留空格,不然读取数据时会取不到值

   (面对如果名称没有错,值也有的情况,那么就观察你的url 看参数名称之后是否出现奇怪的字符)

5,面对多个单选框取值,首先为他们取同一个name 值(这样能保证它只能单选,不会出现多选)->给他们赋value值->循环单选框判断他们的checked 属性,在取值

6, 面对不同手机的不同系统可能会出现宽度不同的情况,

    (

//  actionInfo   myButton 样式

var buttonWidth = $(".actionInfo").width() + 44;
$(".myButton").width(buttonWidth);

)

7,前提页面获取系统当前时间与你所定义时间对比 

 ( new Date().getTime() < new Date(endtime).getTime()  转化成同一串数字 )

8,判断距离结束时间的方法 

1 // endTime 定义的结束时间
2 function D_Time(endTime) {
3 var obj = $("#lbl_Time");
4 var endtime = new Date(endTime).getTime();
5 var nowtime = new Date().getTime();
6 var youtime = endtime - nowtime;
7 var seconds = youtime / 1000;
8 var minutes = Math.floor(seconds / 60);
9 var hours = Math.floor(minutes / 60);
10 var days = Math.floor(hours / 24);
11 var CDay = days;
12 var CHour = hours % 24;
13 var CMinute = minutes % 60;
14 var CSecond = Math.floor(seconds % 60);
15 if (endtime <= nowtime) {
16 obj.html("已过期");
17
18 } else {
19 if (days > 0) {
20 obj.append("" + days + "天" + CHour + "时" + CMinute + "分" + CSecond + "秒");
21 }
22 else if (CHour > 0) {
23 obj.append("" + CHour + "时" + CMinute + "分" + CSecond + "秒");
24 }
25 else if (CMinute > 0) {
26 obj.append("" + CMinute + "分" + CSecond + "秒");
27 }
28 else if (CSecond > 0) {
29 obj.append("" + CSecond + "秒");
30 }
31 }
32 setTimeout("D_Time()", 1000);
33 }

 

 


本文链接: 手机页面开发总结_,转载请注明。

企业应用开发与互联网应用开发区别

$
0
0

注:转自 http://timeson.iteye.com/blog/609045

 

新形式下的企业应用特点: 

  • 企业应用系统从封闭走向开放,由局域网转到互联网,随着涉众面的极大扩展,新的企业应用要求多浏览器支持(IE,FireFox),国际化支持,全球业务的互联互通。这样就要求企业应用不能满足简单的表单、表格、树、菜单;而是要求有较好的用户体验,提倡富互联网应用。
  • 企业应用的内容也发生一些转变:除了企业的核心业务系统,新的企业应用也应运而生,典型的比如有:交互性门户系统(个性化门户,个人工作台等),电子商务平台,企业级2.0(博客,Wiki,RSS,微博),企业级SNS(社区平台),无线企业应用等。
  • 企业需求的提升:除了功能性需求,客户对于安全,性能,大容量,大并发,易维护等特性愈发关注,未来的趋势是企业应用构建在互联网而不局限于局域网,可能是在云,也可能是网格,也可能在其他的新技术上实现。


      企业应用和互联网应用从根本来说是相同的,都是基于因特网、HTTP、浏览器的一种应用,但面向的涉众不一样,从而导致些许差异性,比较如下: 
企业应用(表1): 

1行业领域区分行业,各自领域业务背景不一样,并形成了一定的门槛。
2业务逻辑业务逻辑复杂,涉及大量的数据和多人协同处理。
3数据一致性强调数据一致性,需要通过事务,交易中间件,数据库锁,java同步机制来保证数据的一致性。
4数据复杂度数据复杂,有大量的表,表之间有复杂的牵涉关系,在某些行业维护这些表之间的关系和数据就需要一个团队。
5并发量不是特别大,比如通用应用为100~200并发,重度并发500的系统就能满足国内大部分的系统要求。
6系统集成关键系统需要和很多外部系统集成,集成的方式可能采取esb,jms,web service,socket。
7用户交互强调界面交互和数据表达,需要支持多种数据展现方式,需要众多数据在页面上的展现,传输
8开发过程强调软件过程,讲究行业经验,需要撰写大量的文档和多人的协同,需要版本控制和问题跟踪回溯。



互联网应用(表2): 

1行业领域跨行业,按应用类型区分,比如blog,wiki,个人门店等。
2业务逻辑业务逻辑简单,大部分是通过页面进行数据的增删改查。
3数据一致性要求有事务,但和高并发博弈中,让位给高并发。
4数据复杂度数据不复杂,表之间的关联不多
5并发量强调高并发,支持用户数量多,并采取企业开发中极少采用的技术,比如web反向代理,memcache(分布式缓存),表的垂直分隔、水平分隔,强调高速读低速写。支持百万用户。
6系统集成弱。极少需要和其他系统集成
7用户交互弱。交互不多,表现方式简单,更多的是数据的增删改查。
8开发过程强调敏捷,快速开发,基本不需要版本控制。


   通过简单的比对,由此可见,互联网开发强调的是快速,敏捷,涉众面广的一类系统。 

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



IE11打开某些网站,F12仿真模式中文档模式默认为IE7?

$
0
0

IE11浏览器默认的文档模式为Edge,但是打开有些网站的时候,在 IE11中打开F12开发人员工具,仿真模块下,文档模式选项中Internet Explorer 7显示为默认值。

但是并未打开所有网站都是显示为IE7为默认文档模式,将文档模式设置为其他IE版本以后,在重新打开该网页,任然会显示为IE7。这个是什么原因呢?

文档模式选项中Internet Explorer 7显示为默认值

出现这种一般是两种原因所知,第一种就是网站通过meta标签定义X-UA-Compatible属性,通过这个方法就可以让IE默认以指定的文档模式进行渲染。如下代码:

<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />

第二种情况就是,已经将该网站添加至兼容性视图列表,想取消的话,按ALT键,显示菜单栏,找到工具里面的兼容性视图设置,如下图,在把里面的网址删除掉即可。

删除兼容性视图列表中网址

于是,再看看文档模式,默认为Edge,至于什么是Edge,这个小编暂时也不太清楚,有人说因为Edge是IE11的最高文档模式,是因为这样么?

IE11默认文档模式为Edge

This article addresses: http://www.iefans.net/ie11-f12-fangzhen-wendang-moshi-moren-ie7/

By the time your rss reader get this post here is 1 comments ,Welcome you come to leave your opinion !

关于聪明工作的一些思考

$
0
0
关于聪明工作的一些思考

最近和一个朋友在讨论职场的上的一些问题,为什么有些人升的非常快,有些人升的比较慢。有些人同事都非常认同,但是领导不认同,有些人领导认同,但是同事却不认同。在和他聊过之后,有一些事情自己豁然开朗,比较后悔为什么自己没有早一点明白这个道理。

1 职场上最重要的就是积极的心态。领导永远喜欢的就是积极的人的,也就是我们常说的比较主动。

很多人在职场上,会遇到一些挫折,不想干活,有时候在积极主动一小段时间之后,发现没有结果,就容易放弃。而我的想法就是,要一贯的积极主动。如果你对你自己的工作木有兴趣,实在提不起来精神,就果断的放弃的,找新的环境和事情去挑战。只有积极主动的人,才能够取得成功。也是聪明工作的前提。积极主动不是告诉你要多加班,而是要多学习,多思考。我刚来公司的时候,觉得主动就是要多加班,可能这在刚开始对自己的能力提升是比较由效果的。但是到总会遇到瓶颈。我们有时候要思考工作之外的东西,例如目标,方向,提高影响力,提高表达能力,提高沟通能力。只有每个阶段自己进行反思,才会 由很大的进步。

在这里,我说一个比较明显的例子。我的一个朋友,工作很努力,每天加班到12点,但是早上来的比较晚,有时候中午才过来。这样给领导的感觉就非常不好,领导觉的你这个人非常懒。但是这个人觉得很冤。所以在工作的时候,每天准时考勤是一件非常重要的事情。但是如果晚上确实加班比较晚,怎么办,这里面有一个技巧,下个章节里面说详细提到。



2 要要努力的工作,但最重要的是让别人知道你在在努力的工作,尤其是你的领导。

努力的工作这里就不讲了,大多数人想出头的人都会很努力的工作。程序员性格大多数比较实在和内向,也比较能够抵抗压力,工作非常敬业,能够一个人独自完成领导分配的任务。这个是程序员的优点,也是一个缺点。在大多数公司,一个领导回管理10个人以上,领导不可能知道每个人的状态,所以如何把你的工作能够准确的汇报给你的领导,是一件非常由技巧的事情,也是非常重要的一件事情。

作为一个领导,最怕的就是信息不通畅。宁愿信息多一点,但是不能缺少信息,否则感觉自己没有了存在感,关于存在感这个问题,是一个比较大的话题,这个在sns里面起到关键作用。为什么很多人喜欢sns,其实和每个人的被需要感,即存在感有很大的关系。领导也是人,如果下面的人做了什么,遇到什么问题都不知道的话,被领导的领导问到了,他会怎么想。所以即使领导知道你大体在干什么,也是不介意你把更多的工作信息透露给他的。

如果汇报你的工作,就有两种方式:邮件方式,当面沟通方式

邮件方式比较适合一般性的工作陈述,就是你目前做的工作内容,具体是哪些action,每个action如何解决的,解决过程中有什么好的方法,遇到的小问题等等。这个在周报里面反映比较好。如果在某一个项目里面,项目目前的概况,遇到了哪些问题,需要解决哪些问题等等,自己目前的状态,都是可以总结出来,并且抄送给领导的。让领导知道这些事情。

当面沟通方式就比较适合自己遇到比较大的问题,自己觉得无法推动,或者涉及到多个部门配合,项目有极大的风险的时候,当面找领导,让领导觉得自己有被需要的感觉。不过这个别用的太频繁,别什么破事情就找领导:比如加班太多干不完活,技术问题解决不了等等,找领导的沟通的问题一定是有价值的,并且自己无法独立完成的(非技术性问题),或者说是推进有难度的。

让领导知道你做了什么事情是基本的需要,同时还需要让领导知道你工作的很努力,就拿前面章节那个例子来说,如果一个人加班太晚,如何让领导得知。就好的办法就是下班之前,发一封总结的邮件,给处理相关问题的责任人,汇报解决问题办法,间接的说明你工作的很晚(因为你发出邮件的时间就是你下班的时间),然后顺便第二天请假说来的比较晚。这样领导既不会觉得你拖沓,反而觉得你这个小伙子很努力的在工作。

汇报工作的技巧还有很多,最关键的是自己干了什么,一定要让领导知道。别人称赞自己做的比较好的地方,一定要让领导知道。自己做的好,要让别人告知领导。

3 提高自己的影响力

如果说,在上面一章节里面说的是 要让领导知道你在做什么,这个章节就说的是要让团队的人知道你很牛逼。程序员的最大问题就是不懂得如何去推销自己。我以前也是一个sb,默默无闻的工作,很努力的,加班有很多,但是结果不好。

提升自己影响力就是多在公开场合表达自己的想法,这个需要一个过程的,首先你要懂,你不懂的话,自然就说不出来。其次你要积极,你不积极的话就不会参与,同时你也要口才好,否则表达不出来。所以别看公开表达,这个对人的要求是比较高的。最好展现自己的地方就是在技术问题处理的时候或者技术方案讨论的时候,这个参与的人回比较多。

多和自己团队的技术牛人交流或者请教问题,让技术牛人对你比较了解也很重要。这个就是要紧跟牛人,牛人想推荐人的话,很有可能想到的就是你。

如果团队有自己的技术blog的时候,多发帖子,让团队的人信服你。

整体来说,提高自己的影响力是非常需要本事的,如果说汇报工作是比较偏重技巧的话,那么提高影响力就是比较实在的提高自己的能力。

关于聪明的工作,还会有更多的一些内容,目前只能想到这么多,看到的网友也可以分享一下经验。不过无论怎么样,纯粹的表现技巧是不值得学习的,玩一些虚伪的东西也不值得学习。

已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



iPad真的对儿童有害吗?

$
0
0
近日京都大学的一组研究人员发现,电子书对引起儿童的阅读兴趣有很大帮助。相比印刷书,电子书可以更容易教授阅读。


科学探索

 
iPad对儿童真的有害吗?
 

  现今的孩子看到iPad就会沉迷其中。一些家长把iPad当做“保姆”,另一些则认为使用iPad会对孩子的认知能力产生影响。

 

  近日京都大学的一组研究人员发现,电子书对引起儿童的阅读兴趣有很大帮助。相比印刷书,电子书可以更容易教授阅读。

  该实验创建了一本12页的图片电子书,包含576个字符,和与之相同纸质书。同时选取30位对图书内容认知水平基本相同4岁儿童,并分为两组。

  电子书通过高亮的红色字符显示文字,并自动阅读给孩子。观看纸质书的孩子则在父母阅读下观看。

  观看电子书组的15个4岁的孩子每天在iPad上看两遍。六天后正确读出字符平均增加了3.1个,增至19.5个。

  另一组15个4岁孩子观看纸质书每天两遍。六天后正确读出字符平均增加了0.3个,增至16.9个。

  两组4岁的儿童进行对比发现,观看同样内容的图书后,阅读电子书比纸质书平均多学习三个字符。

  研究者表示:“电子书对儿童认字阅读有很大帮助。”(来源: 驱动之家)

Viewing all 15843 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>