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

Web安全扫描器Netsparker v3.5发布

$
0
0


‍‍Netsparker是一款综合型的web应用安全漏洞扫描工具,它分为专业版和免费版,免费版的功能也比较强大。Netsparker与其他综合 性的web应用安全扫描工具相比的一个特点是它能够更好的检测SQL Injection和 Cross-site Scripting类型的安全漏洞。‍‍

新版本特性

* DOM跨站脚本漏洞检测
* 基于Chrome浏览器的Dom解析
* URL重写规则
* 可晒出某些不必要的扫描结果

新安全检测功能

* Nginx服务器旧版本检测
* Perl源代码泄露
* Python源代码泄露
* Ruby源代码泄露
* Java源代码泄露
* Nginx服务器识别
* Apache服务器识别
* Java栈泄露

提高

* 提高远程包含漏洞导致的远程代码执行准确率
* 提高跨站脚本漏洞检测

下载地址


(总结)Nginx/LVS/HAProxy负载均衡软件的优缺点详解

$
0
0
PS:Nginx/LVS/HAProxy是目前使用最广泛的三种负载均衡软件,本人都在多个项目中实施过,参考了一些资料,结合自己的一些使用经验,总结一下。 一般对负载均衡的使用是随着网站规模的提升根据不同的阶段来使用不同的技术。具体的应用需求还得具体分析,如果是中小型的Web应用,比如日PV小于1000万,用Nginx就完全可以了;如果机器不少,可以用DNS轮询,LVS所耗费的机器还是比较多的;大型网站或重要的服务,且服务器比较多时,可以考虑用LVS。 一种是通过硬件来进行进行,常见的硬件有比较昂贵的F5和Array等商用的负载均衡器,它的优点就是有专业的维护团队来对这些服务进行维护、缺点就是花销太大,所以对于规模较小的网络服务来说暂时还没有需要使用;另外一种就是类似于Nginx/LVS/HAProxy的基于Linux的开源免费的负载均衡软件,这些都是通过软件级别来实现,所以费用非常低廉。 目前关于网站架构一般比较合理流行的架构方案:Web前端采用Nginx/HAProxy+Keepalived作负载均衡器;后端采用MySQL数据库一主多从和读写分离,采用LVS+Keepalived的架构。当然要根据项目具体需求制定方案。 下面说说各自的特点和适用场合。 Nginx的优点是: 1、工作在网络的7层之上,可以针对http应用做一些分流的策略,比如针对域名、目录结构,它的正则规则比HAProxy更为强大和灵活,这也是它目前广泛流行的主要原因之一,Nginx单凭这点可利用的场合就远多于LVS了。 2、Nginx对网络稳定性的依赖非常小,理论上能ping通就就能进行负载功能,这个也是它的优势之一;相反LVS对网络稳定性依赖比较大,这点本人深有体会; 3、Nginx安装和配置比较简单,测试起来比较方便,它基本能把错误用日志打印出来。LVS的配置、测试就要花比较长的时间了,LVS对网络依赖比较大。 3、可以承担高负载压力且稳定,在硬件不差的情况下一般能支撑几万次的并发量,负载度比LVS相对小些。 4、Nginx可以通过端口检测到服务器内部的故障,比如根据服务器处理网页返回的状态码、超时等等,并且会把返回错误的请求重新提交到另一个节点,不过其中缺点就是不支持url来检测。比如用户正在上传一个文件,而处理该上传的节点刚好在上传过程中出现故障,Nginx会把上传切到另一台服务器重新处理,而LVS就直接断掉了,如果是上传一个很大的文件或者很重要的文件的话,用户可能会因此而不满。 5、Nginx不仅仅是一款优秀的负载均衡器/反向代理软件,它同时也是功能强大的Web应用服务器。LNMP也是近几年非常流行的web架构,在高流量的环境中稳定性也很好。 6、Nginx现在作为Web反向加速缓存越来越成熟了,速度比传统的Squid服务器更快,可以考虑用其作为反向代理加速器。比如,Nginx对请求的异步处理可以帮助节点服务器减轻负载,假如使用apache直接对外服务,那么出现的很多窄带链接时apache服务器将会占用大量内存而不能释放,使用Nginx做apache代理的话,这些窄带链接会被Nginx挡住,apache上就不会堆积过多的请求,这样就减少了相当多的资源占用。 7、Nginx可作为中层反向代理使用,这一层面Nginx基本上无对手,唯一可以对比Nginx的就只有lighttpd了,不过lighttpd目前还没有做到Nginx完全的功能,配置也不那么清晰易读,社区资料也远远没Nginx活跃。 8、Nginx也可作为静态网页和图片服务器,这方面的性能也无对手。还有Nginx社区非常活跃,第三方模块也很多。 Nginx的缺点是: 1、Nginx仅能支持http、https和Email协议,这样就在适用范围上面小些,这个是它的缺点。 2、对后端服务器的健康检查,只支持通过端口来检测,不支持通过url来检测。不支持Session的直接保持,但能通过ip_hash来解决。 LVS:使用Linux内核集群实现一个高性能、高可用的负载均衡服务器,它具有很好的可伸缩性(Scalability)、可靠性(Reliability)和可管理性(Manageability)。 LVS的优点是: 1、抗负载能力强、是工作在网络4层之上仅作分发之用,没有流量的产生,这个特点也决定了它在负载均衡软件里的性能最强的,对内存和cpu资源消耗比较低。 2、配置性比较低,这是一个缺点也是一个优点,因为没有可太多配置的东西,所以并不需要太多接触,大大减少了人为出错的几率。 3、工作稳定,因为其本身抗负载能力很强,自身有完整的双机热备方案,如LVS+Keepalived,不过我们在项目实施中用得最多的还是LVS/DR+Keepalived。 4、无流量,LVS只分发请求,而流量并不从它本身出去,这点保证了均衡器IO的性能不会收到大流量的影响。 5、应用范围比较广,因为LVS工作在4层,所以它几乎可以对所有应用做负载均衡,包括http、数据库、在线聊天室等等。 LVS的缺点是: 1、软件本身不支持正则表达式处理,不能做动静分离;而现在许多网站在这方面都有较强的需求,这个是Nginx/HAProxy+Keepalived的优势所在。 2、如果是网站应用比较庞大的话,LVS/DR+Keepalived实施起来就比较复杂了,特别后面有Windows Server的机器的话,如果实施及配置还有维护过程就比较复杂了,相对而言,Nginx/HAProxy+Keepalived就简单多了。 HAProxy的特点是: 1、HAProxy也是支持虚拟主机的。 2、HAProxy的优点能够补充Nginx的一些缺点,比如支持Session的保持,Cookie的引导;同时支持通过获取指定的url来检测后端服务器的状态。 3、HAProxy跟LVS类似,本身就只是一款负载均衡软件;单纯从效率上来讲HAProxy会比Nginx有更出色的负载均衡速度,在并发处理上也是优于Nginx的。 4、HAProxy支持TCP协议的负载均衡转发,可以对MySQL读进行负载均衡,对后端的MySQL节点进行检测和负载均衡,大家可以用LVS+Keepalived对MySQL主从做负载均衡。 5、HAProxy负载均衡策略非常多,HAProxy的负载均衡算法现在具体有如下8种: ① roundrobin,表示简单的轮询,这个不多说,这个是负载均衡基本都具备的; ② static-rr,表示根据权重,建议关注; ③ leastconn,表示最少连接者先处理,建议关注; ④ source,表示根据请求源IP,这个跟Nginx的IP_hash机制类似,我们用其作为解决session问题的一种方法,建议关注; ⑤ ri,表示根据请求的URI; ⑥ rl_param,表示根据请求的URl参数’balance url_param’ requires an URL parameter name; ⑦ hdr(name),表示根据HTTP请求头来锁定每一次HTTP请求; [...]

【译】千万别问用户:你想要什么? ——用户访谈的3个基本问题

$
0
0

用户调研的首要原则:千万别直接问用户他们想要什么

——Erika Hall, just enough research

“如果在访谈中用户能直接说出他想要的是什么就好了。”

“呵呵。”

 

我喜欢做用户访谈,它们低廉、有效(你得到的信息会多于你问的问题)、且快速( 一般访谈5个用户就够了)。

但是,优质的用户访谈需要技巧。

如果你天生对人群敏感且好奇,它能帮助你得到你想要的信息;如果你并不具备此项特质,它也能帮你带入角色去体会其中滋味。例如,google实验室的michael Margolis就喜欢将自己 带入角色去做用户调研。

正如Erika Hall 所述,当你做用户访谈,要避免询问他们想要什么,因为这只能给你带来错误观点。你仅仅能收获用户所设想的解决问题的办法,却无法发现问题的本质原因。

 

 

不要给你的用户访谈设置障碍

(仅仅询问用户想要什么会让访谈变得更困难,同时,你也只能得到错误观 点)

 

当你询问用户想要什么,你其实是让他思考解决问题的所有可能性,无疑这将使用户研究变得越发困难。如果你做用户研究的目的是为了搭建还不存在的新产品或功能,你其实是想知道究竟是什么原因造成用户使用现有工具无法完成任务,循着这个方法,你才能设计新功能或渐进优化现有功能来帮助他们完成任务。

 

我在 Kissmetrics工作时,我曾花费很多时间与用户交流,关于他们使用什么工具或方法来解决当下所遇到问题。我总结了三个高效有用的问题来做用户访谈,并且屡试不爽:

  • 你正在解决什么问题?(收集内容信息)
  • 目前你如何解决该问题?(分析工作流程)
  • 有什么方法能帮助你做的更好(发现机会)

 译者注:Kissmetrics数据分析公司,位于旧金山

 

 

问题1:你正在试着解决什么问题,为什么要解决这个问题?

(为了挖掘产品使用和问题产生的根本原因,我们不断询问用户“为什么要这么做”,直到我们真正理解了用户或客户的痛点)

 

当你调研用户正在做什么时,收集背景信息非常关键,这将有利于你去理解你的用户。调研收集信息诸如:用户工作的小组有多少人以及他们是如何在庞大组织中分工合作,将有利于我们设定所调研问题的范围框架,以便在该框架下使我们的产品更好地发挥功效

想象一下,你是一个工匠,难道你不想知道你当前的任务是修补墙上的一道裂缝,还是去修缮整间屋子吗?根据你任务的不同,你便需要选择不同的工具及对应型号。

 

这同样适用于用户访谈。当你知道用户正在解决的究竟是什么问题,并将这些必要的信息告知你的调研小组,你的pm和rd团队都将会感激你的。

探究问题的根本原因,你要问为什么。使用 5步“为什么”的问法,能让调研变得简单。通过反复问为什么(并不是真的只问5个为什么,视情况而定哈),你将顺其自然地知道用户工作流程如何,或发现其中缺少哪些必要流程。使用此法,我能通过完善流程来满足用户需求,而不需要让工程师再去开发一个新功能来弥补,这样就能将工程师从低效的项目中解救出来。

 

 

问题2:目前你如何解决该问题?

(搞清楚工作流程和组织架构,能帮助我们确定从哪里着手解决问题)

在明确问题的内容范围之后,我倾向去发现用户当下是如何处理该问题的。做这个的好处是让我能够跟随他们的步伐,去体会他们在处理该问题时有多痛苦。有时,用户会使用奇葩的方法去解决问题从而得到他们想要的。但其实我们只要稍做改善下产品就能解决用户花费数小时甚至一周在解决的问题

举个栗子。最近我为一个产品小组调研他们正计划开发的新功能。但我们想了解目前该问题有多严重性,从而排列它的优先级。通过访谈一系列用户目前是如何操作的,我得到了以下一些结论:

译者注:该流程表达的是-客户如何管理邮件列表,及如何评估客源;CTA全称commodity trading advisors,即商品交易顾问;webinar 属于一种线上会议软件

 

知道用户的工作流程也能让你的团队发现你的工作流程中有哪个部分需要优化。再举个栗子,在KISSmetrics,我们最近发现客户在核对数据时更喜欢使用邮件而不是用第三方应用。此前我们已经知道kissmetrics是他们日常工作中不可或缺的一部分,有些用户一到办公室甚至会首先处理该类数据,所以我们目前在升级该项目,即每天给我们客户邮件发送更清晰的数据小结

 

 

问题3:有什么方法能帮助你做的更好?

大部分调研在你思考此问题前其实就结束了。这个问题是让用户给你一些提示:在哪些领域他们最需要帮助。当然,这个问题也能帮助你确认或者推翻你的团队在产品架构方面的某些假设。

如果你一开始就跨过之前的问题去询问用户如何能做的更好,那么你只能得到他们的一些意见却无法知晓他们当下是如何处理目前所遇到问题的

 

“设计产品是一件非常困难的事情,很多时候,只有在产品面世后,人们才知道他们想要什么。”-Steve Jobs

 

这是你发现如何优化产品的机会点,或者是用户宣泄他们目前解决办法的机会点,或者是一直被忽略的问题点。要么你会发现这个机会点足够强大到需要推动整个团队去解决,要么这个机会点其实已经被解决,我们需要挑过它进而去关注另一个假设。

译者注:这里有读者质疑:“有什么方法能帮助你做的更好”恐怕是“你需要什么”的另一种问法,所以,在用户调研方面更倾向让用户去展示在工作的哪一个环节出现问题即让用户描述痛点,而不要去问用户关于任何设计的问题,设计的问题需要交给专业人士来定夺。

译者理解:本文作者给出的“有什么方法能帮助你做的更好”,是对前两个问题的引申,考察用户现阶段的解决办法是什么,帮助研究人员更好地理解用户处境,同时给自己一些灵感,所以严格意义上不算用户决定影响产品,而属于信息收集与参考。

 

 

在用户调研中,使用这3个基本问法,我能为我的团队非常高效得去核实一个个假设,以至于我们能为用户提供长久的价值而不仅仅是给产品打个补丁应付了事。

当你面访用户时候,你喜欢问什么问题,以便帮助你建构或者优化产品?我不喜欢讲太多,而是更爱当用户的忠实听众。

 

译文出处: 《Never Ask What They Want — 3 Better Questions to Ask in User Interviews》    作者:Charles Liu   媒体:Medium

android 使用html5作布局文件: webview跟javascript交互

$
0
0
android 使用html5作布局文件 http://www.cnblogs.com/andgoo/archive/2011/10/11/2207936.html
Android中webview跟JAVASCRIPT中的交互 http://www.oschina.net/question/4873_27156
android软件开发之webView.addJavascriptInterface循环渐进【一】: http://www.sollyu.com/android-software-development-webview-addjavascriptinterface-cycle-of-gradual-one
android软件开发之webView.addJavascriptInterface循环渐进【二】: http://www.sollyu.com/586


在android开发中,通常使用xml格式来描述布局文件。就目前而言,熟悉android布局及美化的人员少之又少,出现了严重的断层。大部分企业,其实还是程序员自己动手布局。这样既浪费时间和精力,也未必能达到理想的效果。但是,在企业级的android开发中,使用html页面进行布局,也有很多的优势(例如:简单,大部分开发人员及美工都熟悉,方便统一进行更新,管理)。据笔者了解,已经有不少的公司在使用这种方式进行布局开发。这也可能是一种趋势。
下面,我将给出一个实例代码,供大家学习使用html页面给android应用布局。
package com.dazhuo.ui;

import java.util.List;

import org.json.JSONArray;
import org.json.JSONObject;

import com.dazhuo.domain.Person;
import com.dazhuo.service.PersonService;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.webkit.WebView;


public class MainActivity extends Activity {
   private PersonService service;
   private WebView webview;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        service =new PersonService();
        webview = (WebView) this.findViewById(R.id.webView);//android内置浏览器对象
        webview.getSettings().setJavaScriptEnabled(true);//启用javascript支持
        //添加一个js交互接口,方便html布局文件中的javascript代码能与后台java代码直接交互访问
        webview.addJavascriptInterface(new PersonPlugin() , "Person");//new类名,交互访问时使用的别名
       // <body onload="javascript:Person.getPersonList()">
        webview.loadUrl("file:///android_asset/index.html");//加载本地的html布局文件
        //其实可以把这个html布局文件放在公网中,这样方便随时更新维护  例如 webview.loadUrl("www.xxxx.com/index.html");
    }
    //定义一个内部类,从java后台(可能是从网络,文件或者sqllite数据库) 获取List集合数据,并转换成json字符串,调用前台js代码
    private final class PersonPlugin{
        public void getPersonList(){
         List<Person> list = service.getPersonList();//获得List数据集合
         //将List泛型集合的数据转换为JSON数据格式
          try {
            JSONArray arr =new JSONArray();
            for(Person person :list)
            {
                JSONObject json =new JSONObject();
                json.put("id", person.getId());
                json.put("name", person.getName());
                json.put("mobile",person.getMobile());
             arr.put(json);
            }
            String JSONStr =arr.toString();//转换成json字符串
            webview.loadUrl("javascript:show('"+ JSONStr +"')");//执行html布局文件中的javascript函数代码--
             Log.i("MainActivity", JSONStr);
          } catch (Exception e) {
            // TODO: handle exception
        }
        }
        //打电话的方法
        public void call(String mobile){
            Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:"+ mobile));
            startActivity(intent);
        }
    }
}

package com.dazhuo.domain;

public class Person {
    private Integer id;
    public Integer getId() {
        return id;
    }
    public Person(Integer id, String name, String mobile) {
        super();
        this.id = id;
        this.name = name;
        this.mobile = mobile;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getMobile() {
        return mobile;
    }
    public void setMobile(String mobile) {
        this.mobile = mobile;
    }
    private String name;
    private String mobile;
}

package com.dazhuo.service;

import java.util.ArrayList;
import java.util.List;

import com.dazhuo.domain.Person;

public class PersonService {
   public List<Person> getPersonList()
   {
       List<Person> list =new ArrayList<Person>();
       list.add(new Person(32, "aa", "13675574545"));
       list.add(new Person(32, "bb", "13698874545"));
       list.add(new Person(32, "cc", "13644464545"));
       list.add(new Person(32, "dd", "13908978877"));
       list.add(new Person(32, "ee", "15908989898"));
     return list;
   }
}

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title><script type="text/javascript">
    function show(jsondata){
            var jsonobjs = eval(jsondata);
            var table = document.getElementById("personTable");
            for(var y=0; y<jsonobjs.length; y++){
                var tr = table.insertRow(table.rows.length); //添加一行
                //添加三列
                var td1 = tr.insertCell(0);
                var td2 = tr.insertCell(1);
                td2.align = "center";
                var td3 = tr.insertCell(2);
                td3.align = "center";
                //设置列内容和属性
                td1.innerHTML = jsonobjs[y].id; 
                td2.innerHTML = jsonobjs[y].name; 
                td3.innerHTML = "<a href='javascript:Person.call(\""+ jsonobjs[y].mobile+ "\")'>"+ jsonobjs[y].mobile+ "</a>"; 
            }
    }</script></head><!-- js代码通过webView调用其插件中的java代码 --><body onload="javascript:Person.getPersonList()"><table border="0" width="100%" id="personTable" cellspacing="0"><tr><td width="20%">编号</td><td width="40%" align="center">姓名</td><td align="center">电话</td></tr></table><a href="javascript:window.location.reload()">刷新</a></body>

/</html>



转自:http://blog.csdn.net/dinglang_2009/article/details/6862627

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


ITeye推荐



Android 的屏幕滚动操作不如 iPhone 流畅跟手,是什么原因导致的?

$
0
0
几个高票答案说的都是现象而非原因。
对 @劉長曦 专栏提到的 g+ 讨论做了个摘要,出处在此
https://plus.google.com/105051985738280261832/posts/2FXDCz8x93s

首先, 安卓不缺硬件加速的支持。从安卓 1.0 开始,安卓就支持硬件加速。菜单的显示/隐藏、提醒的滑动渐变、Activity 之间的过渡以及对话框的显示/隐藏等都是经过硬件加速的。

但硬件加速与安卓「窗口」的概念有关。比如以下这张截图,就包含四个窗口:


状态栏窗口 / 壁纸窗口 / 壁纸上的启动器窗口 / 菜单窗口

安卓一开始的设计目标是「提供开放的应用平台」,在这种设计思路下,安卓通过许多个独立的 UI 元素来分享屏幕:比如输入法窗口和应用窗口就是两个不同的窗口。而同一个安卓应用,也由许多 Activity 组成(比如联系人列表是一个 Activity,联系人详情是另一个 Activity),每个 Activity 又有自己的窗口。发现了吗? 安卓 UI 一切皆窗口:从主屏打开联系人应用,你看到的是主屏窗口和联系人列表窗口的动画;按下联系人查看详情,是联系人列表窗口和联系人详情窗口的动画;显示输入法,是键盘窗口的动画.. . 早期的安卓使用软件来渲染窗口内的内容:在 2.3 前,窗口内容由软件渲染,而窗口的组合 / 移动则通过硬件加速绘制。

自安卓 3.0 起支持「全」硬件加速,原先由 CPU 完成的窗口渲染可通过 GPU 完成了。但许多老应用的 UI 组件不支持此功能,所以在开发者菜单中打开强制硬件加速可能会有各种异常出现。

但为什么即便支持了硬件加速,也不能完全保证流畅度呢?事实上, 安卓的这个多窗口设计,意味着 GPU 需要同时支持不同进程的多个活动 GL 上下文。而即便到现在,多数移动 GPU 执行上下文切换的代价还是相当高的。

虽然安卓有堆硬件这一说,但 硬件加速的资源也很容易被安卓的渲染机制吃光。比方说,Tegra 2 足够在 60 帧下把 1280*800 屏幕的每个像素点渲染 2.5 次。但安卓 3.0 中,光是打开「所有应用」的视图,就需要绘制许多不同的窗口:需要对所有像素绘制一次背景;(往少了说)需要对一半的像素绘制一次 shortcut 和 widget 层;需要对一半的像素绘制一次图标和标签;也需要对所有像素绘制一次「所有应用」视图的黑色背景,还有「所有应用」视图的图标和标签...还不算对这些窗口做最后的组合,就把 GPU 的资源吃光了。当然,安卓对这个机制也有优化,比如把壁纸做成一个比屏幕大的窗口,这样在主屏滚屏时就不需要重绘,只要移动窗口就行。而这个绘制好了的窗口,就不需要额外的 GPU 计算量了。

另一方面,OpenGL 硬件加速绘图也不是万能的,Nexus S 和 Galaxy Nexus 中,每个 OpenGL 应用会占用 8MB 内存。要知道 2MB 的进程开支都是个不小的代价。这 8MB 内存可能从后台进程那里分配而来,造成应用切换速度的下降。

为了提升流畅度,还需要许多其他方面的努力:安卓 1.6 对前后台进程调度的优化、2.3 中对输入系统的重写、 加入并发的垃圾收集等。举一个流畅度不由硬件加速决定的例子:对滚屏操作,Nexus S 在 ICS 上的流畅度比在 2.3 中要低。这其实是因为计时机制发生了变化,有时在 ICS 中,当应用接收触摸事件并绘图时,可能在尚未准备好的情况下就获得了下一个事件,从而导致跟踪手指移动时可能错过一帧,但这时帧率仍然是 60fps (这个 bug 已经修复了)。

@Julius 提到的是关于浏览器的渲染情况。在这方面,安卓和 iOS 的主要差别并非来自硬件加速绘图。早期安卓在渲染网页时做了与 iOS 不同的折衷: 将网页以序列方式连续显示,而非贴片方式这样在滚屏和缩放时不会出现 Safari 那样的占位符,但渲染的帧率不够快。安卓 3.0 后改用了贴片方式,改善了滚屏和缩放的体验。但不论是安卓还是 iOS,贴片都是由 CPU 渲染的。

还有,「安卓后台应用太多吃资源」的说法也有问题。安卓的 UI 线程以默认优先级运行,后台线程以后台优先级运行。切换到后台的应用强制以后台优先级运行。而后台优先级利用了 Linux 的 cgroup 机制,它将所有的后台线程放进一个特别的调度组中, 它们满打满算也无法占用超过 10% 的 CPU 资源

「安卓的触摸事件不像 iOS 那样优先」的说法也是错的。你可以架梯子看看安卓进程的优先级设定
http://developer.android.com/reference/android/os/Process.html#THREAD_PRIORITY_AUDIO

总的来说,根据 Dianne 的说法,多窗口设计对屏幕绘制的开销,是影响安卓的流畅度的已知因素之一。但决定「流畅」的因素还有很多,抓住某个特定技术细节不放的说法都是有失偏颇的。

以上。

— 完 —
本文作者: 知乎用户(登录查看详情)

【知乎日报】 你都看到这啦,快来点我嘛 Σ(▼□▼メ)

此问题还有 41 个回答,查看全部。
延伸阅读:
android手机屏幕适配原理?
Android 手机没有 iPhone 好吗?

Google是如何让照片管理工具“Stories”学会讲故事的?

$
0
0

Stories” 是Google+上一个很棒的照片管理工具,由Google 的社交网络工程师Joseph Smarr 负责。Stories 希望人们把所拍的照片都上传到网上,并自动将其生成为一个 由一系列照片讲述而成的故事

也许Stories 所提供的功能听起来很简单——对人类而言,叙事能力是很自然的一件事。

。而机器不然。想让机器学会叙事的逻辑并不简单,哪怕你以用户在Google 上留下的所有信息为依据。“怎样让机器更懂人类的想法和情感?” 在这个问题上,Smarr 与他的团队花费了大量的时间。

Stories 的早期原型与最终成品差别巨大——Smarr 的团队进行了好一番探索。

最初的产品原型很有些个人年度报告的意思,里面包含了大量的数据,比如,在2012的5月的原型图里(下图)的数据就包括签到和徒步统计、与其他人的互动、音乐喜好,等等等等,数据精挑细选,细致而全面。背后的idea是,打造一款类似于Facebook News Feed 的产品:用算法对你的个人news 进行提炼。

但模型毕竟只是个模型。当真正开始琢磨能利用哪些Google 数据时,Smarr不幸的发现:“人们的历史(数据记录)充满噪音,且不完整。” 于是,他们继续打磨、调整idea,焦点最后总会回到照片上来。

他们开始针对用户进行调查,希望能找到新的思路。

针对用户最近拍摄的10张照片,他们发问:为什么要拍摄这些照片?拍给谁看?调查发现,一共有三种类型:拍照是为了给特定的人看;拍照是为了加强某个记忆,比如喜欢喝的啤酒,曾经路过想要下一次去地点等;第三种则是作为一种探险游历的证明。在调查中他们也发现探险游历类的照片是 其中意图最简单的。并且,前两种类型都相关性强的应用和服务,但第三种需求却没有被很好的满足。

Smarr 团队希望Stories可以解决这一需求。人们出去旅游时总是会拍摄大量的照片回来,但回到家后却不知道该如何处理这些照片。其中一小部分也许会被发布在Instagram上,但大部分就这样永远躺在了用户的电脑或手机里。而Stories就要成为人们一种探险有力的证明,用照片把人们的旅游故事尽量还原出来。

在此基础上,Smarr团队做了更多的测试。他们让更多的人参与到调查中来,让他们把照片打印并在桌子上排列开来。调查发现,大多数人会按照时间顺序从左到右排列照片,此外,很多用户都会挑出一张表示地理位置的照片,放在每次新地点游历的开头。

因此,Stories开始了对地理位置信息的提取。Stories 从每一个用户的”Google数据库“中截取的信息有:照片的定位信息、Google+和Google Maps的数据(通常可以知道用户去过哪里)、通过照片中的景物所推测出来的用户地理位置信息(特别适用于用相机而非手机所拍摄的照片)。Google 将运用它强大的计算能力对这些信息进行分析整合,勾画出一个故事,并用照片流的方式呈现出来。

最后,一起通过 实际的例子来检验检验Stories有没有把故事讲好吧~

除非注明,本站文章均为原创或编译,转载请注明: 文章来自 36氪

36氪官方iOS应用正式上线,支持『一键下载36氪报道的移动App』和『离线阅读』立即下载!




【转】 JBOSS最大连接数配置和jvm内存配置

$
0
0

一.调整JBOSS最大连接数.

配置deploy/jboss-web.deployer/server.xml文件 .
       <Connector
         port="8080"
         address="0.0.0.0"
         maxThreads="1600"
         minSpareThreads="100"
         maxSpareThreads="250"
         emptySessionPath="false"
         enableLookups="false"
         redirectPort="8443"
         acceptCount="800"
         connectionTimeout="20000"
         disableUploadTimeout="true"
         URIEncoding="UTF-8"
         />
maxThreads:表示最多同时处理的连接数。应该将线程数(最大线程数)设置比最大预期负载(同时并发的点击)多25%(经验规则)。
acceptCount:当同时连接的人数达到maxThreads时,还可以接收排队的连接。

minSpareThread:指“启动以后,总是保持该数量的线程空闲等待”;设置比预期负载多25%。

 

maxSpareThread:指“如果超过了minSpareThread,然后总是保持该数量的线程空闲等待”;设置比预期负载多25%。

 

        其中主要修改两个参数maxThreads和acceptCount值。增加maxThreads,减少acceptCount值有利缩短系统的响应时间。但是maxThreads和acceptCount的总和最高值不能超过6000,而且maxThreads过大会增加CPU和内存消耗,故低配置用户可通过降低maxThreads并同时增大acceptCount值来保证系统的稳定。

下表罗列出了在不同并发情况下jboss参数与并发在线的一般关系。

并发数

服务器内存

jboss参数

  maxThreadsacceptCount
50以下2G256800
50-3004G6001024
300-8008G10241528
800-10008G10242048
1000-120012G15262048
1200-150016G20482048
    
    

 

二.调整 jvm参数

A:JVM启动参数共分为三类:
        其一是标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容;
        其二是非标准参数(-X),指的是JVM底层的一些配置参数,这些参数在一般开发中默认即可,不需要任何配置。但是在生产环境中,并不保证所有jvm实现都满足,所以为了提高性能,往往需要调整这些参数,以求系统达到最佳性能。另外这些参数不保证向后兼容,也即是说“如有变更,恕不在后续版本的JDK通知”(这是官网上的原话);
        其三是非Stable参数(-XX),这类参数在jvm中是不稳定的,不适合日常使用的,后续也是可能会在没有通知的情况下就直接取消了,需要慎重使用。
B:而JVM 内存又可分为三个主要的域 :
        新域、旧域以及永久域。JVM生成的所有新对象放在新域中。一旦对象经历了一定数量的垃圾收集循环后,便进入旧域。而在永久域中是用来存储JVM自己的反射对象的,如class和method对象,而且GC(Garbage Collection)不会在主程序运行期对永久域进行清理。其中新域和旧域属于堆,永久域是一个独立域并且不认为是堆的一部分。
C:各主要参数的作用如下 :
        -Xms:设置jvm内存的初始大小
        -Xmx:设置jvm内存的最大值
        -Xmn:设置新域的大小(这个似乎只对 jdk1.4来说是有效的,后来就废弃了)
        -Xss:设置每个线程的堆栈大小(也就是说,在相同物理内存下,减小这个值能生成更多的线程)
        -XX:NewRatio :设置新域与旧域之比,如-XX:NewRatio = 4就表示新域与旧域之比为1:4
        -XX:NewSize:设置新域的初始值
        -XX:MaxNewSize :设置新域的最大值
        -XX:PermSize:设置永久域的初始值
        -XX:MaxPermSize:设置永久域的最大值
        -XX:SurvivorRatio=n:设置新域中Eden区与两个Survivor区的比值。(Eden区主要是用来存放新生的对象,而两个 Survivor区则用来存放每次垃圾回收后存活下来的对象)
D:常见的错误 :
        java.lang.OutOfMemoryError相信很多开发人员都用到过,这个主要就是JVM参数没有配好引起的,但是这种错误又分两种:java.lang.OutOfMemoryError: Java heap space和java.lang.OutOfMemoryError: PermGen space,其中前者是有关堆内存的内存溢出,可以同过配置-Xms和-Xmx参数来设置,而后者是有关永久域的内存溢出,可以通过配置 -XX:MaxPermSize来设置。

下面是个例子,请根据实际情况进行修改,修改run.conf文件中的如下内容
 JAVA_OPTS="-Xms256m -Xmx2048m -XX:NewSize=256m -XX:MaxNewSize=512m -XX:PermSize=128m -XX:MaxPermSize=256m -XX:+UseConcMarkSweepGC -XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled -Djboss.platform.mbeanserver"


日志文件设置:

jboss\server\default\log


        若需要修改JBoss默认的log4j设置,可修改JBoss安装目录"server\default\conf下的jboss-log4j.xml文件,在该文件中可以看到,log4j的日志输出在JBoss安装目录"server\

 

如果使用log4j日志框架,将配置文件放在工程目录下的话容易引起与jboss日志配置的冲突,一个比较好的方式就是将日志配置到jboss的 jboss-log4j.xml文件中,文件的目录是($JBOSS)/server/default/conf。在配置文件中添加以下内容:

<appender name="myLog" class="org.apache.log4j.DailyRollingFileAppender">
    <!--设置通道名称是:file,输出方式DailyRollingFileAppender-->
    <param name="File" value="${jboss.server.home.dir}/log/Mqs.log"/>
    <!--日志文件路径和文件名称 -->
  
    <param name="Append" value="true"/>
    <!-- 设置是否在重新启动服务时,在原有日志的基础添加新日志 -->

    <!-- Rollover at midnight each day -->
    <param name="DatePattern" value="'.'yyyy-MM-dd"/>

  
    <!-- Rollover at the top of each hour
    <param name="DatePattern" value="'.'yyyy-MM-dd-HH"/>
    -->
    <layout class="org.apache.log4j.PatternLayout">
      <!-- The default pattern: Date Priority [Category] Message/n -->
      <param name="ConversionPattern" value="%d %-5p [%c] %m%n"/>
      <!-- The full pattern: Date MS Priority [Category] (Thread:NDC) Message/n
      <param name="ConversionPattern" value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
       -->
    </layout>    
  </appender>

  <logger name="com.all" additivity="false" >
    <level value="INFO" />
    <appender-ref ref="myLog"/>
  </logger>

在web工程中使用日志的时候就可以这样写

Logger log = Logger.getLogger("com.all");

log.debug("test");

其中"com.all"与<logger name="com.all" additivity="false" > 中的name属性值对应。additivity属性非常重要,它表示是否继承root配置的输出通道,默认配置是true,如果不将其设为false的话会导致日志重复输出到控制台与jboss的日志文件中。

default

 

 

jboss\server\default\log



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


ITeye推荐



养狗的好处

$
0
0
苏格兰科学家研究表明养狗好处多多。狗的陪伴不但有益人类身心健康,还可以回拨“年龄时钟”, 能使人类身体活动水平相当于年轻10岁的状态。


科学探索

 
英国科学家:养狗可使主人年轻10岁
 

  狗狗一直是人类最好的朋友。据英国《每日邮报》7月15日报道,苏格兰科学家研究表明养狗好处多多。狗的陪伴不但有益人类身心健康,还可以回拨“年龄时钟”, 能使人类身体活动水平相当于年轻10岁的状态。

  从事这项研究的是冯志强(音译)博士,是圣安德鲁斯大学地理和地质学院的一位高级讲师。他邀请了苏格兰泰赛德区的547位老人参与这项研究,其平均年龄为79岁,住址之间距离不超过60英里。在7天的研究期间,参与者要佩戴计步器,以记录他们的行动。最终研究结果被发表在期刊《预防医学(Preventive Medicine)》上。研究结果表明,参与者中约50人明显显现出更少的焦虑感和沮丧感。除去残疾者和患有重病的老人,养狗会激励人类活动,尤其是65岁以上的老人,使老人克服潜在障碍出门活动,如缺乏社会支持、恶劣天气及顾及人身安全。一般来说, 养狗的老人会比不养狗的活跃度高12%。

  除此之外,冯博士还说道:“养狗或许不会使你得寿命增加十年,但起床出去走走是非常有益的。”在步入老年期间坚持活动能避免众多疾病,尤其是对肌肉和骨骼十分有益,同时还能促进心理健康,减少焦虑感。对此,他还提议建立“狗狗分享”机制,帮助老人享受养狗的益处,而不用去承担养狗的责任。他还坦言,因为妻子不喜欢所以自己并没有养狗,但老了之后他会想养一只。

来源: 环球网


《精益开发实战》笔记

$
0
0

作者: 克里伯格 
出版社: 人民邮电出版社
副标题: 用看板管理大型项目
译者: 李祥青 
出版年: 2012-9
页数: 168
定价: 39.00元
装帧: 平装
ISBN: 9787115291776

豆瓣链接: http://book.douban.com/subject/11620809/

 

第一部分  我们如何工作

第1章  项目背景

1.时间线

每个发布版本都是了解真相的最佳时刻;发布的频率高,补丁小,每次发版的问题和风险也就少。

2.我们如何切割大象

要找到一个合适的方式,来切割大型项目。最理想的就是每次增幅能够独立地为用户带来价值,为团队积累知识。

3.我们如何让客户参与进来

根据客户列出的功能清单,制订项目地概要时间表和版本发布计划;如果可能组织 现场演示反馈定期的验收测试。并且开启 试点用户范围的反馈调查。

第2章  组织团队

软件开发项目的一大挑战在于如何将项目人员划分为多个大小适中的团队,并让这些团队相互之间紧密合作。

团队在一起办公很重要,开发团队转为scrum架构,让每个小团队具有多职能特征,分析、开发、测试人员坐在一起,提高团队的协作水平。

第3章  每天出席鸡尾酒会

每日立会 9:30-10:15。原本需要通过创建文档和流程规则才能解决的很多问题,在这些立会中得到解决。各个团队针对问题进行讨论现场决策,而不用专门为这类问题制定新的策略规划。保持敏捷、不为官僚程序所拖累。

1.第一拨:功能开发团队每日立会

每个功能开发团队可以遵循Scrum模式,回答:

       “我昨天做了什么?”  “我今天打算做什么?”  “遇到什么麻烦?”

当然这一切都要在10-15分钟结束,由团队的主管(等同于Scrum大师)主持。

2.第二拨:不同专业角色的同步立会

测试同步立会:讨论如何最好地利用当天的时间,而隶属于各自所属功能开发团队的测试人员,可以分享各个团队的最新进展。

需求同步会议:跟功能开发团队的分析人员也来参加,给整个需求分析团队汇报最新的信息。

开发同步会议:由各个功能开发团队的主管和开发经理组成,分享各自团队的最新信息。

3.第三拨:项目同步立会

每个专业一个代表,每个功能开发团队一个代表,再加上项目经理、配置经理等少数几个人。

主要关注从需求分析到投入生产的各个工序是否正常运转:

    “每个团队今天在做什么?”  “现在有什么问题阻塞了我们的流程?”  “瓶颈在哪里?”

    “如何消除瓶颈”  “下一个瓶颈可能会出现在哪里?”  “我们的发布计划是否顺利”  “有没有人不知道今天要做什么工作?”

第4章  项目进度板

任务看版工序流向:

    创意-> 功能-> 下十个功能 -> 开发 -> 系统测试 -> 用户验收测试 -> 上线

并非传统的瀑布流模式,在看板系统中,不同阶段都是并行的。

1.我们的节奏

每两周回顾总结,每两周规划会议,持续进行的演示与系统测试,每两个月的版本发布

2.如何处理紧急问题和障碍

不能把任务板塞满,那么整个系统将会瘫痪。设置优先级,着重解决路障和瓶颈问题,否则跳过。

第5章  扩展任务看板

项目开发速度很大程度上取决于团队成员对项目当前状态的熟知程度,如果人人都清楚项目的当前进展和未来走向,就比较容易同心协力向目标前进。

创建项目进度板,用来显示项目所有功能从需求、开发、测试到核准上线的最新状态,让我们对项目整体情况一目了然。

第6章  跟踪总体目标

如果人人都清楚总体目标是什么,就会更关注总体目标。

每周或每两周做一次实现检查,一般在项目同步立会上进行。 “你认为我们能实现这个目标吗?(1-5分)”

根据大家都能看到的数据,订制简单现实的版本发布计划,不试图隐藏任何不确定性。

否则,不了解目标或是对实现目标没有信心,会将自己和业务目标脱离,“只管开心写代码”或“做完份内事就回家”。

第7张  定义“可供”与“完成”

明确进度看板各栏的含义。

1.可供开发

已细分好任务、估算完工作量、澄清了范围的功能,不过尚未决定开发哪些和按照什么顺序开发,类似Scrum产品需求清单。

一个功能要进入可供开发状态,需要具体以下特点:

必须有一个ID;必须有一个联系人(需求分析师,拥有功能相关领域知识);必须对客户用价值;必须经由团队估算;必须有验收测试情景。

2.可供系统测试

是指功能开发团队已完成他们能够想到的所有工作来保证某项功能正常,且没有重大缺陷。

开发团队需要花费一定时间确保功能质量:

验收测试自动化;通过回归测试;已给其他人员演示过;确保相关代码的可追溯性;在开发环境中测试过;已修改冲突,和主干代码合并。

3.两个定义如何提升团队协作

发现问题正是解决问题的第一步和关键一步。

只有不同角色的人员一起合作,对功能进行估算,然后细分成很小却又不会失去客户价值的可交付单位,并就验收测试达成一致意见,可供开发的状态才算真正实现。

第8章  处理技术故事

技术故事是开发者需要完成但客户并不感兴趣的事情,像升级数据库、清理不用的代码、重构糟糕的设计或对原有功能实现自动化测试等。

1.示例1:系统测试瓶颈

当系统测试成为瓶颈的时候,就没有必要再开发新功能增加负荷了。测试经历提出测试需求清单并排序,交给开发人员。

2.示例2:版本发布前一天

如果没有bug需要修复,开发人员就会处理技术故事。

3.示例3:7米长的类

必须马上启动技术故事重构这种类。这个例子表明,一直以来对设计关注不足而仓促行事,后果会有多么严重。

 

第9章  处理bug

1.持续系统测试

持续做系统测试,而不是全部堆到最后一刻。因为修复 bug是真正花费时间的大头。

2.立马修复bug

现在测试人员发现一个bug后,不是直接记录到bug追踪系统中,而是开好task,然后去找对应代码的开发人员。

这样做的特点在于,没有移交、没有延迟、不需要通过bug追踪系统来沟通,更有效率。原因如下:

早发现bug并修复比晚发现和修复有效率;面对面沟通更有效率;开发人员和测试人员能够相互了解对方的工作内容;不需要浪费很多时间去管理冗长的老bug列表

3.为何要限定bug跟踪系统中的bug数量

一般限定30个,这样可以让我们始终专注于最重要的bug,而不会成为管理负担。

4.Bug可视化

再确定前5个bug,登上项目进度看板。于是开发人员就有3个需要关注的输入队列: “下10个功能”、“下5个技术故事”和“下5个bug” 

5.预防bug重现

从某个具体模块中移除一些不必要的代码;

设置例行程序,确保有更多的时间进行重构;

多进行面对面的沟通,少依赖文档沟通;

开发期间与测试人员更加紧密地合作,做得更专业。

第10章  持续改进流程

战略相当简单:即假设人们天生希望项目成功,天生希望解决问题并改进工作方式。所以,我们需要做的就是创建一个能够促进和鼓励这种行为的环境。

如果人人都清楚我们的目标是什么、我们当前所处的位置,而且有正确的沟通平台,那么大家就会自我组织,朝着正确的方向前进,并持续摸索出尽快实现目标的方法。

1.团队回顾

开发团队,一般每一两周开一次,时长30分钟到2个小时不等,目标是:反省哪里做的好,哪里做的不好,以及需要做出什么样的改变。

改变通常包括:更频繁的检入代码;改变每日立会的时间和方式;更新代码编写规范;确定一个新的团队内部角色。

2.流程改进研讨会

交叉团队,目标是澄清并改进我们的工作方式。流程如下:

做好准备:会议开场,设定主题与关注焦点;

收集数据:回顾上次会议之后发生过什么,取得的成果和面临的难点;

产生见解:讨论数据及其意义,专注于最重要的难点问题,确定解决的具体方案;

作出决策:决定要实施哪些改变;

结束会议:决定谁来做什么,以及下次会议之前需要完成什么。

3.掌控改变速率

变化太多太快,会使团队成员迷茫和沮丧。

在犹豫不决时,就选择最简单的方案。这些完全取决于自己。

第11章  管理在制品

当信在邮车中运输时,它处于 工作状态;当信躺在你家的邮筒时,它处于 等待状态

项目看板上会有很多的类似等待状态的缓冲区,在需求和开发中间,有三个缓冲区:

a.已通过分析得到确定但还没有选入“下十个功能”

b.已拉入“下十个功能”但还没有被具体开发团队接手

c.已经在具体开发团队手中但开发工作还没开始(可以简化)

1.采用在制品限额

在制品限额旨在避免同时做太多工作,避免让下游流程负载过重。

2.为什么在制品限额只适用于功能卡

技术故事和bug修复不包括在在制品限额内。

技术故事有助于消除下游瓶颈,能够提高质量,让测试人员更轻松。

在制品限额的精髓在于:努力完成一件事,而不是努力开始做另一件事。

第12章  捕捉并使用流程度量

流量度量可以找出哪里需要改进,变更是否带来了积极的效果。主要有两方面:速率(每周功能数)、周期时间(每个功能的开发时间)。

1.速率(每周功能数)

每周结束计算有多少功能卡可以进入“可供验收测试(本周)”栏。生成燃尽图,显示每周所完成功能的累计数量。燃尽图可以凸显问题,直观显示流量的改进。

2.为何不使用故事点

只计算功能数量,而不计算功能大小,这样过分简单化的速率有没有误导?从实践上来说,功能大小的分布其实相当均匀。

如此,提高精度并未增加多少价值,所以估算故事点就是在浪费时间。

3.周期时间(每个功能所需时间)

周期时间是指完成一项工作所需的时间,在项目中更具体的是指,x功能卡从“下10个功能”移到“可供验收测试”栏需要的时间。因为这部分流程是我们可以控制的。

怎样缩短周期时间,最廉价的方式就是控制在制品限额,找到一个平衡点,保证所有人员相互协作,问题能够得到暴漏,但不是一下子全部暴漏。

4.累计流量

累计流量图实际上并不能反映多少问题,会显得脆弱不堪,杂乱无章,但聊胜于无。

5.流程周期效率

流量时间效率(%)=接触时间(实际开发功能的时间)/经过时间(周期时间)

大多数公司都在10%-15%的范围内,除非专门为此优化过工作流。

第13章  Sprint与版本发布规划

Sprint规划会议的目的在于弄清楚下一步做什么。会议内容分为两部分:梳理需求清单与挑选前十项功能。

1.需求清单梳理

挑选可以转入“可供开发”状态的功能卡。

2.挑选前十个功能

商业价值:客户最乐于看到的功能有哪些;

知识:哪些功能会产生知识(因此会降低风险)?

资源利用:我们需要平衡功能领域,这样才能让所有团队都有事做;

依赖关系:哪些功能最好组合在一起开发;

可测试性:哪些功能放在一起测试最合理,因而需要同期开发。

3.为何将需求清单梳理工作移出Sprint规划会议

先单独进行需求清单的梳理,然后再开Sprint规划会议。因为梳理工作非常耗时,应该重点讨论主要问题。

4.规划版本发布

对于长期规划而言,我们并不一定知道功能总数是多少。我们有的知识一对模糊的创意。我们将其称为综述或功能领域。

单独讨论每个总数并估算应将其划分为多少个功能,这种估算需要需求分析师、开发人员、测试人员都投入时间和精力。估算类似于估算故事点。

这样我们就可以基于真实数据做一个大致的估算,对多久完成一个综述给出一个比较精确的日期。

第14章  我们如何做版本控制

实施主线模型,稳定主干模式。

1.主干无垃圾

对于大规模的项目,主干始终稳定至关重要。稳定就意味着可以进行系统测试。

一个功能的代码部分完成后,将代码检入主干并把功能卡拉入“可供系统测试”栏之前,我们会先从功能层次对其进行彻底测试。只有通过所有功能层次的测试,才会将代码检入主干并移动功能卡。

从主干的角度而言,产品以谨慎的步骤逐渐递增的同时,主干始终保持稳定,且随时可供系统测试。

2.团队分支

每个团队都有自己的分支,政策较主干宽松,不过代码必须编译,单测必须跑通,功能不一定非要完成和测过。

主干、团队分支保持同步的方式类似于git的检入检出方式。(相当于develop分支)

3.系统测试分支

系统测试是持续进行的,需要一个稳定的版本(相当于stage分支)。在主干的基础上创建系统测试分支。发现bug后,开发人员直接在该分支上修复,然后合并到主干上。

系统测试往往是好几轮的。

版本控制系统是多团队开发项目的真正核心。要搞清楚从改动一行代码到上线使用需要花多长时间,这是项目中最最重要的度量指标。

第15章  为何我们只用真实看板

原因在于看板可以演变(变更往往多变)、需要协作(否则立会难以进行)。

第16章  经验教训

通过案例来讲的真正主题是组织变革。

1.了解目标

就“完善”的定义达成共识,完善的流程、组织和工作环境应当是怎样的。这些将是组织变革的指南针。完善是方向,不是终点。

2.不断实验

寻找微小的渐进的改进机会,并将其视作实验机会,在积累中总结经验教训,然后设计新的实验。伟大的流程不是设计出来的,是逐渐演变而来的。

3.拥抱失败

唯一的真正的失败时未能从失败中吸取经验教训。

4.解决真正的问题

不断问自己:“我在解决什么问题?这个问题是真实存在的还是假设的?还有没有其他我应该先解决的更重要的问题?”

5.拥有专职变革推动者

至少拥有一名专职的变革推动者,完全专注于推动、领导和协调变革过程。

6.让人们参与进来

不要自己提出变革提议,将你看到的问题可视化呈现出来,让相关的人员参与进来,提出他们自己的解决方案。这样的变革更容易被接受。

第二部分  技术详解

第17章  敏捷与精益概述

广义而言,精益与敏捷是两组具有高度兼容性得价值观和原则,都阐述了如何成功地进行产品开发。Scrum、XP和看板是将这些原则运用到实践中的具体方法,相互重叠却别具风格。

1.敏捷概述

《敏捷宣言》: 个体和互动胜过流程和工具; 可以工作的软件胜过详尽的文档; 客户合作胜过合同谈判; 响应变化胜过遵循计划。

2.精益概述

精益原则:

1.全局优化(专注于整体价值流、交付完整产品、着眼长期);

2.消灭浪费(构建错误的功能、拒绝学习、辗转现象);

3.品质为先(最终验证不应发现缺陷、采用测试先行的开发模式让流程具有防误机制、打破依赖);

4.不断学习(可预测的性能来自于反馈、保持选择方案、最后可靠时刻);

5.尽快交付(快速交付、高质量和低成本是完全相互兼容的,排队理论适用于软件开发,管理工作流比管理进度表要容易得多);

6.人人参与(自主性、成长性、使命感);

7.不断提高(失败是个学习机会、标准存在的目的就是要被质疑和提高的、使用科学方法)。

3.Scrum概述

基本上前16章就是在讲Scrum。核心概念如下:

1.按优先顺序排列的产品需求清单;2.跨职能团队;3.Sprint;4.持续调整版本发布计划;5.持续调整流程。

4.XP概述

极限编程(XP)除多数理念和Scrum相同外,还增加了团队内部的工程实践,包括:持续继承、结对编程、测试驱动开发、集体代码所有权、增量式设计改进。

5.看板概述

看板是敏捷软件开发的精益方法,规则很简单:可视化工作流、限定在制品、衡量并管理周期时间。

Scrum专注于结构和沟通,XP增加了工程实践,看板则专注于将工作流可视化,并对瓶颈进行管理。

6.看板管理的典型一天

(略)

第18章  缩减测试自动化需求清单

测试未实现自动化,会产生修复时高昂的代价。

1.怎么办

每个迭代周期逐渐提高测试覆盖率。

2.如何每个迭代周期都提高测试覆盖率

a.列出测试用例

b.按风险大小、手动测试的成本、自动化测试的成本将测试分类

c.按优先顺序对列表进行排序

d.从优先级最高的开始,每个迭代周期都对若干测试实施自动化

3.第1步:列出测试用例

用头脑风暴的方式列出最重要的测试用例。

4.第2步:测试分类

丢弃一半的测试,保留哪些测试,分别按照风险大小、自动化测试成本和手动测试成本分类。

5.第3步:按优先顺序对列表进行排序

这个决定依据具体情况来决定。

6.第4步:每个迭代周期自动化若干测试

在做产品需求清单的时候,留20%的时间在测试自动化需求清单上。循序渐进。

7.这能解决问题吗

不会在短期解决,不过会让问题更容易解决。

第19章  用规划扑克估算需求清单大小

规划扑克能让团队以较快的速度完成规划工作(估算开发一项功能需要的工作量),同时也让规划工作更准确、更好玩。

1.不用规划扑克进行估算

在Sprint规划会议上,第一个说出项目功能有多大的人会严重影响团队其余成员的判断。

2.用规划扑克进行估算

能够盘活每一个团队成员,对不同意见进行讨论分析,最终达成一致。(五种牌:小、中、大、?、咖啡)

3.特殊牌

?:完全没概念,功能可能很大,可能很小。如果这个牌出现的频繁,这个时候团队就应该再好好讨论该功能,让团队成员多了解一些信息。

咖啡:“太累了。脑子卡住了。我们休息一下吧。”

如果出现较大的分歧,不可以简单的平均估中。比如认为小是因为某项功能可以重用,认为大时因为存在一些大家都没有考虑到的风险。

规划扑克最大的价值就在于玩牌时引发的对话与沟通,而估算结果本身则只是顺带来的好处。

第20章  因果图

因果图是以图解的方式进行因果分析的一种简易、实用的方法。

1.解决问题,而不是解决症状

症状一般出现在某处,而根本原因则在另一处。

2.精益问题解决方法:A3思维

精益思维的核心宗旨之一是改善——持续改善流程。

在提出解决方案之前,需要花费大量时间对问题的根本原因进行分析并以图示的形式显示。

因果图的优点在于:直观无需解释、显示增强回路。

3.如何使用因果图

选定问题,困扰你的任何问题,并写下来;

“向上”追踪,弄清业务后果,即你的问题所造成的“可见损坏”;

“向下”追踪,找到根本原因;

确定并标出恶性循环(循环路径);

将这些步骤重复几次,调整并理清因果图;

决定要解决哪些根本原因,以及如何解决(即要实施哪些对策)。

4.示例1:发布周期长

不断提问为什么,发现恶性循环,找出根本原因。否则会草率得出结论,并实施无效的甚至是适得其反的变更。

5.示例2:上线版本有缺陷

根本问题通常会导致多个问题。

6.示例3:缺乏结对编程

缺乏信任和所有工作时间都必须有所产出是没有去实践XP的根本原因。

7.示例4:很多问题

很多根本问题,可以通过实践Scrum得到解决。或者将Scrum与看板结合。

8.实际问题:如何创建并维护因果图

实体白板和便利贴。

9.陷阱

太多的剪头和图框(移除多余的图框、专注于深度优先而不是广度优先、接受不完美、将自己限定在范围明确的问题上、将因果图划分为多个部分);

过于简化;

参杂个人情绪(不做个人对象攻击)。

10.为何采用因果图

建立共识、发现问题对业务的影响、找到根本原因、消除恶心循环。

因果图虽然有用,关键却是解决问题的方法本身:提问的角度、由此展开的讨论以及调查发现。甚至无需画图,只要谈话过程中脑中想像就足够了。

第21章  结语

只有经过实践证明的知识才站得住脚。

 

附录  术语表:如何避免高深术语

英语术语 字面意思 实际含义
process improvement meeting流程改进会议sprint回顾总结
deliverable可交付物功能
customer deliverable客户可交付物用户故事
feature area功能区域综述、主题
team lead团队主管scrum大师
project board项目看板项目级别的看板
team board团队看板团队级别得看板


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


ITeye推荐



浏览器设置阻止第三方Cookie保护自己隐私

$
0
0

  当我们在某些大型网站上搜索一些东西时,在另外的一些网站出现了你搜索的东西的相关广告,这种现象的出现说明我们的隐私已经被跟踪。这种侵犯我们用户隐私现象的出现,要归功于第三方cookie所带来的副作用(隐私泄露风险)。

  为了防止用户隐私被跟踪,保护用户网络隐私,一些主流浏览器版本都已经设置了Do not Track不要跟踪这一选项。当用户提出启用“请勿追踪”功能后,具有“请勿追踪”功能的浏览器会在http数据传输中添加一个“头信息”(headers),这个头信息向商业网站的服务器表明用户不希望被追踪。这样,遵守该规则的网站就不会追踪用户的个人信息来用于更精准的在线广告。下面列举一些主流浏览器设置这项功能的过程。

  1.Chrome浏览器设置DNT (打开Chrome浏览器,地址栏输入chrome://settings) 见图片

COOKIE

  2.IE9浏览器做的不错,默认是开启DNT的(见图片)

COOKIE

  3.360浏览器设置 (工具—选项—-高级)见图片

COOKIE

  然后点击“网页内容高级设置” 如下图:在 阻止第三方Cookie 选项处  打钩

COOKIE

  不过对于那些不遵守该DNT规则的网站 ,他们还是可以跟踪你的。

评论《浏览器设置阻止第三方Cookie保护自己隐私》的内容...

相关文章:


微博: 新浪微博 - 微信公众号:williamlonginfo
月光博客投稿信箱:williamlong.info(at)gmail.com
Created by William Long www.williamlong.info

4个代码生成库的性能比较

$
0
0

在本系列的 第一篇文章中,我们介绍了Java的强类型及动态类型系统 。结论就是这个类型系统让你可以写出表述性强,健壮的应用程序,但是它限制了框架API与用户类型协作的能力。我们还知道了为什么Java的反射API并不总是与用户类型交互的最佳方式。为了将这点解释清楚,我们还分析了一个简单的安全库的实现,它使用了反射API,但却破坏了类型安全,为了保留用户类型,我们使用了代码生成的方式。

在文章的第二部分,我们分析了不同的代码生成库,并重点介绍了一下我自己开发的一个库, Byte Buddy。然后我们基于这个库来实现了一个简单的安全框架。

本文是最后一篇,我们想比较一下不同的库实现之间的性能差别。如果你还没读过前面两部分,最好先看一遍再继续阅读本文。我保证,我会等你看完再继续往下讲(注:这是哄小孩子吗:-))

初识代码生成器

总的来说,好的API并不是一个优秀的代码生成库的唯一条件。代码库的运行时性能可能是一个更重要的因素,尤其是当生成的代码在运行的程序中处于一个比较关键的位置的时候。关于代码生成库的性能,坊间有着诸多传闻,不过我还没找到关于任何一项技术的靠谱的基准测试。

在Java中进行微基准测试并不是一件容易的事情 。如果你要测量一个指定的代码块的执行时间,你通常不知道你测量的到底是什么。Java代码在执行的时候,JIT编译器通常都会介入,最极端的情况下,它可能会擦除掉被测量的代码。

然而在过去的几年里,有几个聪明的家伙想出了一些办法来欺骗JIT编译器,并基于这些想法实现了一些微基准测试的库。我个人最喜欢的是 Java Microbenchmarking Harness,它是随着Open JDK发布的一款工具。

在进行数据测量之前,有必要先回答一个问题:基准测试的目标和关注点是什么?很明显,有些任务使用某个库处理起来可能会更高效些,而另一些任务则可能花的时间就要更长一点。

除此之外,代码生成库通常会牺牲创建类的时间来减少生成类的方法调用的时间。当我们在讨论下面这些数据的时候,应当时刻牢记这点。

看下这些数据吧

在记住我们前面说的东西的同时,我们先来看一个直接比较不同任务的运行时间的JMH基准测试的原始数据。下表中的数据是指每个操作所需要的纳秒数,空格中是采样的标准误差。

Byte BuddycglibjavassistJDK proxy
使用stub方法实现接口153.800(0.394)804.000(1.899)706.878(4.929)973.650(1.624)
调用子方法0.001(0.000)0.002(0.000)0.009(0.000)0.005(0.000)
继承类调用父类方法172.126(0.533)1480.525(2.911)625.778(1.954)-
2290.246(7.034)
调用父类方法0.002(0.000)0.019(0.000)0.027(0.000)-
0.003(0.000)

第一行显示的是库生成这18个不同接口的空实现所需的时间。在这些生成的运行时类的基础之上,第二行显示的是调用这个生成类实例中的方法所需要的时间。

在这次测试中,Byte Buddy以及cglib的性能最好,因为这两个库你都能将返回值硬编码到生成类中,而javassist和JDK代理都只允许注册一个相应的回调函数。

这样我们可以得出第一个粗略的结论,这就是运行时类的方法实现越具体的话性能越好。听起来显然应该是这样,但其实不然,因为JIT编译器可能会优化这两种方法的性能。

类继承的情况如何

上表中的第三行显示的是继承一个包含18个方法的类所需要的时间。这次并不是创建一个方法存根,而是重写了方法,并调用了它父类的实现。

你可能已经注意到了,Byte Buddy列出了两个测量值,而第二个斜体的数字明显要更大。两个数值代表的是实现父方法调用的两种不同的实现方式。

正如上周所提到的,JVM只允许在同一个实例内进行父方法的调用。因此,调用super方法的最简单的方式就是在拦截方法里进行父方法的调用,这个拦截方法在第一次测试的时候已经实现好了。

但这个方法的灵活性不够,比方说它并不能根据条件来进行调用。为了克服这一限制,Byte Buddy允许你创建一个类似内部类的东西。在本文的前一部分中我们就介绍过了这种方法,在那篇文章中我们生成了一个实现了Callable接口的代理类。

对于任何调用而言,内部类的实例是通过方法中的一个参数所对应的注解注入到拦截方法里的。正如你所看到的,这种创建了一个额外的类的方式,跟其它使用相同策略的库相比,调用super方法所消耗的时间大大减少了。

与此同时,为每个方法生成一个专门的类会带来生成子类的额外开销。cglib和javassist都选择了一种折中的方案来解决这一问题,它们省掉了创建额外类的开销,代价就是每次父方法调用都会增加额外的开销。

结束语:都是为了提升性能

这里有许多值得讨论的东西,不过与此同时,这也是个结束这次代码生成简介的重要时刻。我希望这次概述能帮助你认识到代码生成其实并没有什么神秘的,这并不是只有大型框架才能使用的。有一个顺手的库的话,即使是很小的项目,你也可以使用代码生成来完成切面关注的漂亮的API,而不用增加显式的依赖关系。

现在Java 8已经开始逐渐流行起来,它的新的元空间不再严格限制Java应用包含的类的数量了。有了这些之后,就没有什么能再束缚住你的手脚了,放手去干吧。

原创文章转载请注明出处: 4个代码生成库的性能比较

英文原文链接

使用Memory Analyzer tool(MAT)分析内存泄漏

$
0
0

前言的前言:本文是自2005年8月以来,首次在一个月之内发布三篇文章。谨以此文献给这么多年始终不济的我。所谓少不入川,而今已非年少。北漂快两年了,何时能回到故乡,回去后又会怎样,也许永远是个未知……

 

前言

 

在平时工作过程中,有时会遇到OutOfMemoryError,我们知道遇到Error一般表明程序存在着严重问题,可能是灾难性的。所以找出是什么原因造成OutOfMemoryError非常重要。现在向大家引荐Eclipse Memory Analyzer tool(MAT),来化解我们遇到的难题。如未说明,本文均使用Java 5.0 on Windows XP SP3环境。

 

为什么用MAT

 

之前的观点,我认为使用实时profiling/monitoring之类的工具,用一种非常实时的方式来分析哪里存在内存泄漏是很正确的。年初使用了某profiler工具测试消息中间件中存在的内存泄漏,发现在吞吐量很高的时候profiler工具自己也无法响应,这让人很头痛。后来了解到这样的工具本身就要消耗性能,且在某些条件下还发现不了泄漏。所以,分析离线数据就非常重要了,MAT正是这样一款工具。

 

为何会内存溢出

 

我们知道JVM根据generation(代)来进行GC,根据下图所示,一共被分为young generation(年轻代)、tenured generation(老年代)、permanent generation(永久代, perm gen),perm gen(或称Non-Heap 非堆)是个异类,稍后会讲到。注意,heap空间不包括perm gen。


绝大多数的对象都在young generation被分配,也在young generation被收回,当young generation的空间被填满,GC会进行minor collection(次回收),这次回收不涉及到heap中的其他generation,minor collection根据weak generational hypothesis(弱年代假设)来假设young generation中大量的对象都是垃圾需要回收,minor collection的过程会非常快。young generation中未被回收的对象被转移到tenured generation,然而tenured generation也会被填满,最终触发major collection(主回收),这次回收针对整个heap,由于涉及到大量对象,所以比minor collection慢得多。

 

JVM有三种垃圾回收器,分别是throughput collector,用来做并行young generation回收,由参数-XX:+UseParallelGC启动;concurrent low pause collector,用来做tenured generation并发回收,由参数-XX:+UseConcMarkSweepGC启动;incremental low pause collector,可以认为是默认的垃圾回收器。不建议直接使用某种垃圾回收器,最好让JVM自己决断,除非自己有足够的把握。

 

Heap中各generation空间是如何划分的?通过JVM的-Xmx=n参数可指定最大heap空间,而 -Xms=n则是指定最小heap空间。在JVM初始化的时候,如果最小heap空间小于最大heap空间的话,如上图所示JVM会把未用到的空间标注为Virtual。除了这两个参数还有-XX:MinHeapFreeRatio=n和 -XX:MaxHeapFreeRatio=n来分别控制最大、最小的剩余空间与活动对象之比例。在32位Solaris SPARC操作系统下,默认值如下,在32位windows xp下,默认值也差不多。

 

参数

默认值

MinHeapFreeRatio

40

MaxHeapFreeRatio

70

-Xms

3670k

-Xmx

64m

 

由于tenured generation的major collection较慢,所以tenured generation空间小于young generation的话,会造成频繁的major collection,影响效率。Server JVM默认的young generation和tenured generation空间比例为1:2,也就是说young generation的eden和survivor空间之和是整个heap(当然不包括perm gen)的三分之一,该比例可以通过-XX:NewRatio=n参数来控制,而Client JVM默认的-XX:NewRatio是8。至于调整young generation空间大小的NewSize=n和MaxNewSize=n参数就不讲了,请参考后面的资料。

 

young generation中幸存的对象被转移到tenured generation,但不幸的是concurrent collector线程在这里进行major collection,而在回收任务结束前空间被耗尽了,这时将会发生Full Collections(Full GC),整个应用程序都会停止下来直到回收完成。Full GC是高负载生产环境的噩梦……

 

现在来说说异类perm gen,它是JVM用来存储无法在Java语言级描述的对象,这些对象分别是类和方法数据(与class loader有关)以及interned strings(字符串驻留)。一般32位OS下perm gen默认64m,可通过参数-XX:MaxPermSize=n指定, JVM Memory Structure一文说,对于这块区域,没有更详细的文献了,神秘。

 

回到问题“为何会内存溢出?”。

要回答这个问题又要引出另外一个话题,既什么样的对象GC才会回收?当然是GC发现通过任何reference chain(引用链)无法访问某个对象的时候,该对象即被回收。名词GC Roots正是分析这一过程的起点,例如JVM自己确保了对象的可到达性(那么JVM就是GC Roots),所以GC Roots就是这样在内存中保持对象可到达性的,一旦不可到达,即被回收。通常GC Roots是一个在current thread(当前线程)的call stack(调用栈)上的对象(例如方法参数和局部变量),或者是线程自身或者是system class loader(系统类加载器)加载的类以及native code(本地代码)保留的活动对象。所以GC Roots是分析对象为何还存活于内存中的利器。知道了什么样的对象GC才会回收后,再来学习下对象引用都包含哪些吧。

 

从最强到最弱,不同的引用(可到达性)级别反映了对象的生命周期。

l  Strong Ref(强引用):通常我们编写的代码都是Strong Ref,于此对应的是强可达性,只有去掉强可达,对象才被回收。

l  Soft Ref(软引用):对应软可达性,只要有足够的内存,就一直保持对象,直到发现内存吃紧且没有Strong Ref时才回收对象。一般可用来实现缓存,通过java.lang.ref.SoftReference类实现。

l  Weak Ref(弱引用):比Soft Ref更弱,当发现不存在Strong Ref时,立刻回收对象而不必等到内存吃紧的时候。通过java.lang.ref.WeakReference和java.util.WeakHashMap类实现。

l  Phantom Ref(虚引用):根本不会在内存中保持任何对象,你只能使用Phantom Ref本身。一般用于在进入finalize()方法后进行特殊的清理过程,通过 java.lang.ref.PhantomReference实现。

 

有了上面的种种我相信很容易就能把heap和perm gen撑破了吧,是的利用Strong Ref,存储大量数据,直到heap撑破;利用interned strings(或者class loader加载大量的类)把perm gen撑破。

 

关于shallow sizeretained size

 

Shallow size就是对象本身占用内存的大小,不包含对其他对象的引用,也就是对象头加成员变量(不是成员变量的值)的总和。在32位系统上,对象头占用8字节,int占用4字节,不管成员变量(对象或数组)是否引用了其他对象(实例)或者赋值为null它始终占用4字节。故此,对于String对象实例来说,它有三个int成员(3*4=12字节)、一个char[]成员(1*4=4字节)以及一个对象头(8字节),总共3*4 +1*4+8=24字节。根据这一原则,对String a=”rosen jiang”来说,实例a的shallow size也是24字节(很多人对此有争议,请看官甄别并留言给我)。

 

Retained size是该对象自己的shallow size,加上从该对象能直接或间接访问到对象的shallow size之和。换句话说,retained size是该对象被GC之后所能回收到内存的总和。为了更好的理解retained size,不妨看个例子。

 

把内存中的对象看成下图中的节点,并且对象和对象之间互相引用。这里有一个特殊的节点GC Roots,正解!这就是reference chain的起点。

retained_objects.gifretained_objects_2.gif

从obj1入手,上图中蓝色节点代表仅仅只有通过obj1才能直接或间接访问的对象。因为可以通过GC Roots访问,所以左图的obj3不是蓝色节点;而在右图却是蓝色,因为它已经被包含在retained集合内。

所以对于左图,obj1的retained size是obj1、obj2、obj4的shallow size总和;右图的retained size是obj1、obj2、obj3、obj4的shallow size总和。obj2的retained size可以通过相同的方式计算。

 

Heap Dump

 

heap dump是特定时间点,java进程的内存快照。有不同的格式来存储这些数据,总的来说包含了快照被触发时java对象和类在heap中的情况。由于快照只是一瞬间的事情,所以heap dump中无法包含一个对象在何时、何地(哪个方法中)被分配这样的信息。

 

在不同平台和不同java版本有不同的方式获取heap dump,而MAT需要的是HPROF格式的heap dump二进制文件。想无需人工干预的话,要这样配置JVM参数:-XX:-HeapDumpOnOutOfMemoryError,当错误发生时,会自动生成heap dump,在生产环境中,只有用这种方式。如果你想自己控制什么时候生成heap dump,在Windows+JDK6环境中可利用JConsole工具,而在Linux或者Mac OS X环境下均可使用JDK5、6自带的jmap工具。当然,还可以配置JVM参数:-XX:+HeapDumpOnCtrlBreak,也就是在控制台使用Ctrl+Break键来生成heap dump。由于我是windows+JDK5,所以选择了-XX:-HeapDumpOnOutOfMemoryError这种方式,更多配置请参考 MAT Wiki

 

参考资料

 

MAT Wiki

Interned Strings

Strong,Soft,Weak,Phantom Reference

Tuning Garbage Collection with the 5.0 Java[tm] Virtual Machine

Permanent Generation

Understanding Weak References译文

Java HotSpot VM Options

Shallow and retained sizes

JVM Memory Structure

GC roots

前言的前言

写blog就是好,在大前提下可以想说什么写什么,不像投稿那么字字斟酌。上周末回了趟成都办事,所以本文来迟了。K117从达州经由达成线往成都方向走的时候,发现铁路边有条河,尽管我现在也不知道其名字,但已被其深深的陶醉。河很宽且水流平缓,河边山丘森林密布,民房星星点点的分布在河边,河里偶尔些小船。当时我就在想,在这里生活是多么的惬意,夏天还可以下去畅游一番,闲来无事也可垂钓。唉,越来越讨厌北漂了。

前言

使用Memory Analyzer tool(MAT)分析内存泄漏(一)中,我介绍了内存泄漏的前因后果。在本文中,将介绍MAT如何根据heap dump分析泄漏根源。由于测试范例可能过于简单,很容易找出问题,但我期待借此举一反三。
一开始不得不说说ClassLoader,本质上,它的工作就是把磁盘上的类文件读入内存,然后调用java.lang.ClassLoader.defineClass方法告诉系统把内存镜像处理成合法的字节码。Java提供了抽象类ClassLoader,所有用户自定义类装载器都实例化自ClassLoader的子类。system class loader在没有指定装载器的情况下默认装载用户类,在Sun Java 1.5中既sun.misc.Launcher$AppClassLoader。更详细的内容请参看下面的资料。


准备heap dump

请看下面的Pilot类,没啥特殊的。

/**
 * Pilot class
 * @author rosen jiang
 */
package org.rosenjiang.bo;

public class Pilot{
    
    String name;
    int age;
    
    public Pilot(String a, int b){
        name = a;
        age = b;
    }
}


然后再看OOMHeapTest类,它是如何撑破heap dump的。

/**
 * OOMHeapTest class
 * @author rosen jiang
 */
package org.rosenjiang.test;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.rosenjiang.bo.Pilot;

public class OOMHeapTest {
    public static void main(String[] args){
        oom();
    }
    
    private static void oom(){
        Map<String, Pilot> map = new HashMap<String, Pilot>();
        Object[] array = new Object[1000000];
        for(int i=0; i<1000000; i++){
            String d = new Date().toString();
            Pilot p = new Pilot(d, i);
            map.put(i+"rosen jiang", p);
            array[i]=p;
        }
    }
}


是的,上面构造了很多的Pilot类实例,向数组和map中放。由于是Strong Ref,GC自然不会回收这些对象,一直放在heap中直到溢出。当然在运行前,先要在Eclipse中配置VM参数-XX:+HeapDumpOnOutOfMemoryError。好了,一会儿功夫内存溢出,控制台打出如下信息。

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid3600.hprof 
Heap dump file created [78233961 bytes in 1.995 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space



java_pid3600.hprof既是heap dump,可以在OOMHeapTest类所在的工程根目录下找到。

MAT安装

话分两头说,有了heap dump还得安装MAT。可以在http://www.eclipse.org/mat/downloads.php选择合适的方式安装。安装完成后切换到Memory Analyzer视图。在Eclipse的左上角有Open Heap Dump按钮,按照刚才说的路径找到java_pid3600.hprof文件并打开。解析hprof文件会花些时间,然后会弹出向导,直接Finish即可。稍后会看到下图所示的界面。



MAT工具分析了heap dump后在界面上非常直观的展示了一个饼图,该图深色区域被怀疑有内存泄漏,可以发现整个heap才64M内存,深色区域就占了99.5%。接下来是一个简短的描述,告诉我们main线程占用了大量内存,并且明确指出system class loader加载的"java.lang.Thread"实例有内存聚集,并建议用关键字"java.lang.Thread"进行检查。所以,MAT通过简单的两句话就说明了问题所在,就算使用者没什么处理内存问题的经验。在下面还有一个"Details"链接,在点开之前不妨考虑一个问题:为何对象实例会聚集在内存中,为何存活(而未被GC)?是的——Strong Ref,那么再走近一些吧。



点击了"Details"链接之后,除了在上一页看到的描述外,还有Shortest Paths To the Accumulation Point和Accumulated Objects部分,这里说明了从GC root到聚集点的最短路径,以及完整的reference chain。观察Accumulated Objects部分,java.util.HashMap和java.lang.Object[1000000]实例的retained heap(size)最大,在上一篇文章中我们知道retained heap代表从该类实例沿着reference chain往下所能收集到的其他类实例的shallow heap(size)总和,所以明显类实例都聚集在HashMap和Object数组中了。这里我们发现一个有趣的现象,既Object数组的shallow heap和retained heap竟然一样,通过 Shallow and retained sizes一文可知,数组的shallow heap和一般对象(非数组)不同,依赖于数组的长度和里面的元素的类型,对数组求shallow heap,也就是求数组集合内所有对象的shallow heap之和。好,再来看org.rosenjiang.bo.Pilot对象实例的shallow heap为何是16,因为对象头是8字节,成员变量int是4字节、String引用是4字节,故总共16字节。



接着往下看,来到了Accumulated Objects by Class区域,顾名思义,这里能找到被聚集的对象实例的类名。org.rosenjiang.bo.Pilot类上头条了,被实例化了290,325次,再返回去看程序,我承认是故意这么干的。还有很多有用的报告可用来协助分析问题,只是本文中的例子太简单,也用不上。以后如有用到,一定撰文详细叙述。

又是perm gen

我们在上一篇文章中知道,perm gen是个异类,里面存储了类和方法数据(与class loader有关)以及interned strings(字符串驻留)。在heap dump中没有包含太多的perm gen信息。那么我们就用这些少量的信息来解决问题吧。

看下面的代码,利用interned strings把perm gen撑破了。

/**
 * OOMPermTest class
 * @author rosen jiang
 */
package org.rosenjiang.test;

public class OOMPermTest {
    public static void main(String[] args){
        oom();
    }
    
    private static void oom(){
        Object[] array = new Object[10000000];
        for(int i=0; i<10000000; i++){
            String d = String.valueOf(i).intern();
            array[i]=d;
        }
    }
}


控制台打印如下的信息,然后把java_pid1824.hprof文件导入到MAT。其实在MAT里,看到的状况应该和“OutOfMemoryError: Java heap space”差不多(用了数组),因为heap dump并没有包含interned strings方面的任何信息。只是在这里需要强调,使用intern()方法的时候应该多加注意。

java.lang.OutOfMemoryError: PermGen space
Dumping heap to java_pid1824.hprof 
Heap dump file created [121273334 bytes in 2.845 secs]
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space



倒是在思考如何把class loader撑破废了些心思。经过尝试,发现使用ASM来动态生成类才能达到目的。ASM(http://asm.objectweb.org)的主要作用是处理已编译类(compiled class),能对已编译类进行生成、转换、分析(功能之一是实现动态代理),而且它运行起来足够的快和小巧,文档也全面,实属居家必备之良品。ASM提供了core API和tree API,前者是基于事件的方式,后者是基于对象的方式,类似于XML的SAX、DOM解析,但是使用tree API性能会有损失。既然下面要用到ASM,这里不得不啰嗦下已编译类的结构,包括:
    1、修饰符(例如public、private)、类名、父类名、接口和annotation部分。
    2、类成员变量声明,包括每个成员的修饰符、名字、类型和annotation。
    3、方法和构造函数描述,包括修饰符、名字、返回和传入参数类型,以及annotation。当然还包括这些方法或构造函数的具体Java字节码。
    4、常量池(constant pool)部分,constant pool是一个包含类中出现的数字、字符串、类型常量的数组。



已编译类和原来的类源码区别在于,已编译类只包含类本身,内部类不会在已编译类中出现,而是生成另外一个已编译类文件;其二,已编译类中没有注释;其三,已编译类没有package和import部分。
这里还得说说已编译类对Java类型的描述,对于原始类型由单个大写字母表示,Z代表boolean、C代表char、B代表byte、S代表short、I代表int、F代表float、J代表long、D代表double;而对类类型的描述使用内部名(internal name)外加前缀L和后面的分号共同表示来表示,所谓内部名就是带全包路径的表示法,例如String的内部名是java/lang/String;对于数组类型,使用单方括号加上数据元素类型的方式描述。最后对于方法的描述,用圆括号来表示,如果返回是void用V表示,具体参考下图。



下面的代码中会使用ASM core API,注意接口ClassVisitor是核心,FieldVisitor、MethodVisitor都是辅助接口。ClassVisitor应该按照这样的方式来调用:visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )*( visitInnerClass | visitField | visitMethod )* visitEnd。就是说visit方法必须首先调用,再调用最多一次的visitSource,再调用最多一次的visitOuterClass方法,接下来再多次调用visitAnnotation和visitAttribute方法,最后是多次调用visitInnerClass、visitField和visitMethod方法。调用完后再调用visitEnd方法作为结尾。

注意ClassWriter类,该类实现了ClassVisitor接口,通过toByteArray方法可以把已编译类直接构建成二进制形式。由于我们要动态生成子类,所以这里只对ClassWriter感兴趣。首先是抽象类原型:

/**
 * @author rosen jiang
 * MyAbsClass class
 */
package org.rosenjiang.test;

public abstract class MyAbsClass {
    int LESS = -1;
    int EQUAL = 0;
    int GREATER = 1;
    abstract int absTo(Object o);
}


其次是自定义类加载器,实在没法,ClassLoader的defineClass方法都是protected的,要加载字节数组形式(因为toByteArray了)的类只有继承一下自己再实现。

/**
 * @author rosen jiang
 * MyClassLoader class
 */
package org.rosenjiang.test;

public class MyClassLoader extends ClassLoader {
    public Class defineClass(String name, byte[] b) {
        return defineClass(name, b, 0, b.length);
    }
}


最后是测试类。

/**
 * @author rosen jiang
 * OOMPermTest class
 */
package org.rosenjiang.test;

import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;

public class OOMPermTest {
    public static void main(String[] args) {
        OOMPermTest o = new OOMPermTest();
        o.oom();
    }

    private void oom() {
        try {
            ClassWriter cw = new ClassWriter(0);
            cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT,
            "org/rosenjiang/test/MyAbsClass", null, "java/lang/Object",
            new String[] {});
            cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "LESS", "I",
            null, new Integer(-1)).visitEnd();
            cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "EQUAL", "I",
            null, new Integer(0)).visitEnd();
            cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "GREATER", "I",
            null, new Integer(1)).visitEnd();
            cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT, "absTo",
            "(Ljava/lang/Object;)I", null, null).visitEnd();
            cw.visitEnd();
            byte[] b = cw.toByteArray();

            List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
            while (true) {
                MyClassLoader classLoader = new MyClassLoader();
                classLoader.defineClass("org.rosenjiang.test.MyAbsClass", b);
                classLoaders.add(classLoader);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


不一会儿,控制台就报错了。

java.lang.OutOfMemoryError: PermGen space
Dumping heap to java_pid3023.hprof 
Heap dump file created [92593641 bytes in 2.405 secs]
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space


打开java_pid3023.hprof文件,注意看下图的Classes: 88.1k和Class Loader: 87.7k部分,从这点可看出class loader加载了大量的类。



更进一步分析,点击上图中红框线圈起来的按钮,选择Java Basics——Class Loader Explorer功能。打开后能看到下图所示的界面,第一列是class loader名字;第二列是class loader已定义类(defined classes)的个数,这里要说一下已定义类和已加载类(loaded classes)了,当需要加载类的时候,相应的class loader会首先把请求委派给父class loader,只有当父class loader加载失败后,该class loader才会自己定义并加载类,这就是Java自己的“双亲委派加载链”结构;第三列是class loader所加载的类的实例数目。



在Class Loader Explorer这里,能发现class loader是否加载了过多的类。另外,还有Duplicate Classes功能,也能协助分析重复加载的类,在此就不再截图了,可以肯定的是MyAbsClass被重复加载了N多次。

最后

其实MAT工具非常的强大,上面故弄玄虚的范例代码根本用不上MAT的其他分析功能,所以就不再描述了。其实对于OOM不只我列举的两种溢出错误,还有多种其他错误,但我想说的是,对于perm gen,如果实在找不出问题所在,建议使用JVM的-verbose参数,该参数会在后台打印出日志,可以用来查看哪个class loader加载了什么类,例:“[Loaded org.rosenjiang.test.MyAbsClass from org.rosenjiang.test.MyClassLoader]”。
全文完。


参考资料

memoryanalyzer Blog
java类加载器体系结构
ClassLoader



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


ITeye推荐



一致性哈希算法与Java实现

$
0
0

一致性哈希算法是分布式系统中常用的算法。比如,一个分布式的存储系统,要将数据存储到具体的节点上,如果采用普通的hash方法,将数据映射到具体的节点上,如key%N,key是数据的key,N是机器节点数,如果有一个机器加入或退出这个集群,则所有的数据映射都无效了,如果是持久化存储则要做数据迁移,如果是分布式缓存,则其他缓存就失效了。

    因此,引入了一致性哈希算法:

 

把数据用hash函数(如MD5),映射到一个很大的空间里,如图所示。数据的存储时,先得到一个hash值,对应到这个环中的每个位置,如k1对应到了图中所示的位置,然后沿顺时针找到一个机器节点B,将k1存储到B这个节点中。

如果B节点宕机了,则B上的数据就会落到C节点上,如下图所示:

 

这样,只会影响C节点,对其他的节点A,D的数据不会造成影响。然而,这又会造成一个“雪崩”的情况,即C节点由于承担了B节点的数据,所以C节点的负载会变高,C节点很容易也宕机,这样依次下去,这样造成整个集群都挂了。

       为此,引入了“虚拟节点”的概念:即把想象在这个环上有很多“虚拟节点”,数据的存储是沿着环的顺时针方向找一个虚拟节点,每个虚拟节点都会关联到一个真实节点,如下图所使用:

图中的A1、A2、B1、B2、C1、C2、D1、D2都是虚拟节点,机器A负载存储A1、A2的数据,机器B负载存储B1、B2的数据,机器C负载存储C1、C2的数据。由于这些虚拟节点数量很多,均匀分布,因此不会造成“雪崩”现象。

 

Java实现:

  1. public class Shard<S> { // S类封装了机器节点的信息 ,如name、password、ip、port等   
  2.   
  3.     private TreeMap<Long, S> nodes; // 虚拟节点   
  4.     private List<S> shards; // 真实机器节点   
  5.     private final int NODE_NUM = 100; // 每个机器节点关联的虚拟节点个数   
  6.   
  7.     public Shard(List<S> shards) {  
  8.         super();  
  9.         this.shards = shards;  
  10.         init();  
  11.     }  
  12.   
  13.     private void init() { // 初始化一致性hash环   
  14.         nodes = new TreeMap<Long, S>();  
  15.         for (int i = 0; i != shards.size(); ++i) { // 每个真实机器节点都需要关联虚拟节点   
  16.             final S shardInfo = shards.get(i);  
  17.   
  18.             for (int n = 0; n < NODE_NUM; n++)  
  19.                 // 一个真实机器节点关联NODE_NUM个虚拟节点   
  20.                 nodes.put(hash("SHARD-" + i + "-NODE-" + n), shardInfo);  
  21.   
  22.         }  
  23.     }  
  24.   
  25.     public S getShardInfo(String key) {  
  26.         SortedMap<Long, S> tail = nodes.tailMap(hash(key)); // 沿环的顺时针找到一个虚拟节点   
  27.         if (tail.size() == 0) {  
  28.             return nodes.get(nodes.firstKey());  
  29.         }  
  30.         return tail.get(tail.firstKey()); // 返回该虚拟节点对应的真实机器节点的信息   
  31.     }  
  32.   
  33.     /** 
  34.      *  MurMurHash算法,是非加密HASH算法,性能很高, 
  35.      *  比传统的CRC32,MD5,SHA-1(这两个算法都是加密HASH算法,复杂度本身就很高,带来的性能上的损害也不可避免) 
  36.      *  等HASH算法要快很多,而且据说这个算法的碰撞率很低. 
  37.      *  http://murmurhash.googlepages.com/ 
  38.      */  
  39.     private Long hash(String key) {  
  40.           
  41.         ByteBuffer buf = ByteBuffer.wrap(key.getBytes());  
  42.         int seed = 0x1234ABCD;  
  43.           
  44.         ByteOrder byteOrder = buf.order();  
  45.         buf.order(ByteOrder.LITTLE_ENDIAN);  
  46.   
  47.         long m = 0xc6a4a7935bd1e995L;  
  48.         int r = 47;  
  49.   
  50.         long h = seed ^ (buf.remaining() * m);  
  51.   
  52.         long k;  
  53.         while (buf.remaining() >= 8) {  
  54.             k = buf.getLong();  
  55.   
  56.             k *= m;  
  57.             k ^= k >>> r;  
  58.             k *= m;  
  59.   
  60.             h ^= k;  
  61.             h *= m;  
  62.         }  
  63.   
  64.         if (buf.remaining() > 0) {  
  65.             ByteBuffer finish = ByteBuffer.allocate(8).order(  
  66.                     ByteOrder.LITTLE_ENDIAN);  
  67.             // for big-endian version, do this first:   
  68.             // finish.position(8-buf.remaining());   
  69.             finish.put(buf).rewind();  
  70.             h ^= finish.getLong();  
  71.             h *= m;  
  72.         }  
  73.   
  74.         h ^= h >>> r;  
  75.         h *= m;  
  76.         h ^= h >>> r;  
  77.   
  78.         buf.order(byteOrder);  
  79.         return h;  
  80.     }  
  81.   
  82. }  


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


ITeye推荐



文本相似度计算-google的simHash汉明距离

$
0
0

一、概述

       针对文本相似性计算,很多开发朋友首先想到的应该是使用向量空间模型VSM(Vector Space Model)。使用VSM计算相似度,先对文本进行分词,然后建立文本向量,把相似度的计算转换成某种特征向量距离的计算,比如余弦角、欧式距离、Jaccard相似系数等。这种方法存在很大一个问题:需要对文本两两进行相似度比较,无法扩展到海量文本的处理。想想像Google这种全网搜索引擎,收录了上百亿的网页,爬虫每天爬取的网页数都是百万千万级别的。为了防止重复收录网页,爬虫需要对网页进行判重处理。如果采用VSM方法,计算量是相当可观的。

 

二、思想

输入为一个N维向量V,比如文本的特征向量,每个特征具有一定权重。输出是一个C位的二进制签名S。

    1)初始化一个C维向量Q为0,C位的二进制签名S为0。

    2)对向量V中的每一个特征,使用传统的Hash算法计算出一个C位的散列值H。对1<=i<=C,

       如果H的第i位为1,则Q的第i个元素加上该特征的权重;

       否则,Q的第i个元素减去该特征的权重。

    3)如果Q的第i个元素大于0,则S的第i位为1;否则为0;

    4)返回签名S。

 

 

三、java实现

import java.math.BigInteger;
import java.util.StringTokenizer;

public class SimHash {

	private String tokens;
	private BigInteger strSimHash;
	private int hashbits = 128;

	public SimHash(String tokens) {
		this.tokens = tokens;
		this.strSimHash = this.simHash();
	}
	
	public SimHash(String tokens, int hashbits) {
		this.tokens = tokens;
		this.hashbits = hashbits;
		this.strSimHash = this.simHash();
	}

	public BigInteger simHash() {
		int[] v = new int[this.hashbits];
		StringTokenizer stringTokens = new StringTokenizer(this.tokens);
		while (stringTokens.hasMoreTokens()) {
			String temp = stringTokens.nextToken();
			BigInteger t = this.hash(temp);
			System.out.println("temp = " + temp+" : " + t);
			for (int i = 0; i < this.hashbits; i++) {
				BigInteger bitmask = new BigInteger("1").shiftLeft(i);
				if (t.and(bitmask).signum() != 0) {
					v[i] += 1;
				} else {
					v[i] -= 1;
				}
			}
		}
		BigInteger fingerprint = new BigInteger("0");
		for (int i = 0; i < this.hashbits; i++) {
			if (v[i] >= 0) {
				fingerprint = fingerprint.add(new BigInteger("1").shiftLeft(i));
			}
		}
		return fingerprint;
	}

	private BigInteger hash(String source) {
		if (source == null || source.length() == 0) {
			return new BigInteger("0");
		} else {
			char[] sourceArray = source.toCharArray();
			BigInteger x = BigInteger.valueOf(((long) sourceArray[0]) << 7);
			BigInteger m = new BigInteger("1000003");
			BigInteger mask = new BigInteger("2").pow(this.hashbits).subtract(
					new BigInteger("1"));
			for (char item : sourceArray) {
				BigInteger temp = BigInteger.valueOf((long) item);
				x = x.multiply(m).xor(temp).and(mask);
			}
			x = x.xor(new BigInteger(String.valueOf(source.length())));
			if (x.equals(new BigInteger("-1"))) {
				x = new BigInteger("-2");
			}
			return x;
		}
	}

	public int hammingDistance(SimHash other) {
		BigInteger m = new BigInteger("1").shiftLeft(this.hashbits).subtract(
				new BigInteger("1"));
		BigInteger x = this.strSimHash.xor(other.strSimHash).and(m);
		int tot = 0;
		while (x.signum() != 0) {
			tot += 1;
			x = x.and(x.subtract(new BigInteger("1")));
		}
		return tot;
	}

	public static void main(String[] args) {
		String s = "China people's Republic of China Chinese China people's Republic of China People's Republic of China";
		SimHash hash1 = new SimHash(s, 128);
		System.out.println(hash1.strSimHash + "  "
				+ hash1.strSimHash.bitLength());

		s = "China people's Republic of China Chinese China people's Republic of China";
		SimHash hash2 = new SimHash(s, 128);
		System.out.println(hash2.strSimHash + "  "
				+ hash2.strSimHash.bitCount());

		s = "China people's Republic";
		SimHash hash3 = new SimHash(s, 128);
		System.out.println(hash3.strSimHash + "  "
				+ hash3.strSimHash.bitCount());

		System.out.println("============================");
		System.out.println(hash1.hammingDistance(hash2));
		System.out.println(hash1.hammingDistance(hash3));
	}
}

 
 



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


ITeye推荐



软件架构师的职责

$
0
0
架构师的职责
架构师需要参与项目开发的全部过程,包括需求分析、架构设计、系统实现、集成、测试和部署各个阶段,负责在整个项目中对技术活动和技术说明进行指导和协调。
架构师主要职责有4条:
1、确认需求
    在项目开发过程中,架构师是在需求规格说明书完成后介入的,需求规格说明书必须得到架构师的认可。架构师需要和分析人员反复交流,以保证自己完整并准确地理解用户需求。
2、系统分解
    依据用户需求,架构师将系统整体分解为更小的子系统和组件,从而形成不同的逻辑层或服务。随后,架构师会确定各层的接口,层与层相互之间的关系。架构师不仅要对整个系统分层,进行“纵向”分解,还要对同一逻辑层分块,进行“横向”分解。
    软件架构师的功力基本体现于此,这是一项相对复杂的工作。
3、技术选型
    架构师通过对系统的一系列的分解,最终形成了软件的整体架构。技术选择主要取决于软件架构。
Web Server运行在Windows上还是Linux上?数据库采用MSSql、Oracle还是Mysql?需要不需要采用MVC或者Spring等轻量级的框架?前端采用富客户端还是瘦客户端方式?类似的工作,都需要在这个阶段提出,并进行评估。
架构师对产品和技术的选型仅仅限于评估,没有决定权,最终的决定权归项目经理。架构师提出的技术方案为项目经理提供了重要的参考信息,项目经理会从项目预算、人力资源、时间进度等实际情况进行权衡,最终进行确认。
4、制定技术规格说明
    架构师在项目开发过程中,是技术权威。他需要协调所有的开发人员,与开发人员一直保持沟通,始终保证开发者依照它的架构意图去实现各项功能。
    架构师与开发者沟通的最重要的形式是技术规格说明书,它可以是UML视图、Word文档,Visio文件等各种表现形式。通过架构师提供的技术规格说明书,保证开发者可以从不同角度去观察、理解各自承担的子系统或者模块。
架构师不仅要保持与开发者的沟通,也需要与项目经理、需求分析员,甚至与最终用户保持沟通。所以,对于架构师来讲,不仅有技术方面的要求,还有人际交流方面的要求。
3.3 架构师的误区
1、架构师就是项目经理
    架构师不是项目经理。项目经理侧重于预算控制、时间进度控制、人员管理、与外部联系和协调等等工作,具备管理职能。一般小型项目中,常见项目经理兼架构师。
2、架构师负责需求分析
    架构师不是需求分析员。需求分析人员的工作是收集需求和分析需求,并与最终用户、产品经理保持联系。架构师只对最终的需求审核和确认,提出需求不清和不完整的部分,他会跟需求分析员时刻保持联系。架构师是技术专家,不是业务专家。

3、架构师从来不写代码
    这是一个尚存争论的问题。目前有两种观点:
观点1:架构师不写代码,写代码纯体力活,架构师写代码大材小用。架构师把UML的各种视图交给开发人员,如果有不明确的地方,可以与架构师随时沟通。
观点2:架构师本来自于程序员,只是比程序员站的层面更高,比程序员唯一多的是经验和知识,所以架构师也免不了写代码。
    我个人觉得这两种说法是与架构师的出身和所处的环境有关。
    架构师首先是一个技术角色,所以一定是来自于技术人员这个群体,比如系统架构师,多是来自于运维人员,可能本身代码写得并不多,或者说写不出来很漂亮的代码。软件架构师多是来自于程序员,有着程序员的血统和情怀,所以在项目开发过程中,可能会写一些核心代码。我们的理想是架构师不用写代码,但事实上有时候过于理想。架构师写不写代码,可能取决于公司的规模、文化、开发人员的素质等现实情况。另外,架构师也不是跟程序员界限分得那么清楚,按照能力也有高中低之分,写不写代码不是区分两者的根本标准。
3.4 架构师的基本素质
周星驰有个片子《喜剧之王》,剧中的尹天仇整天揣着本《演员的自我修养》,一个好演员不仅需要天赋,也需要一定的理论指导,无师自通的人毕竟是少数。架构师的成长过程也是这样。从普通程序员到高级程序员,再到架构师,是一个经验积累和思想升华的过程。经验积累是一个方面,素质培养是另一个方面,两者相辅相成,所以我觉得有必要把架构师的所要具备的素质罗列一下,作为程序员努力的方向。
1、沟通能力
    为了提高效率,架构师必须赢得团队成员、项目经理、客户或用户认同,这就需要架构师具有较强的沟通能力。沟通能力是人类最普遍性的素质要求,技术人员好像容易忽略,想成为架构师就不能忽略。千万不要抱着这样的观念:怀才跟怀孕似的,时间久了总会被人发现的。还是天桥上卖大力丸的哥们说得对:光说不练假把式,光练不说傻把式。看看你周围的头头脑脑们,哪一个不是此中高手,我们千万不要鄙视,认为这是阿谀奉承、投机钻营,凡事都要看到积极的一面,“沟通”的确是一种能力。我认为自己是一个略内向的人,因为我是农村出来的孩子,普通话都说不好,以前或多或少带有点自卑感,幻想着是金子总会发光,所以在职业生涯中吃了不少亏。现在,我深深懂得了沟通的重要性,我会很主动地跟同事们,跟老大们不定时地沟通,感觉工作起来顺畅多了。
    这一条我认为最为重要,所以排在首位。我甚至认为下面几条都可以忽略,唯一这一条得牢记,而且要常常提醒自己。
2、领导能力
      架构师能够推动整个团队的技术进展,能在压力下作出关键性的决策,并将其贯彻到底。架构师如何来保证这种执行力?这就需要架构师具有领导能力。
    架构师的领导能力的取得跟项目经理不太一样。项目经理主要负责解决行政管理,这种能力与技术关系不大,他有人权和财权,再扯上一张“领导”的虎皮,采用“胡萝卜加大棒”的方式,基本上可以保证执行力。架构师在项目里面可能更多地使用非正式的领导力,也就是我们常说的影响力,里面包括个人魅力、技术能力、知识传递等等。
3、抽象思维和分析能力
    架构师必须具备抽象思维和分析的能力,这是你进行系统分析和系统分解的基本素质。只有具备这样的能力,架构师才能看清系统的整体,掌控全局,这也是架构师大局观的形成基础。你如何具备这种能力呢?一是来自于经验,二是来自于学习。架构师不仅要具备在问题领域上的经验,也需要具备在软件工程领域内的经验。也就是说,架构师必须能够准确得理解需求,然后用软件工程的思想,把需求转化和分解成可用计算机语言实现的程度。经验的积累是需要一个时间过程的,这个过程谁也帮不了你,是需要你去经历的。但是,如果你有意识地去培养,不断吸取前人的经验的话,还是可以缩短这个周期的。这也是我写作此系列的始动力之一。
4、技术深度和广度
   架构师最好精通1-2个技术,具备这种技术能力可以更加深入的理解有关架构的工作原理,也可以拉近和开发人员的距离,并形成团队中的影响力。
   架构师的技术知识广度也很重要,需要了解尽可能多的技术,所谓见多识广,只有这样,才可能综合各种技术,选择更加适合项目的解决方案。有的人说,架构师技术广度的要求高于技术深度的要求,这是很有道理的。
总而言之,一句话:架构师是项目团队中的技术权威。

面向过程和面向对象这两个基本概念,不仅架构师需要非常清楚,程序员、设计师也要非常清楚,这也是系统分析、设计和编码最基本的常识。我接触的程序员,很多人只停留在一种“似是而非”的程度,这是不行的,想要继续前进,就得把基础夯实,所以我觉得很有必要先回回炉,补补课。

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


ITeye推荐




[Java][web]利用Spring随时随地获得Request和Session

$
0
0

利用Spring随时随地获得Request和Session

一、准备工作:

在web.xml中添加  

<listener>    <listener-class>    
            org.springframework.web.context.request.RequestContextListener</listener-class>    </listener>

 


二、使用方法:
1、方法一:通过代码实现

HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();



2、方法二:通过注解实现:

@Autowired
private  HttpServletRequest request;



三、关于RequestContextListener的背景知识:
基于LocalThread将HTTP request对象绑定到为该请求提供服务的线程上。这使得具有request和session作用域的bean能够在后面的调用链中被访问到。 

Request作用域 
<bean id="loginAction" class="com.foo.LoginAction" scope="request"/> 


针对每次HTTP请求,Spring容器会根据loginAction bean定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。 

Session作用域 
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/> 
针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,你可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。 

global session作用域 

<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/> 

global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。 

请注意,假如你在编写一个标准的基于Servlet的web应用,并且定义了一个或多个具有global session作用域的bean,系统会使用标准的HTTP Session作用域,并且不会引起任何错误 
二、为什么需要额外的配置RequestContextFilter 
也许会有一个疑问,已经通过ContextLoaderListener(或ContextLoaderServlet)将Web容器与Spring容器整合,为什么这里还要用额外的RequestContextListener以支持Bean的另外3个作用域,原因是ContextLoaderListener实现ServletContextListener监听器接口,而ServletContextListener只负责监听Web容器的启动和关闭的事件。RequestContextFilter实现ServletRequestListener监听器接口,该监听器监听HTTP请求事件,Web服务器接收的每次请求都会通知该监听器。通过配置RequestContextFilter,Spring容器与Web容器结合的更加密切。 
三、作用域依赖问题 

如果将Web相关作用域的Bean注入到singleton或prototype的Bean中,这种情况下,需要Spring AOP

<bean name="car" class="com.demo.Car" scope="request">  

    <aop:scoped-proxy/>  

</bean>  

<bean id="boss" class="com.demo.Boss" >  

   <properrty name="car" ref="car" />  

</bean>  


作者:szwangdf 发表于2014-7-22 15:08:53 原文链接
阅读:51 评论:0 查看评论

成为一个A/B 测试专家的途径

$
0
0

英文原文: Roadmap To Becoming An A/B Testing Expert  翻译: gyro

A/B测试,也叫做分离测试,一种可用来测试两个不同版本的登陆页面的转化率的方法。你可以测试哪个版本能更好地引导用户达到你的目标,比如:注册、提交通讯信息等。你可以测试两个完全不同的页面,也可以测试页面上的一些小调整,比如在副本里改动几个词。

执行A/B测试能帮助访客更好地和网站进行交互,从真实用户中得到真实数据来支持你的设计决策。存在很多工具可用来执行A/B测试(后面详细描述),这让很多非技术人员也可以容易得进行或管理A/B测试。

ab-illustration-opt-500.jpg

什么时候进行A/B测试

只有当你已经拥有足够多的访客和转化(conversion)时,你才可以立马进行A/B测试。(当用户已经完成了你想要的其中一个目标时,转化就会发生)。这意味着什么?

理想的访问数量根据你特定的转化率的不同而不同。假设每个变量(目标)的转化率至少需要 1000 个访客,每个变量(目标)需要 150 个转化。有些网站可能只要几个小时就完成了,而其他一些则需要一整个月。

为了弄清楚执行一个A/B测试具体需要多少访客,你可以在 Evan Miller’s 样本量计算网页中插入几个基本参数。

你可以凭借每个月 80 个访客机就成功经营企业,但是每个月 80 个访客不可能进行达到统计上显著的A/B测试。在你还没有开始做市场营销之前不要进行A/B测试。在为网站做优化之前你要先保证有源源不断的人访问你的网站。

记住在开始A/B测试之前你不需要建立产品。你可在在初始页面上测试未来用户对你计划的功能和价格层会有什么反应。

第一次A/B测试

对于你的第一次A/B测试来说,尽量保持简单。轻微的调整你的登录页面,关注与那些用很少精力就能达到的目标:

  • 副本 h1 和 h3 标题
  • 副本动作按钮 (例如:“免费试一试” vs. “注册” vs. “开始”)
  • 动作按钮的颜色、大小以及位置
  • 短页面 vs. 长页面(通过隐藏超长条)

你可以一次测试多个变量,不仅仅只调整两个属性。也可以一次进行多个A/B测试,但是首先只专注其中一个(为了摸清测试窍门)。

你可以从任何地方开始A/B测试,时间从一两天到一个月不等。A/B测试工具会报告哪个测试版本是统计上显著的优胜者,然后你就可以更新底部代码使这个优胜变量成为网站最终版本的一部分。

之后,就可以着手准备其他A/B测试了。

何去何从

以上提到的是花最少的精力就可以达到的目标,但是A/B测试绝不仅仅是这样。当然,测试按钮的颜色和标题副本可以提高你的转化率。但是我们需要跳出这种思维框架,想想除了页面外观之外的一些东西:

  • 强调产品功能还是强调产品效益。

    在检验产品时你是否更倾向于注重产品功能。通过向消费者描述一副他们梦想的情境来说明产品的效益。

  • 加快页面加载

    使登陆页面保持极简,并且加载速度小于一秒。

  • 展示和人交谈的视频

    试着把截图拿走,拖入视频,视频以和在摄像机前的人交谈的方式说明产品,以帮助和用户建立一种人际关系。

  • 调查新用户

    和新的用户交谈,弄明白是什么使他们注册产品,目前什么对他们最有价值。在随后测试中强调这些。

  • 搞清楚什么会使新用户感到困惑

    问问新用户网站中什么地方使他们感到困惑,什么问题他们不会回答。在A/B测试中加入这些问题。

  • 增加用户评价

    通过客户评价的的方式来获得社会认同。在每个客户名字的旁边放上头像或者公司的 Logo。

  • 改变标题和主要内容的写作风格

    在博客中改变你的标题和内容的写作风格,看看这会如何影响订阅情况。试着为同样的文章写两个不同版本。

  • 对产品价格和商业模式进行试验

    改变你的价格体系,即使只发布在产品的公众主页上,看看潜在用户会何如反应。

  • 使注册过程简短

    移除注册形式中不必要的步骤。

  • 根本上改变设计

    尝试不同的登陆页面,正如 Campaign Monitor用大模型为新访客所做的。

记住转化不是一锤子买卖。当你说你想要更多的注册量时你真正想说的是你想要更多的生命周期。当你说你想要更多的播客听众,你真正想说的就是你想要获得更多忠实的听众,他们会不断地把你的产品介绍费朋友。

监控你的A/B测试将会如何影响你的长期目标。

工具使用

我推荐使用 VWO (Visual Website Optimizer)来进行A/B测试。你不需要在每次测试和重新布局时编辑你的网站代码。相反的,你可以使用该工具中的所见即所得编辑器。

vwo-insights-opt-500.jpg

用 VWO,你能做网址分离测试,即测试两个完全不同的页面。你也有可以做多个变量的测试,一次测试两个以上的变量,就像是A/B/C/D/E/F/G测试。VWO 根据统计显著性来报告有种网页设计更好(优胜)。软件的新版本也将要出来了。Optimizely是另一个所见即所得工具, Google Analytics Content Experiments也是另一个很好的自由选择。如果你的A/B测试对象是电子邮箱广告,你可以使用 Campaign Monitor

设置你的第一次测试,在每一个你要测试的网页的<head>之前插入 代码片段(具体代码及插入位置见链接)。

另一个步骤是定义你的目标。填完这个句子:“我想要...” 或许你想要更多人为免费使用而进行注册,提交通讯信息,下载播客或者从网上商店上买东西。你的目标将会决定测试中哪个变量是优胜者。

切忌太过头

A/B测试不是万能药。优化转化率能使一个好的登陆页面更好,但是若一个产品或者公司有根本问题,A/B测试是无能为力的。

狭隘得关注A/B测试会把用户仅仅变成一些数据点。用户不是被倒在沙漏中的转化次数,他们是有着具体问题的具有的人,他们是到你的网站中来寻找解决问题的方法的。

不要为了短期目标而立马向用户出售商品。在站点中放置一个巨大红色的“免费试用”按钮会增加转化次数。这虽然会让你的商业业绩在 12 个月内不会有好转,但是要在头脑中保有长期目标的思想。

A/B测试的目的不是误导潜在用户去买他们不想要的商品,而是确认你如何你传达产品的效益以及确保用户明白了你所使用的语言。

只要你有自信认为你的产品很棒,那么就用A/B测试调整你向用户呈现你产品的方式。如果你知道你的产品对用户是有帮助的,那么不要试图操纵用户。总是把产品放在第一位。

最后,不要忽视你的直觉。你可以用数据支持你的直觉,但也要依赖你的工作经验。不要成为一个百分百靠数据驱动的人。

 

本文链接

js中的几个重要内容

$
0
0

js函数几个重要内容:

js中不存在函数重载,如果定义了多个同名函数,最后一个会覆盖掉前面所有函数。

 

1:Arguments对象(可以实现模拟重载的效果)

  利用arguments对象的length属性,可以获取函数接收的参数的个数

 

例如:

 

function add(){
	if(arguments.length == 2){
		return arguments[0] + arguments[1];		
	}else if(arguments.length == 3){
		return arguments[0] + arguments[1] + arguments[2];
	}
}	

add(2,4);    //output      6
add(2,4,5);    //output     11

 

 

 

2:变量的作用域

  在js中定义变量是可以不用var修饰符的,定义局部变量时,如果没有使用var修饰符,

  js会自动将这个变量定义为全局变量

例如:

 

var a = "a";
function fn(){
	b = "b";
	alert(a);   //output   a
	alert(b);   //output   b
}

fn();
	
alert(a);  //output   a
alert(b);  //output   b

 

 

全局变量和局部变量同名,函数在调用时,会寻找局部变量(js中,局部变量的优先级高于全局变量)

 

例如:

 

var a = "a";  //定义并初始化变量a

function fn(){   //函数fn加载,未调用
	alert(a);  //output   undefind

	var a = "b";  //定义变量a,但未初始化

	alert(a);   //output   b
}
	
fn();    //调用函数fn
alert(a );   //output   a

 

 

3: js中的几种特殊函数

 

(1)匿名函数:(可以将函数作为参数传递)

可以定义某个匿名函数来执行某些一次性任务

function(参数){方法体}

例如:

 

function add(a, b){
	return a() + b() ;
}

var one = function(){
	return 2;
};
var two = function(){
	return 4;
};

alert(add(one,two));    //output   6

 

 

one 和 two 是函数

调用函数 add() 时,将 one 和 two 函数作为参数传递

函数 add() 在调用时,分别执行了函数 one 和 two

将函数 one 和 two 的运算结果,再次进行运算

这时,函数 one 和 two 就叫做回调函数

 

(2)回调函数:就是以函数作为参数传递给另外的函数来调用,作为参数的函数就叫做回调函数

 

alert( add( function(){return 2;}, function(){return 4;} ) );  //output   6

 

这里作为参数传递的匿名函数,就叫做匿名回调函数

 

(3)自调函数:(一般情况下,自调函数封装的函数都是匿名函数)用来执行某些一次性任务

第一个小括号:封装函数

第二个小括号:调用封装的函数

 

例如:

不含参数:

(
	function(){
		alert("javaScript");
	}
)();

 

 

含有参数:

(
	function(str){
		alert(str);
	}
)("hello");	

 

 

(4)私有(内部)函数:

节省了全局的命名空间

保证了私有性:只能自己范围内使用,外部不能访问

 

例如:

function fn(){
	var a = "hello";
	function n(){
		return a + "javaScript";
	};

	return n();
}
	
alert(fn());  //output  hello javaScript
alert(n());    //output    undefind

 

 

(5)返回函数的函数:

 

例如:

function fn(){
	//逻辑
	return function(){
		//逻辑
		return "javaScript";
	};
}

alert(fn());  //output   function(){return "javaScript";}

//要打印“javaScript”
alert(fn()());   //output   javaScript
var fun = fn();
alert(fun());   output    javaScript

 



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


ITeye推荐



mysql语句执行顺序

$
0
0
首先,SELECT语句的基本语法如下:  
 
  SELECT selection_list # What columns to select  
 
  FROM table_list # Which tables to select rows from  
 
  WHERE primary_constraint # What conditions rows must satisfy  
 
  GROUP BY grouping_columns # How to group results  
 
  HAVING secondary_constraint # Secondary conditions rows must satisfy  
 
  ORDER BY sorting_columns # How to sort results  
 
  LIMIT from, count; # Limiting row count on results 对于MySQL,除了第一行,其他部分都是可选的,有的DBMS要求FROM从句也是必须的。  
 
  SQL Select语句完整的执行顺序【从DBMS使用者角度】:  
 
  1、from子句组装来自不同数据源的数据;  
 
  2、where子句基于指定的条件对记录行进行筛选;  
 
  3、group by子句将数据划分为多个分组->便于后续聚合函数计算;  
 
  4、执行select语句,使用聚集函数进行计算,再执行distinct, distinct一般放在select最后,(聚合函数忽略null值), count(distinct(id))这种先执行distinct再聚合;  
 
  5、使用having子句筛选分组;  
 
  6、计算所有的表达式;  
 
  7、使用order by对结果集进行排序。  
 
  SQL Select语句的执行步骤【从DBMS实现者角度,这个对我们用户意义不大】:  
 
  1)语法分析,分析语句的语法是否符合规范,衡量语句中各表达式的意义。  
 
  2) 语义分析,检查语句中涉及的所有数据库对象是否存在,且用户有相应的权限。  
 
  3)视图转换,将涉及视图的查询语句转换为相应的对基表查询语句。  
 
  4)表达式转换, 将复杂的 SQL 表达式转换为较简单的等效连接表达式。  
 
  5)选择优化器,不同的优化器一般产生不同的“执行计划”  
 
  6)选择连接方式, ORACLE 有三种连接方式,对多表连接 ORACLE 可选择适当的连接方式。  
 
  7)选择连接顺序, 对多表连接 ORACLE 选择哪一对表先连接,选择这两表中哪个表做为源数据表。  
 
  8)选择数据的搜索路径,根据以上条件选择合适的数据搜索路径,如是选用全表搜索还是利用索引或是其他的方式。  
 
  9)运行“执行计划”。  
 
  举例说明:  
 
  SELECTstate AS State,AVG((TO_DAYS(death)-TO_DAYS(birth))/365) AS Age  
 
  FROM president  
 
  WHERE death IS NOT NULL  
 
  GROUP BY state  
 
  ORDER BY Age;  
 
  这句SQL查询的意思是:  
 
  从总统表中查询已经死亡的总统,然后按州分组,然后针对每个组计算总统的平均寿命【AVG((TO_DAYS(death)-TO_DAYS(birth))/365)】,然后按寿命升序排列。  
 
  最后的结果就是类似这个样子:  
 
  +-------+-----------+  
 
  | State | Age |  
 
  +-------+-----------+  
 
  | KY | 56.208219 |  
 
  | VT | 58.852055 |  
 
  | NC | 60.141096 |  
 
  | OH | 62.866145 |  
 
  | NH | 64.917808 |  
 
  | NY | 69.342466 |  
 
  | NJ | 71.315068 |  
 
  | TX | 71.476712 |  
 
  | MA | 72.642009 |  
 
  | VA | 72.822945 |  
 
  | PA | 77.158904 |  
 
  | SC | 78.284932 |  
 
  | CA | 81.336986 |  
 
  | MO | 88.693151 |  
 
  | IA | 90.254795 |  
 
  | IL | 93.391781 |  
 
  +-------+-----------+  

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


ITeye推荐



点击浏览器前进、后退键时刷新页面而不读取缓存

$
0
0
如何在点击浏览器前进、后退键时刷新页面而不读取缓存
点击浏览器的后退键,总是会读取缓存,这样会导致有时候获取不到页面上的值,如果点击后退键时刷新页面而不读取缓存,这样就不会产生获取不到值的问题。
1、在jsp页面或者写在controller里面
<%
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","No-cache");
response.setDateHeader("Expires", -1);
response.setHeader("Cache-Control", "No-store");
%>
2、JavaScript解决方案(我没有试)

<script type="text/javascript" src="jquery-1.4.2.min.js"></script>

<script type="text/javascript">
$(document).ready(function(){
$("#test").click(
function (e){
     var ev = window.event;//获取event对象
  location.replace(this.href);
  ev.returnValue=false;
});
});
</script>
<a id="test" href="b.htm" >test1</a>


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


ITeye推荐



Viewing all 15843 articles
Browse latest View live


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