blogjava-凯发k8网页登录

blogjava-凯发k8网页登录http://www.blogjava.net/freeman1984/standing on the shoulders of giants zh-cnsat, 08 apr 2023 20:34:52 gmtsat, 08 apr 2023 20:34:52 gmt60 后续内容请移步公众号“duomi88” http://www.blogjava.net/freeman1984/archive/2019/04/03/433700.html疯狂疯狂wed, 03 apr 2019 01:55:00 gmthttp://www.blogjava.net/freeman1984/archive/2019/04/03/433700.html
定期发布一些技术和产品内容,欢迎关注交流。  阅读全文

疯狂 2019-04-03 09:55 发表评论
]]>
netty百万级推送服务(转)http://www.blogjava.net/freeman1984/archive/2016/07/13/431178.html疯狂疯狂wed, 13 jul 2016 02:03:00 gmthttp://www.blogjava.net/freeman1984/archive/2016/07/13/431178.htmlhttp://www.blogjava.net/freeman1984/comments/431178.htmlhttp://www.blogjava.net/freeman1984/archive/2016/07/13/431178.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/431178.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/431178.html转载自:http://www.open-open.com/lib/view/open1420623195375.html

1. 背景

1.1. 话题来源

最近很多从事移动互联网和物联网开发的同学给我发邮件或者微博私信我,咨询推送服务相关的问题。问题五花八门,在帮助大家答疑解惑的过程中,我也对问题进行了总结,大概可以归纳为如下几类:

  1. netty是否可以做推送服务器?
  2. 如果使用netty开发推送服务,一个服务器最多可以支撑多少个客户端?
  3. 使用netty开发推送服务遇到的各种技术问题。

由于咨询者众多,关注点也比较集中,我希望通过本文的案例分析和对推送服务设计要点的总结,帮助大家在实际工作中少走弯路。

1.2. 推送服务

移动互联网时代,推送(push)服务成为app应用不可或缺的重要组成部分,推送服务可以提升用户的活跃度和留存率。我们的手机每天接收到各种各样的广告和提示消息等大多数都是通过推送服务实现的。

随着物联网的发展,大多数的智能家居都支持移动推送服务,未来所有接入物联网的智能设备都将是推送服务的客户端,这就意味着推送服务未来会面临海量的设备和终端接入。

1.3. 推送服务的特点

移动推送服务的主要特点如下:

  1. 使用的网络主要是运营商的无线移动网络,网络质量不稳定,例如在地铁上信号就很差,容易发生网络闪断;
  2. 海量的客户端接入,而且通常使用长连接,无论是客户端还是服务端,资源消耗都非常大;
  3. 由于谷歌的推送框架无法在国内使用,android的长连接是由每个应用各自维护的,这就意味着每台安卓设备上会存在多个长连接。即便没有消息需要推送,长连接本身的心跳消息量也是非常巨大的,这就会导致流量和耗电量的增加;
  4. 不稳定:消息丢失、重复推送、延迟送达、过期推送时有发生;
  5. 垃圾消息满天飞,缺乏统一的服务治理能力。

为了解决上述弊端,一些企业也给出了自己的凯发天生赢家一触即发官网的解决方案,例如京东云推出的推送服务,可以实现多应用单服务单连接模式,使用alarmmanager定时心跳节省电量和流量。

2. 智能家居领域的一个真实案例

2.1. 问题描述

智能家居mqtt消息服务中间件,保持10万用户在线长连接,2万用户并发做消息请求。程序运行一段时间之后,发现内存泄露,怀疑是netty的bug。其它相关信息如下:

  1. mqtt消息服务中间件服务器内存16g,8个核心cpu;
  2. netty中boss线程池大小为1,worker线程池大小为6,其余线程分配给业务使用。该分配方式后来调整为worker线程池大小为11,问题依旧;
  3. netty版本为4.0.8.final。

2.2. 问题定位

首先需要dump内存堆栈,对疑似内存泄露的对象和引用关系进行分析,如下所示:

我们发现netty的scheduledfuturetask增加了9076%,达到110w个左右的实例,通过对业务代码的分析发现用户使用idlestatehandler用于在链路空闲时进行业务逻辑处理,但是空闲时间设置的比较大,为15分钟。

netty 的idlestatehandler会根据用户的使用场景,启动三类定时任务,分别是:readeridletimeouttask、 writeridletimeouttask和allidletimeouttask,它们都会被加入到nioeventloop的task队列中被调度 和执行。

由 于超时时间过长,10w个长链接链路会创建10w个scheduledfuturetask对象,每个对象还保存有业务的成员变量,非常消耗内存。用户的 持久代设置的比较大,一些定时任务被老化到持久代中,没有被jvm垃圾回收掉,内存一直在增长,用户误认为存在内存泄露。

事实上,我们进一步分析发现,用户的超时时间设置的非常不合理,15分钟的超时达不到设计目标,重新设计之后将超时时间设置为45秒,内存可以正常回收,问题解决。

2.3. 问题总结

如果是100个长连接,即便是长周期的定时任务,也不存在内存泄露问题,在新生代通过minor gc就可以实现内存回收。正是因为十万级的长连接,导致小问题被放大,引出了后续的各种问题。

事实上,如果用户确实有长周期运行的定时任务,该如何处理?对于海量长连接的推送服务,代码处理稍有不慎,就满盘皆输,下面我们针对netty的架构特点,介绍下如何使用netty实现百万级客户端的推送服务。

3. netty海量推送服务设计要点

作为高性能的nio框架,利用netty开发高效的推送服务技术上是可行的,但是由于推送服务自身的复杂性,想要开发出稳定、高性能的推送服务并非易事,需要在设计阶段针对推送服务的特点进行合理设计。

3.1. 最大句柄数修改

百万长连接接入,首先需要优化的就是linux内核参数,其中linux最大文件句柄数是最重要的调优参数之一,默认单进程打开的最大句柄数是1024,通过ulimit -a可以查看相关参数,示例如下:

[root@lilinfeng ~]# ulimit -a core file size          (blocks, -c) 0 data seg size           (kbytes, -d) unlimited scheduling priority             (-e) 0 file size               (blocks, -f) unlimited pending signals                 (-i) 256324 max locked memory       (kbytes, -l) 64 max memory size         (kbytes, -m) unlimited open files                      (-n) 1024  ......后续输出省略

当单个推送服务接收到的链接超过上限后,就会报“too many open files”,所有新的客户端接入将失败。

通过vi /etc/security/limits.conf 添加如下配置参数:修改之后保存,注销当前用户,重新登录,通过ulimit -a 查看修改的状态是否生效。

*  soft        nofile        1000000 *  hard        nofile        1000000

需要指出的是,尽管我们可以将单个进程打开的最大句柄数修改的非常大,但是当句柄数达到一定数量级之后,处理效率将出现明显下降,因此,需要根据服务器的硬件配置和处理能力进行合理设置。如果单个服务器性能不行也可以通过集群的方式实现。

3.2. 当心close_wait

从事移动推送服务开发的同学可能都有体会,移动无线网络可靠性非常差,经常存在客户端重置连接,网络闪断等。

在百万长连接的推送系统中,服务端需要能够正确处理这些网络异常,设计要点如下:

  1. 客户端的重连间隔需要合理设置,防止连接过于频繁导致的连接失败(例如端口还没有被释放);
  2. 客户端重复登陆拒绝机制;
  3. 服务端正确处理i/o异常和解码异常等,防止句柄泄露。

最 后特别需要注意的一点就是close_wait 过多问题,由于网络不稳定经常会导致客户端断连,如果服务端没有能够及时关闭socket,就会导致处于close_wait状态的链路过多。 close_wait状态的链路并不释放句柄和内存等资源,如果积压过多可能会导致系统句柄耗尽,发生“too many open files”异常,新的客户端无法接入,涉及创建或者打开句柄的操作都将失败。

下面对close_wait状态进行下简单介绍,被动关闭tcp连接状态迁移图如下所示:

图3-1 被动关闭tcp连接状态迁移图

close_wait 是被动关闭连接是形成的,根据tcp状态机,服务器端收到客户端发送的fin,tcp协议栈会自动发送ack,链接进入close_wait状态。但如果 服务器端不执行socket的close()操作,状态就不能由close_wait迁移到last_ack,则系统中会存在很多close_wait状 态的连接。通常来说,一个close_wait会维持至少2个小时的时间(系统默认超时时间的是7200秒,也就是2小时)。如果服务端程序因某个原因导 致系统造成一堆close_wait消耗资源,那么通常是等不到释放那一刻,系统就已崩溃。

导致close_wait过多的可能原因如下:

  1. 程序处理bug,导致接收到对方的fin之后没有及时关闭socket,这可能是netty的bug,也可能是业务层bug,需要具体问题具体分析;
  2. 关闭socket不及时:例如i/o线程被意外阻塞,或者i/o线程执行的用户自定义task比例过高,导致i/o操作处理不及时,链路不能被及时释放。

下面我们结合netty的原理,对潜在的故障点进行分析。

设 计要点1:不要在netty的i/o线程上处理业务(心跳发送和检测除外)。why? 对于java进程,线程不能无限增长,这就意味着netty的reactor线程数必须收敛。netty的默认值是cpu核数 * 2,通常情况下,i/o密集型应用建议线程数尽量设置大些,但这主要是针对传统同步i/o而言,对于非阻塞i/o,线程数并不建议设置太大,尽管没有最优 值,但是i/o线程数经验值是[cpu核数 1,cpu核数*2 ]之间。

假 如单个服务器支撑100万个长连接,服务器内核数为32,则单个i/o线程处理的链接数l = 100/(32 * 2) = 15625。 假如每5s有一次消息交互(新消息推送、心跳消息和其它管理消息),则平均caps = 15625 / 5 = 3125条/秒。这个数值相比于netty的处理性能而言压力并不大,但是在实际业务处理中,经常会有一些额外的复杂逻辑处理,例如性能统计、记录接口日 志等,这些业务操作性能开销也比较大,如果在i/o线程上直接做业务逻辑处理,可能会阻塞i/o线程,影响对其它链路的读写操作,这就会导致被动关闭的链 路不能及时关闭,造成close_wait堆积。

设计要点2:在i/o线程上执行自定义task要当心。netty的i/o处理线程nioeventloop支持两种自定义task的执行:

  1. 普通的runnable: 通过调用nioeventloop的execute(runnable task)方法执行;
  2. 定时任务scheduledfuturetask:通过调用nioeventloop的schedule(runnable command, long delay, timeunit unit)系列接口执行。

为什么nioeventloop要支持用户自定义runnable和scheduledfuturetask的执行,并不是本文要讨论的重点,后续会有专题文章进行介绍。本文重点对它们的影响进行分析。

在 nioeventloop中执行runnable和scheduledfuturetask,意味着允许用户在nioeventloop中执行非i/o操 作类的业务逻辑,这些业务逻辑通常用消息报文的处理和协议管理相关。它们的执行会抢占nioeventloop i/o读写的cpu时间,如果用户自定义task过多,或者单个task执行周期过长,会导致i/o读写操作被阻塞,这样也间接导致close_wait 堆积。

所 以,如果用户在代码中使用到了runnable和scheduledfuturetask,请合理设置ioratio的比例,通过 nioeventloop的setioratio(int ioratio)方法可以设置该值,默认值为50,即i/o操作和用户自定义任务的执行时间比为1:1。

我的建议是当服务端处理海量客户端长连接的时候,不要在nioeventloop中执行自定义task,或者非心跳类的定时任务。

设 计要点3:idlestatehandler使用要当心。很多用户会使用idlestatehandler做心跳发送和检测,这种用法值得提倡。相比于自 己启定时任务发送心跳,这种方式更高效。但是在实际开发中需要注意的是,在心跳的业务逻辑处理中,无论是正常还是异常场景,处理时延要可控,防止时延不可 控导致的nioeventloop被意外阻塞。例如,心跳超时或者发生i/o异常时,业务调用email发送接口告警,由于email服务端处理超时,导 致邮件发送客户端被阻塞,级联引起idlestatehandler的allidletimeouttask任务被阻塞,最终nioeventloop多 路复用器上其它的链路读写被阻塞。

对于readtimeouthandler和writetimeouthandler,约束同样存在。

3.3. 合理的心跳周期

百万级的推送服务,意味着会存在百万个长连接,每个长连接都需要靠和app之间的心跳来维持链路。合理设置心跳周期是非常重要的工作,推送服务的心跳周期设置需要考虑移动无线网络的特点。

当 一台智能手机连上移动网络时,其实并没有真正连接上internet,运营商分配给手机的ip其实是运营商的内网ip,手机终端要连接上internet 还必须通过运营商的网关进行ip地址的转换,这个网关简称为nat(network address translation),简单来说就是手机终端连接internet 其实就是移动内网ip,端口,外网ip之间相互映射。

ggsn(gateway gprs support note)模块就实现了nat功能,由于大部分的移动无线网络运营商为了减少网关nat映射表的负荷,如果一个链路有一段时间没有通信时就会删除其对应 表,造成链路中断,正是这种刻意缩短空闲连接的释放超时,原本是想节省信道资源的作用,没想到让互联网的应用不得以远高于正常频率发送心跳来维护推送的长 连接。以中移动的2.5g网络为例,大约5分钟左右的基带空闲,连接就会被释放。

由 于移动无线网络的特点,推送服务的心跳周期并不能设置的太长,否则长连接会被释放,造成频繁的客户端重连,但是也不能设置太短,否则在当前缺乏统一心跳框 架的机制下很容易导致信令风暴(例如微信心跳信令风暴问题)。具体的心跳周期并没有统一的标准,180s也许是个不错的选择,微信为300s。

在netty中,可以通过在channelpipeline中增加idlestatehandler的方式实现心跳检测,在构造函数中指定链路空闲时间,然后实现空闲回调接口,实现心跳的发送和检测,代码如下:

public void initchannel({@link channel} channel) {  channel.pipeline().addlast("idlestatehandler", new {@link   idlestatehandler}(0, 0, 180));  channel.pipeline().addlast("myhandler", new myhandler()); } 拦截链路空闲事件并处理心跳:  public class myhandler extends {@link channelhandleradapter} {      {@code @override}       public void usereventtriggered({@link channelhandlercontext} ctx, {@link object} evt) throws {@link exception} {           if (evt instanceof {@link idlestateevent}} {               //心跳处理           }       }   }

3.4. 合理设置接收和发送缓冲区容量

对于长链接,每个链路都需要维护自己的消息接收和发送缓冲区,jdk原生的nio类库使用的是java.nio.bytebuffer,它实际是一个长度固定的byte数组,我们都知道数组无法动态扩容,bytebuffer也有这个限制,相关代码如下:

public abstract class bytebuffer     extends buffer     implements comparable {     final byte[] hb; // non-null only for heap buffers     final int offset;     boolean isreadonly;

容 量无法动态扩展会给用户带来一些麻烦,例如由于无法预测每条消息报文的长度,可能需要预分配一个比较大的bytebuffer,这通常也没有问题。但是在 海量推送服务系统中,这会给服务端带来沉重的内存负担。假设单条推送消息最大上限为10k,消息平均大小为5k,为了满足10k消息的处 理,bytebuffer的容量被设置为10k,这样每条链路实际上多消耗了5k内存,如果长链接链路数为100万,每个链路都独立持有 bytebuffer接收缓冲区,则额外损耗的总内存 total(m) = 1000000 * 5k = 4882m。内存消耗过大,不仅仅增加了硬件成本,而且大内存容易导致长时间的full gc,对系统稳定性会造成比较大的冲击。

实际上,最灵活的处理方式就是能够动态调整内存,即接收缓冲区可以根据以往接收的消息进行计算,动态调整内存,利用cpu资源来换内存资源,具体的策略如下:

  1. bytebuffer支持容量的扩展和收缩,可以按需灵活调整,以节约内存;
  2. 接收消息的时候,可以按照指定的算法对之前接收的消息大小进行分析,并预测未来的消息大小,按照预测值灵活调整缓冲区容量,以做到最小的资源损耗满足程序正常功能。

幸运的是,netty提供的bytebuf支持容量动态调整,对于接收缓冲区的内存分配器,netty提供了两种:

  1. fixedrecvbytebufallocator:固定长度的接收缓冲区分配器,由它分配的bytebuf长度都是固定大小的,并不会根据实际数据报 的大小动态收缩。但是,如果容量不足,支持动态扩展。动态扩展是netty bytebuf的一项基本功能,与bytebuf分配器的实现没有关系;
  2. adaptiverecvbytebufallocator:容量动态调整的接收缓冲区分配器,它会根据之前channel接收到的数据报大小进行计算, 如果连续填充满接收缓冲区的可写空间,则动态扩展容量。如果连续2次接收到的数据报都小于指定值,则收缩当前的容量,以节约内存。

相对于fixedrecvbytebufallocator,使用adaptiverecvbytebufallocator更为合理,可以在创建客户端或者服务端的时候指定recvbytebufallocator,代码如下:

 bootstrap b = new bootstrap();             b.group(group)              .channel(niosocketchannel.class)              .option(channeloption.tcp_nodelay, true)              .option(channeloption.rcvbuf_allocator, adaptiverecvbytebufallocator.default)

如果默认没有设置,则使用adaptiverecvbytebufallocator。

另外值得注意的是,无论是接收缓冲区还是发送缓冲区,缓冲区的大小建议设置为消息的平均大小,不要设置成最大消息的上限,这会导致额外的内存浪费。通过如下方式可以设置接收缓冲区的初始大小:

/** 	 * creates a new predictor with the specified parameters. 	 *  	 * @param minimum 	 *            the inclusive lower bound of the expected buffer size 	 * @param initial 	 *            the initial buffer size when no feed back was received 	 * @param maximum 	 *            the inclusive upper bound of the expected buffer size 	 */ 	public adaptiverecvbytebufallocator(int minimum, int initial, int maximum) 

对于消息发送,通常需要用户自己构造bytebuf并编码,例如通过如下工具类创建消息发送缓冲区:

图3-2 构造指定容量的缓冲区

3.5. 内存池

推送服务器承载了海量的长链接,每个长链接实际就是一个会话。如果每个会话都持有心跳数据、接收缓冲区、指令集等数据结构,而且这些实例随着消息的处理朝生夕灭,这就会给服务器带来沉重的gc压力,同时消耗大量的内存。

最 有效的解决策略就是使用内存池,每个nioeventloop线程处理n个链路,在线程内部,链路的处理时串行的。假如a链路首先被处理,它会创建接收缓 冲区等对象,待解码完成之后,构造的pojo对象被封装成task后投递到后台的线程池中执行,然后接收缓冲区会被释放,每条消息的接收和处理都会重复接 收缓冲区的创建和释放。如果使用内存池,则当a链路接收到新的数据报之后,从nioeventloop的内存池中申请空闲的bytebuf,解码完成之 后,调用release将bytebuf释放到内存池中,供后续b链路继续使用。

使用内存池优化之后,单个nioeventloop的bytebuf申请和gc次数从原来的n = 1000000/64 = 15625 次减少为最少0次(假设每次申请都有可用的内存)。

下面我们以推特使用netty4的pooledbytebufallocator进行gc优化作为案例,对内存池的效果进行评估,结果如下:

垃圾生成速度是原来的1/5,而垃圾清理速度快了5倍。使用新的内存池机制,几乎可以把网络带宽压满。

netty4 之前的版本问题如下:每当收到新信息或者用户发送信息到远程端,netty 3均会创建一个新的堆缓冲区。这意味着,对应每一个新的缓冲区,都会有一个new byte[capacity]。这些缓冲区会导致gc压力,并消耗内存带宽。为了安全起见,新的字节数组分配时会用零填充,这会消耗内存带宽。然而,用零 填充的数组很可能会再次用实际的数据填充,这又会消耗同样的内存带宽。如果java虚拟机(jvm)提供了创建新字节数组而又无需用零填充的方式,那么我 们本来就可以将内存带宽消耗减少50%,但是目前没有那样一种方式。

在netty 4中实现了一个新的bytebuf内存池,它是一个纯java版本的  (facebook也在用)。现在,netty不会再因为用零填充缓冲区而浪费内存带宽了。不过,由于它不依赖于gc,开发人员需要小心内存泄漏。如果忘记在处理程序中释放缓冲区,那么内存使用率会无限地增长。

netty默认不使用内存池,需要在创建客户端或者服务端的时候进行指定,代码如下:

bootstrap b = new bootstrap();             b.group(group)              .channel(niosocketchannel.class)              .option(channeloption.tcp_nodelay, true)              .option(channeloption.allocator, pooledbytebufallocator.default)

使用内存池之后,内存的申请和释放必须成对出现,即retain()和release()要成对出现,否则会导致内存泄露。

值得注意的是,如果使用内存池,完成bytebuf的解码工作之后必须显式的调用referencecountutil.release(msg)对接收缓冲区bytebuf进行内存释放,否则它会被认为仍然在使用中,这样会导致内存泄露。

3.6. 当心“日志隐形杀手”

通常情况下,大家都知道不能在netty的i/o线程上做执行时间不可控的操作,例如访问数据库、发送email等。但是有个常用但是非常危险的操作却容易被忽略,那便是记录日志。

通 常,在生产环境中,需要实时打印接口日志,其它日志处于error级别,当推送服务发生i/o异常之后,会记录异常日志。如果当前磁盘的wio比较高,可 能会发生写日志文件操作被同步阻塞,阻塞时间无法预测。这就会导致netty的nioeventloop线程被阻塞,socket链路无法被及时关闭、其 它的链路也无法进行读写操作等。

以最常用的log4j为例,尽管它支持异步写日志(asyncappender),但是当日志队列满之后,它会同步阻塞业务线程,直到日志队列有空闲位置可用,相关代码如下:

 synchronized (this.buffer) {       while (true) {         int previoussize = this.buffer.size();         if (previoussize < this.buffersize) {           this.buffer.add(event);           if (previoussize != 0) break;           this.buffer.notifyall(); break;         }         boolean discard = true;         if ((this.blocking) && (!thread.interrupted()) && (thread.currentthread() != this.dispatcher)) //判断是业务线程         {           try           {             this.buffer.wait();//阻塞业务线程             discard = false;           }           catch (interruptedexception e)           {             thread.currentthread().interrupt();           }          }

类似这类bug具有极强的隐蔽性,往往wio高的时间持续非常短,或者是偶现的,在测试环境中很难模拟此类故障,问题定位难度非常大。这就要求读者在平时写代码的时候一定要当心,注意那些隐性地雷。

3.7. tcp参数优化

常用的tcp参数,例如tcp层面的接收和发送缓冲区大小设置,在netty中分别对应channeloption的so_sndbuf和so_rcvbuf,需要根据推送消息的大小,合理设置,对于海量长连接,通常32k是个不错的选择。

另外一个比较常用的优化手段就是软中断,如图所示:如果所有的软中断都运行在cpu0相应网卡的硬件中断上,那么始终都是cpu0在处理软中断,而此时其它cpu资源就被浪费了,因为无法并行的执行多个软中断。

图3-3 中断信息

大 于等于2.6.35版本的linux kernel内核,开启rps,网络通信性能提升20%之上。rps的基本原理:根据数据包的源地址,目的地址以及目的和源端口,计算出一个hash值, 然后根据这个hash值来选择软中断运行的cpu。从上层来看,也就是说将每个连接和cpu绑定,并通过这个hash值,来均衡软中断运行在多个cpu 上,从而提升通信性能。

3.8. jvm参数

最重要的参数调整有两个:

  • -xmx:jvm最大内存需要根据内存模型进行计算并得出相对合理的值;
  • gc相关的参数: 例如新生代和老生代、永久代的比例,gc的策略,新生代各区的比例等,需要根据具体的场景进行设置和测试,并不断的优化,尽量将full gc的频率降到最低。

4. 作者简介

李林锋,2007年毕业于东北大学,2008年进入华为公司从事高性能通信软件的设计和开发工作,有6年nio设计和开发经验,精通netty、mina等nio框架。netty中国社区创始人,《netty权威指南》作者。



疯狂 2016-07-13 10:03 发表评论
]]>
netty 概述(转)http://www.blogjava.net/freeman1984/archive/2016/07/13/431176.html疯狂疯狂wed, 13 jul 2016 01:49:00 gmthttp://www.blogjava.net/freeman1984/archive/2016/07/13/431176.htmlhttp://www.blogjava.net/freeman1984/comments/431176.htmlhttp://www.blogjava.net/freeman1984/archive/2016/07/13/431176.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/431176.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/431176.html转载自http://blog.csdn.net/zxhoo/article/details/17264263

netty4

是一个和类似的java nio框架,目前的最新版本是,这两个框架的主要作者好像都是。

channel


channel是netty最核心的接口,一个channel就是一个联络socket的通道,通过channel,你可以对socket进行各种操作。

channelhandler

用netty编写网络程序的时候,你很少直接操纵channel,而是通过channelhandler来间接操纵channel。

netty中的所有handler都实现自channelhandler接口。按照输出输出来分,分为channelinboundhandler、channeloutboundhandler两大类

channelinboundhandler对从客户端发往服务器的报文进行处理,一般用来执行解码、读取客户端数据、进行业务处理等;

channeloutboundhandler对从服务器发往客户端的报文进行处理,一般用来进行编码、发送报文到客户端。

channelpipeline

channelpipeline实际上应该叫做channelhandlerpipeline,可以把channelpipeline看成是一个chandlerhandler的链表,当需要对channel进行某种处理的时候,pipeline负责依次调用每一个handler进行处理。每个channel都有一个属于自己的pipeline,调用channel#pipeline()方法可以获得channel的pipeline,调用pipeline#channel()方法可以获得pipeline的channel。

channelpipeline的方法有很多,其中一部分是用来管理channelhandler的,如下面这些:

channelpipeline addfirst(string name, channelhandler handler); channelpipeline addlast(string name, channelhandler handler); channelpipeline addbefore(string basename, string name, channelhandler handler); channelpipeline addafter(string basename, string name, channelhandler handler); channelpipeline remove(channelhandler handler); channelhandler remove(string name); channelhandler removefirst(); channelhandler removelast(); channelpipeline replace(channelhandler oldhandler, string newname, channelhandler newhandler); channelhandler replace(string oldname, string newname, channelhandler newhandler); channelhandler first(); channelhandler last(); channelhandler get(string name);

channelhandlercontext

channelpipeline并不是直接管理channelhandler,而是通过channelhandlercontext来间接管理,这一点通过channelpipeline的默认实现defaultchannelpipeline可以看出来。

调用channelhandlercontext#channel()方法可以得到和context绑定的channel,调用channelhandlercontext#handler()方法可以得到和context绑定的handler。

channelpipeline和channelhandlercontext默认实现

defaultchannelhandlercontext和defaultchannelpipeline是channelhandlercontext和channelpipeline的默认实现,下面是它们的部分代码:

final class defaultchannelhandlercontext extends defaultattributemap implements channelhandlercontext {      volatile defaultchannelhandlercontext next;     volatile defaultchannelhandlercontext prev;      private final boolean inbound;     private final boolean outbound;     private final abstractchannel channel;     private final defaultchannelpipeline pipeline;     private final string name;     private final channelhandler handler;     private boolean removed;      // ... }
final class defaultchannelpipeline implements channelpipeline {     // ...      final defaultchannelhandlercontext head;     final defaultchannelhandlercontext tail;      // ... }

从上面的代码可以看出,在defaultpipeline内部,defaultchannelhandlercontext组成了一个双向链表:

再来看看defaultchannelpipeline的构造函数:

    public defaultchannelpipeline(abstractchannel channel) {         if (channel == null) {             throw new nullpointerexception("channel");         }         this.channel = channel;          tailhandler tailhandler = new tailhandler();         tail = new defaultchannelhandlercontext(this, null, generatename(tailhandler), tailhandler);          headhandler headhandler = new headhandler(channel.unsafe());         head = new defaultchannelhandlercontext(this, null, generatename(headhandler), headhandler);          head.next = tail;         tail.prev = head;     }

可以看到,defaultchinnelpipeline内部使用了两个特殊的handler来表示handler链的头和尾:

channelhandler的种类

netty中的所有handler都实现自channelhandler接口。按照输出输出来分,分为channelinboundhandler、channeloutboundhandler两大类。channelinboundhandler对从客户端发往服务器的报文进行处理,一般用来执行解码、读取客户端数据、进行业务处理等;channeloutboundhandler对从服务器发往客户端的报文进行处理,一般用来进行编码、发送报文到客户端。

从上面defaultchannelhandlercontext代码可以知道,handler实际上分为两种,inbound和outbound,这一点也可以从channelhandler接口的子接口得到证明:

public interface channelinboundhandler extends channelhandler {   // ... }  public interface channeloutboundhandler extends channelhandler {   // ... }


事件的传播

为了搞清楚事件如何在pipeline里传播,让我们从channel的抽象子类abstractchannel开始,下面是abstractchannel#write()方法的实现:

public abstract class abstractchannel extends defaultattributemap implements channel {     // ...     @override     public channel write(object msg) {         return pipeline.write(msg);     }     // ... }

abstractchannel直接调用了pipeline的write()方法:

final class defaultchannelpipeline implements channelpipeline {     // ...     @override     public channelfuture write(object msg) {         return tail.write(msg);     }     // ... }

因为write是个outbound事件,所以defaultchannelpipeline直接找到tail部分的context,调用其write()方法:

接着看defaultchannelhandlercontext的write()方法:

final class defaultchannelhandlercontext extends defaultattributemap implements channelhandlercontext {     // ...     @override     public channelfuture write(object msg) {         return write(msg, newpromise());     }      @override     public channelfuture write(final object msg, final channelpromise promise) {         if (msg == null) {             throw new nullpointerexception("msg");         }          validatepromise(promise, true);          write(msg, false, promise);          return promise;     }      private void write(object msg, boolean flush, channelpromise promise) {         defaultchannelhandlercontext next = findcontextoutbound();         next.invokewrite(msg, promise);         if (flush) {             next.invokeflush();         }     }      private defaultchannelhandlercontext findcontextoutbound() {         defaultchannelhandlercontext ctx = this;         do {             ctx = ctx.prev;         } while (!ctx.outbound);         return ctx;     }      private void invokewrite(object msg, channelpromise promise) {         try {             ((channeloutboundhandler) handler).write(this, msg, promise);         } catch (throwable t) {             notifyoutboundhandlerexception(t, promise);         }     }      // ... }

context的write()方法沿着context链往前找,直至找到一个outbound类型的context为止,然后调用其invokewrite()方法:

invokewrite()接着调用handler的write()方法:

最后看看channeloutboundhandleradapter的write()方法实现:

public class channeloutboundhandleradapter extends channelhandleradapter implements channeloutboundhandler {     // ...     @override     public void write(channelhandlercontext ctx, object msg, channelpromise promise) throws exception {         ctx.write(msg, promise);     }     // ... }

默认的实现调用了context的write()方法而不做任何处理,这样write事件就沿着outbound链继续传播:

可见,pipeline的事件传播,是靠pipeline,context和handler共同协作完成的。



疯狂 2016-07-13 09:49 发表评论
]]>
netty优雅退出机制和原理(转)http://www.blogjava.net/freeman1984/archive/2016/07/13/431175.html疯狂疯狂wed, 13 jul 2016 01:39:00 gmthttp://www.blogjava.net/freeman1984/archive/2016/07/13/431175.htmlhttp://www.blogjava.net/freeman1984/comments/431175.htmlhttp://www.blogjava.net/freeman1984/archive/2016/07/13/431175.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/431175.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/431175.html阅读全文

疯狂 2016-07-13 09:39 发表评论
]]>
使用javamail searchterm 收取邮件http://www.blogjava.net/freeman1984/archive/2016/02/02/429278.html疯狂疯狂tue, 02 feb 2016 08:08:00 gmthttp://www.blogjava.net/freeman1984/archive/2016/02/02/429278.htmlhttp://www.blogjava.net/freeman1984/comments/429278.htmlhttp://www.blogjava.net/freeman1984/archive/2016/02/02/429278.html#feedback1http://www.blogjava.net/freeman1984/comments/commentrss/429278.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/429278.htmljavamail在javax.mail.search包中定义了一个用于创建搜索条件的searchterm类,应用程序创建searchterm类的实例对象后,就可以调用folder.search(searchterm st)方法搜索邮件夹中符合搜索条件的所有邮件。searchterm是一个抽象类,javamail提供了22个实现子类以帮助应用程序创建不同的搜索条件,这22个类可分为两大类型,如下所示:

1、用于创建逻辑组合关系的类

  • and条件(andterm类)
  • or条件(orterm类)
  • not条件(notterm类)
  • comparison条件(comparisonterm类)

2、用于创建具体搜索条件的类

  • date条件(sentdateterm、receiveddateterm类)
  • content条件(bodyterm类)
  • header条件(fromstringterm、recipientstringterm、subjectterm类等)

下面通过实现来说明以上类的用法及含义:

1、搜索发件人为“智联招聘“,而且邮件正文包含“java工程师“的所有邮件

searchterm andterm = new andterm( new fromstringterm("智联招聘"), new bodyterm("java工程师")); message[] messages = folder.search(andterm); 

2、搜索发件人为“智联招聘“或主题包含“最新职位信息“的所有邮件

searchterm orterm = new orterm( new fromstringterm("智联招聘"), new subjectterm("最新职位信息")); message[] messages = folder.search(orterm); 

3、搜索发件人不包含“智联招聘“所有的邮件

searchterm notterm = new notterm(new fromstringterm("智联招聘")); message[] messages = folder.search(notterm); 

4、搜索周一到今天收到的的所有邮件

calendar calendar = calendar.getinstance();  
calendar.set(calendar.day_of_week, calendar.get(calendar.day_of_week - (calendar.day_of_week - 1)) - 1); 
date mondaydate = calendar.gettime(); 
searchterm comparisontermge = new sentdateterm(comparisonterm.ge, mondaydate); 
searchterm comparisontermle = new sentdateterm(comparisonterm.le, new date()); 
searchterm comparisonandterm = new andterm(comparisontermge, comparisontermle); 
message[] messages = folder.search(comparisonandterm); 

5、搜索大于或等100kb的所有邮件

int mailsize = 1024 * 100; searchterm intcomparisonterm = new sizeterm(integercomparisonterm.ge, mailsize); 
message[] messages = folder.search(intcomparisonterm); 

comparisonterm类常用于日期和数字比较中,它使用六个常量eq(=)、ge(>=)、gt(>)、le(<=)、lt(<)、ne(!=)来表示六种不同的比较操作。

下面是完整代码:

import java.io.bufferedreader;   
import java.io.inputstreamreader;   
import java.util.calendar;   
import java.util.date;   
import java.util.properties;   

import javax.mail.flags.flag;   
import javax.mail.folder;   
import javax.mail.message;   
import javax.mail.session;   
import javax.mail.store;   
import javax.mail.urlname;   
import javax.mail.internet.mimemessage;   
import javax.mail.internet.mimeutility;   
import javax.mail.search.andterm;   
import javax.mail.search.bodyterm;   
import javax.mail.search.comparisonterm;   
import javax.mail.search.fromstringterm;   
import javax.mail.search.integercomparisonterm;   
import javax.mail.search.notterm;   
import javax.mail.search.orterm;   
import javax.mail.search.searchterm;   
import javax.mail.search.sentdateterm;   
import javax.mail.search.sizeterm;   
import javax.mail.search.subjectterm;   

/** 
 * 搜索邮件 
 
*/   
public class searchmailtest {   

    public static void main(string[] args) throws exception {   
        properties props = new properties();   
        props.setproperty("mail.pop3.auth", "true");   
        session session = session.getinstance(props);   
        urlname url = new urlname("pop3", "pop3.163.com", 110, null, "xyang81@163.com", "yx546900873");   
        store store = session.getstore(url);   
        store.connect();   
        // 得到收件箱   
        folder folder = store.getfolder("inbox");   
        // 以读写模式打开收件箱   
        folder.open(folder.read_write);   

        message[] messages = search(folder);   

        system.out.println("收件箱中共有:"   folder.getmessagecount()   "封邮件,搜索到"   messages.length   "封符合条件的邮件!");   

        // 解析邮件搜索到的邮件   
        pop3receivemailtest.parsemessage(messages);    

        // 根据用户输入的条件搜索所有邮件,并提示用户是否删除   
        
//searchdemo(folder);   

        folder.close(true);   
        store.close();   
    }   
             www.2cto.com  
    public static message[] search(folder folder) throws exception {   
        // 搜索主题包含美食的邮件   
        string subject = "java培训";   
        searchterm subjectterm = new subjectterm(subject);   

        // 搜索发件人包含支付宝的邮件   
        searchterm fromterm = new fromstringterm("支付宝");   

        // 搜索邮件内容包含"招聘"的邮件   
        searchterm bodyterm = new bodyterm("招聘");   

        // 搜索发件人不包含“智联招聘”的邮件   
        searchterm notterm = new notterm(new fromstringterm("智联招聘"));   

        // 搜索发件人为“智联招聘”,而且内容包含“java工程师“的邮件   
        searchterm andterm = new andterm(   
                new fromstringterm("智联招聘"),   
                new bodyterm("java工程师"));   

        // 搜索发件人为”智联招聘“或主题包含”最新职位信息“的邮件   
        searchterm orterm = new orterm(   
                new fromstringterm("智联招聘"),    
                new subjectterm("最新职位信息"));   

        // 搜索周一到今天收到的的所有邮件   
        calendar calendar = calendar.getinstance();   
        calendar.set(calendar.day_of_week, calendar.get(calendar.day_of_week - (calendar.day_of_week - 1)) - 1);   
        date mondaydate = calendar.gettime();   
        searchterm comparisontermge = new sentdateterm(comparisonterm.ge, mondaydate);   
        searchterm comparisontermle = new sentdateterm(comparisonterm.le, new date());   
        searchterm comparisonandterm = new andterm(comparisontermge, comparisontermle);   

        // 搜索大于或等100kb的所有邮件   
        int mailsize = 1024 * 100;   
        searchterm intcomparisonterm = new sizeterm(integercomparisonterm.ge, mailsize);   

        return folder.search(intcomparisonterm);   
    }   
 }   
实例:根据用户输入的收件人(email地址或姓名)和主题作为搜索条件,并提示用户是否删除搜索到的邮件?  
[java] view plaincopy  
/** 
     * 根据用户输入的收件人地址(包括email地址和姓名)和主题作为搜索条件,并提示用户是否删除搜索到的邮件 
     * 
@param from 收件人 
     * 
@param subject 主题 
     
*/   
    public static void searchdemo(folder folder) throws exception {   
        string notifymsg = "收件箱中一共有"   folder.getmessagecount()   "封邮件。请选择操作:\n";   
        notifymsg  = "1、输入收件人\n"   "2、输入主题\n"   "3、开始搜索\n"   "4、退出";   
        system.out.println(notifymsg);   
        string from = null;   
        string subject = null;   
        bufferedreader reader = new bufferedreader(new inputstreamreader(system.in));   
        string oper = reader.readline().trim();   
        while(!(from != null && subject != null)) {   
            if ("4".equals(oper)) {   
                system.exit(0);   
            } else {   
                if ("1".equals(oper)) {   
                    system.out.print("请输入收件人:");   
                    from = reader.readline();   
                }  else if ("2".equals(oper)) {   
                    system.out.print("请输入主题:");   
                    subject = reader.readline();   
                } else if ("3".equals(oper)) {   
                    if (from == null || subject == null) {   
                        system.out.println("未输入搜索条件,无法进行搜索!");   
                    } else {   
                        break;   
                    }   
                }   
            }   
            system.out.print("请选择操作:");   
            oper = reader.readline().trim();   
        }   

        system.out.println("\n系统正在根据搜索条件查询所有邮件,请稍候\n");   

        // 根据输入的条件,创建searchterm实例对象   
        searchterm orterm = new orterm(   
                new fromstringterm(from),   
                new subjectterm(subject)   
                );   

        // 根据搜索条件得到搜索到的邮件列表   
        message[] messages = folder.search(orterm);   

        system.out.println("共搜索到"   messages.length   "封满足搜索条件的邮件!\n\n请选择操作:1、查看邮件\t 2、删除所有邮件");   

        string deletequestion = "是否要删除搜索到的邮件?(yes/no)";   

        string searchresultoper = reader.readline();   

        if ("1".equals(searchresultoper)) {   
            for (message message : messages) {   
                mimemessage msg = (mimemessage) message;   
                string sub = pop3receivemailtest.getsubject(msg);   
                system.out.println("开始查看第"   msg.getmessagenumber()   "封邮件");   
                system.out.println("主题: "   sub);   
                system.out.println("发件人: "   pop3receivemailtest.getfrom(msg));   
                system.out.println("收件人:"   pop3receivemailtest.getreceiveaddress(msg, null));   
                system.out.println("发送时间:"   pop3receivemailtest.getsentdate(msg, null));   
                system.out.println(deletequestion);   
                string answer = reader.readline();   
                if ("yes".equals(answer)) {   
                    msg.setflag(flag.deleted, true);   
                    system.out.println("邮件["   sub   "]删除成功!");   
                } else if ("no".equals(answer)) {   
                    system.out.println("第"   msg.getmessagenumber()   "封邮件查看完成!");   
                } else if ("stop".equals(answer)) {   
                    system.exit(0);   
                }   
                system.out.println();   
            }   
        } else {   
            system.out.println(deletequestion);   
            string answer = reader.readline();   
            if ("yes".equals(answer)) {   
                for (message message : messages) {   
                    string sub = mimeutility.decodetext(message.getsubject());   
                    message.setflag(flag.deleted, true);   
                    system.out.println("邮件["   sub   "]删除成功!");   
                }   
            }   
        }   
    }
}


疯狂 2016-02-02 16:08 发表评论
]]>
java thread dump 分析综述http://www.blogjava.net/freeman1984/archive/2015/12/14/428645.html疯狂疯狂mon, 14 dec 2015 10:04:00 gmthttp://www.blogjava.net/freeman1984/archive/2015/12/14/428645.htmlhttp://www.blogjava.net/freeman1984/comments/428645.htmlhttp://www.blogjava.net/freeman1984/archive/2015/12/14/428645.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/428645.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/428645.html阅读全文

疯狂 2015-12-14 18:04 发表评论
]]>
oracle 索引字段包含date类型,使用spring jdbc更新时不走索引,而是走table access full的问题http://www.blogjava.net/freeman1984/archive/2015/11/15/428197.html疯狂疯狂sat, 14 nov 2015 16:04:00 gmthttp://www.blogjava.net/freeman1984/archive/2015/11/15/428197.htmlhttp://www.blogjava.net/freeman1984/comments/428197.htmlhttp://www.blogjava.net/freeman1984/archive/2015/11/15/428197.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/428197.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/428197.html阅读全文

疯狂 2015-11-15 00:04 发表评论
]]>
ftp主动模式和被动模式的比较(转载)http://www.blogjava.net/freeman1984/archive/2015/02/07/422809.html疯狂疯狂sat, 07 feb 2015 05:06:00 gmthttp://www.blogjava.net/freeman1984/archive/2015/02/07/422809.htmlhttp://www.blogjava.net/freeman1984/comments/422809.htmlhttp://www.blogjava.net/freeman1984/archive/2015/02/07/422809.html#feedback1http://www.blogjava.net/freeman1984/comments/commentrss/422809.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/422809.html我们在上传文件或图片到主机的时候,都不可避免的用到ftp软件这个上传工具。在上传的时候,有两种传输方式大家一定不会不熟悉,那就是主动模式和被动模式,他们的英文简写是port和pasv。常常有客户无法上传的时候,我们都会告诉他们,把ftp软件被动模式前面的钩去掉再试试。这说明,ftp软件默认是使用被动模式的,这种传输模式较先进一些。而有些客户无法在这种模式下上传的原因,往往是由于他们电脑中安装了防火墙、杀毒软件或者他们电脑所在的局域网做了特殊限制造成。

首先我们要知道,我们在使用ftp上传、删除、修改文件的时候其实要经过两个过程。一个是命令连接,一个是数据传输。命令连接的过程是我们电脑->服务器,通常是我们电脑中一个随机的大于1024的端口->服务器的21端口。我们熟知的ftp的21端口就是建议命令连接时所必需的。对于这主动和被动这两种上传模式而言,命令连接的过程是完全相同的,其区别就在数据传输过程中。主动模式下,当需要传送数据时,客户端在命令连接上用port命令告诉服务器:“我打开了xxxx端口(通常是一个随机的大于1024的端口),你过来连接我”。于是服务器从20端口向客户端的这个端口发送连接请求,建立一条数据连接来传送数据,这就是服务器->客户电脑的过程。而在被动模式下,服务器在命令连接上用pasv命令告诉客户端:“我打开了xxxx端口(通常是在ftp服务端设置的一个高位端口范围),你过来连接我”。于是客户端向服务器的这个端口范围发送连接请求,建立一条数据连接来传送数据。

一句话,主动模式的ftp是指服务器主动连接客户端的数据端口,被动模式的ftp是指服务器被动地等待客户端连接自己的数据端口。故通过防火墙对高位端口的阻塞,会造成主动模式受限,被动模式比较先进。另外通过设置被动模式的端口范围,只开放一定的端口对外传输数据,也大大减少了服务器安全性暴露的风险。

那么,在哪里设置被动模式的端口范围呢。windows环境下的iis ftp的设置方法是:打开c:\windows\system32\inetsrv\metabase.xml,当然首先要停止iis admin service服务。然后编辑这个文件里面区块下的passiveportrange,比如设置成passiveportrange="50030-50039",这就指定了10个端口来传输数据。而linux环境下的proftp的设置方法是:打开proftp的配置文件,命令是vi /etc/proftpd.conf,然后在区块里面添加passiveports 50030 50039这样的一段文字,也和上面iis中的端口设置是一样的功能。

最后在说下这两种模式的优缺点。主动模式对ftp服务器的管理有利,但对客户端的管理不利。因为ftp服务器企图与客户端的高位随机端口建立连接,而这个端口很有可能被客户端的防火墙阻塞掉。被动模式对ftp客户端的管理有利,但对服务器端的管理不利。因为客户端要与服务器端建立两个连接,其中一个连到一个高位随机端口,而这个端口很有可能被服务器端的防火墙阻塞掉。比较明智的方法是,同时做好服务器和客户电脑的安全设置,尽量不要开防火墙,就能比较顺利的通过ftp上传和管理我们的文件。



疯狂 2015-02-07 13:06 发表评论
]]>
关于java rmi在多网卡下(或者启动360,百度,腾讯等wifi共享下)无法连接问题(java.rmi.connectexception: connection refused to host: xx)http://www.blogjava.net/freeman1984/archive/2014/12/05/421085.html疯狂疯狂fri, 05 dec 2014 06:08:00 gmthttp://www.blogjava.net/freeman1984/archive/2014/12/05/421085.htmlhttp://www.blogjava.net/freeman1984/comments/421085.htmlhttp://www.blogjava.net/freeman1984/archive/2014/12/05/421085.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/421085.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/421085.html场景:java rmi 在服务端者启动360 wifi共享,报错java.rmi.connectexception: connection refused to host: xx。
         也就是服务端在调用时使用了wifi共享网卡的地址。此地址在rmi客户端pc上无法ping通。(因为没有连接此wifi。当然rmi客户端pc如果连接此wifi是不会报错的)。
想关资料:

http://docs.huihoo.com/java/rmi/whitepage/index.html
比较全的解释rmi的英文资料:http://docs.oracle.com/javase/1.5.0/docs/guide/rmi/faq.html#netunknownhost
http://www.blogjava.net/shaolijun/archive/2007/05/22/119213.html

测试代码

(一)服务端:
  • 服务接口
import java.rmi.remote;
import java.rmi.remoteexception;
/**
 * rmi remote 接口
 * 
@author joe
 * @2014-12-5 @上午11:49:10
 
*/
public interface rmiinterface extends remote{
    
    public string say(string name) throws remoteexception;

}
  • 接口实现
import java.rmi.remoteexception;
import java.rmi.server.unicastremoteobject;


public class rmiserver extends unicastremoteobject implements rmiinterface{
    
    private static final long serialversionuid = 1l;

    protected rmiserver() throws remoteexception {
        super();
    }

    public string say(string name) throws remoteexception {
        return "hello," name;
    }
}
  • 发布服务
public static void main(string[] args) throws malformedurlexception, remoteexception, alreadyboundexception {
        rmiserver server=new rmiserver();
        locateregistry.createregistry(8808);  
        naming.rebind("//10.10.xx.xx:8808/sample-server", server);  
    }

(二)客户端 调用服务:

public static void main(string[] args) throws exception {
        rmiinterface server=(rmiinterface) naming.lookup("//10.10.116.xx:8808/sample-server");
        system.out.println(server.say("张三"));
    }

此时报错,java.rmi.connectexception: connection refused to host: 192.168.23.x。

rmi的调用原理基本如下:

大致翻译如下:首先客户端必须通过naming.lookup得到服务端服务的一个指针或者叫指针,一旦拥有的这个应用,客户端将使用服务的引用里面包含的主机名(ip)和端口来访问服务。
    也就是说:虽然我们就服务端的ip和端口去naming.lookup("//10.10.116.xx:8808/sample-server");,但是服务端返回的服务的引用里面包含的ip并不是lookup时的ip。
官方说法:
【in many  (all versions of the jdk except in  and the , java rmi may default to using an unresolvable server hostname (for example: unqualified names, windows internet naming service (wins) names, or unqualified dhcp names). when a java rmi client invokes a remote method using a reference that contains an unresolvable server hostname, the client will throw an unknownhostexception.】

in order to generate functional remote references, java rmi servers must be able to supply a fully qualified hostname or ip address that is resolvable from all java rmi clients (an example of a fully qualified hostname is foo.bar.com). if a java rmi program provides a remote callback operation, then that program serves a java rmi object and consequently, must be able to determine a resolvable hostname to use as its server hostname in the remote references it passes to java rmi clients. vms that make calls to applets that serve remote objects may throwunknownhostexceptions because the applet has failed to provide a usable server hostname.

if your java rmi application throws an unknownhostexception, you can look at the resulting stack trace to see if the hostname that the client is using to contact its remote server is incorrect or not fully qualified.【 if necessary, you can set the java.rmi.server.hostname property on the server to the correct ip address or hostname of the server machine and java rmi will use this property's value to generate remote references to the server.】

解决办法就是在服务端发布注册服务的之前设置:
system.setproperty("java.rmi.server.hostname", 指定ip);

对应到本文例子就是:
public static void main(string[] args) throws malformedurlexception, remoteexception, alreadyboundexception {
        rmiserver server=new rmiserver();
        system.setproperty("java.rmi.server.hostname", 指定ip);
        locateregistry.createregistry(8808);  
        naming.rebind("//10.10.116.74:8808/sample-server", server);  
    }

但是此时还是报相同的错没法访问,百思不得其解,原来java.rmi.server.hostname的设置必须在服务对象创建之前。
public static void main(string[] args) throws malformedurlexception, remoteexception, alreadyboundexception {
        system.setproperty("java.rmi.server.hostname", 指定ip);
        rmiserver server=new rmiserver();
        locateregistry.createregistry(8808);  
        naming.rebind("//10.10.116.74:8808/sample-server", server);  
    }
为什么呢:
    rmiserver 这个实现类使用了unicastremoteobject去联接rmi系统。在我们的例子中,我们是直接的从unicastremoteobject这个类上继承的,事实上并不一定要这样做,当然也可以不是从unicastrmeoteobject上继承,那必须使用它的exportobject()方法去联接到rmi。如果一个类继承自unicastremoteobject,那么它必须提供一个构造函数并且声明抛出一个remoteexception对象。当这个构造函数调用了super(),它久激活unicastremoteobject中的代码完成rmi的连接和远程对象的初始化。而此时应该已经决定了使用哪个hostname来实例化远程对象。因此必须在服务对象创建之前指定绑定的hostname。

~~~完。


疯狂 2014-12-05 14:08 发表评论
]]>
(转)oracle数据库如何授权收费(database licensing) http://www.blogjava.net/freeman1984/archive/2014/10/27/419097.html疯狂疯狂mon, 27 oct 2014 08:43:00 gmthttp://www.blogjava.net/freeman1984/archive/2014/10/27/419097.htmlhttp://www.blogjava.net/freeman1984/comments/419097.htmlhttp://www.blogjava.net/freeman1984/archive/2014/10/27/419097.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/419097.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/419097.html
说白了,oracle license就是一张纸,一张许可证。这跟许多软件产品是一样的,有没有许可证的产品在功能上是没有区别的,仅仅是一个法律问题。也就是说,随便到网上下的oracle都可以免费正常使用,只不过这个使用是有区别的,如果是测试或研发,那没关系,随便用;如果是用于商业用途,那就是违法的了,oracle公司有权起诉!

 

与免费的下载版本不同,正式版的oracle在购买后,用户会得到一个产品服务码,凭此oracle服务码,可以得到oracle的在线升级等服务,oracle的服务分为好多级,不同级别提供相对应的在线或是现场服务。

 

现在oracle有两种授权方式,按cpu(process)数和按用户数(nameduser plus)。前一种方式一般用于用户数不确定或者用户数量很大的情况,典型的如互联网环境,而后一种则通常被用于用户数确定或者较少的情况。

 

按cpu:license数=cpu数*系数。系数来自oracle的一个参数表,如ibm power6的处理器为1,amd和intel的处理器为0.5,详细情况见下:

参数

处理器型号

0.25

sun ultrasparc t1 处理器

0.50

sun ultrasparc t1处理器

0.50

intel、amd处理器

0.50

sun ultrasparc t2 处理器

1.00

ibm power6、power7 处理器

0.75

其他多核处理器

1.00

单核处理器

则根据公式可以算出,一个sun ultrasparc t1的4*8核处理器需要4*8*0.25=8个cpu licenses

 

按用户数:oracle数据库按照用户数授权,是指最终端的连接到oracle数据库的用户数。按照用户数来买的时候只能用于一个系统,不允许在多台机器上安装。每一个访问oracle数据库的用户,无论是自然人还是设备,都算作一个用户 (named user)。如果是b/s架构,那么是指连接到中间件上的用户数。

 

named user plus: is defined as anindividual authorized by you to use the programs which are installed on a singleserver or multiple servers, regardless of whether the individual is activelyusing the programs at any given time. a non human operated device will becounted.

 

按用户数购买则对应相应的产品有对应的license的最低购买量限制,如下:

产品

最低license数

oracle database standard edition one

5 named user plus licenses

oracle database standard edition

5 named user plus licenses

oracle database enterprise edition

25 named user plus licenses per cpu

oracle application server standard edition one

5 named user plus licenses

all other oracle application server products

10 named user plus licenses per cpu

当然用户应该根据自己的实际用户数订购,且不少于相应版本所要求的最低用户数。

一般情况下,1cpu的费用约等于50user的费用,所以如果用户数>cpu数*系数*50,则按cpu订购反而更为经济。

 

更换服务器,oraclelicense要重新购买吗?

如果用户是按照用户数购买的oracle,更改硬件不需要重新购买license;

如果是按照cpu个数买,有相应的换算方法,具体请咨询oracle公司。

 

这里是oracle 11g企业版的销售价格:

每个license还有有效期的分类(不论是user license还是cpu license),分别为:1年、2年、3年、4年、5年、永久。当然价格也是依次增加。

 

当前oracle 11g的user license无限使用期的价格为人民币3千5左右,按50个user license无限使用期的购买量则价格为17.5万;每个cpu license无限使用期的价格为17万9千,按ibm小机的系数计算,则购买价格为17万9千,和50个user license的价格相近。

 

关于服务价格:一般地,购买oracle的license都包含首年的服务费,以后的费用按每年原价的22%计算。

 

更多的产品价格可以访问http://shop.oracle.com查看。


这里我们再介绍一下如何查看服务器上物理cpu总数以及核数:

a.如果已安装了数据库实例,那么直接查看v$license视图即可:

sql> select cpu_count_current,cpu_core_count_current,cpu_socket_count_current from v$license;
cpu_count_current cpu_core_count_current cpu_socket_count_current
----------------- ---------------------- ------------------------
                2                      2                        1
以上通过v$license 视图反应了数据库服务器当前的逻辑cpu总数为2,而总的核数也是2,实际的物理cpu socket是1,那么说明是1个双核的物理cpu。


b. 如果服务器上尚没有部署实例则不能使用v$license视图,那么可以通过os 命令来获取必要的信息。

在x86 linux服务器上:


列出当前使用的物理cpu的个数:
grep core\ id /proc/cpuinfo | grep -c \ 0$ | grep ^0$ >> /dev/null && grep -c processor /proc/cpuinfo || \
grep core\ id /proc/cpuinfo | grep -c \ 0$
列出单个物理cpu的核数
grep "cpu cores" /proc/cpuinfo |uniq

 

 

在power系列的ibm小机上按照cpu模块方式来购买,在ibm dual-core module(双核模块)的power芯片上,一个双核模块(内含2颗物理cpu)只需要购买1.5个license , 具体的模块类型可以咨询ibm厂家或者集成商。

转自:

当然随着cpu计算能力的提高,cpu个数有可能并不需要太多。oracle的收费模式肯定会变化。



疯狂 2014-10-27 16:43 发表评论
]]>
成功的 web 应用系统性能测试 (转载)http://www.blogjava.net/freeman1984/archive/2014/10/07/418488.html疯狂疯狂tue, 07 oct 2014 13:09:00 gmthttp://www.blogjava.net/freeman1984/archive/2014/10/07/418488.htmlhttp://www.blogjava.net/freeman1984/comments/418488.htmlhttp://www.blogjava.net/freeman1984/archive/2014/10/07/418488.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/418488.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/418488.html

转载自:

性能测试是 web 应用系统的一项重要质量保证措施。在现实中,很多 web 性能测试项目由于性能测试需求定义不合理或不明确,导致性能测试项目不能达到预期目标或进度超期。本文针对 web 应用系统的技术架构和系统使用特点,探讨如何有效实施性能测试过程,并重点介绍如何分析获得合理的性能测试需求,最终对 web 应用系统性能进行科学、准确的评估。

基于web服务器的应用系统由于提供浏览器界面而无须安装,大大降低了系统部署和升级成本,得以普遍应用。目前,很多企业的核心业务系统均是web应用,但当web应用的数据量和访问用户量日益增加,系统不得不面临性能和可靠性方面的挑战。因此,无论是web应用系统的开发商或最终用户,都要求在上线前对系统进行性能,科学评价系统的性能,从而降低系统上线后的性能风险。

在很多性能测试项目中,由于不能合理定义系统的性能测试需求,不能建立和真实环境相符的负载模型,不能科学分析性能测试结果,导致性能测试项目持续时间很长或不能真正评价系统性能并提出性能改进措施。

本文在总结许多web应用系统性能测试实践经验和教训的基础上,从与性能测试工具无关的角度介绍web应用系统性能测试的方法和实施过程,以及如何定义合理的性能测试需求。

性能测试:通过模拟大量浏览器客户端同时访问web服务器,获得系统的性能数据。

虚拟用户:模拟浏览器向web服务器发送请求并接收响应的一个进程或线程。

响应时间:浏览器向web服务器提交一个请求到收到响应之间的间隔时间。

思考时间:浏览器在收到响应后到提交下一个请求之间的间隔时间。

请求成功率:web服务器正确处理的请求数量和接收到的请求数量的比。

吞吐量:单位时间内web服务器成功处理的http页面或http请求数量。

在线用户:用户通过浏览器访问登录web应用系统后,并不退出该应用系统。通常一个web应用服务器的在线用户对应web应用服务器的一个session。

并发用户数:web服务器在一段时间内为处理浏览器请求而建立的http连接数或生成的处理线程数。当所有在线用户发送http请求的思考时间为零时,web服务器的并发用户数等于在线用户数。

web应用系统的前端为浏览器,后台为web服务器(如apache,microsoft internet information server),浏览器和web服务器之间的交互基于http协议。http协议本身是无连接的,web服务器通过session机制来建立一个浏览器所发出的先后连接之间的关联。通过实验证明,当浏览器客户端在首次访问web服务器后,如果该浏览器客户端不发送后续请求,服务器维持该浏览器客户端的session变量所消耗的系统资源非常小。

标准的web应用系统性能测试过程包括确定性能测试需求,开发性能测试脚本,定义性能测试负载模型,执行性能测试和形成性能测试报告。本章将分别介绍上述过程,并通过举例说明如何完成每一环节。

科学定义web应用系统性能测试需求对一个成功的性能测试非常重要。通常,web应用系统的性能测试需求有如下两种描述方法。

2.1.1 基于在线用户的性能测试需求

该需求描述方法主要基于web应用系统的在线用户和响应时间来度量系统性能。当web应用系统在上线后所支持的在线用户数以及操作习惯(包括操作和请求之间的延迟)很容易获得,如企业的内部应用系统, 通常采用基于在线用户的方式来描述性能测试需求。以提供网上购物的web应用系统为例,基于在线用户的性能测试需求可描述为:10个在线用户按正常操作速度访问网上购物系统的下定单功能,下定单交易的成功率是100%,而且90%的下定单请求响应时间不大于8秒;当90%的请求响应时间不大于用户的最大容忍时间20秒时,系统能支持50个在线用户

2.1.2 基于吞吐量的性能测试需求

该需求描述方法主要基于web应用系统的吞吐量和响应时间来度量系统性能。当web应用在上线后所支持的在线用户无法确定,如基于internet的网上购物系统,可通过每天下定单的业务量直接计算其吞吐量,从而采取基于吞吐量的方式来描述性能测试需求。以网上购物系统为例,基于吞吐量的性能测试需求可描述为:网上购物系统在每分钟内需处理10笔下定单操作,交易成功率为100%,而且90%的请求响应时间不大于8秒

在确定web应用系统性能测试需求后,就要根据性能测试需求中确定的功能开发性能测试脚本。比如,针对前面定义的网上购物系统的性能测试需求,将开发下定单功能的性能测试脚本。

性能测试脚本是描述单个浏览器向web服务器发送的http请求序列。每个性能测试工具(如ibm rational performance tester, loadrunner)所提供的测试脚本语法是不同的。测试人员利用性能测试工具可从头手工编写测试脚本,也可以通过录制浏览器和web服务器之间的网络通信数据而自动形成测试脚本。

任何性能测试工具都不能保证录制形成的性能测试脚本的正确性,测试人员应通过在单用户下运行性能测试脚本对其正确性进行验证。测试脚本不正确的一个重要原因就是脚本的数据关联不正确,也就是并没完全建立一个测试请求和前面的响应内容之间的关联。测试脚本http请求和响应之间的数据关联是否正确的一个重要标准是单用户运行脚本,脚本能完成期望的功能。

在完成性能测试脚本的数据关联后,需要对脚本进行参数化,也就是把脚本中的某些请求数据替换成变量,变量的值来于一个独立的数据文件,从而保证在多虚拟用户运行脚本的情况下,每个虚拟用户所提交的数据是不同的。

此外,为了测试web应用的可靠性,还需要对请求所收到的响应进行验证(比如验证响应的http返回码或验证响应的内容),便于性能测试完成后统计请求成功率。

性能测试负载模型定义了测试工具如何向web应用系统提交请求,包括向web应用系统发送请求的虚拟用户数,每个虚拟用户发送请求的速度和频率。针对前面介绍的网上购物系统的性能测试需求,在性能测试工具中定义的性能测试负载模型应包括如下信息:

虚拟用户数:性能测试不仅仅是执行一次,而且每次执行时虚拟用户数也不固定,因此在性能测试负载模型中定义的虚拟用户数将在测试执行时进行设置。

虚拟用户发送请求的思考时间和迭代次数:虚拟用户发送请求的思考时间长短是决定web应用系统负载量的重要因素之一,而迭代次数将决定性能测试的执行持续时间。对基于在线用户的性能测试需求,将基于录制脚本时记录的思考时间,而且由于现实中不同用户访问系统的思考时间不同,可把思考时间设置为在一定范围内的随机值。对于基于吞吐量的性能测试需求,将把思考时间设置为零,此时web应用系统的在线用户数量将等于并发用户数。同时,为了避免性能测试压力的随机性,将增加请求的迭代次数来增加测试执行持续时间,从而获得系统在稳定压力下的性能数据。

虚拟用户启动模式:在现实中,web应用系统的用户不太可能同时做相同的操作,因此为了让web应用系统所承担的压力随时间均匀分布,建议虚拟用户依次启动,同时也避免大量用户同时登录造成系统阻塞。以10个虚拟用户模拟下定单为例,可设置每个虚拟用户间隔30秒启动,这样10个虚拟用户可在5分钟后完成启动,并保证10个虚拟用户不会在同一时刻下定单,从而更符合实际情况。

执行性能测试是指通过多次运行性能测试负载模型,获得系统的性能数据。在执行过程中,需利用测试工具、操作系统、系统软件(如web server或db server)提供的资源监控手段对资源进行监控和分析,帮助发现资源瓶颈,并在系统层面进行优化。同时,还需对应用进行性能分析,帮助定位应用代码中的性能问题,切实解决系统的性能问题。

性能测试项目的最后阶段就是向相关人员提交性能测试报告,汇报性能测试结果。在向相关人员汇报性能测试结果时,并不是性能测试报告越丰富、性能数据越多越好。好的性能测试报告是能准确、简单地传递性能测试结论,而不需太多的技术细节。

针对基于在线用户数的性能测试需求,可通过下图总结性能测试结论。其中横轴是在线用户数,纵轴是响应时间,如40在线用户访问网上购物系统时,90%的下定单请求响应时间不超过10秒。



 

针对基于吞吐量的性能测试需求,可通过下图的曲线来描述性能测试结果。以网上购物系统为例,下图描述下定单的并发用户、下定单响应时间以及吞吐量(服务器每秒处理定单笔数)之间的关系,从而快速判断系统是否能满足性能测试需求。从下图中可看出,并发用户增加,请求的响应时间也增加。服务器的吞吐量是先随并发用户数增加而增加,当吞吐量到达一定峰值后,再增加并发用户数,吞吐量会减少。原因在于当并发用户数少时,向web服务器提交的请求量不大,服务器处理能力还有富余,所以吞吐量逐步增大;但当并发用户数超过某一值时,由于向服务器提交的请求太多,造成服务器阻塞,反而导致吞吐量减少。



 

前一章介绍了web应用系统的性能测试过程,确定性能测试需求是整个性能测试的起点和成功的重要因素。性能测试需求定义得过高,虽然确保系统上线后能满足性能需求,但可能会造成硬件资源的浪费;性能测试需求定义得过低,系统上线后可能会出现性能问题。如何通过分析系统上线后可能的用户访问行为,来获得合理的性能测试需求指标呢?

假设现有一个基于web的办公自动化系统(简称oa系统),该系统提供公文收发和查询功能。在部署该系统前,将对该系统进行性能测试。下面将详细介绍如何分析该oa系统的使用情况,定义合理的性能测试需求。

在线用户数量是指在特定时间区间内,有多少用户访问web应用系统(对应到web服务器的session数),根据系统可能访问用户数以及每个用户访问系统的时间长短来确定。

对于将要部署的oa系统,通过分析获得该系统有8000个注册用户,基本上所有的用户每天(8小时工作时间)都会访问oa系统,平均在线时间(从登录oa系统到退出oa系统之间的时间间隔,也可以是多次在线时间的合计)为12分钟,那么该oa系统的平均在线数(也就是web应用session变量数)为200个(8000 * 0.2 / 8),假设峰值在线用户数是平均在线用户数的3倍(该倍数可根据实际情况调整),则性能测试需求的在线用户数为600。

由于时间和资源限制,不可能对web应用系统的所有功能进行性能测试,而是从业务的角度(如某一功能操作的用户多)和技术的角度(如某一功能虽然访问用户不多,但内部处理逻辑复杂或处理数据量大)来选择web应用系统的特定功能作为性能测试用例。

以oa系统为例,由于所有用户都经常公文查询功能,因此确定的性能测试用例为公文查询。

响应时间的快慢直接影响了系统使用用户的满意度,采用平均响应时间来描述系统系统性能测试需求是不科学的,因为无法直接和客户的满意度挂钩。而且,在做性能测试,如果某一请求的响应时间过长或过短,将导致平均响应时间和实际情况偏离。

以oa系统为例,定义的响应时间需求为:90%(该百分比和要求的系统用户满意度相关)的查询请求响应时间不超过8秒(该时间可根据实际情况确定)。

单位时间内web应用系统需处理多少笔特定的交易可通过统计获得。以oa系统为例,假设每个用户每天(一天按8小时计算)平均会查询公文4次,那oa应用的web服务器平均每分钟要能处理8000 * 4 / ( 60 * 8 ) = 66.67笔查询公文交易,考虑到峰值因素,要求每分钟能处理66.7 * 3=200笔查询公文交易。

通过前面的分析,能明确定义合理的性能测试需求。oa系统性能测试需求定义如下:

基于在线用户数的性能测试需求:600个在线用户按正常操作速度访问oa系统的查询公文功能,所有查询请求的成功率是100%,而且90%的查询请求响应时间不大于8秒。

基于吞吐量的性能测试需求:oa系统在每分钟内需处理200笔查询公文操作,交易成功率为100%,而且90%的请求响应时间不大于8秒。

web应用性能测试项目成功的关键不在于性能测试工具,而在于有效的性能测试分析方法和实践。只有切实掌握性能测试需求分析方法,性能测试实践经验,才能保证一个web应用性能测试的成功。



疯狂 2014-10-07 21:09 发表评论
]]>
it is indirectly referenced from required .class file异常http://www.blogjava.net/freeman1984/archive/2014/09/23/418187.html疯狂疯狂tue, 23 sep 2014 06:33:00 gmthttp://www.blogjava.net/freeman1984/archive/2014/09/23/418187.htmlhttp://www.blogjava.net/freeman1984/comments/418187.htmlhttp://www.blogjava.net/freeman1984/archive/2014/09/23/418187.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/418187.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/418187.html环境:maven项目。

突然出现异常,显示某个类无法引用,it is indirectly referenced from required .class file异常,通过跟踪到这个类,发现无法点击查看这个类,报:java.util.zip.zipexception: invalid loc header (bad signature)。

解决办法,删除本地仓库对应的jar包重新下载,即可!

疯狂 2014-09-23 14:33 发表评论
]]>
(转)svn cleanup failed–previous operation has not finished; run cleanup if it was interruptedhttp://www.blogjava.net/freeman1984/archive/2014/07/22/416070.html疯狂疯狂tue, 22 jul 2014 01:46:00 gmthttp://www.blogjava.net/freeman1984/archive/2014/07/22/416070.htmlhttp://www.blogjava.net/freeman1984/comments/416070.htmlhttp://www.blogjava.net/freeman1984/archive/2014/07/22/416070.html#feedback1http://www.blogjava.net/freeman1984/comments/commentrss/416070.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/416070.html转自:http://blog.csdn.net/luojian520025/article/details/22196865

svn提交遇到恶心的问题,可能是因为上次cleanup中断后,进入死循环了。

错误如下:

 

解决方法:清空svn的队列

1.下载 

2.找到你项目的.svn文件,查看是否存在wc.db

3.将sqlite3.exe放到.svn的同级目录

4.启动cmd执行sqlite3 .svn/wc.db "select * from work_queue"

5.看到很多记录,下一步执行delete from work_queue

 

6.ok了,现在在到项目里面,执行cleanup,完全没问题了,图标状态也已经恢复了。



疯狂 2014-07-22 09:46 发表评论
]]>
automation服务器不能创建对象 解决办法http://www.blogjava.net/freeman1984/archive/2014/03/21/411308.html疯狂疯狂fri, 21 mar 2014 01:39:00 gmthttp://www.blogjava.net/freeman1984/archive/2014/03/21/411308.htmlhttp://www.blogjava.net/freeman1984/comments/411308.htmlhttp://www.blogjava.net/freeman1984/archive/2014/03/21/411308.html#feedback1http://www.blogjava.net/freeman1984/comments/commentrss/411308.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/411308.htmljavascript脚本中报这个错误。应将ie的安全设置“不允许运行未标记为安全的activex控件”启用即可。


疯狂 2014-03-21 09:39 发表评论
]]>
error: transport error 202: gethostbyname: unknown host 解决办法http://www.blogjava.net/freeman1984/archive/2014/03/12/410935.html疯狂疯狂wed, 12 mar 2014 05:55:00 gmthttp://www.blogjava.net/freeman1984/archive/2014/03/12/410935.htmlhttp://www.blogjava.net/freeman1984/comments/410935.htmlhttp://www.blogjava.net/freeman1984/archive/2014/03/12/410935.html#feedback1http://www.blogjava.net/freeman1984/comments/commentrss/410935.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/410935.html

疯狂 2014-03-12 13:55 发表评论
]]>
javascript 跨浏览器事件处理(转)http://www.blogjava.net/freeman1984/archive/2013/12/28/408152.html疯狂疯狂sat, 28 dec 2013 07:19:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/12/28/408152.htmlhttp://www.blogjava.net/freeman1984/comments/408152.htmlhttp://www.blogjava.net/freeman1984/archive/2013/12/28/408152.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/408152.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/408152.html如果项目中没有使用诸如 jquery 之类的库,如何方便地为元素绑定事件,并兼容各种浏览器呢?下面这个简单的 utility 应该可以考虑。
var eventutility = {
	addevent : function(el, type, fn) {
		if(typeof addeventlistener !== "undefined") {
			el.addeventlistener(type, fn, false);
		} else if(typeof attachevent !== "undefined") {
			el.attachevent("on"  type, fn);
		} else {
			el["on"  type] = fn;
		}
	},
	removeevent : function(el, type, fn) {
		if(typeof removeeventlistener !== "undefined") {
			el.removeeventlistener(type, fn, false);
		} else if(typeof detachevent !== "undefined") {
			el.detachevent("on"  type, fn);
		} else {
			el["on"  type] = null;
		}
	},
	gettarget : function(event) {
		if(typeof event.target !== "undefined") {
			return event.target;
		} else {
			return event.srcelement;
		}
	},
	preventdefault : function(event) {
		if(typeof event.preventdefault !== "undefined") {
			event.preventdefault();
		} else {
			event.returnvalue = false;
		}
	}
};

使用方法示例:

var eventhandler = function(evt) {
	var target = eventutility.gettarget(evt),
		tagname = target.tagname;
	if(evt.type === "click") {
		if(tagname === "a" || tagname === "button") {
			alert("you clicked on an a element, and the innerhtml is "  target.innerhtml  "!");
			eventutility.preventdefault(evt);
		}
	} else if(evt.type === "mouseover" && tagname === "a") {
		alert("mouseovered "  target.innerhtml);
	}
	
};
eventutility.addevent(document, "click", eventhandler);
eventutility.addevent(document, "mouseover", eventhandler);
eventutility.removeevent(document, "mouseover", eventhandler);


疯狂 2013-12-28 15:19 发表评论
]]>
函数声明 vs 函数表达式(转)http://www.blogjava.net/freeman1984/archive/2013/12/28/408151.html疯狂疯狂sat, 28 dec 2013 07:16:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/12/28/408151.htmlhttp://www.blogjava.net/freeman1984/comments/408151.htmlhttp://www.blogjava.net/freeman1984/archive/2013/12/28/408151.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/408151.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/408151.html转自:
javascript 中需要创建函数的话,有两种方法:函数声明、函数表达式,各自写法如下:
// 方法一:函数声明
function foo() {}
// 方法二:函数表达式
var foo = function () {};

另外还有一种自执行函数表达式,主要用于创建一个新的作用域,在此作用域内声明的变量不会和其它作用域内的变量冲突或混淆,大多是以匿名函数方式存在,且立即自动执行:

(function () {
    // var x = ...
})();

此种自执行函数表达式归类于以上两种方法的第二种,也算是函数表达式。

方法一和方法二都创建了一个函数,且命名为 foo,但是二者还是有区别的。javascript 解释器中存在一种变量声明被提升(hoisting)的机制,也就是说变量(函数)的声明会被提升到作用域的最前面,即使写代码的时候是写在最后面,也还是会被提升至最前面。

例如以下代码段:

alert(foo); // function foo() {}
alert(bar); // undefined
function foo() {}
var bar = function bar_fn() {};
alert(foo); // function foo() {}
alert(bar); // function bar_fn() {}

输出结果分别是function foo() {}undefinedfunction foo() {}function bar_fn() {}

可以看到 foo 的声明是写在 alert 之后,仍然可以被正确调用,因为 javascript 解释器会将其提升到 alert 前面,而以函数表达式创建的函数 bar 则不享受此待遇。

那么bar 究竟有没有被提升呢,其实用 var 声明的变量都会被提升,只不过是被先赋值为 undefined 罢了,所以第二个 alert 弹出了 undefined

所以,javascript 引擎执行以上代码的顺序可能是这样的:

  1. 创建变量 foobar,并将它们都赋值为 undefined
  2. 创建函数 foo 的函数体,并将其赋值给变量 foo
  3. 执行前面的两个 alert。
  4. 创建函数 bar_fn,并将其赋值给 bar
  5. 执行后面的两个 alert。

注:

严格地说,再 javascript 中创建函数的话,还有另外一种方法,称为“函数构造法”:

var foo = function('alert("hi!");');
var foo = new function('alert("hi!");'); // 等同于上面一行

此方法以一个字符串作为参数形成函数体。但是用这种方法,执行效率方面会打折扣,且似乎无法传递参数,所以少用为妙。



疯狂 2013-12-28 15:16 发表评论
]]>
ora-06548错误http://www.blogjava.net/freeman1984/archive/2013/11/28/406952.html疯狂疯狂thu, 28 nov 2013 08:44:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/11/28/406952.htmlhttp://www.blogjava.net/freeman1984/comments/406952.htmlhttp://www.blogjava.net/freeman1984/archive/2013/11/28/406952.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/406952.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/406952.html原因:有可能是通过pl/sql等客户端执行管道输出函数(pipelined)造成,pl/sql客户端一般不会显示全部结果,管道输出挂起。

疯狂 2013-11-28 16:44 发表评论
]]>
项目规划与管理记录2 http://www.blogjava.net/freeman1984/archive/2013/11/18/406461.html疯狂疯狂mon, 18 nov 2013 04:51:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/11/18/406461.htmlhttp://www.blogjava.net/freeman1984/comments/406461.htmlhttp://www.blogjava.net/freeman1984/archive/2013/11/18/406461.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/406461.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/406461.htmlhttp://www.blogjava.net/cheneyfree/archive/2013/11/09/406169.html 
8、9、10三月,需求依旧爆棚,相比纯业务功能的开发,数据的汇聚、整理、分析、统计成为重点,具体细节不一一展开,按如下关键词:任务计划、项目沟通、项目流程、客户汇报、业务关注、时间评估、管理笔记做一些笔录,持续更新:
    1、任务计划:
      1.决策前考虑充分,决策后不再怀疑。
      2.任务精细、描述清晰,对内分解针对到负责人、给外汇报针对产品功能。
      3.计划制定时,请成员预审任务量,再和开发、测试确认时间,由成员承诺时间。
      4.安排任务多人完成时,指定一个牵头人。
      5.大的需求,组织讨论,小的需求,点对点沟通,最后要全部闸口到文档。
      6.任务分配:根据人员特点(善于思考型、认真较劲型、粗枝大叶型)安排难易程度对应的任务。
      7.紧急需求多时,一次尽可能给成员分配较少任务并约定完成时间、完成后再约定下一批任务。
      8.根据任务分配情况,了解到谁相对空闲,安排新任务给空闲的成员。
      9.碰到的问题整理一份清单,尽可能一次性地转交开发、测试人员。
      10.形成人员对开发任务的备份机制,不局限某个模块的开发,关注整体。
      11.每一轮版本,制定《风险管理清单》,协调、跟进、解决风险问题。
      12.加班如遇特殊情况不能陪同,托管工作给指定接口人。
      13.成员加班晚,第二天要根据情况保证人力参与白天工作。

   2、项目沟通
      1.重要事项亲历亲为。
      2.每周的项目周报,除了知悉公司外,同步知悉客户。
      3.需求的分析及客户确认用原型图。需求的确认启动开发,必须是业务部、信息化部、公司三方达成一致。
      4.技术实现上会就会,不会就不会,和时间的多少没有线性关系。
      5.控制好工作节奏,多和领导沟通,自己的经历正是人家过去经历的重现。
      6.沟通项目存在的问题时尊重客观事实和软件开发规律,做不完的跟客户说明原因,该拒绝的果断。
      7.上线前的客户运作,除了项目层面的客户沟通,也请领导高层向客户高层沟通。
      8.强调需求变更会影响项目计划的执行,变更需要付出代价(优先策略:时间调整、功能调整、人力调整)。
      9.遵循‘做得少但质量好,忌讳做的多却质量差’的原则。
      10.项目风险且一时对得不到公司资源倾斜时,也可借助客户的力量(下下策)。
      11.客户的跟催,多耐心少急躁,多自省少借口,多客观少主观。
      12.交付时间的答复,不能当下给出的待评估后再给。

   3、项目流程:
      需求频繁且变更较多时,建议0.5周~1周一轮版本。每次汇报后产生的新需求、遗留需求、待开发的功能:
      1.整理《项目跟踪表》,含:新需求、遗留问题、关联影响(指导测试做关联模块的验证)
      2.测试人员将已明确的开发任务登记到bug管理系统
      3.需求人员与开发、测试人员评审需求
      4.负责人牵头需求、开发、测试人员确定开发、测试、发布时间计划。
      5.与客户反馈交付时间点(对于小需求的交付时间可以直接答复、大需求要内部讨论再答复)。
      6.项目进度跟踪,以天为单位,测试人员更新《项目跟踪表》。
      7.需求验证介入
        1)开发提交代码后,需求人员、测试人员分别开展系统测试(业务角度,关注界面交互、主干流程、数据展现)、业务测试(系统角度,关注功能操作及边界、业务流程输入输出)。
        2)需求人员发现的问题,属于开发bug登记到bug管理系统、需求bug与开发人员、甚至客户澄清,如不影响版本发布时间直接加入在研版本、如影响则提交变更请求,与客户沟通得到变更认可或与开发沟通得到变更认可。
      9.升级前,针对环境数据的准备,编写《升级注意事项(含数据清理、数据准备等)》给测试人员交代。
      10.升级后,需求人员在演示环境走业务测试(建议离正式演示前2天完成升级验证,提前1天环境冻结。)
      11.上线前,与客户沟通准备事项(含压力测试、工程部署、资料交付、培训计划、试运行计划等)

   4、客户汇报:
      1.小型汇报,前一天,系统冻结,不再做升级,只做业务验证、回归。
      2.大型汇报,前三天,系统冻结,不再做升级,只做业务验证、回归。
      3.汇报前,拿用户的笔记本做预演,防止操作系统、浏览器兼容问题。
      4.汇报思路:
        1)以一个业务主线,给客户讲故事,结合不同客户关注的角度出发,让平台能融入不同客户的日常工作。
        2)信息化前是什么样,信息化后是什么样(提高日常办公的效率、平台统计的数据提升客户的业绩等)
      5.项目初期注意界面ui友好,中期注意数据的规范及业务流程正确,后期注重统计数据的真实、准确。
      6.版本的技术问题、项目进度的问题、用户需求的问题、沟通协调的问题,实事求是汇报客户。
      7.汇报前与客户业务接口人沟通汇报内容、汇报方式,掌握领导听取汇报时关注的点。
      8.汇报前,关注业务部门的需求、信息化部门的建议、更要关注客户领导的关心点。

   5、业务关注:
      1.关注平台的数据输入、处理、输出数据的完整性、准确性、关联性。
      2.抓住平台的建设理念,所有业务模块围绕该理念开展需求分析、设计。
      3.降低线上复杂的业务操作流程(更多考虑结果数据的录入、留痕,流程多了很难同时在线协同办公)。
      4.技术实现的同时要考虑业务机制的配套。
      5.考虑现实性[在现有业务规章制度的环境下运行]、前瞻性[业务层面的发展方向]、可行性[技术是否支持]。
      6.关注和周边平台的数据结构一致和标准
      7.<待补充>

   6、时间评估:
      1.每天列出当天要做的工作、每天下班前回顾当天工作,每周列出本周工作成果、下周工作计划。
      2.任务时间评估 = [成员评估 结合 自评估] 开发冗余(根据平台特性、开发人员经验及态度判断) 测试冗余(根据平台特性、测试人员经验及态度判断) 需求冗余(根据平台特性、需求人员经验及态度判断) 其他冗余(如请假、生病、开会等非项目因素)

   7、一些管理笔记:
      1.任务精准、给予空间
      2.狠抓住干、适当放权
      3.敢于承担、顶住冲击
      4.讲究诚信、承诺到位
      5.规则考核、理解包容
      6.用人不疑、疑人不用
      7.功劳大家、黑锅自己



疯狂 2013-11-18 12:51 发表评论
]]>
tmpfile.renameto(classfile) failedhttp://www.blogjava.net/freeman1984/archive/2013/10/21/405469.html疯狂疯狂mon, 21 oct 2013 09:42:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/10/21/405469.htmlhttp://www.blogjava.net/freeman1984/comments/405469.htmlhttp://www.blogjava.net/freeman1984/archive/2013/10/21/405469.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/405469.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/405469.html

疯狂 2013-10-21 17:42 发表评论
]]>
redhat6.4 64位安装rlwraphttp://www.blogjava.net/freeman1984/archive/2013/09/04/403675.html疯狂疯狂wed, 04 sep 2013 12:33:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/09/04/403675.htmlhttp://www.blogjava.net/freeman1984/comments/403675.htmlhttp://www.blogjava.net/freeman1984/archive/2013/09/04/403675.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/403675.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/403675.htmlftp://mirror.switch.ch/pool/1/mirror/epel/6/x86_64/rlwrap-0.37-1.el6.x86_64.rpm
2 rpm -ivh rlwrap-0.37-1.el6.x86_64.rpm
ok!

疯狂 2013-09-04 20:33 发表评论
]]>
ora-01031:insufficient privilegeshttp://www.blogjava.net/freeman1984/archive/2013/09/04/403674.html疯狂疯狂wed, 04 sep 2013 12:03:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/09/04/403674.htmlhttp://www.blogjava.net/freeman1984/comments/403674.htmlhttp://www.blogjava.net/freeman1984/archive/2013/09/04/403674.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/403674.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/403674.html如果密码文件存在的话(路径:$oracle_home/dbs/),去查看密码文件(格式:orapw$oracle_sid)里面sid和当前oracle_sid大小写是否一致。必须一样才行。
不一样是用mv 修改成一样的。即可。

相关文章参考。



疯狂 2013-09-04 20:03 发表评论
]]>
mysql远程连接问题 access denied for user 'root'@' ip ' (using password: yes)http://www.blogjava.net/freeman1984/archive/2013/07/12/401488.html疯狂疯狂fri, 12 jul 2013 02:43:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/07/12/401488.htmlhttp://www.blogjava.net/freeman1984/comments/401488.htmlhttp://www.blogjava.net/freeman1984/archive/2013/07/12/401488.html#feedback1http://www.blogjava.net/freeman1984/comments/commentrss/401488.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/401488.html服务器登录mysql,然后执行:

grant all privileges on *.* to'root'@'%' identified by 'root' with grant option;

flush privileges;

疯狂 2013-07-12 10:43 发表评论
]]>
dbcp重连(转) http://www.blogjava.net/freeman1984/archive/2013/06/06/400263.html疯狂疯狂thu, 06 jun 2013 01:38:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/06/06/400263.htmlhttp://www.blogjava.net/freeman1984/comments/400263.htmlhttp://www.blogjava.net/freeman1984/archive/2013/06/06/400263.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/400263.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/400263.html转自:

 

使用数据库连接池时,免不了会遇到断网、数据库挂掉等异常状况,当网络或数据库恢复时,若无法恢复连接池中的连接,那必然会是一场灾难。

关于dbcp的自动重连配置,网上相关的资料也不少,通过以下资料,并对照官方文档中的参数说明,大致能了解各项配置的含义,我就不冗诉了,本文的目的主要是对问题排查的经过做个简单的记录。
参考资料:

    测试环境:
    • dbcp版本——1.4
    • 数据库——postgressql 9.10(简称pg)
    • 本地(以下称为client)操作系统及数据库服务器(以下称为server)操作系统均为linux
    • server位于内网环境,client需要通过vpn或网线直连内网才能访问数据库

    首先模拟的是断网的情况
    在本地测试dbcp的重连配置时,发现断网后,连接池无法重建连接,分别试过testonborrow和testwhileidle两种validate方式,都没能解决,现象如下:
    1. 正常启动应用,在server端通过"select * from pg_stat_activity"查看连接数,会有initialsize个来自client的idle连接。——正常
    2. 在client端执行各种查询操作,连接数保持不变,且在server端的db log中能看到validate query。——正常
    3. 手动切断vpn,client与server断开,查询无法返回结果;然后重连,再次查看连接数,连接数仍保持不变,且连接的创建时间为断网前,即是说连接池认为之前的连接仍然有效,没有销毁旧连接&创建新连接。
    4. 此时在应用中执行各种查询操作,均无响应,等待一段时间后(分钟级),超时抛出异常:
    caused by: org.postgresql.util.psqlexception: an i/o error occured while sending to the backend.
    caused by: java.net.socketexception: connection timed out.
    5. 继续通过"select * from pg_stat_activity"查看连接数,隔一段时间后,连接消失。

    问题:断网后,仍留在线程池内的连接是否有效?若有效,为什么网络恢复后查询无响应?若无效,为何线程池没有发现并重新创建有效连接?
    排查过程:
    1.重连vpn后,通过netstat查看client至server的连接
    java代码  
    1. sudo netstat -antop | grep :5432  | grep java  
    注:5432为pg端口,grep java是为了过滤client上的其他形式的连接。
    发现连接数和在server端看到的连接数一致,且均为establish状态。
    2. 但在client上执行查询时,通过tcpdump查看client发往server的tcp请求,并无任何请求产生。
    java代码  
    1. sudo tcpdump -s 65535 -x -i eth0 host xxx.xxx.xxx  
    可见当前线程池中的连接实际上已经失效了,但dbcp仍认为它是有效的,因此仍在尝试用旧连接访问数据库,直至网络超时。

    于是,开始怀疑是vpn的问题,将client接上网线直连内网后,再次重试上述步骤,只是把断网的方式由切断vpn换成了拔网线,发现这次使用断网前的连接能够正常访问数据库,于是断定是vpn的问题,猜测是重连vpn后,虽然client端ip没有变,但路由的路径已经变了,之前的连接无法复用,但dbcp并不知道。对网络细节不是太熟悉,就不多加揣测了。

    接下来模拟数据库断开client连接的情况
    由于pg采用的是进程模型,与数据库建立的每一个连接都是单独的一个进程,故尝试采用kill进程的方式模拟数据库断开连接。
    预期的结果是:kill掉一个连接进程后,dbcp通过validate query发现该连接失效,将销毁该连接并重新创建新连接。
    但实际情况确是:kill掉一个连接后,所有连接全部被销毁。
    问题:究竟是数据库还是dbcp销毁了所有连接?
    排查过程:
    熟悉pg的同事认为pg之所以采用进程模型,就是为了避免连接之间的影响,因此不可能发生kill一个连接,其他连接也被销毁的情况。在这个理论前提下,问题就变得很诡异,因为dbcp的validate肯定是针对一个连接的,也不可能会在validate一个连接失效的情况下销毁所有连接,于是越想越偏,甚至开始怀疑是pg的jdbc driver有问题,最终放弃了深究。
    但我总觉得有点不太对劲,于是推翻之前的前提,开始怀疑是pg销毁了所有连接。于是,在使用连接池的应用之外,通过pg的数据库客户端psql连接db,这就建立了一个与dbcp无关的连接,接着继续在server端kill了一个连接池中的连接,继而发现psql创建的连接也被销毁了,这就能确定是pg在销毁连接,因为dbcp不可能控制自身范围之外的连接。
    后来才知道,pg之所以会这么做,是因为我们kill连接时使用的是kill -9(简称9杀),9杀太过粗暴,pg会重启很多内部进程,以保证所有进程正常,之前的连接也将会丢失,换用普通的kill命令,则不会发生以上情况。可见9杀很多情况下是十分危险的,试想一个线上db,若是9杀一个连接,后果不堪设想。。。

    总结
    说是dbcp问题排查,但大家可以看到最终问题的根源都跟dbcp没有什么关系。实际工作中的很多问题,关联的因素众多,需要有各方面的知识储备才能找到真正问题根源,否则就会把问题归结到一个自己不太了解的领域。
    另外,看到这篇文章中对连接池重连有两句不错的总结,引用一下:
    引用
    1. 数据库意外重启后,原先的数据库连接池能自动废弃老的无用的链接,建立新的数据库链接
    2. 网络异常中断后,原先的建立的tcp链接,应该能进行自动切换


    最后附上测试使用的dbcp配置。
    testonborrow配置:
    xml代码  
    1. <bean id="datasource" class="org.apache.commons.dbcp.basicdatasource" destroy-method="close">  
    2.         <property name="driverclassname" value="${jdbc.driver}" />  
    3.         <property name="url" value="${jdbc.url}"/>  
    4.         <property name="username" value="${jdbc.user}" />  
    5.         <property name="password" value="${jdbc.passwd}" />  
    6.         <property name="removeabandoned" value="true"/>  
    7.           <property name="initialsize" value="10" />  
    8.           <property name="maxidle" value="10" />  
    9.           <property name="minidle" value="10" />  
    10.            <property name="maxactive" value="30" />  
    11.            <property name="maxwait" value="30000" />  
    12.            <property name"testwhileidle" value="false" />  
    13.         <property name"testonborrow" value="true" />  
    14.         <property name"testonreturn" value="false" />  
    15.         <property name"validationquery" value="select 1" />  
    16.           
    17.     bean>  
    testwhileidle配置:
    xml代码  
    1. <bean id="datasource" class="org.apache.commons.dbcp.basicdatasource" destroy-method="close">  
    2.         <property name="driverclassname" value="${jdbc.driver}" />  
    3.         <property name="url" value="${jdbc.url}"/>  
    4.         <property name="username" value="${jdbc.user}" />  
    5.         <property name="password" value="${jdbc.passwd}" />  
    6.         <property name="removeabandoned" value="true"/>  
    7.           <property name="initialsize" value="10" />  
    8.           <property name="maxidle" value="10" />  
    9.           <property name="minidle" value="10" />  
    10.            <property name="maxactive" value="30" />  
    11.            <property name="maxwait" value="30000" />  
    12.            <property name"testwhileidle" value="true" />  
    13.         <property name"testonborrow" value="false" />  
    14.         <property name"testonreturn" value="false" />  
    15.         <property name"validationquery" value="select 1" />  
    16.           
    17.         <property name"timebetweenevictionrunsmillis" value="30000" />  
    18.         <property name"numtestsperevictionrun" value="30" />  
    19.         <property name="minevictableidletimemillis" value="1800000" />  
    20.     bean>  
    注:testonborrow只会发现当前连接失效,再创建一个连接供当前查询使用,而testwhileidle会定时校验numtestsperevictionrun个连接,只要发现连接失效,就将其移除再重新创建。

     



    疯狂 2013-06-06 09:38 发表评论
    ]]>
    解决vmware workstation上安装linux系统不能ssh远程连接的问题 http://www.blogjava.net/freeman1984/archive/2013/05/31/400040.html疯狂疯狂fri, 31 may 2013 13:18:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/05/31/400040.htmlhttp://www.blogjava.net/freeman1984/comments/400040.htmlhttp://www.blogjava.net/freeman1984/archive/2013/05/31/400040.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/400040.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/400040.html打开本机设备管理器中找到本机的网卡,打开其属性窗口,在高级标签中找到“ipv4 校验和分载传输”将其值置为“禁用”,按确认后可以通过ssh访问了。如图:


    疯狂 2013-05-31 21:18 发表评论
    ]]>
    url最大长度限制(转)http://www.blogjava.net/freeman1984/archive/2013/05/30/399942.html疯狂疯狂thu, 30 may 2013 01:29:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/05/30/399942.htmlhttp://www.blogjava.net/freeman1984/comments/399942.htmlhttp://www.blogjava.net/freeman1984/archive/2013/05/30/399942.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/399942.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/399942.html

    转自:

    1. url不能大于255bytes的说法确实存在,在中提到:

      the http protocol does not place any a priori limit on the length of a uri. servers must be able to handle the uri of any resource they serve, and should be able to handle uris of unbounded length if they provide get-based forms that could generate such uris. a server should return 414 (request-uri too long) status if a uri is longer than the server can handle (see section 10.4.15).

      note: servers ought to be cautious about depending on uri lengths above 255 bytes, because some older client or proxy implementations might not properly support these lengths.

    2. 从上一点也可以看出,255bytes的说法也是为了兼容性考虑。实际上现代浏览器的限制如下:

      microsoft internet explorer (browser)
      microsoft states that the maximum length of a url in internet explorer is , with no more than 2,048 characters in the path portion of the url. in my tests, attempts to use urls longer than this produced a clear error message in internet explorer.
      firefox (browser)
      after 65,536 characters, the location bar no longer displays the url in windows firefox 1.5.x. however, longer urls will work. i stopped testing after 100,000 characters.
      safari (browser)
      at least 80,000 characters will work. i stopped testing after 80,000 characters.
      opera (browser)
      at least 190,000 characters will work. i stopped testing after 190,000 characters. opera 9 for windows continued to display a fully editable, copyable and pasteable url in the location bar even at 190,000 characters.
      apache (server)
      my early attempts to measure the maximum url length in web browsers bumped into a server url length limit of approximately 4,000 characters, after which apache produces a “413 entity too large” error. i used the current up to date apache build found in red hat enterprise linux 4. the official apache documentation only mentions an 8,192-byte limit on an individual field in a request.
      microsoft internet information server
      the default limit is 16,384 characters (yes, microsoft’s web server accepts longer urls than microsoft’s web browser). this is configurable.
      perl http::daemon (server)
      up to 8,000 bytes will work. those constructing web application servers with perl’s http::daemon module will encounter a 16,384 byte limit on the combined size of all http request headers. this does not include post-method form data, file uploads, etc., but it does include the url. in practice this resulted in a 413 error when a url was significantly longer than 8,000 characters. this limitation can be easily removed. look for all occurrences of 16×1024 in daemon.pm and replace them with a larger value. of course, this does increase your exposure to denial of service attacks.

    3. 另外值得注意的是,有文章提到作为


      疯狂 2013-05-30 09:29 发表评论
      ]]>
      用myeclipse测试发送email时报java.lang.noclassdeffounderror: com/sun/mail/util/lineinputstream http://www.blogjava.net/freeman1984/archive/2013/05/28/399863.html疯狂疯狂tue, 28 may 2013 05:05:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/05/28/399863.htmlhttp://www.blogjava.net/freeman1984/comments/399863.htmlhttp://www.blogjava.net/freeman1984/archive/2013/05/28/399863.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/399863.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/399863.html将java ee5 替换为java ee6 即可。java ee5 里面不包含mail.jar等jar包。如果使用java ee5需要另外导入相关jar包。


      疯狂 2013-05-28 13:05 发表评论
      ]]>
      我应该采用哪一种 wsdl 样式?(转载)http://www.blogjava.net/freeman1984/archive/2013/05/27/399821.html疯狂疯狂mon, 27 may 2013 07:18:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/05/27/399821.htmlhttp://www.blogjava.net/freeman1984/comments/399821.htmlhttp://www.blogjava.net/freeman1984/archive/2013/05/27/399821.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/399821.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/399821.html转自ibm java dev
      ------------------
      web 服务是通过 wsdl 文档来描述的。wsdl 绑定描述了如何把服务绑定到消息传递协议(特别是 soap 消息传递协议)。wsdl soap 绑定可以是 rpc 样式的绑定,也可以是文档样式的绑定。同样,soap 绑定可以有编码的用法,也可以有文字的用法。这给我们提供了四种样式/用法模型:
      1. rpc/编码
      2. rpc/文字
      3. 文档/编码
      4. 文档/文字

      除了这些样式之外,还有一种样式也很常见,它称为文档/文字包装的样式,算上这一种,在创建 wsdl 文件时您就有了五种绑定样式可以从中选择。您应该选择哪一种呢?

      对于本文的讨论,让我们从 中的 java 方法开始,并且对其应用 jax-rpc java-to-wsdl 规则


      public void mymethod(int x);
      

      采用 中的方法并且使用您喜欢的 java-to-wsdl 工具来运行它,指定您想让它生成 rpc/编码的 wsdl。您最后应该得到如 所示的 wsdl 片断。


      
          
      
      
      
          
              
              
          
        
      

      现在用“5”作为参数 x 的值来调用此方法。我们将发送一个与 类似的 soap 消息。


      
          
              
                  5
              
          
      
      

      关于前缀和名称空间的注意事项

      为了简单起见,在本文的大部分 xml 示例中,我省略了名称空间和前缀。不过,我还是使用了少数前缀,您可以假定它们是用下列名称空间进行定义的。

      • xmlns:xsd="http://www.w3.org/2001/xmlschema"
      • xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
      • xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"

       

      对于这个 rpc/编码的示例中的 wsdl 和 soap 消息,有许多需要注意的事项:

      • wsdl 基本达到了尽可能地简单易懂的要求。
      • 操作名出现在消息中,这样接收者就可以很轻松地把消息发送到方法的实现。
      • 类型编码信息(比如 xsi:type="xsd:int" )通常就是降低吞吐量性能的开销。
      • 您不能简单地检验此消息的有效性,因为只有 5 行包含在 schema 中定义的内容;其余的 soap:body 内容都来自 wsdl 定义。

      有没有一种方法能够保留这些优点而消除其中的缺点呢?或许有。让我们来看一看 rpc/文字的样式。


      用于我们的方法的 rpc/文字的 wsdl 看起来与 rpc/编码的 wsdl 几乎一样(请参见 )。只是绑定的用法由 编码改为 文字。仅此而已。



      
          
      
      
      
          
              
              
          
        
      
            

      rpc/文字的 soap 消息又是怎样的呢(请参见 )?这里的更改要多一点。去掉了类型编码。



      
          
              
                  5
              
          
      
      

      下面是这种方法的优点和缺点:

      • wsdl 还是基本达到了尽可能地简单易懂的要求。
      • 操作名仍然出现在消息中。
      • 去掉了类型编码。
      • 您仍然不能简单地检验此消息的有效性,因为只有 5 行包含在 schema 中定义的内容;其余的 soap:body 内容都来自 wsdl 定义。

      文档样式如何呢?它们能够帮助克服这些困难吗?


      我不知道有谁懂得这种方法的真正含义。我也不知道这种方法的任何实现。它将可能从 wsdl 的后续版本中消失。所以我们还是讨论别的吧。


      文档/文字的 wsdl 对 rpc/文字的 wsdl 作了一些更改。它们之间的不同之处显示在 中。



              
          
              
          
      
      
          element="xelement"/>
      
      
      
          
              
              
          
        
      
            

      而现在的 soap 应该如 所示:



      
          
              5
          
      
      

      关于消息组成部分的注意事项

      我本来可以只更改绑定,就像我从 rpc/编码转到 rpc/所做的那样。它将是合法的 wsdl。然而,ws-i 基本概要(ws-i basic profile)(请参见 )规定文档/文字的消息的组成部分引用元素而不是类型,所以我遵循了 ws-i(并且此处使用元素部分可以很好地把我们带到关于文档/文字包装的样式的讨论)。

      下面是这种方法的优点和缺点:

      • 没有编码信息
      • 您可以在最后用任何 xml 检验器检验此消息的有效性。 soap:body5 )中每项内容都定义在 schema 中。
      • wsdl 变得有些复杂。不过,这是一个非常小的缺点,因为 wsdl 并没有打算由人来读取。
      • soap 消息中缺少操作名。而如果没有操作名,发送就可能比较困难,并且有时变得不可能。

      文档/文字的样式看起来似乎只是重新安排了 rpc/文字的模型的优点和缺点。您可以检验消息的有效性,但是您失去了操作名。有没有一种方法可以改进这一点呢?有的。它就是文档/文字包装的样式。


      在我说明文档/文字包装的样式的含义之前,让我给您展示 和 中的 wsdl 和 soap 消息。



      
          
              
              
                  
                      
                          
                      
                  
              
          
      
      
          
      
      
      
          
              
              
          
        
            

      wsdl schema 现在把参数放在包装中(请参见 )。



      
          
              
                  5
              
          
      

      注意到此 soap 消息看起来非常类似于 rpc/文字的 soap 消息。您可能会说,它看起来与 rpc/文字的 soap 消息是完全一样的,不过,这两种消息之间存在着微妙的区别。在 rpc/文字的 soap 消息中, 子句是操作的名称。在文档/文字包装的 soap 消息中, 子句是单个输入消息的组成部分引用的元素的名称。因此,包装的样式具有这样的一个特征,输入元素的名称与操作的名称是相同的。此样式是把操作名放入 soap 消息的一种巧妙方式。

      文档/文字包装的样式的特征有:

      • 输入消息只有一个组成部分。
      • 该部分就是一个元素。
      • 该元素有与操作相同的名称。
      • 该元素的复杂类型没有属性。

      下面是这种方法的优点和缺点:

      • 没有编码信息。
      • 出现在 soap:body 中的每项内容都是由 schema 定义的,所以您现在可以很容易地检验此消息的有效性。
      • 方法名又出现在 soap 消息中。
      • wsdl 甚至更复杂,但是这仍然是一个非常小的缺点。

      如您所见,文档/文字包装的样式还是有一些缺点,不过与优点比起来,它们都显得无足轻重。

      rpc/文字包装的样式?

      从 wsdl 的角度来看,没有理由只是把把包装的样式和文档/文字绑定联系在一起。它可以很容易地应用于 rpc/文字绑定。但是这样做是相当不明智的。soap 将包含操作的一个 mymethod 元素和元素名称的子 mymethod 元素。另外,即使它是一个合法的 wsdl,rpc/文字元素部分也不遵循 ws-i。


      这种包装的样式来源于 microsoft。没有定义这种样式的规范;所以虽然这种样式是一个好的东西,但不幸的是,为了与 microsoft 和其他公司的实现进行互操作,现在惟一的选择就是根据 microsoft wsdl 的输出来猜测它是如何工作的。文档/文字包装的样式也实现在 ibm websphere sdk for web services 中(请参见 )。在这个示例中,样式是相当明显的;但是也存在个别情况,在这些情况中,由于缺少定义而导致需要操作的适当事项不够特别清晰。我们希望看到的最理想的情况就是将来能有像 web 服务互操作组织(web services interoperability organization)这样的独立团体来帮助对此进行稳定化和标准化。


      至此,本文已经给了您这样的一个印象,文档/文字包装的样式是最好的方法。而实际的情况往往确实如此。不过,仍然存在着一些情况,在这些情况下,您最好是换一种别的样式。


      如果您已经重载了操作,就不能采用文档/文字包装的样式。

      想象一下,除了我们一直在使用的方法之外,还有另一种方法,请参见 。



      public void mymethod(int x);
              public void mymethod(int x, string y);
            

      关于重载的操作的注意事项

      wsdl 的下一个版本可能将不允许重载的操作。

      wsdl 允许重载的操作。但是当您添加包装的样式到 wsdl 时,需要元素有与操作相同的名称,并且在 xml 中不能有两个名称相同的元素。所以您必须采用文档/文字非包装的样式或某种 rpc 样式。


      由于文档/文字非包装的样式没有提供操作名,所以在有些情况下,您将需要采用某种 rpc 样式。比如说 中的一组方法。



      public void mymethod(int x);
      public void mymethod(int x, string y);
              public void someothermethod(int x);
            

      现在假定您的服务器接收到文档/文字的 soap 消息(您可以回过头在 中看一看它)。服务器应该发送哪一种方法呢?所有您能确切知道的就是,它一定不是 mymethod(int x, string x) ,因为消息只有一个参数,而这种方法需要两个参数。它可能是其他两种方法中的一种。采用文档/文字的样式,您没有办法知道是哪一种方法。

      假定服务器接收到一个 rpc/文字的消息(比如 中的),而不是文档/文字的消息。对于这种消息,服务器很容易决定把它发送到哪一种方法。您知道操作名是 mymethod,并且也知道只有一个参数,所以它必定是 mymethod(int x)


      采用 rpc/编码的理由有很多。其中两个主要的原因是:

      • 数据图形
      • 多态性

      数据图形

      设想您有一个二进制树,其中的节点定义在 中。



      
          
              
              
              
          
      
      

      根据这种节点定义,我们可以构造一个树形结构,它的根节点 a 通过它左边和右边的的链接可以指向节点 b(请参见 )。



      发送数据图形的标准方式是使用 href 标记,它是 rpc/编码的样式( )的一部分。



      
          b
          
          
      
      

      在任何文字的样式中,href 属性都是不可用的,这样图形链接就不再起作用了( 和 )。您仍然有一个根节点 a,它从左边指向一个节点 b,从右边指向另一个节点 b。这两个 b 节点是等同的,但它们不是相同的节点。是复制了数据而不是引用了两次数据。






      在文字样式中,您可以通过各种方法构造图形,但是却没有标准的方法;所以您做的任何事情很可能不能与网络中其他端点上的服务进行互操作。

      多态性

      看一看 中使用多态性 schema 的 wsdl。



      
        
          
            
              
            
          
          
            
              
                
                  
                
              
            
          
        
      
      
        
      
      
      
        
          
          
        
      

      当您把一个 dog 的实例传送给 train 操作时,所生成的 soap 消息必须包含类型编码信息,这样接收终端才能知道它所接收的是 animal 的哪一个扩展(请参见 )。这种类型编码信息可用在 rpc/编码的样式中。



      
        
          
            
              bob
              bloodhound
            
          
        
      


      有四种绑定样式(其实真正有五种,不过文档/编码的样式没有什么意义)。虽然每种样式都有自己的用武之地,但是在大多数情况下,最好的样式是文档/文字包装的样式。



      疯狂 2013-05-27 15:18 发表评论
      ]]>
      linux 挂载ntfs usb 出现mount: unknown filesystem type 'ntfs'http://www.blogjava.net/freeman1984/archive/2013/05/17/399427.html疯狂疯狂fri, 17 may 2013 10:06:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/05/17/399427.htmlhttp://www.blogjava.net/freeman1984/comments/399427.htmlhttp://www.blogjava.net/freeman1984/archive/2013/05/17/399427.html#feedback0http://www.blogjava.net/freeman1984/comments/commentrss/399427.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/399427.html问题:
            # mount –t ntfs /dev/sdc1 /mnt/usb
            mount: unknown filesystem type ‘ntfs’
      这是由于 上无法识别ntfs格式的分区。

      解决办法:
            通过使用 ntfs-3g 来解决。
            打开ntfs-3g的下载点http://www.tuxera.com/community/ntfs-3g-download/ ,将最新稳定ntfs-3g-2011.1.13下载到linux,
      执行以下命令安装:
            1) 编译安装 ntfs-3g:
      # tar zxvf  ntfs-3g-2011.1.13.tgz
      # cd ntfs-3g-2011.1.15
      #./configure
      #make
      #make install
      mount -t ntfs-3g  /dev/sdc1 /mnt/usb

      完~



      疯狂 2013-05-17 18:06 发表评论
      ]]>
      11g oracle 用户密码过期问题http://www.blogjava.net/freeman1984/archive/2013/04/23/398301.html疯狂疯狂tue, 23 apr 2013 09:25:00 gmthttp://www.blogjava.net/freeman1984/archive/2013/04/23/398301.htmlhttp://www.blogjava.net/freeman1984/comments/398301.htmlhttp://www.blogjava.net/freeman1984/archive/2013/04/23/398301.html#feedback1http://www.blogjava.net/freeman1984/comments/commentrss/398301.htmlhttp://www.blogjava.net/freeman1984/services/trackbacks/398301.html阅读全文

      疯狂 2013-04-23 17:25 发表评论
      ]]>
      网站地图