
十多年的JAVA码农,带着小组向前冲
默认,springboot已经支持了定时任务Schedule模块,所以一般情况已经完全能够满足我们的实际需求,一般来说,没有必要在加入其他类似于:quartz 另外,在这里提一个实际项目中,关于定时任务的架构上的一些考虑: 一般来说,实际项目中,为了提高服务的响应能力,我们一般会通过负载均衡的方式,或者反向代理多个节点的方式来进行。通俗点来说,我们一般会将项目部署多实例,或者说部署多份,每个实例不同的启动端口。但是每个实例的代码其实都是一样的。如果我们将定时任务写在我们的项目中,就会面临一个麻烦,就是比如我们部署了3个实例,三个实例一启动,就会把定时任务都启动,那么在同一个时间点,定时任务会一起执行,也就是会执行3次,这样很可能会导致我们的业务出现错误。 一般来说,我们有几种简单的办法来处理: 1、配置文件中增加自定义配置,通过开关来进行控制:比如增加:schedule=enable , schedule=disable,这样在我们的实际代码中,在进行判断,也就是我们可以通过配置,达到,只有一个实例真正执行定时任务,其他的是实例不执行。但是,这种做法实际是还是定时任务都启动,只是在执行中,我们人工来进行判断,执行于不执行真正的处理逻辑。 2、逻辑分离,就是我们将真正要定时任务处理的逻辑,写成rest服务,或者rpc服务,然后我们可以新建一个单独的定时任务项目,这个项目应该是没有任何的业务代码的,他纯粹只有定时任务功能,几点启动,或者每隔多少时间启动,启动后,通过rest或者rpc的方式,调用真正处理逻辑的服务。同时,我们甚至可以不用新建一个项目,我们通过linux的cron就可以进行。同时,这种方式还有一个好处,比如有些时候,我们的定时任务也会因为某些原因出现问题,没有执行,那么我们就可以通过curl 或者wget等等很多方式,再次定时任务的执行。 所以,个人一般偏向使用第二种方式,达到定时任务和业务处理的分离。 而在spring boot中,如何使用定时任务,相对比较简单。按第二种方式,实际上,我需要新建一个项目来完成定时任务的功能,其实,我们完全可以新建一个普通的java项目,引入quartz来达到,但是这里,我是通过spring boot来完成,新建一个spring boot项目,项目的初始化可以使用:http://start.spring.io 初始化之后,我们在spring boot的入口类Application.java中,允许支持schedule @SpringBootApplication @EnableScheduling public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 然后,新建一个执行类Jobs.java @Component public class Jobs { public final static long ONE_Minute = 60 * 1000; @Scheduled(fixedDelay=ONE_Minute) public void fixedDelayJob(){ System.out.println(Dates.format_yyyyMMddHHmmss(new Date())+" >>fixedDelay执行...."); } @Scheduled(fixedRate=ONE_Minute) public void fixedRateJob(){ System.out.println(Dates.format_yyyyMMddHHmmss(new Date())+" >>fixedRate执行...."); } @Scheduled(cron="0 15 3 * * ?") public void cronJob(){ System.out.println(Dates.format_yyyyMMddHHmmss(new Date())+" >>cron执行...."); } } 这是最简单的2种方式,多少分钟执行一次,fixedDelay和fixedRate,单位是毫秒,所以1分钟就是60秒×1000他们的区别在于,fixedRate就是每多次分钟一次,不论你业务执行花费了多少时间。我都是1分钟执行1次,而fixedDelay是当任务执行完毕后1分钟在执行。所以根据实际业务不同,我们会选择不同的方式。 而还有一类定时任务,比如是每天的3点15分执行,那么我们就需要用另外一种方式:cron表达式 cron表达式,有专门的语法,而且感觉有点绕人,不过简单来说,大家记住一些常用的用法即可,特殊的语法可以单独去查。cron一共有7位,但是最后一位是年,可以留空,所以我们可以写6位: * 第一位,表示秒,取值0-59 * 第二位,表示分,取值0-59 * 第三位,表示小时,取值0-23 * 第四位,日期天/日,取值1-31 * 第五位,日期月份,取值1-12 * 第六位,星期,取值1-7,星期一,星期二...,注:不是第1周,第二周的意思 另外:1表示星期天,2表示星期一。 * 第7为,年份,可以留空,取值1970-2099 cron中,还有一些特殊的符号,含义如下: (*)星号:可以理解为每的意思,每秒,每分,每天,每月,每年... (?)问号:问号只能出现在日期和星期这两个位置,表示这个位置的值不确定,每天3点执行,所以第六位星期的位置,我们是不需要关注的,就是不确定的值。同时:日期和星期是两个相互排斥的元素,通过问号来表明不指定值。比如,1月10日,比如是星期1,如果在星期的位置是另指定星期二,就前后冲突矛盾了。 (-)减号:表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12 (,)逗号:表达一个列表值,如在星期字段中使用“1,2,4”,则表示星期一,星期二,星期四 (/)斜杠:如:x/y,x是开始值,y是步长,比如在第一位(秒) 0/15就是,从0秒开始,每15秒,最后就是0,15,30,45,60 另:*/y,等同于0/y 下面列举几个例子供大家来验证: 0 0 3 * * ? 每天3点执行 0 5 3 * * ? 每天3点5分执行 0 5 3 ? * * 每天3点5分执行,与上面作用相同 0 5/10 3 * * ? 每天3点的 5分,15分,25分,35分,45分,55分这几个时间点执行 0 10 3 ? * 1 每周星期天,3点10分 执行,注:1表示星期天 0 10 3 ? * 1#3 每个月的第三个星期,星期天 执行,#号只能出现在星期的位置 作者:陶清清链接:http://www.jianshu.com/p/ef18af5a9c1d來源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
redis官网地址:http://www.redis.io/ 最新版本:2.8.3 在Linux下安装Redis非常简单,具体步骤如下(官网有说明): 1、下载源码,解压缩后编译源码。 $ wget http://download.redis.io/releases/redis-2.8.3.tar.gz $ tar xzf redis-2.8.3.tar.gz $ cd redis-2.8.3 $ make 2、编译完成后,在Src目录下,有四个可执行文件redis-server、redis-benchmark、redis-cli和redis.conf。然后拷贝到一个目录下。 mkdir /usr/redis cp redis-server /usr/redis cp redis-benchmark /usr/redis cp redis-cli /usr/redis cp redis.conf /usr/redis cd /usr/redis 3、启动Redis服务。 ./redis-server redis.conf 4、然后用客户端测试一下是否启动成功。 ./redis-cli redis> set foo bar OK redis> get foo "bar"
比特币交易涉及到很多密码学知识:公钥、私钥、哈希、对称加密、非对称加密、签名等等。那么哪些是需要用户认真保管不能对外泄露的,那些是需要用户公开的呢?先从钱包地址的生成说起。 1. 首先使用随机数发生器生成一个『私钥』。一般来说这是一个256bits的数,拥有了这串数字就可以对相应『钱包地址』中的比特币进行操作,所以必须被安全地保存起来。 2. 『私钥』经过SECP256K1算法处理生成了『公钥』。SECP256K1是一种椭圆曲线算法,通过一个已知『私钥』时可以算得『公钥』,而『公钥』已知时却无法反向计算出『私钥』。这是保障比特币安全的算法基础。 3. 同SHA256一样,RIPEMD160也是一种Hash算法,由『公钥』可以计算得到『公钥哈希』,而反过来是行不通的。 4. 将一个字节的地址版本号连接到『公钥哈希』头部(对于比特币网络的pubkey地址,这一字节为“0”),然后对其进行两次SHA256运算,将结果的前4字节作为『公钥哈希』的校验值,连接在其尾部。 5. 将上一步结果使用BASE58进行编码(比特币定制版本),就得到了『钱包地址』。 比如, 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa 『私钥』『公钥』『钱包地址』间的关系 在上述的五个步骤里只有“BASE58编码”有相应的可逆算法(“BASE58解码”),其他算法都是不可逆的,所以这些数据之间的关系可以表示为: 可以看到: 通过『私钥』可以得到上述计算过程中所有的值。『公钥哈希』和『钱包地址』可以通过互逆运算进行转换,所以它们是等价的。 使用『私钥』对交易进行签名 比特币钱包间的转账是通过交易(Transaction)实现的。交易数据是由转出钱包『私钥』的所有者生成,也就是说有了『私钥』就可以花费该钱包的比特币余额。生成交易的过程如下: 1. 交易的原始数据包括“转账数额”和“转入钱包地址”,但是仅有这些是不够的,因为无法证明交易的生成者对“转出钱包地址”余额有动用的权利。所以需要用『私钥』对原始数据进行签名。2. 生成“转出钱包公钥”,这一过程与生成『钱包地址』的第2步是一样的。3. 将“转出签名”和“转出公钥”添加到原始交易数据中,生成了正式的交易数据,这样它就可以被广播到比特币网络进行转账了。 使用『公钥』对签名进行验证 交易数据被广播到比特币网络后,节点会对这个交易数据进行检验,其中就包括对签名的校验。如果校验正确,那么这笔余额就成功地从“转出钱包”转移到“转入钱包”了。 小结 如果一个『钱包地址』从未曾发送余额到其他『钱包地址』,那么它的『公钥』是不会暴露在比特币网络上的。而公钥生成算法(SECP256K1)是不可逆的,即使『公钥』暴露,也很难对『私钥』的安全性造成影响(难易取决于『私钥』的生成算法)。 『私钥』用来生成『公钥』和『钱包地址』,也用来对交易进行签名。拥有了『私钥』就是拥有了对这个钱包余额的一切操作权力。 所以,保护『私钥』是所有比特币钱包应用最基本也是最重要的功能。
这个事情一下困扰了好几天,今天终于解决了。做个记录 事情的原因为在linux下环境下需要用流输出一个文件,其实这是是件很简单的事情。但麻烦的是输出的文件中文件内容的确被编码成了GBK,可是文件名却一直是utf-8。百思不得其解。也初步调过linux的字符集,确认了的确是GB2312.可是问题还是不能被解决。 回头还是以为程序的问题,把短的可怜的几段代码,反复测试,用byte【】把文件名都进行了GBK编码,然后字节输出,可是发现没有用。也试了改tomcat的强制gbk输出。 其实最后发现的问题,还是在于LINUX的字符集没有被真正改过来。 首先确认一件事情,我们常用的字节流编码,其实只能对流文件中的内容进行编码。 OutputStreamWriter fos = new OutputStreamWriter(new FileOutputStream(path),"GBK"); 很熟悉的代码 吧,其中PATH,也就是文件的输出名称,是不能被编码的!!决定这个文件名编码是你的操作系统当前编码格式。 关于字符集: 一般来说要设置 linux 系统的环境变量只需要在 /etc/profile (全局) 或者 ~/.bashrc (单个用户) 即可,但是在本案例中没有用。你如要用locale去查看,发现已经变成gb2312,但!!BUT!!这是没有用的。 首先需要确认系统是不是有该字符集包: /usr/share/i18n/charmaps(目录) 这个目录下存放了该linux操作系统可用字符集的安装包,如果你的操作系统上没有安装某个字符集可以到这个目录下寻找安装包。例如:GB2312的安装包名字为“GB2312.gz 修改字符编码配置文件 # vi /etc/sysconfig/i18n 修改后内容如下: LANG="zh_CN.GB2312"SUPPORTED="zh_CN.GB2312:zh_CN:zh:en_US:en"SYSFONT="latarcyrheb-sun16"我直接把UTF-8的全去掉了。 接下来就是最重要的,一定要重启。不要以为linux不用重启,用类似于source这种命令去重应用。没有用的。。太天真了。最后还是用 reboot重启了之后。才生效的。 我的天啊,花了两天的时间呢。
直接修改ANT的运行文件:ant.bat ,设置JAVA_HOME,这样所有的ANT都使用设定的JDK也是一种方法; 我以前做Jetty也是这么做的,挺方便; <javac srcdir="${src}" destdir="${build}" fork="yes" executable="/opt/java/jdk1.1/bin/javac" compiler="javac1.5" />
提起攻击,第一反应就是海量的流量、海量的报文。但有一种攻击却反其道而行之,以慢著称,以至于有些攻击目标被打死了都不知道是怎么死的,这就是慢速连接攻击。 slowhttptest是一款对服务器进行慢攻击的测试软件,包含了几种攻击方式,像Slowloris、SlowHTTP POST、Slow Read attack等。 总而言之,该工具的原理就是设法让服务器等待,当服务器在保持连接等待时,就消耗了资源。 1、 最具代表性的是rsnake发明的Slowloris,又被称为slow headers。 【攻击原理】 HTTP协议规定,HTTP Request以\r\n\r\n(0d0a0d0a)结尾表示客户端发送结束,服务端开始处理。那么,如果永远不发送\r\n\r\n会如何?Slowloris就是利用这一点来做DDoS攻击的。攻击者在HTTP请求头中将Connection设置为Keep-Alive,要求Web Server保持TCP连接不要断开,随后缓慢地每隔几分钟发送一个key-value格式的数据到服务端,如a:b\r\n,导致服务端认为HTTP头部没有接收完成而一直等待。如果攻击者使用多线程或者傀儡机来做同样的操作,服务器的Web容器很快就被攻击者占满了TCP连接而不再接受新的请求。 【工具演示】 用Wireshark抓包查看http请求头中有随机的key-value键值对,如下图红圈所示,且http请求头结尾不完整,是“0d 0a” 如果是正常的http请求头,结尾是“0d0a 0d 0a”,正常结束客户端请求 如下图所示 2、Slowloris的变种--Slow HTTP POST,也称为Slow body。 【攻击原理】 在POST提交方式中,允许在HTTP的头中声明content-length,也就是POST内容的长度。 在提交了头以后,将后面的body部分卡住不发送,这时服务器在接受了POST长度以后,就会等待客户端发送POST的内容,攻击者保持连接并且以10S-100S一个字节的速度去发送,就达到了消耗资源的效果,因此不断地增加这样的链接,就会使得服务器的资源被消耗,最后可能宕机。 【工具演示】 用Wireshark抓包可以看到,header结尾是正常的“0d 0a 0d 0a”,但Content-Length字段设置为一个很大的值8192,同时不在一个包中发送完整post数据而是每间隔100秒发送随机的key-value键值对。 3、Slow Read attack 【攻击原理】 采用调整TCP协议中的滑动窗口大小,来对服务器单次发送的数据大小进行控制,使得服务器需要对一个回应分成很多个包来发送。要使这种攻击效果更加明显,请求的资源要尽量大。 【工具演示】 用Wireshark抓包可以看出,当请求a.wmv资源(大小有9M多)时,客户端windowssize被刻意设置为1152字节。客户端缓冲区在被来自服务器的数据填满后,发出了[TCP ZeroWindow]告警,迫使服务端等待。 受到以上各种慢速攻击后,服务器再无法访问 1. 关于HTTP POST慢速DOS攻击 HTTP Post慢速DOS攻击第一次在技术社区被正式披露是今年的OWASP大会上,由Wong Onn Chee 和 Tom Brennan共同演示了使用这一技术攻击的威力。他们的slides在这里: http://www.darkreading.com/galleries/security/application-security/228400167/slide-show-ddos-with-the-slow-http-post-attack.html 这个攻击的基本原理如下: 针对任意HTTP Server,建立一个连接,指定一个比较大的content-length,然后以很低的速度发包,比如10-100s发一个字节,hold住这个连接不断开。如果客户端持续建立这样的连接,那么服务器上可用的连接将很快被占满,从而导致DOS. 这一攻击引起我注意的原因有这几点: 1. 它可以针对任意Web服务。HTTP协议在接收到request之前是无法对请求内容作校验的,所以即使你的Web应用没有可用form表单,这个攻击一样有效。 2. 廉价。在客户端以单线程方式建立较大数量的无用连接,并保持持续发包的代价非常低廉。实际试验中一台普通PC可以建立的Socket连接在3000个以上。这对一台普通的web server,将是致命的打击。更不用说结合肉鸡群做分布式DOS了。 2. 攻击示范 为演示这个攻击,我做了一个简单的POC,C#代码如下。 [csharp] view plain copy using System; using System.Collections.Generic; using System.Text; using System.Net.Sockets; using System.Threading; namespace HTTPPostDoS { class TestDemo { static void Main(string[] args) { string host = "target"; int port = 8080; int max_number_of_connection = 3000; List<TcpClient> clients = new List<TcpClient>(); for (int i = 0; i < max_number_of_connection; i++) { TcpClient client = new TcpClient(); clients.Add(client); client.Connect(host, port); if (client.Connected) { string header = "POST /a HTTP/1.1\r\n" + "HOST: " + host + "\r\n" + "Connection: keep-alive\r\n" + "Keep-Alive: 900\r\n" + "Content-Length: 100000000\r\n" + "Content_Type: application/x-www-form-urlencoded\r\n" + "Accept: *.*\r\n"; int sent = client.Client.Send(System.Text.Encoding.Default.GetBytes(header)); if (sent <= 0) { Console.WriteLine("Error while connecting to server"); } else { Console.WriteLine("Connected"); } } } while (true) { int i = 0; foreach (TcpClient client in clients) { i++; client.Client.Send(System.Text.Encoding.Default.GetBytes("a")); Console.WriteLine("Client " + i + " just sent a byte."); } Thread.Sleep(1000); } } } } 这段代码向目标服务器的示例Web Server发起攻击,每秒钟向服务器post一个字节以保证连接不会过期。 这个攻击对Apache的效果十分明显,Apache的maxClients几乎在瞬间被hold住,浏览器在攻击进行期间无法访问测试页面。 但是针对IIS的攻击被证明没有效果,同时我还测试了公司使用最多的Jetty,有了更多有意思的发现。 3. Jetty Server 在NIO和BIO模式下对此攻击的不同反应 在针对Jetty做测试时,我发现针对不同的Jetty Connector配置,攻击的效果有天壤之别。 我们知道Jetty在配置Connector时,可以有NIO和BIO两种模式供选择。当使用BIO模式时,Jetty的max thread被瞬间耗尽,服务停止。但是在使用NIO模式时,即使客户端的恶意socket连接数已经达到3000个,但是服务依然没有受到任何影响。这个应该很好理解,由于这两种模式下的Connector直接影响到服务端处理Socket的模型。IIS的情况我不是很清楚,但是猜测MS在实现时采用了NIO Socket模型。 详细的配置情况,请参见Jetty的官方文档。 其它的Web Server,我没有做进一步测试。如有兴趣请自行验证。 4. 检测与防范 目前针对Apache服务器,官方尚没有解决方案出台。建议: 1. 检查日志,查找类似的错误报警: [error] server reached MaxClients setting, consider raising the MaxClients setting 看看有没有被这种攻击盯上。 2. 调整防火墙设置。有条件的,在IPS上做一下规则设置。 注意这些都还只是暂时措施,因为这种攻击的变化可能很多,事实上使用HTTP GET也可以达到一样的效果。要知道GET也是可以传输数据的。 针对Jetty服务器,强烈建议使用NIO非阻塞模式,对服务器可以起到很好的保护作用。 相关阅读: 1. New HTTP POST DDoS Attack Tools Released 2. Wong Onn Chee 和Tom Brennan的Paper 3. Using HTTP POST for denial of service 补充1:我的同事,Active安全经理Eric Zhong和应用安全工程师Vian Ma对此文有贡献,谨此致谢 补充2:凤凰网的孙立先生指出,微软的IIS实现了完成端口(IOCP),IO效率相当高。这解释了为何此攻击对IIS无效。 原文地址:http://www.unclejoey.com/2010/12/28/http-post%E6%85%A2%E9%80%9Fdos%E6%94%BB%E5%87%BB%E5%88%9D%E6%8E%A2/
String.getBytes()的问题String 的getBytes()方法是得到一个字串的字节数组,这是众所周知的。但特别要注意的是,本方法将返回该操作系统默认的编码格式的字节数组。如果你在使 用这个方法时不考虑到这一点,你会发现在一个平台上运行良好的系统,放到另外一台机器后会产生意想不到的问题。比如下面的程序: class TestCharset { public static void main(String[] args) { new TestCharset().execute(); } private void execute() { String s = "Hello!你好!"; byte[] bytes = s.getBytes(); System.out.println("bytes lenght is:" + bytes.length); } } 在一个中文WindowsXP系统下,运行时,结果为: bytes lenght is:12 但是如果放到了一个英文的UNIX环境下运行: $ java TestCharset bytes lenght is:9 如果你的程序依赖于该结果,将在后续操作中引起问题。为什么在一个系统中结果为12,而在另外一个却变成了9了呢?上面已经提到了,该方法是和平台(编码)相关的。 在中文操作系统中,getBytes方法返回的是一个GBK或者GB2312的中文编码的字节数组,其中中文字符,各占两个字节。而在英文平台中,一般的默认编码是“ISO-8859-1”,每个字符都只取一个字节(而不管是否非拉丁字符)。 Java中的编码支持 Java是支持多国编码的,在Java中,字符都是以Unicode进行存储的,比如,“你”字的Unicode编码是“4f60”,我们可以通过下面的实验代码来验证: class TestCharset { public static void main(String[] args) { char c = '你'; int i = c; System.out.println(c); System.out.println(i); } } 不管你在任何平台上执行,都会有相同的输出: 20320 20320就是Unicode “4f60”的整数值。其实,你可以反编译上面的类,可以发现在生成的.class文件中字符“你”(或者其它任何中文字串)本身就是以Unicode编码进行存储的: char c = '/u4F60'; ... ... 即使你知道了编码的编码格式,比如: javac -encoding GBK TestCharset.java 编译后生成的.class文件中仍然是以Unicode格式存储中文字符或字符串的。使用String.getBytes(String charset)方法 所以,为了避免这种问题,我建议大家都在编码中使用String.getBytes(String charset)方法。下面我们将从字串分别提取ISO-8859-1和GBK两种编码格式的字节数组,看看会有什么结果: [java] view plain copy package org.bruce.file.handle.experiment; class TestCharset3 { public static void main(String[] args) { new TestCharset3().execute(); } private void execute() { String s = "Hello!你好!"; byte[] bytesISO8859 = null; byte[] bytesGBK = null; try { bytesISO8859 = s.getBytes("iso-8859-1"); bytesGBK = s.getBytes("GBK"); } catch (java.io.UnsupportedEncodingException e) { e.printStackTrace(); } System.out.println("-------------- /n 8859 bytes:"); System.out.println("bytes is: " + arrayToString(bytesISO8859)); System.out.println("hex format is:" + encodeHex(bytesISO8859)); System.out.println(); System.out.println("-------------- /n GBK bytes:"); System.out.println("bytes is: " + arrayToString(bytesGBK)); System.out.println("hex format is:" + encodeHex(bytesGBK)); } public static final String encodeHex(byte[] bytes) { StringBuffer buff = new StringBuffer(bytes.length * 2); String b; for (int i = 0; i < bytes.length; i++) { b = Integer.toHexString(bytes[i]); // byte是两个字节的, 而上面的Integer.toHexString会把字节扩展为4个字节 buff.append(b.length() > 2 ? b.substring(6, 8) : b); buff.append(" "); } return buff.toString(); } public static final String arrayToString(byte[] bytes) { StringBuffer buff = new StringBuffer(); for (int i = 0; i < bytes.length; i++) { buff.append(bytes[i] + " "); } return buff.toString(); } } 执行上面程序将打印出: -------------- 8859 bytes: bytes is: 72 101 108 108 111 33 63 63 63 hex format is:48 65 6c 6c 6f 21 3f 3f 3f -------------- GBK bytes: bytes is: 72 101 108 108 111 33 -60 -29 -70 -61 -93 -95 hex format is:48 65 6c 6c 6f 21 c4 e3 ba c3 a3 a1 可见,在s中提取的8859-1格式的字节数组长度为9,中文字符都变成了“63”,ASCII码为63的是“?”,一些国外的程序在国内中文环境下运行时,经常出现乱码,上面布满了“?”,就是因为编码没有进行正确处理的结果。 而提取的GBK编码的字节数组中正确得到了中文字符的GBK编码。字符“你”“好”“!”的GBK编码分别是:“c4e3”“bac3”“a3a1”。得到了正确的以GBK编码的字节数组,以后需要还原为中文字串时,可以使用下面方法: new String(byte[] bytes, String charset) mysql 不支持 unicode,所以比较麻烦。 将 connectionString 设置成 encoding 为 gb2312 String connectionString = "jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=gb2312"; 测试代码: String str = "汉字"; PreparedStatement pStmt = conn.prepareStatement("INSERT INTO test VALUES (?)"); pStmt.setString(1,str); pStmt.executeUpdate(); 数据库表格: create table test ( name char(10) ) 连接 Oracle Database Server ------------------------------------------------------------------------------- 在把汉字字符串插入数据库前做如下转换操作: String(str.getBytes("ISO8859_1"),"gb2312") 测试代码: String str = "汉字"; PreparedStatement pStmt = conn.prepareStatement("INSERT INTO test VALUES (?)"); pStmt.setString(1,new String(str.getBytes("ISO8859_1"),"gb2312"); pStmt.executeUpdate(); Servlet ------------------------------------------------------------------------------- 在 Servlet 开头加上两句话: response.setContentType("text/html;charset=UTF-8"); request.setCharacterEncoding("UTF-8"); JSP -------------------------------------------------------------------------------
1. Get方法长度限制 Http Get方法提交的数据大小长度并没有限制,HTTP协议规范没有对URL长度进行限制。这个限制是特定的浏览器及服务器对它的限制。 如:IE对URL长度的限制是2083字节(2K+35)。 下面就是对各种浏览器和服务器的最大处理能力做一些说明. Microsoft Internet Explorer (Browser) IE浏览器对URL的最大限制为2083个字符,如果超过这个数字,提交按钮没有任何反应。Firefox (Browser) 对于Firefox浏览器URL的长度限制为65,536个字符。 Safari (Browser) URL最大长度限制为 80,000个字符。 Opera (Browser) URL最大长度限制为190,000个字符。 Google (chrome) URL最大长度限制为8182个字符。 Apache (Server) 能接受最大url长度为8,192个字符。 Microsoft Internet Information Server(IIS) 能接受最大url的长度为16,384个字符。 通过上面的数据可知,为了让所有的用户都能正常浏览, URL最好不要超过IE的最大长度限制(2083个字符),当然,如果URL不直接提供给用户,而是提供给程序调用,这时的长度就只受Web服务器影响了。 注:对于中文的传递,最终会为urlencode后的编码形式进行传递,如果浏览器的编码为UTF8的话,一个汉字最终编码后的字符长度为9个字符。 因此如果使用的 GET 方法,最大长度等于URL最大长度减去实际路径中的字符数。 2. POST方法长度限制 理论上讲,POST是没有大小限制的。HTTP协议规范也没有进行大小限制,起限制作用的是服务器的处理程序的处理能力。 如:在Tomcat下取消POST大小的限制(Tomcat默认2M); 打开tomcat目录下的conf目录,打开server.xml 文件,修改 debug="0" acceptCount="100" connectionTimeout="20000" disableUploadTimeout="true" port="8080" redirectPort="8443" enableLookups="false" minSpareThreads="25" maxSpareThreads="75" maxThreads="150" maxPostSize="0" URIEncoding="GBK" > 增加红色字体部分 maxPostSize="0" (设为0是取消POST的大小限制) 刚看到群里又有同学在说 HTTP 协议下的 Get 请求参数长度是有大小限制的,最大不能超过 XX,而 Post 是无限制的,看到这里,我想他们定是看多了一些以讹传讹的博客或者书籍, 导致一种理解上的误区: 1、首先即使有长度限制,也是限制的是整个 URI 长度,而不仅仅是你的参数值数据长度。 2、HTTP 协议从未规定 GET/POST 的请求长度限制是多少。 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. 3、所谓的请求长度限制是由浏览器和 web 服务器决定和设置的,各种浏览器和 web 服务器的设定 均不一样,这依赖于各个浏览器厂家的规定或者可以根据 web 服务器的处理能力来设定。 The limit is in MSIE and Safari about 2KB, in Opera about 4KB and in Firefox about 8KB, (255 bytes if we count very old browsers) . We may thus assume that 8KB is the maximum possible length and that 2KB is a more affordable length to rely on at the server side and that 255 bytes is the safest length to assume that the entire URL will come in. If the limit is exceeded in either the browser or the server, most will just truncate the characters outside the limit without any warning. Some servers however may send a HTTP 414 error. If you need to send large data, then better use POST instead of GET. Its limit is much higher, but more dependent on the server used than the client. Usually up to around 2GB is allowed by the average webserver. This is also configureable somewhere in the server settings. The average server will display a server-specific error/exception when the POST limit is exceeded, usually as HTTP 500 error. HTTP 1.1 defines Status Code 414 Request-URI Too Long for the cases where a server-defined limit is reached. You can see further details on RFC 2616. For the case of client-defined limits, there is no sense on the server returning something, because the server won't receive the request at all. The server is refusing to service the request because the Request-URI is longer than the server is willing to interpret. This rare condition is only likely to occur when a client has improperly converted a POST request to a GET request with long query information, when the client has descended into a URI "black hole" of redirection (e.g., a redirected URI prefix that points to a suffix of itself), or when the server is under attack by a client attempting to exploit security holes present in some servers using fixed-length buffers for reading or manipulating the Request-URI. 附 GET VS POST: 1、多数浏览器对于POST采用两阶段发送数据的,先发送请求头,再发送请求体,即使参数再少再短,也会被分成两个步骤来发送(相对于GET),也就是第一步发送header数据,第二步再发送body部分。HTTP是应用层的协议,而在传输层有些情况TCP会出现两次连结的过程,HTTP协议本身不保存状态信息,一次请求一次响应。对于TCP而言,通信次数越多反而靠性越低,能在一次连结中传输完需要的消息是最可靠的,尽量使用GET请求来减少网络耗时。如果通信时间增加,这段时间客户端与服务器端一直保持连接状态,在服务器侧负载可能会增加,可靠性会下降。 Tips:关于这点你可以参考:Yahoo网站性能优化指南之服务器篇 http://segmentfault.com/a/1190000000353790 http://developer.yahoo.com/performance/rules.html http://blogread.cn/it/article/6100?f=wb YSLOW法则中,为什么yahoo推荐用GET代替POST? 上面这篇文章介绍了 wireshark 抓包验证 post 两次发包,get 一次发包的全过程,推荐阅读。 2、GET请求能够被cache,GET请求能够被保存在浏览器的浏览历史里面(密码等重要数据GET提交,别人查看历史记录,就可以直接看到这些私密数据)POST不进行缓存。 3、GET参数是带在URL后面,传统IE中URL的最大可用长度为2048字符,其他浏览器对URL长度限制实现上有所不同。POST请求无长度限制(目前理论上是这样的)。 4、GET提交的数据大小,不同浏览器的限制不同,一般在2k-8K之间,POST提交数据比较大,大小靠服务器的设定值限制,而且某些数据只能用 POST 方法「携带」,比如 file。 5、全部用POST不是十分合理,最好先把请求按功能和场景分下类,对数据请求频繁,数据不敏感且数据量在普通浏览器最小限定的2k范围内,这样的情况使用GET。其他地方使用POST。 6、GET 的本质是「得」,而 POST 的本质是「给」。而且,GET 是「幂等」的,在这一点上,GET 被认为是「安全的」。但实际上 server 端也可以用作资源更新,但是这种用法违反了约定,容易造成 CSRF(跨站请求伪造)。 REF: maximum length of HTTP GET request? http://stackoverflow.com/questions/2659952/maximum-length-of-http-get-request http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.15 Request-URI Too Long http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.2.1 General Syntax http://www.cnblogs.com/xiaotaomaomao/articles/986070.html http://www.cnblogs.com/TankXiao/archive/2012/02/13/2342672.html HTTP协议详解 post方式相比get安全,携带数据更大,我准备所有数据都用post方式获取,这样好吗? http://segmentfault.com/q/1010000000213082 http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html 浅谈CSRF攻击方式
root/12345 (只能用ROOT操作)iptables -I INPUT -s x.x.x.x -p tcp --dport 8091 -j ACCEPT #允许x.x.x.x访问本机的8091端口iptables -I INPUT -s x.x.x.x -p tcp --dport 11211 -j ACCEPT #允许x.x.x.x访问本机的11211端口访问couchbase服务的所有应用服务器都要添加保存规则service iptables saveservice iptables restar
突然user io占用率很很高,看了一个AWR报告,发现direct path read temp,direct path write temp的的数率很高,后来怀疑是临时表空间不够了,就试着设了一下让临时表自动增长,问题就解决了。可爽! 具体如下,用红色字休就方法就行了: 1、查看临时表空间select tablespace_name,file_name,bytes/1024/1024 file_size,autoextensible from dba_temp_files; 2、缩小临时表空间大小alter database tempfile 'D:\ORACLE\PRODUCT\10.2.0\ORADATA\TELEMT\TEMP01.DBF' resize 100M;3、扩展临时表空间:方法一、增大临时文件大小:SQL> alter database tempfile ‘/u01/app/oracle/oradata/orcl/temp01.dbf’ resize 100m;方法二、将临时数据文件设为自动扩展:SQL> alter database tempfile ‘/u01/app/oracle/oradata/orcl/temp01.dbf’ autoextend on next 5m maxsize unlimited;方法三、向临时表空间中添加数据文件:SQL> alter tablespace temp add tempfile ‘/u01/app/oracle/oradata/orcl/temp02.dbf’ size 100m;4、创建临时表空间:SQL> create temporary tablespace temp1 tempfile ‘/u01/app/oracle/oradata/orcl/temp11.dbf’ size 10M;5、更改系统的默认临时表空间:--查询默认临时表空间select * from database_properties where property_name='DEFAULT_TEMP_TABLESPACE';--修改默认临时表空间alter database default temporary tablespace temp1;所有用户的默认临时表空间都将切换为新的临时表空间:select username,temporary_tablespace,default_ from dba_users;--更改某一用户的临时表空间:alter user scott temporary tablespace temp;6、删除临时表空间删除临时表空间的一个数据文件:SQL> alter database tempfile ‘/u01/app/oracle/oradata/orcl/temp02.dbf’ drop;删除临时表空间(彻底删除):SQL> drop tablespace temp1 including contents and datafiles cascade constraints;
不同的集群产品都有自己的特点,RAC的特点包括如下几点: ·双机并行。RAC是一种并行模式,并不是传统的主备模式。也就是说,RAC集群的所有成员都可以同时接收客户端的请求。 ·高可用性。RAC是Oracle数据库产品高可用性的解决方案,能够保证在集群中只要有一个节点存活,就能正常对外提供服务。 ·易伸缩性。RAC可以非常容易地添加、删除节点,以满足系统自身的调整。 ·低成本。能使用较低廉的服务器来实现高可用性、高吞吐量的集群环境,这要比通过对某台高端服务器增加硬件实现高可用性、高吞吐量花费的成本低很多。 ·高吞吐量。随着节点数的增加,整个RAC的吞吐量也在不断增长。 下面详细讨论这五大特点。 一、双机并行 RAC是一种充分利用服务器资源的高可用性实现方案,RAC的并行模式实现方式与传统的双机热备实现方式截然不同,图1-4是两者的比较。 如图1-4所示,两个节点在传统的双机热备环境中,始终有一台机器作为备用机,只有当主节点出现问题的时候才会切换到备用机上;如果主机一直没有出现问题,那么备用机始终处于空闲状态,这在资源的利用上以及成本方面都是巨大的浪费。但RAC是一种并行模式的架构,也就是说,两个节点的集群节点间是一种并行运行的关系,当一台机器出现问题,请求会自动转发到另一台机器,没有任何一台机器作为备用机一直不被使用,这样就充分利用了服务器资源。同时,传统的双机热备构架在出现问题时,常常需要数分钟的切换时间,而RAC在出现问题时,针对存在的会话只需要数十秒的时间就可以完成失败切换过程,对新会话的创建不会产生影响,在切换时间上也有比较大的优势。 ▲图1-4 双机热备与RAC并行模式对比 二、高可用性 RAC是Oracle数据库高可用性解决方案。高可用性包含两部分的内容:首先是在这种解决方案下要确保数据不丢失,这是最基础的也是必须要保证的;其次是确保不停机,使Oracle数据库一直维持在正常的运行状态,避免停机给客户带来的损失,这是讨论最多的内容。 停机一般分为两类,计划停机和非计划停机。所谓计划停机是有计划地安排节点或者系统的停机,一般在Oracle升级、系统维护或者硬件维护的情况下会出现。非计划停机就是在非人为计划的情况下突然停机,这种情况一般是在Oracle bug、系统故障、硬件故障或人为操作失败的时候出现。 在没有较高花费的情况下,想实现系统100%的不停机几乎是不可能的。表1-1列出了特定百分比高可用性比率运行停机的时间,详细记录了每种高可用性比率每年、每月、每周可以出现最大的停机时间。 通常情况下,以每月停机时间来计算对应的可用性比率。根据系统的重要性情况,应该为系统设定合理的可用性比率。 集群最大的优势在于它的高可用性,通过使用RAC可以在一定程度上避免因为硬件或软件故障引起的数据丢失和非计划停机,并在一定程度上减少或排除计划停机时间。这是很多客户选择RAC的最直接原因。 RAC中包含了非常多的高可用特性,主要包含如下几点: ·实现节点间的负载均衡。 ·实现失败切换的功能。 ·通过Service组件来控制客户端的访问路径。 ·集群软件能够自动化管理各个资源,并且有定时的节点状态检测机制,能自动对一些失败的进程以及心跳检测失败的节点进行重启,使其重新恢复到正常的运行状态。 在Oracle 11gR2版本中,Clusterware得到了改善,提供了更高的可用性。例如,大量新的基于代理的监控系统用于监控所有的资源。这些代理使用更少的资源执行更频繁的检查,即更快速的失败扫描和更短的恢复时间。在Oracle监听的例子中,平均失败扫描时间从5分钟减少到30秒,同时,检查间隔从每10分钟减少到1分钟。另外,Clusterware的“Out-of-Place Upgrade”等特性也减少了软件维护需要的停机时间。 三、易伸缩性 RAC为需要重新规划的应用提供了易扩展性。为了在系统初始阶段保持较低的成本,避免造成不必要的浪费,集群可以按照标准硬件配置,选择适当的服务器资源、存储资源来搭建数据库环境。当系统需要更多的处理能力或者需要增加存储时,通过添加另一台服务器或存储设备到集群中,能够在不停机的情况下获得水平的扩展。在一个集群中, Clusterware和RAC支持多达100个集群节点。 当某个集群的处理能力过剩,另一个集群的处理能力不够时,可以从处理能力过剩的集群移动一个节点到处理能力不够的集群中。这样能够充分利用服务器资源,节约成本。11gR2版本中推出了网格即插即用(Grid Plug and Play,GPnP),可以实现节点的快速添加。 四、低成本 通过多台普通的PC服务器组成一个集群,可以提高集群的处理能力,这样要比采用一台高性能的服务器的成本低很多。如果想提高系统的处理能力,给集群添加节点比为高性能服务器添加硬件要容易得多。另外,使用集群还能动态地移除节点,更加充分地利用管理者掌握的所有服务器资源,从服务器整体使用上降低了服务器的采购成本。越来越多的企业愿意将集群解决方案应用到他们的系统中,以降低成本,提高系统的可用性。 五、高吞吐量 RAC是由多台服务器构成的逻辑主体,比单台数据库服务器能接收更多的客户端请求。这在要求高吞吐量的系统中,能够得到非常明显的体现。在RAC的架构中,多个实例分布在多个服务器上,能同时打开同一个数据库,而每个实例能够接收相等数量的客户端请求,这样,随着服务器的增加,吞吐量也在不断地增加。 在以上讨论的特点中,高可用性是RAC最大的特点。 RAC存在的问题 虽然RAC有非常多的优点,但由于部署一套RAC会涉及服务器、存储设备、HBA卡、操作系统等多方面的技术,且从实现上要比单实例数据库更复杂,对硬件设备的稳定性、设备之间以及设备与操作系统的兼容性上要求也更高,Oracle的bug也会造成RAC运行出现问题。所以,从实际的运行情况来看,RAC要比单实例的数据库存在更多的问题,问题的原因也各不相同。RAC存在的问题主要体现在稳定性和高性能方面,下面讨论这两个问题。 一、稳定性 数据库的稳定运行是系统稳定运行的基础和前提,数据库的运行依赖于操作系统、服务器、存储设备等软硬件设备的运行情况。 由于各种硬件设备、操作系统的厂商不同,有时候在兼容性上会存在问题,即使同一个厂商的服务器,由于驱动、固件版本的不同也可能导致硬件出现问题以及与其他设备的兼容性问题。同时,由于RAC本身也存在不少bug,很多部署的RAC环境缺乏在上线前对环境的检查和测试,导致在运行过程中出现一系列不稳定的情况,这样高可用性并没有得到充分的体现。 由此来看,稳定的硬件环境加上稳定的RAC版本,决定着RAC运行的稳定性。数据库工程师与硬件工程师在安装配置前大量的环境检查、验证,以及部署后的大量测试工作都是非常重要的。 二、高性能 高性能也是大部分从单机环境迁移到RAC环境比较头疼的问题,RAC并不是高性能的解决方案。在目前普遍使用千兆网络的硬件环境中,很多时候系统的数据库从原来的单机迁移至RAC环境,系统的性能反而下降。在这种情况下,数据库管理员应该根据RAC的特点对系统调整提出合理的建议,经过合理的设计、开发,使用RAC是能够提高系统的处理性能的。 以上两个问题是需要特别注意的。另外,与硬件工程师、系统开发人员进行良好的沟通,以及对系统合理的设计是保证RAC稳定运行和高性能运行的前提。
AWR(Automatic Workload Repository)报告是我们进行日常数据库性能评定、问题SQL发现的重要手段。熟练掌握AWR报告,是做好开发、运维DBA工作的重要基本功。 AWR报告的原理是基于Oracle数据库的定时镜像功能。默认情况下,Oracle数据库后台进程会以一定间隔(一小时)收集系统当前状态镜像,并且保存在数据库中。生成AWR报告时,只需要指定进行分析的时间段(开始镜像编号和结束镜像编号),就可以生成该时间段的性能分析情况。AWR镜像保存在数据库中的时间为一个月左右。 目前Oracle10g之后,AWR报告取代了原先的Statspack报告成为一个主流性能分析报告。通常可以从OEM(Oracle Enterprise ManagerConsole)平台上生成查看AWR报告。在OEM中,使用图形化方法更加容易。本篇中介绍使用手工脚本方式生成AWR的方法,脱离OEM的限制。 1、 运行脚本 首先,准备一个目录作为AWR生成报告的路径。 [oracle@bspdev /]$ ls -l | greptest drwxr-xr-x. 2oracle oinstall 4096 Jun 21 13:01test [oracle@bspdev /]$ cdtest 启动sqlplus等开发工具,调用生成脚本。程序脚本一般保存在$ORACLE_HOME下的rdbms/admin中,名称为awrrpt.sql。 [oracle@bspdev test]$ sqlplus/nolog SQL*Plus: Release11.2.0.1.0 Production onTue Jun 21 13:04:44 2011 Copyright (c) 1982, 2009,Oracle. Allrights reserved. SQL> conn / as sysdba Connected. --调用脚本,生成文件 SQL>@?/rdbms/admin/awrrpt.sql 之后进入报告参数输入模块。 2、输入报告参数 之后,要持续输入一系列的报告参数。 ü 输入生成报告类型,目前AWR提供txt和html两种格式。需要确认生成格式,默认是html格式。 Current Instance ~~~~~~~~~~~~~~~~ DBId DBName InstNum Instance ----------- ------------ -------------------- 4143510747 ORA11G 1ora11g Specify the Report Type ~~~~~~~~~~~~~~~~~~~~~~~ Would you like an HTML report, or a plaintext report? Enter 'html' for an HTMLreport, or 'text' for plain text Defaults to'html' ü 报告涉及天数范围 启动报告后,会显示生成实例的名称等基本信息。 默认情况下,AWR会将镜像信息保留一个月。手工生成的时候,需要确认生成AWR报告的时间范围。一般情况下,特别是生产环境下,我们通常设置1-7天也就够用了。 Instances in this Workload Repositoryschema ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DBId InstNum DB Name Instance Host ------------ -------- ------------------------ ------------ * 4143510747 1ORA11G ora11g bspdev.local domain Using 4143510747 for databaseId Using 1for instance number Specify the number of days of snapshots tochoose from ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Entering the number of days (n) will resultin the most recent (n) days of snapshots beinglisted. Pressingwithout specifying a number lists all completedsnapshots. Enter value for num_days:3 ü 输入开始和结束的snapshot编号 输入天数信息后,AWR生成代码会将天数范围内的snapshot镜像点列出,供输入选择。 Listing the last 3 days of CompletedSnapshots Snap Instance DBName SnapId SnapStarted Level ------------ ------------ --------------------------- ----- ora11g ORA11G 178920 Jun 2011 13:01 1 179020 Jun 2011 14:00 1 179120 Jun 2011 15:00 1 179220 Jun 2011 16:00 1 (篇幅原因,有省略……) 181121 Jun 2011 11:00 1 181221 Jun 2011 12:00 1 181321 Jun 2011 13:00 1 Specify the Begin and End SnapshotIds ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 之后,我们需要根据列出的时间范围,输入开始和结束的snap编号。 Specify the Begin and End SnapshotIds ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Enter value for begin_snap:1796 Begin Snapshot Id specified:1796 Enter value for end_snap:1813 ü 确定报告名称 最后就是确定生成报告的名称。一般采用默认的名称就可以了。 Specify the Report Name ~~~~~~~~~~~~~~~~~~~~~~~ The default report file name isawrrpt_1_1796_1813.html. To use thisname, press to continue, otherwise enter analternative. Enter value forreport_name: 之后输出内容很多,此处不加以累述。最后提示报告生成成功。 Report written toawrrpt_1_1796_1813.html 于是,指定目录上可以看到相应的报告文件。 [oracle@bspdev test]$ ls-l total 508 -rw-r--r--. 1 oracle oinstall 515262 Jun 2113:10 awrrpt_1_1796_1813.html 3、说明两个问题 首先,此处生成的html格式的报表。如果要求生成txt格式,就在生成过程中选择text格式报表。 Specify the Report Type ~~~~~~~~~~~~~~~~~~~~~~~ Would you like an HTML report, or a plaintext report? Enter 'html' for an HTML report, or 'text'for plain text Defaults to 'html' Enter value for report_type:text Type Specified: text End of Report Report written toawrrpt_1_1789_1800.txt [oracle@bspdev test]$ ls-l total 692 -rw-r--r--. 1 oracle oinstall 180601 Jun 2113:27 awrrpt_1_1789_1800.txt -rw-r--r--. 1 oracle oinstall 515262 Jun 2113:10 awrrpt_1_1796_1813.html 第二个就是调用脚本的方式问题。调用时使用的sqlplus客户端可以在Oracle服务器本机上(远程登录),也可以在客户端机器本机上。笔者建议是在客户端本机上进行生成,这样可以避免报告文件来回拷贝的工作。但是最好要保证客户端版本与服务器版本相匹配。 4、结论 手工生成AWR报告,可以避免受到OEM的限制约束,而且灵活度高。本篇记录,权当备忘。
log file sync等待时间发生在redo log从log buffer写入到log file期间。 下面对log file sync做个详细的解释。 何时发生日志写入: 1.commit或者rollback 2.每3秒 3.log buffer 1/3满或者已经有1M的redo数据。 更精确的解释:_LOG_IO_SIZE 大小默认是LOG_BUFFER的1/3,当log buffer中redo数据达到_LOG_IO_SIZE 大小时,发生日志写入。 4.DBWR写之前 _log_io_size隐含参数: LOG_BUFFER(bytes)写入的数量超过_LOG_IO_SIZE会触发lgwr写日志的条件,缺省值为LOG BUFFER的1/3或1M。 但是这个说法通过查询并不能验证,隐含参数尽量不要修改。 col name for a25 col VALUE for a20 col DESCRIB for a50 SELECT x.ksppinm NAME, y.ksppstvl VALUE, x.ksppdesc describ FROM SYS.x$ksppi x, SYS.x$ksppcv y WHERE x.inst_id = USERENV ('Instance') AND y.inst_id = USERENV ('Instance') AND x.indx = y.indx AND x.ksppinm LIKE '_log_io_size'; NAME VALUE DESCRIB ------------------------- -------------------- -------------------------------------------------- _log_io_size 0 automatically initiate log write if this many redo blocks in buffer log file sync发生的过程: 此等待事件用户发出提交或回滚声明后,等待提交完成的事件,提交命令会去做日志同步,也就是写日志缓存到日志文件, 在提交命令未完成前,用户将会看见此等待事件. 注意,它专指因提交,回滚而造成的写缓存到日志文件的等待.当发生此等待事件时,有时也会伴随log file parallel write.因为此等待事件将会写日志缓存,如果日志的I/O系统较为缓慢的话, 这必将造成log file parallel write 等待.当发生log file sync等待后,判断是否由于缓慢的日志I/O造成的,可以查看两个等待事件的等待时间,如果比较接近,就证明日志I/O比较缓慢或重做日志过多,这时,造成log file sync的原因是因为log file parallel write,可以参考解决log file parallel write的方法解决问题, **如果log file sync的等待时间很高,而log file parallel write的等待时间并不高,这意味着log file sync的原因并不是缓慢的日志I/O,而是应用程序过多的提交造成。 当log file sync的等待时间和 log file parallel write等待时间基本相同,说明是IO问题造成的log file sync等待事件。 ----- 更好理解的解释: 回顾一下单机数据库中的'log file sync' 等待事件,当user session 提交(commit)时,user session会通知LGWR进程将redo buffer中的信息写入到redo log file,当LGWR进程完成写操作后,LGWR再post(通知)user session 写操作已经完成,user session 接收到LGWR的通知后提交操作才完成。因此user session 在没有收到LGWR post(通知)之前一致处于等待状态,具体的等待事件为'log file sync'。 ----- 引起log file sync的原因: 1.频繁提交或者rollback,检查应用是否有过多的短小的事务,如果有,可以使用批处理来缓解。 2.OS的IO缓慢:解决办法是将日志文件放裸设备上或绑定在RAID 0或RAID 1+0中,而不是绑定在RAID 5中。 3.过大的日志缓冲区(log_buffer ) 过大的log_buffer,允许LGWR变得懒惰,因为log buffer中的数据量无法达不到_LOG_IO_SIZE,导致更多的重做条目堆积在日志缓冲区中。 当事务提交或者3s醒来时,LGWR才会把所有数据都写入到redo log file中。 由于数据很多,LGWR要用更多时间等待redo写完毕。 这种情况,可以调小参数_LOG_IO_SIZE参数,其默认值是LOG_BUFFER的1/3或1MB,取两者之中较小的值。 换句话说,你可以具有较大的日志缓冲区,但较小的_LOG_IO_SIZE将增加后台写入次数,从而减少log file sync的等待时间。 4.CPU负载高。详见下面的描述。 5.RAC私有网络性能差,导致LMS同步commit SCN慢。 如何诊断log file sync: 1.AWR:发生log file sync时,先做个snapshot,然后做AWR,AWR时间选择在10-30分钟。 已发生的log file sync,那么通过AWR依然可以分析,也要保持在10-30分钟。 2.Lgwr trace file(10.2.0.4开始),大于500ms会写入 trace文件中如果有Warning: log write time 1000ms, size 2KB,很有可能IO慢。 3.分析CPU资源使用情况的工具,CPU过于繁忙,lgwr无法及时获取CPU调度,出现log file sync。 vmstat,关注r是否大于CPU核数,大于说明cpu繁忙。 OSW:OSWatcher,同上。 4.Alert:确认log file 15到20分钟切换一次 5.Script to Collect Log File Sync Diagnostic Information (lfsdiag.sql) [Document 1064487.1] 解决办法: 1.如果确实是因为频繁提交造成的log file sync,那么减少commit。 2.如果确实是因为io引起的,那么解决办法是将日志文件放裸设备上或绑定在RAID 1+0中,而不是放在在RAID 5中(切记,redo log file一定不要放在SSD上!!!)。 3.确保CPU资源充足。CPU资源不足,LGWR通知user session后,user session无法及时获得CPU调度,不能正常工作。 4.是否有些表可以使用nologging,会减少redo产生量 5.检查redo log file足够大,确保redo log file每15到20分钟切换一次。 更深入分析log file sync: 如果上面的分析没有解决log file sync等待事件,那么需要做下面的分析。 The log file sync wait may be broken down into the following components: log file sync 能拆解为一下步骤: 1. Wakeup LGWR if idle 1.唤醒LGWR进程 2. LGWR gathers the redo to be written and issue the I/O 2.LGWR进程收集redo,然后发给I/O 3. Time for the log write I/O to complete 3.等待log写入I/O完成 4. LGWR I/O post processing 4.LGWR I/O post processing 5. LGWR posting the foreground/user session that the write has completed 5.LGWR通知前台/用户回话,redo写入完成 6. Foreground/user session wakeup 6.前台/用户会话唤醒 Steps 2 and 3 are accumulated in the "redo write time" statistic. (i.e. as found under STATISICS section of Statspack and AWR) 步骤2和3消耗的时间在AWR中的"redo write time"中有所体现。(AWR中 Instance Activity Stats ) Step 3 is the "log file parallel write" wait event. (Document:34583.1 "log file parallel write" Reference Note) 步骤3产生"log file parallel write"等待事件。 另外:如果是最大保护模式的DATAGUARD(SYNC传输),这一步骤还包含网络写、RFS/redo写入到备库的standby log file sync的时间。 Steps 5 and 6 may become very significant as the system load increases. This is because even after the foreground has been posted it may take a some time for the OS to schedule it to run. May require monitoring from O/S level. 在系统负载高时(尤其是CPU高的情况,看vmstat r值),步骤5和6会变得非常明显。因为,前台收到LGWR写入完成的通知后,操作系统需要消耗一些时间调度Foreground/user session进程唤醒(也就是CPU调度)。需要系统级别监控。 几个技术指标: log file sync 等待时间小于20ms算正常 log file parallel write 等待时间小于20ms算正常 log file parallel wirte 和log file sync等待时间很接近,说明就是IO问题,因为大部分时间都花在了log写入到磁盘上。 相关脚本: --等待时间平均等待时间 select EVENT,TOTAL_WAITS,TOTAL_TIMEOUTS,TIME_WAITED,AVERAGE_WAIT from v$system_event where event in ('log file sync','log file parallel write'); select value from v$parameter where name = 'log_buffer'; ---------------新特性:log file sync 两种方式-------------- Adaptive Log File Sync Adaptive Log File sync was introduced in 11.2. The parameter controlling this feature, _use_adaptive_log_file_sync, is set to false by default in 11.2.0.1 and 11.2.0.2. _use_adaptive_log_file_sync参数在11gR2提出。11.2.0.1和11.2.0.2两个版本该参数默认是false。 从11.2.0.3开始,这个参数默认值是true,也就是开始启用“自适应日志同步机制”。 11.2.0.1和11.2.0.2也可以开启改参数 ALTER SYSTEM SET "_use_adaptive_log_file_sync"= scope=; 开启改参数后,日志同步机制会在2种方式中切换。 该参数决定了,foreground/user session 和LGWR进程通过什么方式获知commit操作已完成(也就是redo写log file完成)。 Post/wait, traditional method for posting completion of writes to redo log 传统方式,在11.2.0.3之前,user session等待LGWR通知redo写入到log file完毕,被动方式。 优点:post/wait方式,user session几乎能立即发现redo已刷到磁盘。 Polling, a new method where the foreground process checks if the LGWR has completed the write. 新方式,主动监测LGWR是否完成写入,主动方式。这种方式比Post/wait方式响应速度慢,但是可以节约CPU资源。 优点:当commit完成后,LGWR会把commit完成的消息通知给很多user session,这个过程消耗大量CPU。 Polling方式采用朱勇监测LGWR释放写入redo完成,所以释放了LGWR占用的CPU资源。 系统负载高(CPU繁忙)采用Polling方式更好。 系统负载低(CPU清闲)采用post/wait方式更好,它能够提供比polling方式更好的响应时间。 ORACLE根据内部统计信息决定采用何种方式。post/wait和polling方式互相切换能引起过热,为了确保安全,切换不要太频繁。 LGWR的trace文件记录了switch记录,关键字是 "Log file sync switching to ...": Switch to polling: *** 2015-01-21 08:19:04.077 kcrfw_update_adaptive_sync_mode: post->poll long#=2 sync#=5 sync=62 poll=1056 rw=454 ack=0 min_sleep=1056 *** 2015-01-21 08:19:04.077 Log file sync switching to polling Current scheduling delay is 1 usec Current approximate redo synch write rate is 1 per sec kcrfw_update_adaptive_sync_mode: poll->post current_sched_delay=0 switch_sched_delay=1 current_sync_count_delta=1 switch_sync_count_delta=5 Switch to post/wait: *** 2015-01-21 08:46:09.428 Log file sync switching to post/wait Current approximate redo synch write rate is 0 per sec *** 2015-01-21 08:47:46.473 kcrfw_update_adaptive_sync_mode: post->poll long#=2 sync#=11 sync=228 poll=1442 rw=721 ack=0 min_sleep=1056 相关脚本: 查询当前log file sync 方式是post-wait还是poll SQL> select name,value from v$sysstat where name in ('redo sync poll writes','redo synch polls'); NAME VALUE ---------------------------------------------------------------- ---------- redo synch polls 325355850 每小时采用poll log file sync方式的次数 col begin_interval_time format a25 col instance_number format 99 heading INST col stat_name format a25 select snap.BEGIN_INTERVAL_TIME,hist.instance_number , hist.stat_name,hist.redo_synch_polls from ( select snap_id,instance_number,stat_name,value -lag(value,1,null) over ( order by snap_id,instance_number,stat_name) redo_synch_polls from dba_hist_sysstat where stat_name='redo synch polls' and dbid=(select dbid from v$database) and instance_number = nvl('&instance_number',1)) hist, dba_hist_snapshot snap where redo_synch_polls >0 and hist.snap_id=snap.snap_id and hist.instance_number=snap.instance_number order by 1,2 / BEGIN_INTERVAL_TIME INST STAT_NAME REDO_SYNCH_POLLS ------------------------- ---- ------------------------- ---------------- 06-JAN-15 07.00.02.884 AM 2 redo synch polls 734 06-JAN-15 08.00.08.425 AM 2 redo synch polls 23767 06-JAN-15 09.00.13.770 AM 2 redo synch polls 39827 06-JAN-15 10.00.19.233 AM 2 redo synch polls 48479 06-JAN-15 11.00.24.431 AM 2 redo synch polls 41541 06-JAN-15 12.00.29.670 PM 2 redo synch polls 47566 06-JAN-15 01.00.35.029 PM 2 redo synch polls 32169 06-JAN-15 02.00.04.159 PM 2 redo synch polls 37405 06-JAN-15 02.59.04.536 PM 2 redo synch polls 41469 06-JAN-15 04.00.08.556 PM 2 redo synch polls 38683 06-JAN-15 05.00.12.523 PM 2 redo synch polls 51618 06-JAN-15 06.00.16.584 PM 2 redo synch polls 52511 06-JAN-15 07.00.03.352 PM 2 redo synch polls 42229 06-JAN-15 08.00.08.663 PM 2 redo synch polls 35229 06-JAN-15 09.00.13.882 PM 2 redo synch polls 18499
这是3月份某客户的情况,原因是服务器硬件故障后进行更换之后,业务翻译偶尔出现提交缓慢的情况。我们先来看下awr的情况。 我们可以看到,该系统的load profile信息其实并不高,每秒才21个transaction。先来看看top5events: 从top 5event,我们可以发现,log file sync的avg wait非常之高,高达124ms。大家应该知道,对于绝大多数情况 下,log file sync的平均等待时间是小于5ms的,这个值有点高的离谱。 我们知道,产生log file sync等待的原因有很多。关于log file sync,tanel Poder大神写过一篇很牛的pdf,大家可以参考下。 这里我主要引用大神的图,来简单描述产生log file sync的原因可能有哪些,首先我们来看下从前端进程提交到最后得到反馈时,以及中间处理的整个流程情况: 从上图中,我们可以清楚的看到整个流程。这里可以进行简单的描述: 1、当user发起一个commit后; 2、前端进程(即Server 进程)会post一个信息给lgwr进程,告诉它,你应该去写redo buffer了。 3、当LGWR进程得到指示后,开始调用操作系统函数进行物理写,在进行物理写的这段时间内,会出现 log file parallel write等待。这里或许有人会有疑问,为什么12c之前只有一个lgwr进程,这里却是parallel write呢?这里需要说明一下,lgwr进程在将redo buffer中的数据写出到log file文件中时,也是以batch方式 进程的(实际上,dbwN进程也是batch的模式),有相关的隐含参数控制。 4、当LGWR完成wrtie操作之后,LGWR进程会返回一个信息给前端进程(Server进程),告诉它,我已经写完了, 你可以完成提交了。 5. user 完成commit操作。 这里补充一下,这是由于Oracle 日志写优先的原则,假设在commit之前redo buffer的相关entry信息不立即写到redo log file中,那么如果数据库出现crash,那么这是会丢数据的。 从上面的流程图,我们其实也可以看到,log file sync和log file parallel write可以说是相互关联的。换句话讲,如果log file parallel write的时间很长,那么必然导致log file sync等待时间拉长。 我们假设log file parallel write 等待很高,那么着可能通常是物理磁盘IO的问题,如下: 我们从上图可以发行,如果LGWR进程在完成IO操作的过程中时间过长,那么将导致log file parallel write等待升高。 实际上,在整个当用户发出commit到完成commit的过程中,涉及到很多环节,并不是仅仅只有物理IO会影响log file sync/log file parallel write。还有CPU也会影响Log file sync和log file parallel write。我们再来看个图: 我们可以看到,上述流程中的4个环节都涉及到CPU的调度,如果在整个事务commit的过程中,系统CPU出现极度紧张,那么这可能会导致LGWR进程无法获得CPU,会进行排队等待,显然,这势必将导致log file sync或log file parallel write等待 的升高。 备注:Oracle中还可以通过隐含参数_high_priority_processes 来控制进程获取CPU的优先级。在一个cpu相对缺乏的系统中,可以通过设置该参数来进行缓解。 最后我们再回到这个案例中来,客户这里的环境,我们是可以排除CPU问题。那么最大的嫌疑可能就是存储本身的问题,导致IO很慢,然而,实际上这也是可以排除的,大家其实应该注意到前面的Top 5 event了,log file parallel write的平均等待 时间并不高,如果是存储IO问题,那么这个event的平均等待时间应该是比较高才对。 我们可以看到log file sync和log file parallel write的waits都是差不多的。但是log file parallel write的avg wait time仅仅只有4ms,这是一个正常的值。也就是说可以我们排除存储IO问题。 那么问题是什么呢 ?我们利用Oracle MOS提供的脚本来查询下log file sync和log file parallel write等待的分布情况: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 INST_ID EVENT WAIT_TIME_MILLI WAIT_COUNT ---------- ---------------------------------------- --------------- ---------- 1 log file sync 1 259306 1 log file sync 2 2948999 1 log file sync 4 1865918 1 log file sync 8 173699 1 log file sync 16 43194 1 log file sync 32 6095 1 log file sync 64 1717 1 log file sync 128 2458 1 log file sync 256 5180 1 log file sync 512 9140 1 log file sync 1024 558347 1 log file parallel write 1 5262 1 log file parallel write 2 4502377 1 log file parallel write 4 1319211 1 log file parallel write 8 46055 1 log file parallel write 16 23694 1 log file parallel write 32 3149 1 log file parallel write 64 283 1 log file parallel write 128 267 1 log file parallel write 256 157 1 log file parallel write 512 73 1 log file parallel write 1024 42 1 log file parallel write 2048 39 1 log file parallel write 4096 103 1 log file parallel write 8192 21 1 log file parallel write 16384 22 1 log file parallel write 32768 190 1 log file parallel write 65536 1 大家可以简单的计算一下,其实log file sync和log file parallel write 等待事件,几乎99%左右的平均等待时间都是 小于等于4ms的,这是属于正常的情况;然而有少数的情况其等待时间是很长的,例如log file sync最高的单次等待 时间高达1秒,由于偶尔的等待很高,因此将整个log file sync的平均等待时间拉高了。 到最后,问题就比较清楚了,我认为这是由于主机和存储之间的链路可能出现异常或不稳定导致。临时的解决方法 将redo logfile 挪到本地磁盘,解决了该问题。 后记:经客户后面确认,确实是存储光纤线接口松了。 哈哈
edis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。[1] Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。 Redis-benchmark是官方自带的Redis性能测试工具,可以有效的测试Redis服务的性能。 指令说明: [ruby] view plain copy print? Usage: redis-benchmark [-h <host>] [-p <port>] [-c <clients>] [-n <requests]> [-k <boolean>] -h <hostname> Server hostname (default 127.0.0.1) -p <port> Server port (default 6379) -s <socket> Server socket (overrides host and port) -c <clients> Number of parallel connections (default 50) -n <requests> Total number of requests (default 10000) -d <size> Data size of SET/GET value in bytes (default 2) -k <boolean> 1=keep alive 0=reconnect (default 1) -r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD Using this option the benchmark will get/set keys in the form mykey_rand:000000012456 instead of constant keys, the <keyspacelen> argument determines the max number of values for the random number. For instance if set to 10 only rand:000000000000 - rand:000000000009 range will be allowed. -P <numreq> Pipeline <numreq> requests. Default 1 (no pipeline). -q Quiet. Just show query/sec values 只显示每秒钟能处理多少请求数结果 --csv Output in CSV format -l Loop. Run the tests forever 永久测试 -t <tests> Only run the comma separated list of tests. The test names are the same as the ones produced as output. -I Idle mode. Just open N idle connections and wait. 实例:redis-benchmark -h 127.0.0.1 -p 6379 -q -d 100 SET/GET 100 bytes 检测host为127.0.0.1 端口为6379的redis服务器性能redis-benchmark -h 127.0.0.1 -p 6379 -c 5000 -n 100000 5000个并发连接,100000个请求,检测host为127.0.0.1 端口为6379的redis服务器性能 benchmark工具测试信息:测试命令:redis-benchmark -n 100000 -c 60向redis服务器发送100000个请求,每个请求附带60个并发客户端结果(部分):====== SET ======对集合写入测试 100000 requests completed in 2.38 seconds100000个请求在2.38秒内完成 60 parallel clients每次请求有60个并发客户端 3 bytes payload每次写入3个字节的数据 keep alive: 1保持一个连接,一台服务器来处理这些请求93.06% <= 15 milliseconds99.96% <= 31 milliseconds99.98% <= 46 milliseconds99.99% <= 62 milliseconds100.00% <= 62 milliseconds所有请求在62毫秒内完成42105.26 requests per second每秒处理42105.26次请求 [plain] view plain copy print? [root@localhost ~]# redis-benchmark -h 127.0.0.1 -p 6379 -c 5000 -n 100000 -d 100 -q PING_INLINE: 34506.55 requests per second PING_BULK: 34059.95 requests per second SET: 31959.09 requests per second GET: 31466.33 requests per second INCR: 33311.12 requests per second LPUSH: 29265.44 requests per second LPOP: 36968.58 requests per second SADD: 32030.75 requests per second SPOP: 33344.45 requests per second LPUSH (needed to benchmark LRANGE): 29735.36 requests per second LRANGE_100 (first 100 elements): 16116.04 requests per second LRANGE_300 (first 300 elements): 6659.56 requests per second LRANGE_500 (first 450 elements): 4108.29 requests per second
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Lockers * 在多线程编程里面一个重要的概念是锁定,如果一个资源是多个线程共享的,为了保证数据的完整性, * 在进行事务性操作时需要将共享资源锁定,这样可以保证在做事务性操作时只有一个线程能对资源进行操作, * 从而保证数据的完整性。在5.0以前,锁定的功能是由Synchronized关键字来实现的。 */ public class Lockers { /** * 测试Lock的使用。在方法中使用Lock,可以避免使用Synchronized关键字。 */ public static class LockTest { Lock lock = new ReentrantLock();// 锁 double value = 0d; // 值 int addtimes = 0; /** * 增加value的值,该方法的操作分为2步,而且相互依赖,必须实现在一个事务中 * 所以该方法必须同步,以前的做法是在方法声明中使用Synchronized关键字。 */ public void addValue(double v) { lock.lock();// 取得锁 System.out.println("LockTest to addValue: " + v + " " + System.currentTimeMillis()); try { Thread.sleep(1000); } catch (InterruptedException e) { } this.value += v; this.addtimes++; lock.unlock();// 释放锁 } public double getValue() { return this.value; } } public static void testLockTest() throws Exception{ final LockTest lockTest = new LockTest(); // 新建任务1,调用lockTest的addValue方法 Runnable task1 = new Runnable(){ public void run(){ lockTest.addValue(55.55); } }; // 新建任务2,调用lockTest的getValue方法 Runnable task2 = new Runnable(){ public void run(){ System.out.println("value: " + lockTest.getValue()); } }; // 新建任务执行服务 ExecutorService cachedService = Executors.newCachedThreadPool(); Future future = null; // 同时执行任务1三次,由于addValue方法使用了锁机制,所以,实质上会顺序执行 for (int i=0; i<3; i++){ future = cachedService.submit(task1); } // 等待最后一个任务1被执行完 future.get(); // 再执行任务2,输出结果 future = cachedService.submit(task2); // 等待任务2执行完后,关闭任务执行服务 future.get(); cachedService.shutdownNow(); } /** * ReadWriteLock内置两个Lock,一个是读的Lock,一个是写的Lock。 * 多个线程可同时得到读的Lock,但只有一个线程能得到写的Lock, * 而且写的Lock被锁定后,任何线程都不能得到Lock。ReadWriteLock提供的方法有: * readLock(): 返回一个读的lock * writeLock(): 返回一个写的lock, 此lock是排他的。 * ReadWriteLockTest很适合处理类似文件的读写操作。 * 读的时候可以同时读,但不能写;写的时候既不能同时写也不能读。 */ public static class ReadWriteLockTest{ // 锁 ReadWriteLock lock = new ReentrantReadWriteLock(); // 值 double value = 0d; int addtimes = 0; /** * 增加value的值,不允许多个线程同时进入该方法 */ public void addValue(double v) { // 得到writeLock并锁定 Lock writeLock = lock.writeLock(); writeLock.lock(); System.out.println("ReadWriteLockTest to addValue: " + v + " " + System.currentTimeMillis()); try { Thread.sleep(1000); } catch (InterruptedException e) { } try { // 做写的工作 this.value += v; this.addtimes++; } finally { // 释放writeLock锁 writeLock.unlock(); } } /** * 获得信息。当有线程在调用addValue方法时,getInfo得到的信息可能是不正确的。 * 所以,也必须保证该方法在被调用时,没有方法在调用addValue方法。 */ public String getInfo() { // 得到readLock并锁定 Lock readLock = lock.readLock(); readLock.lock(); System.out.println("ReadWriteLockTest to getInfo " + System.currentTimeMillis()); try { Thread.sleep(1000); } catch (InterruptedException e) { } try { // 做读的工作 return this.value + " : " + this.addtimes; } finally { // 释放readLock readLock.unlock(); } } } public static void testReadWriteLockTest() throws Exception{ final ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest(); // 新建任务1,调用lockTest的addValue方法 Runnable task_1 = new Runnable(){ public void run(){ readWriteLockTest.addValue(55.55); } }; // 新建任务2,调用lockTest的getValue方法 Runnable task_2 = new Runnable(){ public void run(){ System.out.println("info: " + readWriteLockTest.getInfo()); } }; // 新建任务执行服务 ExecutorService cachedService_1 = Executors.newCachedThreadPool(); Future future_1 = null; // 同时执行5个任务,其中前2个任务是task_1,后两个任务是task_2 for (int i=0; i<2; i++){ future_1 = cachedService_1.submit(task_1); } for (int i=0; i<2; i++){ future_1 = cachedService_1.submit(task_2); } // 最后一个任务是task_1 future_1 = cachedService_1.submit(task_1); // 这5个任务的执行顺序应该是: // 第一个task_1先执行,第二个task_1再执行;这是因为不能同时写,所以必须等。 // 然后2个task_2同时执行;这是因为在写的时候,就不能读,所以都等待写结束, // 又因为可以同时读,所以它们同时执行 // 最后一个task_1再执行。这是因为在读的时候,也不能写,所以必须等待读结束后,才能写。 // 等待最后一个task_2被执行完 future_1.get(); cachedService_1.shutdownNow(); } public static void main(String[] args) throws Exception{ Lockers.testLockTest(); System.out.println("---------------------"); Lockers.testReadWriteLockTest(); } }
面对蓬勃发展的互联网消费金融的风控需求,针对中小型消费金融平台技术能力薄弱的特点,恒生电子推出了大数据风控平台,为中小型消费金融厂商提供强大的风控服务,从三个方面提供专业的大数据风控支持。 2015 年的“双 11 全球狂欢节”中, “蚂蚁花呗”获得了交易总笔数 6048 万笔、占支付宝整体交易 8.5% 的骄人成绩。不仅仅是花呗,其他的同类消费金融产品如“京东白条”和苏宁“任性付”也取得了 800% 和836% 的不俗增长。当你看着这些数字时,不知有没有想过,这些使用消费金融产品的交易中,有多少笔到期还款时可能会违约,又有多少笔是故意借着购物来套取现金的,甚至有多少笔是被人冒名盗用的?这些问题交易累积到什么样的程度,才会危及到该消费金融产品的生存,进而引发消费金融平台的倒闭?为此,消费金融平台的风险控制变得越来越关键,决定着平台的生存与发展。做好了风险防控,才能在消费金融这片争夺异常惨烈的蓝海中成功,也有可能打造出类似于京东白条、蚂蚁花呗的消费金融产品。 那如何才能做好风险防控呢?在我们看来,风险防控是一个体系,包括贷前、贷中、贷后的全风控流程管理和内审、内控等监察手段。但对于互联网消费金融平台来说,在这个体系中最核心的是大数据风控的能力。首先,在获客环节就要利用大数据风控。从客户群体上看,消费金融面对的主要是年轻人群和中低收入人群,这部分人群的征信数据不健全,如果按传统的方法依靠线下收集客户信息来判断其还款能力和还款意愿,不光效率低下无法获得尽可能多的客户,还无法对客户进行有效的信用评估。通过互联网利用大数据的方式来进行客户信息收集,通过对客户群体的消费数据分析,进行客户评级,获得有效的风控模型,进而对客户进行分流和筛查,进行差异化管理,并不断优化风控模型和信贷审核流程,达到可量化的自动化决策的目的。面对蓬勃发展的互联网消费金融的风控需求,针对中小型消费金融平台技术能力薄弱的特点,恒生电子推出了大数据风控平台,为中小型消费金融厂商提供强大的风控服务,从三个方面提供专业的大数据风控支持。 58其次,将大数据风控植入到一个个消费场景中。消费金融的场景化有助于明确贷款的实际购买商品,避免了贷款的挪用造成的风险,是互联网消费金融的一大趋势,除了网购之外,教育培训、旅行、租房、购车、婚庆、美容等 O2O 场景都具有良好的消费金融属性。不同的场景有不同的用户群,消费金融公司需设计各不相同的消费金融产品和制定有针对性的贷款政策,而大数据风控可以通过数据的采集与分析,根据各个消费场景和消费群体的特点,确定差异化的贷款政策。再次,还要加强对网络欺诈的重点防控。互联网消费金融具有互联网的特殊性,一般为纯线上交易,容易被不法团伙所利用,产生盗号、套现等欺诈行为,且网络欺诈作案手段隐蔽、形式多样,扩散也极快,对风险控制提出了很高的要求。那么,最核心的问题来了,该如何解决大数据风控的“大数据”的来源呢?对京东白条、蚂蚁花呗等产品来说,可以依托自身的海量用户多年累积的行为数据来进行风控模型的完善。而对大部分的互联网消费金融公司来说,收集到的客户信息不完善,获得的信用数据也不完整,必须采用与第三方数据源或征信机构合作的方法来进行风控,而在选取适合的第三方时,也要考虑自身的业务场景和客户群体定位,进行针对性的选择。同时,也可以运用多种风险分散手段,如与保险和担保机构合作。保险机构在提供各种信用保证保险产品的同时,也可将其自身的征信服务提供出来;担保公司由于在风险防范机制上比较专业,可用来完善消费金融公司自身的风控模型。面对蓬勃发展的互联网消费金融的风控需求,针对中小型消费金融平台技术能力薄弱的特点,恒生电子推出了大数据风控平台,为中小型消费金融厂商提供强大的风控服务,从三个方面提供专业的大数据风控支持:外部数据源整合:整合第三方数据源与征信服务机构,从反欺诈、证据保全到第三方征信、电商平台等多维度全方位的数据与服务。风控模型与评分:从还款能力与还款意愿等多角度对客户进行审核,对不同种类的客户进行差异化评估,并基于评分卡进行审批、授信、差异化定价、风险预警、额度调整等流程的设计,实现信贷工厂的批量化与规模化的要求。自动化决策:针对互联网消费金融的快速放贷的要求,搭建了一套自动化决策模型和风控体系,进行欺诈风险的评估,计算信用风险等级,并给出可信任的参考授信额度,达到快速授信、实时放贷的目的。
多租户技术(multi-tenancy technology)实际是一种软件架构技术,它是在探讨与实现如何在多用户的环境下共用相同的系统或程序组件,并且仍可以确保各用户的业务不互相影响。 “我在南方的艳阳里大雪纷飞,你在北方的寒夜里四季如春。”又是一个供暖季,我这个来自北方的狼在杭州的深夜里被冻成了狗,只能靠抖动身体来缓解身体的冰冷。冬夜的深冷让我怀念起北方城市里的集中供暖。 集中供暖在经济学上可归为一种共享经济,大家统一向供暖公司采购暖气服务,因为规模经济的效益,显著降低了个体取暖成本。对于这一经济模式,若转为 IT 技术术语,那就是多租户技术。 多租户技术(multi-tenancy technology)实际是一种软件架构技术,它是在探讨与实现如何在多用户的环境下共用相同的系统或程序组件,并且仍可以确保各用户的业务不互相影响。多租户技术源于 20 世纪 60 年代,早期主要是实现宝贵的计算资源的共享,后来逐渐衍生到软件服务。 近几年随着云计算技术的成熟,云计算多租户技术在 SAAS 服务领域更是取得较大的发展和应用。那么金融行业是否可以基于云计算资源,打造一个多租户业务平台呢?金融行业是一个特殊行业,金融 IT 系统相比其他行业的 IT 系统,具有很多独特的技术要求,比如系统的高可用性、数据安全的敏感性、业务操作的严格校验等。传统的金融 IT 系统多是自建模式,整个系统资源,从硬件服务器到应用软件都在金融机构自己手中。所以若要向金融机构提供云服务,则需要根据金融机构 IT 系统的特点,进行合理的多租户平台业务架构设计。 多租户业务平台首先要保证不同租户业务的隔离,业务隔离主要包含以下 2 个方面: 物理隔离,租户开展业务所依赖的所有计算资源完全独立; 逻辑隔离,通过技术手段,隔离租户业务流程和业务数据,一个租户只能访问自身数据。业务平台在设计时,需要能够根据客户需求提供不同形式的服务,即支持计算资源共享模式,也可支持计算资源独享模式。所以在业务设计时,从数据库表结构到服务请求,都需要加上租户标识。通过租户标识,解决了不同租户业务数据逻辑隔离的需求,那么对于租户独享计算资源模式,则通过为其部署单独的应用实例予以解决,业务平台通过统一的云公共管理模块进行多应用实例管理,从而解决大规模业务资源管理问题。多租户业务平台除了业务隔离外,另外一个重点就是能够根据客户业务需要提供弹性的计算资源。其途径有两种,一种是提升单位计算资源的配置,比如提升 CPU、内存、存储配置,另一种就是借助分布式系统架构设计,支持业务单元的横向扩展。在解决了上述 2 个基本问题后,我们再更深入地考虑如何设计一个多租户业务平台。想到多租户平台,大家可能首先想到淘宝,一个个卖家在淘宝上开店卖货。那么淘宝如何实现为多商家服务的呢?我们是否可以像淘宝一样搭建一个金融云多租户平台?其中又有哪些因为业务不同而需要特殊注意的呢?淘宝为了支持多卖家服务,主体需要构建如下业务模块:商户管理、商户销售员管理、商品管理、商品销售管理、平台运营管理、多租户技术(multi-tenancy technology)实际是一种软件架构技术,它是在探讨与实现如何在多用户的环境下共用相同的系统或程序组件,并且仍可以确保各用户的业务不互相影响。 56自然人自然人平台转化用户 用户自然人平台操作员管理机构-租户合作方操作员授权租户操作员支持服务 支持服务(图-2:操作员之间的业务关系)产品-A 产品-B 产品A’ 产品-C(图-1:平台用户相互转化)平台运维管理。但在电商平台上,有一个突出特点,买家不属于任何一个卖家,买家属于淘宝平台,只有当交易发生时,才会发生买家、卖家的业务关联,买家与卖家的联系实际是通过商品实现的间接联系。但金融业务云平台因为业务主体是各类金融机构,业务用户属于租户,这一显著差异决定了金融云平台的业务架构不能完全照搬电商模式。首先我们看一下金融云平台业务主体,主要包括四类:平台提供商、租户(各类金融机构)、用户(金融机构服务的客户)、业务合作方(与金融机构合作的机构)。平台提供商为租户、业务合作方提供业务服务,租户为其客户提供服务。 对上述四类业务需求进行技术抽象,则可归类为: 对人的管理(用户和操作员):用户是租户的用户,同时也是平台服务的用户,通过平台整体服务的连接效应和业务运营,实现不同租户之间用户的相互转化,从而实现为用户提供更多的服务;操作员根据所属业务主体不同,分为平台操作员、租户操作员、合作方操作员。通过操作员的分类和角色划分,通过操作权限和数据权限的组合,实现操作员的灵活管理。 对机构的管理:根据业务角色不同,机构可分为平台服务商、业务租户和业务合作方。在某一业务中,一个机构是业务租户,但在另一个业务中,此机构有可能是一个业务合作方;通过机构和机构业务角色管理,实现对机构的多维护管理。 对业务的管理:一类业务对应一个产品,用户、操作员、机构各自分配相应的产品权限,通过人与业务的关联,实现多业务流程的整合和流转。从金融机构客户需求出发,采用合理的业务和技术设计,不断地探索和实践,相信在不远的将来,在金融 IT 系统领域,多租户云平台定会迎来蓬勃发展。
FindBugs是基于Bug Patterns概念,查找javabytecode(.class文件)中的潜在bug,主要检查bytecode中的bug patterns,如NullPoint空指针检查、没有合理关闭资源、字符串相同判断错(==,而不是equals)等 一、Security 关于代码安全性防护 1.Dm: Hardcoded constant database password (DMI_CONSTANT_DB_PASSWORD) 代码中创建DB的密码时采用了写死的密码。 2.Dm: Empty database password (DMI_EMPTY_DB_PASSWORD) 创建数据库连接时没有为数据库设置密码,这会使数据库没有必要的保护。 3.HRS: HTTP cookie formed from untrusted input (HRS_REQUEST_PARAMETER_TO_COOKIE) 此代码使用不受信任的HTTP参数构造一个HTTP Cookie。 4.HRS: HTTP Response splitting vulnerability (HRS_REQUEST_PARAMETER_TO_HTTP_HEADER) 在代码中直接把一个HTTP的参数写入一个HTTP头文件中,它为HTTP的响应暴露了漏洞。 5.SQL: Nonconstant string passed to execute method on an SQL statement (SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE) 该方法以字符串的形式来调用SQLstatement的execute方法,它似乎是动态生成SQL语句的方法。这会更容易受到SQL注入攻击。 6.XSS: JSP reflected cross site scripting vulnerability (XSS_REQUEST_PARAMETER_TO_JSP_WRITER) 在代码中在JSP输出中直接写入一个HTTP参数,这会造成一个跨站点的脚本漏洞。 二、Experimental 1.LG: Potential lost logger changes due to weak reference in OpenJDK (LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE) OpenJDK的引入了一种潜在的不兼容问题,特别是,java.util.logging.Logger的行为改变时。它现在使用内部弱引用,而不是强引用。–logger配置改变,它就是丢失对logger的引用,这本是一个合理的变化,但不幸的是一些代码对旧的行为有依赖关系。这意味着,当进行垃圾收集时对logger配置将会丢失。例如: public static void initLogging() throws Exception { Logger logger = Logger.getLogger("edu.umd.cs"); logger.addHandler(new FileHandler()); // call to change logger configuration logger.setUseParentHandlers(false); // another call to change logger configuration } 该方法结束时logger的引用就丢失了,如果你刚刚结束调用initLogging方法后进行垃圾回收,logger的配置将会丢失(因为只有保持记录器弱引用)。 public static void main(String[] args) throws Exception { initLogging(); // adds a file handler to the logger System.gc(); // logger configuration lost Logger.getLogger("edu.umd.cs").info("Some message"); // this isn't logged to the file as expected } 2.OBL: Method may fail to clean up stream or resource (OBL_UNSATISFIED_OBLIGATION) 这种方法可能无法清除(关闭,处置)一个流,数据库对象,或其他资源需要一个明确的清理行动。 一般来说,如果一个方法打开一个流或其他资源,该方法应该使用try / finally块来确保在方法返回之前流或资源已经被清除了。这种错误模式基本上和OS_OPEN_STREAM和ODR_OPEN_DATABASE_RESOURCE错误模式相同,但是是在不同在静态分析技术。我们正为这个错误模式的效用收集反馈意见。 三、Bad practice代码实现中的一些坏习惯 1.AM: Creates an empty jar file entry (AM_CREATES_EMPTY_JAR_FILE_ENTRY) 调用putNextEntry()方法写入新的 jar 文件条目时立即调用closeEntry()方法。这样会造成JarFile条目为空。 2.AM: Creates an empty zip file entry (AM_CREATES_EMPTY_ZIP_FILE_ENTRY) 调用putNextEntry()方法写入新的 zip 文件条目时立即调用closeEntry()方法。这样会造成ZipFile条目为空。 3.BC: Equals method should not assume anything about the type of its argument (BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS) equals(Object o)方法不能对参数o的类型做任何的假设。比较此对象与指定的对象。当且仅当该参数不为 null,并且是表示与此对象相同的类型的对象时,结果才为 true。 4.BC: Random object created and used only once (DMI_RANDOM_USED_ONLY_ONCE) 随机创建对象只使用过一次就抛弃 5.BIT: Check for sign of bitwise operation (BIT_SIGNED_CHECK) 检查位操作符运行是否合理 ((event.detail & SWT.SELECTED) > 0) If SWT.SELECTED is a negative number, this is a candidate for a bug. Even when SWT.SELECTED is not negative, it seems good practice to use '!= 0' instead of '> 0'. 6.CN: Class implements Cloneable but does not define or use clone method (CN_IDIOM) 按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(它是受保护的),以获得有关重写此方法的详细信息。此接口不 包含 clone 方法。因此,因为某个对象实现了此接口就克隆它是不可能的,应该实现此接口的类应该使用公共方法重写 Object.clone 7.CN: clone method does not call super.clone() (CN_IDIOM_NO_SUPER_CALL) 一个非final类型的类定义了clone()方法而没有调用super.clone()方法。例如:B扩展自A,如果B中clone方法调用了spuer.clone(),而A中的clone没有调用spuer.clone(),就会造成结果类型不准确。要求A的clone方法中调用spuer.clone()方法。 8.CN: Class defines clone() but doesn't implement Cloneable (CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE) 类中定义了clone方法但是它没有实现Cloneable接口 9.Co: Abstract class defines covariant compareTo() method (CO_ABSTRACT_SELF) 抽象类中定义了多个compareTo()方法,正确的是覆写Comparable中的compareTo方法,方法的参数为Object类型,如下例: int compareTo(T o) 比较此对象与指定对象的顺序。 10.Co: Covariant compareTo() method defined (CO_SELF_NO_OBJECT) 类中定义了多个compareTo()方法,正确的是覆写Comparable中的compareTo方法,方法的参数为Object类型 11.DE: Method might drop exception (DE_MIGHT_DROP) 方法可能抛出异常 12.DE: Method might ignore exception (DE_MIGHT_IGNORE) 方法可能忽略异常 13.DMI: Don't use removeAll to clear a collection (DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION) 不要用removeAll方法去clear一个集合 14.DP: Classloaders should only be created inside doPrivileged block (DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED) 类加载器只能建立在特殊的方法体内 15.Dm: Method invokes System.exit(...) (DM_EXIT) 在方法中调用System.exit(...)语句,考虑用RuntimeException来代替 16.Dm: Method invokes dangerous method runFinalizersOnExit (DM_RUN_FINALIZERS_ON_EXIT) 在方法中调用了System.runFinalizersOnExit 或者Runtime.runFinalizersOnExit方法,因为这样做是很危险的。 17.ES: Comparison of String parameter using == or != (ES_COMPARING_PARAMETER_STRING_WITH_EQ) 用==或者!=方法去比较String类型的参数 18.ES: Comparison of String objects using == or != (ES_COMPARING_STRINGS_WITH_EQ) 用==或者!=去比较String类型的对象 19.Eq: Abstract class defines covariant equals() method (EQ_ABSTRACT_SELF) 20.Eq: Equals checks for noncompatible operand (EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS) equals方法检查不一致的操作。两个类根本就是父子关系而去调用equals方法去判读对象是否相等。 public boolean equals(Object o) { if (o instanceof Foo) return name.equals(((Foo)o).name); else if (o instanceof String) return name.equals(o); else return false; 21.Eq: Class defines compareTo(...) and uses Object.equals() (EQ_COMPARETO_USE_OBJECT_EQUALS) 类中定义了compareTo方法但是继承了Object中的compareTo方法 22.Eq: equals method fails for subtypes (EQ_GETCLASS_AND_CLASS_CONSTANT) 类中的equals方法可能被子类中的方法所破坏,当使用类似于Foo.class == o.getClass()的判断时考虑用this.getClass() == o.getClass()来替换 23.Eq: Covariant equals() method defined (EQ_SELF_NO_OBJECT) 类中定义了多个equals方法。正确的做法是覆写Object中的equals方法,它的参数为Object类型的对象。 24.FI: Empty finalizer should be deleted (FI_EMPTY) 为空的finalizer方法应该删除。一下关于finalizer的内容省略 25.GC: Unchecked type in generic call (GC_UNCHECKED_TYPE_IN_GENERIC_CALL) This call to a generic collection method passes an argument while compile type Object where a specific type from the generic type parameters is expected. Thus, neither the standard Java type system nor static analysis can provide useful information on whether the object being passed as a parameter is of an appropriate type. 26.HE: Class defines equals() but not hashCode() (HE_EQUALS_NO_HASHCODE) 方法定义了equals方法却没有定义hashCode方法 27.HE: Class defines hashCode() but not equals() (HE_HASHCODE_NO_EQUALS) 类定义了hashCode方法去没有定义equal方法 28.HE: Class defines equals() and uses Object.hashCode() (HE_EQUALS_USE_HASHCODE) 一个类覆写了equals方法,没有覆写hashCode方法,使用了Object对象的hashCode方法 29.HE: Class inherits equals() and uses Object.hashCode() (HE_INHERITS_EQUALS_USE_HASHCODE) 子类继承了父类的equals方法却使用了Object的hashCode方法 30.IC: Superclass uses subclass during initialization (IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION) 子类在父类未初始化之前使用父类对象实例 public class CircularClassInitialization { static class InnerClassSingleton extends CircularClassInitialization { static InnerClassSingleton singleton = new InnerClassSingleton(); } static CircularClassInitialization foo = InnerClassSingleton.singleton; } 31.IMSE: Dubious catching of IllegalMonitorStateException (IMSE_DONT_CATCH_IMSE) 捕捉违法的监控状态异常,例如当没有获取到对象锁时使用其wait和notify方法 32.ISC: Needless instantiation of class that only supplies static methods (ISC_INSTANTIATE_STATIC_CLASS) 为使用静态方法而创建一个实例对象。调用静态方法时只需要使用类名+静态方法名就可以了。 33.It: Iterator next() method can't throw NoSuchElementException (IT_NO_SUCH_ELEMENT) 迭代器的next方法不能够抛出NoSuchElementException 34.J2EE: Store of non serializable object into HttpSession (J2EE_STORE_OF_NON_SERIALIZABLE_OBJECT_INTO_SESSION) 在HttpSession对象中保存非连续的对象 35.JCIP: Fields of immutable classes should be final (JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS) The class is annotated with net.jcip.annotations.Immutable, and the rules for that annotation require that all fields are final. . 36.NP: Method with Boolean return type returns explicit null (NP_BOOLEAN_RETURN_NULL) 返回值为boolean类型的方法直接返回null,这样会导致空指针异常 37.NP: equals() method does not check for null argument (NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT) 变量调用equals方法时没有进行是否为null的判断 38.NP: toString method may return null (NP_TOSTRING_COULD_RETURN_NULL) toString方法可能返回null 39.Nm: Class names should start with an upper case letter (NM_CLASS_NAMING_CONVENTION) 类的名称以大写字母名称开头 40.Nm: Class is not derived from an Exception, even though it is named as such (NM_CLASS_NOT_EXCEPTION) 类的名称中含有Exception但是却不是一个异常类的子类,这种名称会造成混淆 41.Nm: Confusing method names (NM_CONFUSING) 令人迷惑的方面命名 42.Nm: Field names should start with a lower case letter (NM_FIELD_NAMING_CONVENTION) 非final类型的字段需要遵循驼峰命名原则 43.Nm: Use of identifier that is a keyword in later versions of Java (NM_FUTURE_KEYWORD_USED_AS_IDENTIFIER) 验证是否是java预留关键字 44.Nm: Use of identifier that is a keyword in later versions of Java (NM_FUTURE_KEYWORD_USED_AS_MEMBER_IDENTIFIER) 验证是否时java中的关键字 45.Nm: Method names should start with a lower case letter (NM_METHOD_NAMING_CONVENTION) 方法名称以小写字母开头 46.Nm: Class names shouldn't shadow simple name of implemented interface (NM_SAME_SIMPLE_NAME_AS_INTERFACE) 实现同一接口实现类不能使用相同的名称,即使它们位于不同的包中 47.Nm: Class names shouldn't shadow simple name of superclass (NM_SAME_SIMPLE_NAME_AS_SUPERCLASS) 继承同一父类的子类不能使用相同的名称,即使它们位于不同的包中 48.Nm: Very confusing method names (but perhaps intentional) (NM_VERY_CONFUSING_INTENTIONAL) 很容易混淆的方法命名,例如方法的名称名称使用使用大小写来区别两个不同的方法。 49.Nm: Method doesn't override method in superclass due to wrong package for parameter (NM_WRONG_PACKAGE_INTENTIONAL) 由于错误引用了不同包中相同类名的对象而不能够正确的覆写父类中的方法 import alpha.Foo; public class A { public int f(Foo x) { return 17; } } import beta.Foo; public class B extends A { public int f(Foo x) { return 42; } public int f(alpha.Foo x) { return 27; } } 50.ODR: Method may fail to close database resource (ODR_OPEN_DATABASE_RESOURCE) 方法中可能存在关闭数据连接失败的情况 51.OS: Method may fail to close stream (OS_OPEN_STREAM) 方法中可能存在关闭流失败的情况 52.OS: Method may fail to close stream on exception (OS_OPEN_STREAM_EXCEPTION_PATH) 方法中可能存在关闭流时出现异常情况 53.RC: Suspicious reference comparison to constant (RC_REF_COMPARISON_BAD_PRACTICE) 当两者为不同类型的对象时使用equals方法来比较它们的值是否相等,而不是使用==方法。例如比较的两者为java.lang.Integer, java.lang.Float 54.RC: Suspicious reference comparison of Boolean values (RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN) 使用== 或者 !=操作符来比较两个 Boolean类型的对象,建议使用equals方法。 55.RR: Method ignores results of InputStream.read() (RR_NOT_CHECKED) InputStream.read方法忽略返回的多个字符,如果对结果没有检查就没法正确处理用户读取少量字符请求的情况。 56.RR: Method ignores results of InputStream.skip() (SR_NOT_CHECKED) InputStream.skip()方法忽略返回的多个字符,如果对结果没有检查就没法正确处理用户跳过少量字符请求的情况 57.RV: Method ignores exceptional return value (RV_RETURN_VALUE_IGNORED_BAD_PRACTICE) 方法忽略返回值的异常信息 58.SI: Static initializer creates instance before all static final fields assigned (SI_INSTANCE_BEFORE_FINALS_ASSIGNED) 在所有的static final字段赋值之前去使用静态初始化的方法创建一个类的实例。 59.Se: Non-serializable value stored into instance field of a serializable class (SE_BAD_FIELD_STORE) 非序列化的值保存在声明为序列化的的非序列化字段中 60.Se: Comparator doesn't implement Serializable (SE_COMPARATOR_SHOULD_BE_SERIALIZABLE) Comparator接口没有实现Serializable接口 61.Se: Serializable inner class (SE_INNER_CLASS) 序列化内部类 62.Se: serialVersionUID isn't final (SE_NONFINAL_SERIALVERSIONID) 关于UID类的检查内容省略 63.Se: Class is Serializable but its superclass doesn't define a void constructor (SE_NO_SUITABLE_CONSTRUCTOR) 子类序列化时父类没有提供一个void的构造函数 64.Se: Class is Externalizable but doesn't define a void constructor (SE_NO_SUITABLE_CONSTRUCTOR_FOR_EXTERNALIZATION) Externalizable 实例类没有定义一个void类型的构造函数 65.Se: The readResolve method must be declared with a return type of Object. (SE_READ_RESOLVE_MUST_RETURN_OBJECT) readResolve从流中读取类的一个实例,此方法必须声明返回一个Object类型的对象 66.Se: Transient field that isn't set by deserialization. (SE_TRANSIENT_FIELD_NOT_RESTORED) This class contains a field that is updated at multiple places in the class, thus it seems to be part of the state of the class. However, since the field is marked as transient and not set in readObject or readResolve, it will contain the default value in any deserialized instance of the class. 67.SnVI: Class is Serializable, but doesn't define serialVersionUID (SE_NO_SERIALVERSIONID) 一个类实现了Serializable接口但是没有定义serialVersionUID类型的变量。序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException。可序列化类可以通过声明名为 "serialVersionUID" 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID: ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; 68.UI: Usage of GetResource may be unsafe if class is extended (UI_INHERITANCE_UNSAFE_GETRESOURCE) 当一个类被子类继承后不要使用this.getClass().getResource(...)来获取资源 四、Correctness关于代码正确性相关方面的 1.BC: Impossible cast (BC_IMPOSSIBLE_CAST) 不可能的类转换,执行时会抛出ClassCastException 2.BC: Impossible downcast (BC_IMPOSSIBLE_DOWNCAST) 父类在向下进行类型转换时抛出ClassCastException 3.BC: Impossible downcast of toArray() result (BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY) 集合转换为数组元素时发生的类转换错误。 This code is casting the result of calling toArray() on a collection to a type more specific than Object[], as in: String[] getAsArray(Collection<String> c) { return (String[]) c.toArray(); } This will usually fail by throwing a ClassCastException. The toArray() of almost all collections return an Object[]. They can't really do anything else, since the Collection object has no reference to the declared generic type of the collection. The correct way to do get an array of a specific type from a collection is to use c.toArray(new String[]); or c.toArray(new String[c.size()]); (the latter is slightly more efficient). 4.BC: instanceof will always return false (BC_IMPOSSIBLE_INSTANCEOF) 采用instaneof方法进行比较时总是返回false。前提是保证它不是由于某些逻辑错误造成的。 5.BIT: Incompatible bit masks (BIT_AND) 错误的使用&位操作符,例如(e & C) 6.BIT: Check to see if ((...) & 0) == 0 (BIT_AND_ZZ) 检查恒等的逻辑错误 7.BIT: Incompatible bit masks (BIT_IOR) 错误的使用|位操作符,例如(e | C) 8.BIT: Check for sign of bitwise operation (BIT_SIGNED_CHECK_HIGH_BIT) 检查逻辑运算符操作返回的标识。例如((event.detail & SWT.SELECTED) > 0),建议采用!=0代替>0 9.BOA: Class overrides a method implemented in super class Adapter wrongly (BOA_BADLY_OVERRIDDEN_ADAPTER) 子类错误的覆写父类中用于适配监听其他事件的方法,从而导致当触发条件发生时不能被监听者调用 10.Bx: Primitive value is unboxed and coerced for ternary operator (BX_UNBOXED_AND_COERCED_FOR_TERNARY_OPERATOR) 在三元运算符操作时如果没有对值进行封装或者类型转换。例如:b ? e1 : e2 11.DLS: Dead store of class literal (DLS_DEAD_STORE_OF_CLASS_LITERAL) 以类的字面名称方式为一个字段赋值后再也没有去使用它,在1.4jdk中它会自动调用静态的初始化方法,而在jdk1.5中却不会去执行。 12.DLS: Overwritten increment (DLS_OVERWRITTEN_INCREMENT) 覆写增量增加错误i = i++ 13.DMI: Bad constant value for month (DMI_BAD_MONTH) hashNext方法调用next方法。 14.DMI: Collections should not contain themselves (DMI_COLLECTIONS_SHOULD_NOT_CONTAIN_THEMSELVES) 集合没有包含他们自己本身。 15.DMI: Invocation of hashCode on an array (DMI_INVOKING_HASHCODE_ON_ARRAY) 数组直接使用hashCode方法来返回哈希码。 int [] a1 = new int[]{1,2,3,4}; System.out.println(a1.hashCode()); System.out.println(java.util.Arrays.hashCode(a1)); 16.DMI: Double.longBitsToDouble invoked on an int (DMI_LONG_BITS_TO_DOUBLE_INVOKED_ON_INT) 17.DMI: Vacuous call to collections (DMI_VACUOUS_SELF_COLLECTION_CALL) 集合的调用不能被感知。例如c.containsAll(c)总是返回true,而c.retainAll(c)的返回值不能被感知。 18.Dm: Can't use reflection to check for presence of annotation without runtime retention (DMI_ANNOTATION_IS_NOT_VISIBLE_TO_REFLECTION) Unless an annotation has itself been annotated with @Retention(RetentionPolicy.RUNTIME), the annotation can't be observed using reflection (e.g., by using the isAnnotationPresent method). . 19.Dm: Useless/vacuous call to EasyMock method (DMI_VACUOUS_CALL_TO_EASYMOCK_METHOD) While ScheduledThreadPoolExecutor inherits from ThreadPoolExecutor, a few of the inherited tuning methods are not useful for it. In particular, because it acts as a fixed-sized pool using corePoolSize threads and an unbounded queue, adjustments to maximumPoolSize have no useful effect. 20.EC: equals() used to compare array and nonarray (EC_ARRAY_AND_NONARRAY) 数组对象使用equals方法和非数组对象进行比较。即使比较的双方都是数组对象也不应该使用equals方法,而应该比较它们的内容是否相等使用java.util.Arrays.equals(Object[], Object[]); 21.EC: equals(...) used to compare incompatible arrays (EC_INCOMPATIBLE_ARRAY_COMPARE) 使用equls方法去比较类型不相同的数组。例如:String[] and StringBuffer[], or String[] and int[] 22.EC: Call to equals() with null argument (EC_NULL_ARG) 调用equals的对象为null 23.EC: Call to equals() comparing unrelated class and interface (EC_UNRELATED_CLASS_AND_INTERFACE) 使用equals方法比较不相关的类和接口 24.EC: Call to equals() comparing different interface types (EC_UNRELATED_INTERFACES) 调用equals方法比较不同类型的接口 25.EC: Call to equals() comparing different types (EC_UNRELATED_TYPES) 调用equals方法比较不同类型的类 26.EC: Using pointer equality to compare different types (EC_UNRELATED_TYPES_USING_POINTER_EQUALITY) This method uses using pointer equality to compare two references that seem to be of different types. The result of this comparison will always be false at runtime. 27.Eq: equals method always returns false (EQ_ALWAYS_FALSE) 使用equals方法返回值总是false 28.Eq: equals method always returns true (EQ_ALWAYS_TRUE) equals方法返回值总是true 29.Eq: equals method compares class names rather than class objects (EQ_COMPARING_CLASS_NAMES) 使用equals方法去比较一个类的实例和类的类型 30.Eq: Covariant equals() method defined for enum (EQ_DONT_DEFINE_EQUALS_FOR_ENUM) This class defines an enumeration, and equality on enumerations are defined using object identity. Defining a covariant equals method for an enumeration value is exceptionally bad practice, since it would likely result in having two different enumeration values that compare as equals using the covariant enum method, and as not equal when compared normally. Don't do it. 31.Eq: equals() method defined that doesn't override equals(Object) (EQ_OTHER_NO_OBJECT) 类中定义的equals方法时不要覆写equals(Object)方法 32.Eq: equals() method defined that doesn't override Object.equals(Object) (EQ_OTHER_USE_OBJECT) 类中定义的equals方法时不要覆写Object中的equals(Object)方法 33.Eq: equals method overrides equals in superclass and may not be symmetric (EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC) 34.Eq: Covariant equals() method defined, Object.equals(Object) inherited (EQ_SELF_USE_OBJECT) 类中定义了一组equals方法,但是都是继承的java.lang.Object class中的equals(Object)方法 35.FE: Doomed test for equality to NaN (FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER) This code checks to see if a floating point value is equal to the special Not A Number value (e.g., if (x == Double.NaN)). However, because of the special semantics of NaN, no value is equal to Nan, including NaN. Thus, x == Double.NaN always evaluates to false. To check to see if a value contained in x is the special Not A Number value, use Double.isNaN(x) (or Float.isNaN(x) if x is floating point precision). 36.FS: Format string placeholder incompatible with passed argument (VA_FORMAT_STRING_BAD_ARGUMENT) 错误使用参数类型来格式化字符串 37.FS: The type of a supplied argument doesn't match format specifier (VA_FORMAT_STRING_BAD_CONVERSION) 指定的格式字符串和参数类型不匹配,例如:String.format("%d", "1") 38.FS: MessageFormat supplied where printf style format expected (VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED) 但用String的format方法时实际调用了MessageFormat中干的格式化方法而引起格式化结果出错。 39.FS: More arguments are passed than are actually used in the format string (VA_FORMAT_STRING_EXTRA_ARGUMENTS_PASSED) 使用String的format方法时有非法的参数也经过了格式化操作。 40.FS: Illegal format string (VA_FORMAT_STRING_ILLEGAL) 格式化String对象语句错误 41.FS: Format string references missing argument (VA_FORMAT_STRING_MISSING_ARGUMENT) String的format操作缺少必要的参数。 42.FS: No previous argument for format string (VA_FORMAT_STRING_NO_PREVIOUS_ARGUMENT) 格式字符串定义错误,例如:formatter.format("%<s %s", "a", "b"); 抛出MissingFormatArgumentException异常 43.GC: No relationship between generic parameter and method argument (GC_UNRELATED_TYPES) This call to a generic collection method contains an argument with an incompatible class from that of the collection's parameter (i.e., the type of the argument is neither a supertype nor a subtype of the corresponding generic type argument). Therefore, it is unlikely that the collection contains any objects that are equal to the method argument used here. Most likely, the wrong value is being passed to the method. In general, instances of two unrelated classes are not equal. For example, if the Foo and Bar classes are not related by subtyping, then an instance of Foo should not be equal to an instance of Bar. Among other issues, doing so will likely result in an equals method that is not symmetrical. For example, if you define the Foo class so that a Foo can be equal to a String, your equals method isn't symmetrical since a String can only be equal to a String. In rare cases, people do define nonsymmetrical equals methods and still manage to make their code work. Although none of the APIs document or guarantee it, it is typically the case that if you check if a Collection<String> contains a Foo, the equals method of argument (e.g., the equals method of the Foo class) used to perform the equality checks. 44.HE: Signature declares use of unhashable class in hashed construct (HE_SIGNATURE_DECLARES_HASHING_OF_UNHASHABLE_CLASS) A method, field or class declares a generic signature where a non-hashable class is used in context where a hashable class is required. A class that declares an equals method but inherits a hashCode() method from Object is unhashable, since it doesn't fulfill the requirement that equal objects have equal hashCodes. 45.HE: Use of class without a hashCode() method in a hashed data structure (HE_USE_OF_UNHASHABLE_CLASS) A class defines an equals(Object) method but not a hashCode() method, and thus doesn't fulfill the requirement that equal objects have equal hashCodes. An instance of this class is used in a hash data structure, making the need to fix this problem of highest importance. 46.ICAST: integral value cast to double and then passed to Math.ceil (ICAST_INT_CAST_TO_DOUBLE_PASSED_TO_CEIL) integral的值转换为double后使用了Math.ceil方法 47.ICAST: int value cast to float and then passed to Math.round (ICAST_INT_CAST_TO_FLOAT_PASSED_TO_ROUND) int 类型的值转换为float类型之后调用了Math.round方法 48.IJU: JUnit assertion in run method will not be noticed by JUnit (IJU_ASSERT_METHOD_INVOKED_FROM_RUN_METHOD) 在JUnit中的断言在run方法中不会被告知 49.IJU: TestCase declares a bad suite method (IJU_BAD_SUITE_METHOD) 在一个JUnit类中声明的一个suite()方法必须声明为 public static junit.framework.Test suite() 或者 public static junit.framework.TestSuite suite()的形式。 50.IL: A collection is added to itself (IL_CONTAINER_ADDED_TO_ITSELF) 集合本身作为add方法的参数,这样会引起内容溢出。 51.IL: An apparent infinite loop (IL_INFINITE_LOOP) 方法的自调用引起的死循环 52.IM: Integer multiply of result of integer remainder (IM_MULTIPLYING_RESULT_OF_IREM) 和整数余数进行乘法运算。例如:i % 60 * 1000 是进行(i % 60) * 1000运算而不是 i % (60 * 1000) 53.INT: Bad comparison of nonnegative value with negative constant (INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE) 保证非负数和负数进行比较 54.INT: Bad comparison of signed byte (INT_BAD_COMPARISON_WITH_SIGNED_BYTE) 比较有符合数,要先把有符号数转换为无符合数再进行比较 55.IO: Doomed attempt to append to an object output stream (IO_APPENDING_TO_OBJECT_OUTPUT_STREAM) 宣布试图在对象的输出流处添加元素,如果你希望能够添加进一个对象的输出流中必须保证对象的输出流处于打开状态。 56.IP: A parameter is dead upon entry to a method but overwritten (IP_PARAMETER_IS_DEAD_BUT_OVERWRITTEN) The initial value of this parameter is ignored, and the parameter is overwritten here. This often indicates a mistaken belief that the write to the parameter will be conveyed back to the caller. 传入参数的值被忽略,但是对传入值进行了修改,并返回给了调用者 57.MF: Class defines field that masks a superclass field (MF_CLASS_MASKS_FIELD) 子类中定义了和父类中同名的字段。在调用时会出错 58.MF: Method defines a variable that obscures a field (MF_METHOD_MASKS_FIELD) 在方法中定义的局部变量和类变量或者父类变量同名,从而引起字段混淆。 59.NP: Null pointer dereference (NP_ALWAYS_NULL) 对象赋为null值后 没有被重新赋值 60.NP: Null pointer dereference in method on exception path (NP_ALWAYS_NULL_EXCEPTION) A pointer which is null on an exception path is dereferenced here. This will lead to a NullPointerException when the code is executed. Note that because FindBugs currently does not prune infeasible exception paths, this may be a false warning. Also note that FindBugs considers the default case of a switch statement to be an exception path, since the default case is often infeasible. 空指针引用上调用去除引用方法,将发生空指针异常 61.NP: Method does not check for null argument (NP_ARGUMENT_MIGHT_BE_NULL) 方法没有判断参数是否为空 62.NP: close() invoked on a value that is always null (NP_CLOSING_NULL) 一个为空的对象调用close方法 63.NP: Null value is guaranteed to be dereferenced (NP_GUARANTEED_DEREF) There is a statement or branch that if executed guarantees that a value is null at this point, and that value that is guaranteed to be dereferenced (except on forward paths involving runtime exceptions). 在正常的null判断分支上,对象去除引用操作是受保护的不允许的 64.NP: Value is null and guaranteed to be dereferenced on exception path (NP_GUARANTEED_DEREF_ON_EXCEPTION_PATH) There is a statement or branch on an exception path that if executed guarantees that a value is null at this point, and that value that is guaranteed to be dereferenced (except on forward paths involving runtime exceptions). 65.NP: Method call passes null to a nonnull parameter (NP_NONNULL_PARAM_VIOLATION) 方法中为null的参数没有被重新赋值 void test(){ String ss = null; sya(ss); } public void sya(String ad){ ad.getBytes(); } 66.NP: Method may return null, but is declared @NonNull (NP_NONNULL_RETURN_VIOLATION) 方法声明了返回值不能为空,但是方法中有可能返回null 67.NP: A known null value is checked to see if it is an instance of a type (NP_NULL_INSTANCEOF) 检查一个为null的值是否是想要的类型对象,而不是由于粗心或者逻辑错误引起的 68.NP: Possible null pointer dereference (NP_NULL_ON_SOME_PATH) 对象可能没有重新赋值 69.NP: Possible null pointer dereference in method on exception path (NP_NULL_ON_SOME_PATH_EXCEPTION) A reference value which is null on some exception control path is dereferenced here. This may lead to a NullPointerException when the code is executed. Note that because FindBugs currently does not prune infeasible exception paths, this may be a false warning. Also note that FindBugs considers the default case of a switch statement to be an exception path, since the default case is often infeasible. 在异常null值处理分支调用的方法上,可能存在对象去除引用操作 70.NP: Method call passes null for nonnull parameter (NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS) 方法参数中声明为nonnull类型的参数为null void test(){ String ss = null; sya(ss); } public void sya(@nonnull String ad){ ad.getBytes(); } 71.NP: Store of null value into field annotated NonNull (NP_STORE_INTO_NONNULL_FIELD) 为一个已经声明为不能为null值的属性赋值为null。 72.Nm: Class defines equal(Object); should it be equals(Object)? (NM_BAD_EQUAL) 类中定义了一个equal方法但是却不是覆写的Object对象的equals方法 73.Nm: Class defines hashcode(); should it be hashCode()? (NM_LCASE_HASHCODE) 类中定义了一个hashCode方法但是却不是覆写的Object中的hashCode方法 74.Nm: Class defines tostring(); should it be toString()? (NM_LCASE_TOSTRING) 类中定义了一个toString方法但是却不是覆写的Object中的toString方法 75.Nm: Apparent method/constructor confusion (NM_METHOD_CONSTRUCTOR_CONFUSION) 构造方法定义混乱,保证一个标准的构造函数。 例如: SA(){ } void SA(){ } 76.Nm: Very confusing method names (NM_VERY_CONFUSING) 混乱的方法命名,如getName和getname方法同时出现的时候 77.Nm: Method doesn't override method in superclass due to wrong package for parameter (NM_WRONG_PACKAGE) 方法因为取了不同包中的同名的对象而没有正确覆写父类中的同名方法 import alpha.Foo; public class A { public int f(Foo x) { return 17; } } ---- import beta.Foo; public class B extends A { public int f(Foo x) { return 42; } } 78.QBA: Method assigns boolean literal in boolean expression (QBA_QUESTIONABLE_BOOLEAN_ASSIGNMENT) 再if或者while表达式中使用boolean类型的值时应该使用==去判断,而不是采用=操作 79.RC: Suspicious reference comparison (RC_REF_COMPARISON) 比较两个对象值是否相等时应该采用equals方法,而不是==方法 80.RE: Invalid syntax for regular expression (RE_BAD_SYNTAX_FOR_REGULAR_EXPRESSION) 对正则表达式使用了错误的语法,会抛出未经检查的异常,表明正则表达式模式中的语法错误。 81.RE: File.separator used for regular expression (RE_CANT_USE_FILE_SEPARATOR_AS_REGULAR_EXPRESSION) 使用正则表达式使用了错误的文件分隔符,在windows系统中正则表达式不会匹配’\’而应该使用'\\' 82.RV: Random value from 0 to 1 is coerced to the integer 0 (RV_01_TO_INT) 从0到1随机值被强制为整数值0。在强制得到一个整数之前,你可能想得到多个随机值。或使用Random.nextInt(n)的方法。 83.RV: Bad attempt to compute absolute value of signed 32-bit hashcode (RV_ABSOLUTE_VALUE_OF_HASHCODE) 此代码生成一个哈希码,然后计算该哈希码的绝对值。如果哈希码是Integer.MIN_VALUE的,那么结果将是负数(因为Math.abs(Integer.MIN_VALUE的)== Integer.MIN_VALUE的)。 在2^ 32值之外字符串有一个Integer.MIN_VALUE的hashCode包括“polygenelubricants”,“GydZG_”和“,”DESIGNING WORKHOUSES “。 84.RV: Bad attempt to compute absolute value of signed 32-bit random integer (RV_ABSOLUTE_VALUE_OF_RANDOM_INT) 此代码生成一个随机的符号整数,然后计算该随机整数的绝对值。如果随机数生成数绝对值为Integer.MIN_VALUE的,那么结果将是负数(因为Math.abs(Integer.MIN_VALUE的)== Integer.MIN_VALUE的)。 85.RV: Exception created and dropped rather than thrown (RV_EXCEPTION_NOT_THROWN) 此代码创建一个异常(或错误)的对象,但不会用它做任何事情。例如:if (x < 0) new IllegalArgumentException("x must be nonnegative"); 这可能是程序员的意图抛出创建的异常: if (x < 0) throw new IllegalArgumentException("x must be nonnegative"); 86.RV: Method ignores return value (RV_RETURN_VALUE_IGNORED) 该方法的返回值应该进行检查。这种警告通常出现在调用一个不可变对象的方法,认为它更新了对象的值。例如:String dateString = getHeaderField(name); dateString.trim(); 程序员似乎以为trim()方法将更新dateString引用的字符串。但由于字符串是不可改变的,trim()函数返回一个新字符串值,在这里它是被忽略了。该代码应更正: String dateString = getHeaderField(name); dateString = dateString.trim(); 87.RpC: Repeated conditional tests (RpC_REPEATED_CONDITIONAL_TEST) 该代码包含对同一个条件试验了两次,两边完全一样例如:(如X == 0 | | x == 0)。可能第二次出现是打算判断别的不同条件(如X == 0 | | y== 0)。 88.SA: Double assignment of field (SA_FIELD_DOUBLE_ASSIGNMENT) 方法中的字段包含了双重任务,例如: int x; public void foo() { x = x = 17; } 这种为变量赋值是无用的,并可能表明一个逻辑错误或拼写错误。 89.SA: Self assignment of field (SA_FIELD_SELF_ASSIGNMENT) 方法中包含自己对自己赋值的字段。例如: int x; public void foo() { x = x; } 90.SA: Self comparison of field with itself (SA_FIELD_SELF_COMPARISON) 字段自己进行自比较可能表明错误或逻辑错误。 91.SA: Self comparison of value with itself (SA_LOCAL_SELF_COMPARISON) 方法中对一个局部变量自身进行比较运算,并可说明错误或逻辑错误。请确保您是比较正确的事情。 92.SA: Nonsensical self computation involving a variable (e.g., x & x) (SA_LOCAL_SELF_COMPUTATION) 此方法对同一变量执行了荒谬的计算(如x&x或x-x)操作。由于计算的性质,这一行动似乎没有意义,并可能表明错误或逻辑错误。 93.SF: Dead store due to switch statement fall through (SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH) 在swtich中先前的case值因为swtich执行失败而被覆写,这就像是忘记使用break推出或者没有使用return语句放回先前的值一样。 94.SF: Dead store due to switch statement fall through to throw (SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH_TO_THROW) 在swtich中因为出现异常而忽略了对case值的保存。 95.SIC: Deadly embrace of non-static inner class and thread local (SIC_THREADLOCAL_DEADLY_EMBRACE) 如果是一个静态内部类。实际上,在内部类和当前线程有死锁的可能。由于内部类不是静态的,它保留了对外部类的引用。如果线程包含对一个内部类实例的引用,那么内外实例的实例都可以被获取,这样就不具备垃圾会回收的资格。 96.SIO: Unnecessary type check done using instanceof operator (SIO_SUPERFLUOUS_INSTANCEOF) 在进行instanceof操作时进行没有必要的类型检查 97.STI: Unneeded use of currentThread() call, to call interrupted() (STI_INTERRUPTED_ON_CURRENTTHREAD) 此方法调用Thread.currentThread()调用,只需调用interrupted()方法。由于interrupted()是一个静态方法, Thread.interrupted()更简单易用。 98.STI: Static Thread.interrupted() method invoked on thread instance (STI_INTERRUPTED_ON_UNKNOWNTHREAD) 调用不是当前线程对象的Thread.interrupted()方法,由于interrupted()方法是静态的,interrupted方法将会调用一个和作者原计划不同的对象。 99.Se: Method must be private in order for serialization to work (SE_METHOD_MUST_BE_PRIVATE) 这个类实现了Serializable接口,并定义自定义序列化的方法/反序列化。但由于这种方法不能声明为private,将被序列化/反序列化的API忽略掉。 100.Se: The readResolve method must not be declared as a static method. (SE_READ_RESOLVE_IS_STATIC) 为使readResolve方法得到序列化机制的识别,不能作为一个静态方法来声明。 101.UMAC: Uncallable method defined in anonymous class (UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS) 在匿名类中定义了一个既没有覆写超类中方法也不能直接调用的方法。因为在其他类的方法不能直接引用匿名类声明的方法,似乎这种方法不能被调用,这种方法可能只是没有任何作用的代码,但也可能覆写超类中声明。 102.UR: Uninitialized read of field in constructor (UR_UNINIT_READ) 此构造方法中使用了一个尚未赋值的字段或属性。 String a; public SA() { String abc = a; System.out.println(abc); } 103.UR: Uninitialized read of field method called from constructor of superclass (UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR) 方法被超类的构造函数调用时,在当前类中的字段或属性还没有被初始化。例如: abstract class A { int hashCode; abstract Object getValue(); A() { hashCode = getValue().hashCode(); } } class B extends A { Object value; B(Object v) { this.value = v; } Object getValue() { return value; } } 当B是创建时,A的构造函数将在B为value赋值之前触发,然而在A的初始化方法调用getValue方法时value这个变量还没有被初始化。 104.USELESS_STRING: Invocation of toString on an array (DMI_INVOKING_TOSTRING_ON_ANONYMOUS_ARRAY) 该代码调用上匿名数组的toString()方法,产生的结果形如[@ 16f0472并没有实际的意义。考虑使用Arrays.toString方法来转换成可读的字符串,提供该数组的内容数组。例如: String[] a = { "a" }; System.out.println(a.toString()); //正确的使用为 System.out.println(Arrays.toString(a)); 105.USELESS_STRING: Invocation of toString on an array (DMI_INVOKING_TOSTRING_ON_ARRAY) 该代码调用上数组的toString()方法,产生的结果形如[@ 16f0472并不能显示数组的真实内容。考虑使用Arrays.toString方法来转换成可读的字符串,提供该数组的内容数组 106.UwF: Field only ever set to null (UWF_NULL_FIELD) 字段的值总是为null值,所有读取该字段的值都为null。检查错误,如果它确实没有用就删除掉。 107.UwF: Unwritten field (UWF_UNWRITTEN_FIELD 此字段是永远不会写入值。所有读取将返回默认值。检查错误(如果它被初始化?),如果它确实没有用就删除掉。 五:Performance关于代码性能相关方面的 1.Bx: Primitive value is boxed and then immediately unboxed (BX_BOXING_IMMEDIATELY_UNBOXED) 对原始值进行装箱,然后立即取消装箱。这可能是在一个未要求装箱的地方进行了手动装箱,从而迫使编译器进行立即撤消装箱的操作 2.Bx: Primitive value is boxed then unboxed to perform primitive coercion (BX_BOXING_IMMEDIATELY_UNBOXED_TO_PERFORM_COERCION) 对原始值进行装箱然后立即把它强制转换为另外一种原始类型。例如: new Double(d).intValue()应该直接进行强制转换例如:(int) d 3.Bx: Method allocates a boxed primitive just to call toString (DM_BOXED_PRIMITIVE_TOSTRING) 仅仅为了调用封装类的toString()而对原始类型进行封装操作。比这种方法更有效的是调用封装类的toString(…)方法例如: new Integer(1).toString() 替换为 Integer.toString(1) new Long(1).toString() 替换为 Long.toString(1) new Float(1.0).toString() 替换为 Float.toString(1.0) new Double(1.0).toString() 替换为 Double.toString(1.0) new Byte(1).toString() 替换为 Byte.toString(1) new Short(1).toString() 替换为 Short.toString(1) new Boolean(true).toString() 替换为 Boolean.toString(true) 4.Bx: Method invokes inefficient floating-point Number constructor; use static valueOf instead (DM_FP_NUMBER_CTOR) 使用new Double(double)方法总是会创建一个新的对象,然而使用Double.valueOf(double)方法可以把值保存在编辑器或者class library、JVM中。使用存储值的方式来避免对象的分配可以或得更好的代码性能 除非类必须符合Java 1.5以前的JVM,否则请使用自动装箱或valueOf()方法创建Double和Float实例。 5.Bx: Method invokes inefficient Number constructor; use static valueOf instead (DM_NUMBER_CTOR) 使用new Integer(int)方法总是会创建一个新的对象,然而使用Integer.valueOf(int)方法可以把值保存在编辑器或者class library、JVM中。使用存储值的方式来避免对象的分配可以或得更好的代码性能 除非类必须符合Java 1.5以前的JVM,否则请使用自动装箱或valueOf()方法创建Long, Integer, Short, Character, Byte实例。 6.Dm: The equals and hashCode methods of URL are blocking (DMI_BLOCKING_METHODS_ON_URL) 使用equals和hashCode方法来对url进行资源标识符解析时会引起堵塞。考虑使用java.net.URI来代替。 7.Dm: Maps and sets of URLs can be performance hogs (DMI_COLLECTION_OF_URLS) 方法或者字段使用url的map/set集合。因为equals方法或者hashCode方法来进行资源标识符解析时都会引起堵塞。考虑使用java.net.URI来代替。 8.Dm: Method invokes inefficient Boolean constructor; use Boolean.valueOf(...) instead (DM_BOOLEAN_CTOR) 使用new方法创建一个java.lang.Boolean类型能够的实例对象是浪费空间的,因为Boolean对象是不可变的而且只有两个有用的值。使用Boolean.valueOf()或者Java1.5中的自动装箱功能来创建一个Boolean实例。 9.Dm: Explicit garbage collection; extremely dubious except in benchmarking code (DM_GC) 在代码中显式的调用垃圾回收命名,这样做并不能起作用。在过去,有人在关闭操作或者finalize方法中调用垃圾回收方法导致了很多的性能浪费。这样大规模回收对象时会造成处理器运行缓慢。 10.Dm: Use the nextInt method of Random rather than nextDouble to generate a random integer (DM_NEXTINT_VIA_NEXTDOUBLE) 如果r是一个java.util.Random对象,你可以使r.nextInt(n)生成一个0到n-1之前的随机数,而不是使用(int)(r.nextDouble() * n) 11.Dm: Method invokes inefficient new String(String) constructor (DM_STRING_CTOR) 使用java.lang.String(String)构造函数会浪费内存因为这种构造方式和String作为参数在功能上容易混乱。只是使用String直接作为参数的形式 12.Dm: Method invokes toString() method on a String (DM_STRING_TOSTRING) 调用String.toString()是多余的操作,只要使用String就可以了。 13.Dm: Method invokes inefficient new String() constructor (DM_STRING_VOID_CTOR) 使用没有参数的构造方法去创建新的String对象是浪费内存空间的,因为这样创建会和空字符串“”混淆。Java中保证完成相同的构造方法会产生描绘相同的String对象。所以你只要使用空字符串来创建就可以了。 14.ITA: Method uses toArray() with zero-length array argument (ITA_INEFFICIENT_TO_ARRAY) 当使用集合的toArray()方法时使用数组长度为0的数组作为参数。比这更有效的一种方法是 myCollection.toArray(new Foo[myCollection.size()]),如果数组的长度足够大就可以直接把集合中的内容包装到数组中直接返回从而避免了第二次创建一个新的数组来存放集合中值。 15.SBSC: Method concatenates strings using + in a loop (SBSC_USE_STRINGBUFFER_CONCATENATION) 在循环中构建一个String对象时从性能上讲使用StringBuffer来代替String对象 例如: // This is bad String s = ""; for (int i = 0; i < field.length; ++i) { s = s + field[i]; } // This is better StringBuffer buf = new StringBuffer(); for (int i = 0; i < field.length; ++i) { buf.append(field[i]); } String s = buf.toString(); 16.SS: Unread field: should this field be static? (SS_SHOULD_BE_STATIC) 类中所包含的final属性字段在编译器中初始化为静态的值。考虑在定义时就把它定义为static类型的。 17.UM: Method calls static Math class method on a constant value (UM_UNNECESSARY_MATH) 在方法中使用了java.lang.Math的静态方法代替常量来使用,使用常量速度和准确度会更好。 以下为Math中的方法产生的值。 Method Parameter abs -any- acos 0.0 or 1.0 asin 0.0 or 1.0 atan 0.0 or 1.0 atan2 0.0 cbrt 0.0 or 1.0 ceil -any- cos 0.0 cosh 0.0 exp 0.0 or 1.0 expm1 0.0 floor -any- log 0.0 or 1.0 log10 0.0 or 1.0 rint -any- round -any- sin 0.0 sinh 0.0 sqrt 0.0 or 1.0 tan 0.0 tanh 0.0 toDegrees 0.0 or 1.0 toRadians 0.0 18.UPM: Private method is never called (UPM_UNCALLED_PRIVATE_METHOD) 定义为Private类型方法从未被调用,应该被删除。 19.UrF: Unread field (URF_UNREAD_FIELD) 类中定义的属性从未被调用,建议删除。 20.UuF: Unused field (UUF_UNUSED_FIELD) 类中定义的属性从未被使用,建议删除。 21.WMI: Inefficient use of keySet iterator instead of entrySet iterator (WMI_WRONG_MAP_ITERATOR) 当方法中接受一个Map类型的参数时,使用keySet的迭代器比使用entrySet的迭代器效率要高。 六:Internationalization关于代码国际化相关方面的 Dm: Consider using Locale parameterized version of invoked method (DM_CONVERT_CASE) 使用平台默认的编码格式对字符串进行大小写转换,这可能导致国际字符的转换不当。使用以下方式对字符进行转换 String.toUpperCase( Locale l ) String.toLowerCase( Locale l ) 七:Multithreaded correctness关于代码多线程正确性相关方面的 1.DL: Synchronization on Boolean could lead to deadlock (DL_SYNCHRONIZATION_ON_BOOLEAN) 该代码同步一个封装的原始常量,例如一个Boolean类型。 private static Boolean inited = Boolean.FALSE; ... synchronized(inited) { if (!inited) { init(); inited = Boolean.TRUE; } } ... 由于通常只存在两个布尔对象,此代码可能是同步的其他无关的代码中相同的对象,这时会导致反应迟钝和可能死锁 2.DL: Synchronization on boxed primitive could lead to deadlock (DL_SYNCHRONIZATION_ON_BOXED_PRIMITIVE) 该代码同步一个封装的原始常量,例如一个Integer类型。 private static Integer count = 0; ... synchronized(count) { count++; } ... 由于Integer对象可以共享和保存,此代码可能是同步的其他无关的代码中相同的对象,这时会导致反应迟钝和可能死锁 3.DL: Synchronization on interned String could lead to deadlock (DL_SYNCHRONIZATION_ON_SHARED_CONSTANT) 同步String类型的常量时,由于它被JVM中多个其他的对象所共有,这样在其他代码中会引起死锁。 4.DL: Synchronization on boxed primitive values (DL_SYNCHRONIZATION_ON_UNSHARED_BOXED_PRIMITIVE) 同步一个显然不是共有封装的原始值,例如一个Integer类型的对象。例如: private static final Integer fileLock = new Integer(1); ... synchronized(fileLock) { .. do something .. } ... 它最后被定义为以下方式来代替:private static final Object fileLock = new Object(); 5.Dm: Monitor wait() called on Condition (DM_MONITOR_WAIT_ON_CONDITION) 方法中以java.util.concurrent.locks.Condition对象调用wait()。等待一个条件发生时应该使用在Condition接口中定义的await()方法。 6.Dm: A thread was created using the default empty run method (DM_USELESS_THREAD) 这个方法没有通过run方法或者具体声明Thread类,也没有通过一个Runnable对象去定义一个线程,而这个线程出来浪费资源却什么也没有去做。 7.ESync: Empty synchronized block (ESync_EMPTY_SYNC) 该代码包含一个空的同步块:synchronized() {} 8.IS: Inconsistent synchronization (IS2_INCONSISTENT_SYNC) 不合理的同步 9.IS: Field not guarded against concurrent access (IS_FIELD_NOT_GUARDED) 域不是良好的同步访问--- 此字段被标注为net.jcip.annotations.GuardedBy,但可以在某种程度上违反注释而去访问 10.JLM: Synchronization performed on Lock (JLM_JSR166_LOCK_MONITORENTER) 实现java.util.concurrent.locks.Lock的对象调用了同步的方法。应该这样处理,对象被锁定/解锁时使用acquire()/ release()方法而不是使用同步的方法。 11.LI: Incorrect lazy initialization of static field (LI_LAZY_INIT_STATIC) 静态域不正确的延迟初始化-- 这种方法包含了一个不同步延迟初始化的非volatile静态字段。因为编译器或处理器可能会重新排列指令,如果该方法可以被多个线程调用,线程不能保证看到一个完全初始化的对象。你可以让字段可变来解决此问题 12.LI: Incorrect lazy initialization and update of static field (LI_LAZY_INIT_UPDATE_STATIC) 这种方法包含一个不同步延迟初始化的静态字段。之后为字段赋值,对象存储到该位置后进一步更新或访问。字段后尽快让其他线程能够访问。如果该方法的进一步访问该字段为初始化对象提供服务,然后你有一个非常严重的多线程bug,除非别的东西阻止任何其他线程访问存储的对象,直到它完全初始化。 即使你有信心,该方法是永远不会被多个线程调用时,在它的值还没有被充分初始化或移动,不把它设定为static字段时它可能会更好。 13.ML: Method synchronizes on an updated field (ML_SYNC_ON_UPDATED_FIELD) 对象获取一个可变字段时进行同步。这是没有意义的,因为不同的线程可以在不同的对象同步。 14.MSF: Mutable servlet field (MSF_MUTABLE_SERVLET_FIELD) 一个web服务一般只能创建一个servlet或者jsp的实例(例如:treates是一个单利类),它会被多个线程调用这个实例的方法服务于多个同时的请求。因此使用易变的字段属性产生竞争的情况。 15.MWN: Mismatched notify() (MWN_MISMATCHED_NOTIFY) 此方法调用Object.notify()或Object.notifyAll()而没有获取到该对象的对象锁。调用notify()或notifyAll()而没有持有该对象的对象锁,将导致IllegalMonitorStateException异常。 16.MWN: Mismatched wait() (MWN_MISMATCHED_WAIT) 此方法调用Object.wait()而没有获取到该对象的对象锁。调用wait()而没有持有该对象的对象锁,将导致IllegalMonitorStateException异常。 17.NP: Synchronize and null check on the same field. (NP_SYNC_AND_NULL_CHECK_FIELD) 如果代码块是同步的,那么久不可能为空。如果是空,同步时就会抛出NullPointerException异常。最好是在另一个代码块中进行同步。 18.No: Using notify() rather than notifyAll() (NO_NOTIFY_NOT_NOTIFYALL) 调用notify()而不是notifyAll()方法。 Java的监控器通常用于多个条件。调用notify()只唤醒一个线程,这意味着该线程被唤醒只是满足的当前的唯一条件。 19.RS: Class's readObject() method is synchronized (RS_READOBJECT_SYNC) 序列化类中定义了同步的readObject()。通过定义,反序列化创建的对象只有一个线程可以访问,因此没有必要的readObject()进行同步。如果的readObject()方法本身造成对象对另一个线程可见,那么这本身就是不好的编码方式。 20.Ru: Invokes run on a thread (did you mean to start it instead?) (RU_INVOKE_RUN) 这种方法显式调用一个对象的run()。一般来说,类是实现Runnable接口的,因为在一个新的线程他们将有自己的run()方法,在这种情况下Thread.start()方法调用是正确的。 21.SC: Constructor invokes Thread.start() (SC_START_IN_CTOR) 在构造函数中启动一个线程。如果类曾经被子类扩展过,那么这很可能是错的,因为线程将在子类构造之前开始启动。 22.SP: Method spins on field (SP_SPIN_ON_FIELD) 方法无限循环读取一个字段。编译器可合法悬挂宣读循环,变成一个无限循环的代码。这个类应该改变,所以使用适当的同步(包括等待和通知要求) 23.STCAL: Call to static Calendar (STCAL_INVOKE_ON_STATIC_CALENDAR_INSTANCE) 即使JavaDoc对此不包含暗示,而Calendars本身在多线程中使用就是不安全的。探测器发现当调用Calendars的实例时将会获得一个静态对象。 Calendar rightNow = Calendar.getInstance(); 24.STCAL: Call to static DateFormat (STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE) 在官方的JavaDoc,DateFormats多线程使用本事就是不安全的。探测器发现调用一个DateFormat的实例将会获得一个静态对象。 myString = DateFormat.getDateInstance().format(myDate); 25.STCAL: Static Calendar (STCAL_STATIC_CALENDAR_INSTANCE) Calendar在多线程中本身就是不安全的,如果在线程范围中共享一个Calendarde 实例而不使用一个同步的方法在应用中就会出现一些奇怪的行为。在sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate()中会抛出ArrayIndexOutOfBoundsExceptions or IndexOutOfBoundsExceptions异常。 26.STCAL: Static DateFormat (STCAL_STATIC_SIMPLE_DATE_FORMAT_INSTANCE) DateFormat 在多线程中本身就是不安全的,如果在线程范围中共享一个DateFormat的实例而不使用一个同步的方法在应用中就会出现一些奇怪的行为。 27.SWL: Method calls Thread.sleep() with a lock held (SWL_SLEEP_WITH_LOCK_HELD) 当持有对象时调用Thread.sleep()。这可能会导致很差的性能和可扩展性,或陷入死锁,因为其他线程可能正在等待获得锁。调用wait()是一个更好的主意,释放对象的持有以允许其他线程运行。 28.UG: Unsynchronized get method, synchronized set method (UG_SYNC_SET_UNSYNC_GET) 这个类包含类似命名的get和set方法。在set方法是同步方法和get方法是非同步方法。这可能会导致在运行时的不正确行为,因为调用的get方法不一定返回对象一致状态。 GET方法应该同步。 29.UL: Method does not release lock on all paths (UL_UNRELEASED_LOCK) 方法获得了当前的对象所,但是在方法中始终没有释放它。一个正确的示例如下: Lock l = ...; l.lock(); try { // do something } finally { l.unlock(); } 30.UL: Method does not release lock on all exception paths (UL_UNRELEASED_LOCK_EXCEPTION_PATH) 方法获得了当前的对象所,但是在所有的异常处理中始终没有释放它。一个正确的示例如下: Lock l = ...; l.lock(); try { // do something } finally { l.unlock(); } 31.UW: Unconditional wait (UW_UNCOND_WAIT) 方法中包含调用java.lang.Object.wait(),而却没有放到条件流程控制中。该代码应确认条件尚未满足之前等待;先前任何通知将被忽略。 32.VO: A volatile reference to an array doesn't treat the array elements as volatile (VO_VOLATILE_REFERENCE_TO_ARRAY) 声明一个变量引用数组,这可能不是你想要的。如果一个变量引用数组,那么对引用数组的读和写都是不安全的,但是数组元素不是变量。取得数组的变量值你可以使用java.util.concurrent包中的数组的原子性特性 33.WL: Sychronization on getClass rather than class literal (WL_USING_GETCLASS_RATHER_THAN_CLASS_LITERAL) 实例的方法中同步this.getClass(),如果这个类有子类集合,那么子类集合中的对象将会在这个类的各个子类上进行同步,这不是我们想要的效果,我们只要同步当前的类对象而不包含它的所有子类,可以同步类名.getClass()。例如,java.awt.Label的代码: private static final String base = "label"; private static int nameCounter = 0; String constructComponentName() { synchronized (getClass()) { return base + nameCounter++; } } Label中的子类集合不可能在同一个子对象上进行同步,替换上面的方法为: private static final String base = "label"; private static int nameCounter = 0; String constructComponentName() { synchronized (Label.class) { return base + nameCounter++; } } 34.WS: Class's writeObject() method is synchronized but nothing else is (WS_WRITEOBJECT_SYNC) 这个类有一个writeObject()方法是同步的,但是这个类中没有其他的同步方法。 35.Wa: Condition.await() not in loop (WA_AWAIT_NOT_IN_LOOP) 方法没有在循环中调用java.util.concurrent.await()。如果对象是用于多种条件,打算调用wait()方法的条件可能不是实际发生的。 36.Wa: Wait not in loop (WA_NOT_IN_LOOP) 这种方法包含调用java.lang.Object.wait(),而这并不是一个循环。如果监视器用于多个条件,打算调用wait()方法的条件可能不是实际发生的。 八:Malicious codevulnerability关于恶意破坏代码相关方面的 1.EI: May expose internal representation by returning reference to mutable object (EI_EXPOSE_REP) 返回一个易变对象引用并把它保存在对象字段中时会暴露对象内部的字段描述,如果接受不守信任的代码访问或者没有检查就去改变易变对象的会涉及对象的安全和其他重要属性的安全。返回一个对象的新副本,在很多情况下更好的办法。 2.EI2: May expose internal representation by incorporating reference to mutable object (EI_EXPOSE_REP2) 此代码把外部可变对象引用存储到对象的内部表示。如果实例受到不信任的代码的访问和没有检查的变化危及对象和重要属性的安全。存储一个对象的副本,在很多情况下是更好的办法。 3.FI: Finalizer should be protected, not public (FI_PUBLIC_SHOULD_BE_PROTECTED) 一个类中的finalize()方法必须声明为protected,而不能为public类型 4.MS: Public static method may expose internal representation by returning array (MS_EXPOSE_REP) 一个public类型的静态方法返回一个数组,可能引用内部属性的暴露。任何代码调用此方法都可以自由修改底层数组。一个解决办法是返回一个数组的副本。 5.MS: Field should be both final and package protected (MS_FINAL_PKGPROTECT) 一个静态字段可能被恶意代码或另外一个包所改变的。字段可以放到protected包中也可以定义为final类型的以避免此问题。 6.MS: Field is a mutable array (MS_MUTABLE_ARRAY) 一个定义为final类型的静态字段引用一个数组时它可以被恶意代码或在另其他包中所使用。这些代码可以自由修改数组的内容。 7.MS: Field is a mutable Hashtable (MS_MUTABLE_HASHTABLE) 一个定义为final类型的静态字段引用一个Hashtable时可以被恶意代码或者在其他包中被调用,这些方法可以修改Hashtable的值。 8.MS: Field should be moved out of an interface and made package protected (MS_OOI_PKGPROTECT) 将域尽量不要定义在接口中,并声明为包保护 在接口中定义了一个final类型的静态字段,如数组或哈希表等易变对象。这些对象可以被恶意代码或者在其他包中被调用,为了解决这个问题,需要把它定义到一个具体的实体类中并且声明为保护类型以避免这种错误。 9.MS: Field should be package protected (MS_PKGPROTECT) 一个静态字段是可以改变的恶意代码或其他的包访问修改。可以把这种类型的字段声明为final类型的以防止这种错误。 十:Dodgy关于代码运行期安全方面的 1.XSS: Servlet reflected cross site scripting vulnerability (XSS_REQUEST_PARAMETER_TO_SEND_ERROR) 在代码中在Servlet输出中直接写入一个HTTP参数,这会造成一个跨站点的脚本漏洞。 2.XSS: Servlet reflected cross site scripting vulnerability (XSS_REQUEST_PARAMETER_TO_SERVLET_WRITER) 代码直接写入参数的HTTP服务器错误页(使用HttpServletResponse.sendError)。表达了类似的不受信任的输入会引起跨站点脚本漏洞。 3.BC: Questionable cast to abstract collection (BC_BAD_CAST_TO_ABSTRACT_COLLECTION) 在代码投把一个集合强制类型转换为一个抽象的集合(如list,set或map)。保证该对象类型和将要转换的类型是一致的。如果你只是想要便利一个集合,那么你就不必将它转换为Set或List。 4.BC: Questionable cast to concrete collection (BC_BAD_CAST_TO_CONCRETE_COLLECTION) 代码把抽象的集合(如List,Set,或Collection)强制转换为具体落实类型(如一个ArrayList或HashSet)。这可能不正确,也可能使您的代码很脆弱,因为它使得难以在今后的切换指向其他具体实现。除非你有特别理由这样做,否则只需要使用抽象的集合类。 5.BC: Unchecked/unconfirmed cast (BC_UNCONFIRMED_CAST) 强制类型转换操作没有经过验证,而且不是所有的此种类型装换过的类都可以再强制类型转换为原类型。在代码中需要进行逻辑判断以保证可以进行这样的操作。 6.BC: instanceof will always return true (BC_VACUOUS_INSTANCEOF) instanceof测试将始终返回真(除非被测试的值为空)。虽然这是安全,确保它是不是说明一些误解或其他一些逻辑错误。如果你真的想测试是空的价值,也许会更清楚这样做的更好空试验,而不是一个instanceof测试。 7.BSHIFT: Unsigned right shift cast to short/byte (ICAST_QUESTIONABLE_UNSIGNED_RIGHT_SHIFT) 无符号数右移后进行转换为short或者byte类型时可能会丢弃掉高位的值,这样的结果就是有符合数和无符号数无法区分(这取决于移位大小) 8.CI: Class is final but declares protected field (CI_CONFUSED_INHERITANCE) 这个类被声明为final的,而是字段属性却声明为保护类型的。由于是final类,它不能再被继承,而再声明为保护类型的很容易造成混淆。为了从外部能正确的使用它应该把它们声明为private或者public类型。 9.DB: Method uses the same code for two branches (DB_DUPLICATE_BRANCHES) 此方法使用相同的代码,以实现两个有条件的分支。检查以确保这是不是一个编码错误。 10.DB: Method uses the same code for two switch clauses (DB_DUPLICATE_SWITCH_CLAUSES) 他的方法使用相同的代码来实现两个switch的声明条款。这可能是重复代码的情况,但可能也显示出编码的错误。 11.DLS: Dead store to local variable (DLS_DEAD_LOCAL_STORE) 该指令为局部变量赋值,但在其后的没有对她做任何使用。通常,这表明一个错误,因为值从未使用过。 12.DLS: Useless assignment in return statement (DLS_DEAD_LOCAL_STORE_IN_RETURN) 本声明把一个局部变量放到方法的返回语句中。这对于方法中局部变量来说是没有意思的。 13.DLS: Dead store of null to local variable (DLS_DEAD_LOCAL_STORE_OF_NULL) 把一个本地变量赋值为null值,并且再也没有对这个变量做任何的操作。这样可能是为了垃圾回收,而是Java SE 6.0,这已不再需要。 14.DMI: Code contains a hard coded reference to an absolute pathname (DMI_HARDCODED_ABSOLUTE_FILENAME) 此代码包含文件对象为一个绝对路径名(例如,新的文件(“/ home/dannyc/workspace/j2ee/src/share/com/sun/enterprise/deployment”); 15.DMI: Non serializable object written to ObjectOutput (DMI_NONSERIALIZABLE_OBJECT_WRITTEN) 代码中让一个非序列化的对象出现在ObjectOutput.writeObject()方法中,这样会引起一个错误。 16.DMI: Invocation of substring(0), which returns the original value (DMI_USELESS_SUBSTRING) 此代码调用了subString(0)方法,它将返回原来的值。 17.Eq: Class doesn't override equals in superclass (EQ_DOESNT_OVERRIDE_EQUALS) 子类定义了一个新的equals方法但是却不是覆写了父类本省的equals()方法。 18.FE: Test for floating point equality (FE_FLOATING_POINT_EQUALITY) 此操作比较两个浮点值是否相等。由于浮点运算可能会涉及到舍入,计算float和double值可能不准确。如果要求值必须准确,如货币值,可以考虑使用固定精度类型,如BigDecimal类型的值来比较 19.FS: Non-Boolean argument formatted using %b format specifier (VA_FORMAT_STRING_BAD_CONVERSION_TO_BOOLEAN) 使用%b去格式化Boolean类型的值不正确的但是它不会抛出异常,任何非空的值都会输出true,任何为空的值都会输出false 20.IC: Initialization circularity (IC_INIT_CIRCULARITY) 在引用两个相互调用为环状static方法去初始化一个实例时是错误的。 21.ICAST: integral division result cast to double or float (ICAST_IDIV_CAST_TO_DOUBLE) 整形数除法强制转换为double或者float类型。 int x = 2; int y = 5; // Wrong: yields result 0.0 double value1 = x / y; // Right: yields result 0.4 double value2 = x / (double) y; 22.ICAST: Result of integer multiplication cast to long (ICAST_INTEGER_MULTIPLY_CAST_TO_LONG) 整形数做乘法运算结果转换为long值时如果采用 long convertDaysToMilliseconds(int days) { return 1000*3600*24*days; } 结果会因为超出整形的范围而出错。 如果使用: long convertDaysToMilliseconds(int days) { return 1000L*3600*24*days; } 或者: static final long MILLISECONDS_PER_DAY = 24L*3600*1000; long convertDaysToMilliseconds(int days) { return days * MILLISECONDS_PER_DAY; } 都可以避免此问题。 23.IM: Computation of average could overflow (IM_AVERAGE_COMPUTATION_COULD_OVERFLOW) 代码中使用x % 2 == 1的方法去验证运算是否存在余数的情况,但是如果出现负数的情况就不起作用了。使用x & 1 == 1, or x % 2 != 0来代替 24.INT: Vacuous comparison of integer value (INT_VACUOUS_COMPARISON) 整形数进行比较结果总是不变。例如:x <= Integer.MAX_VALUE 25.MTIA: Class extends Servlet class and uses instance variables (MTIA_SUSPECT_SERVLET_INSTANCE_FIELD) 这个类扩展从Servlet类,并使用实例的成员变量。由于只有一个Servlet类的实例,并在多线程方式使用,这种模式有可能存在问题。考虑只使用方法的局部变量。 26.MTIA: Class extends Struts Action class and uses instance variables (MTIA_SUSPECT_STRUTS_INSTANCE_FIELD) 类扩展自Struts的Action类并使用这个实例的成员变量,因为在Struts框架中只存在一个Action实例对象并且使用在多线程的情况下很可能会出现问题。 27.NP: Dereference of the result of readLine() without nullcheck (NP_DEREFERENCE_OF_READLINE_VALUE) 对readLine()的结果值没有进行判空操作就去重新赋值,这样的操作可以会抛出空指针异常。 28.NP: Immediate dereference of the result of readLine() (NP_IMMEDIATE_DEREFERENCE_OF_READLINE) 对readLine()的结果立即赋值,这样的操作可以会抛出空指针异常。 29.NP: Possible null pointer dereference due to return value of called method (NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE) 方法的返回值没有进行是否为空的检查就重新赋值,这样可能会出现空指针异常。 30.NP: Parameter must be nonnull but is marked as nullable (NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE) 参数值在任何情况下都不能为空,但是有明确的注释它可以为空。 31.NS: Potentially dangerous use of non-short-circuit logic (NS_DANGEROUS_NON_SHORT_CIRCUIT) 代码中使用(& or |)代替(&& or ||)操作,这会造成潜在的危险。 32.NS: Questionable use of non-short-circuit logic (NS_NON_SHORT_CIRCUIT) 代码中使用(& or |)代替(&& or ||)操作,会引起不安全的操作 33.PZLA: Consider returning a zero length array rather than null (PZLA_PREFER_ZERO_LENGTH_ARRAYS) 考虑返回一个零长度的数组,而不是null值 34.QF: Complicated, subtle or wrong increment in for-loop (QF_QUESTIONABLE_FOR_LOOP) 确定这个循环是正确的变量递增,看起来,另一个变量被初始化,检查的循环。这是由于for循环中太复杂的定义造成的。 35.RCN: Redundant comparison of non-null value to null (RCN_REDUNDANT_COMPARISON_OF_NULL_AND_NONNULL_VALUE) 方法中包含一个不能为空的赋值还包含一个可以为空的赋值。冗余比较非空值为空。 36.RCN: Redundant comparison of two null values (RCN_REDUNDANT_COMPARISON_TWO_NULL_VALUES) 方法中对两个null值进行比较 37.RCN: Redundant nullcheck of value known to be non-null (RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE) 方法中对不为空的值进行为空的判断。 38.REC: Exception is caught when Exception is not thrown (REC_CATCH_EXCEPTION) 在try/catch块中捕获异常,但是异常没有在try语句中抛出而RuntimeException又没有明确的被捕获 39.RI: Class implements same interface as superclass (RI_REDUNDANT_INTERFACES) 子类和父类都实现了同一个接口,这种定义是多余的。 40.RV: Method discards result of readLine after checking if it is nonnull (RV_DONT_JUST_NULL_CHECK_READLINE) readLine方法的结果不为空时被抛弃 41.RV: Remainder of 32-bit signed random integer (RV_REM_OF_RANDOM_INT) 此代码生成一个随机的符号整数,然后计算另一个值的。由于随机数可以是负数,所以其余操作的结果也可以是负面的。考虑使用Random.nextInt(int)方法代替。 42.SA: Double assignment of local variable (SA_LOCAL_DOUBLE_ASSIGNMENT) 为一个局部变量两次赋值,这样是没有意义的。例如: public void foo() { int x,y; x = x = 17; } 43.SA: Self assignment of local variable (SA_LOCAL_SELF_ASSIGNMENT) 局部变量使用自身给自己赋值 public void foo() { int x = 3; x = x; } 44.SF: Switch statement found where one case falls through to the next case (SF_SWITCH_FALLTHROUGH) Switch语句中一个分支执行后又执行了下一个分支。通常case后面要跟break 或者return语句来跳出。 45.SF: Switch statement found where default case is missing (SF_SWITCH_NO_DEFAULT) Switch没有默认情况下执行的case语句。 46.Se: private readResolve method not inherited by subclasses (SE_PRIVATE_READ_RESOLVE_NOT_INHERITED) 声明为private的序列化方法被子类继承 47.UCF: Useless control flow (UCF_USELESS_CONTROL_FLOW) 没有任何作用的条件语句。 if (argv.length == 0) { // TODO: handle this case } 48.UCF: Useless control flow to next line (UCF_USELESS_CONTROL_FLOW_NEXT_LINE) 无效的条件控制语句,注意if (argv.length == 1);以“;”结尾,下面的语句无论是否满足都会运行。 if (argv.length == 1); System.out.println("Hello, " + argv[0]); 49.UwF: Field not initialized in constructor (UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR) 字段从来没有在任何构造函数初始化,对象被创建后值为空。如果该字段未被定义就重新赋值会产生一个空指针异常。 50.XFB: Method directly allocates a specific implementation of xml interfaces (XFB_XML_FACTORY_BYPASS) 方法自定义了一种XML接口的实现类。最好是使用官方提供的工厂类来创建这些对象,以便可以在运行期中改变。例如: javax.xml.parsers.DocumentBuilderFactory javax.xml.parsers.SAXParserFactory javax.xml.transform.TransformerFactory org.w3c.dom.Document.createXXXX
如果Nginx没有仅仅只能代理一台服务器的话,那它也不可能像今天这么火,Nginx可以配置代理多台服务器,当一台服务器宕机之后,仍能保持系统可用。具体配置过程如下: 1. 在http节点下,添加upstream节点。 upstream linuxidc { server 10.0.6.108:7080; server 10.0.0.85:8980; } 2. 将server节点下的location节点中的proxy_pass配置为:http:// + upstream名称,即“http://linuxidc”. location / { root html; index index.html index.htm; proxy_pass http://linuxidc; } 3. 现在负载均衡初步完成了。upstream按照轮询(默认)方式进行负载,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。虽然这种方式简便、成本低廉。但缺点是:可靠性低和负载分配不均衡。适用于图片服务器集群和纯静态页面服务器集群。 除此之外,upstream还有其它的分配策略,分别如下: weight(权重) 指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。如下所示,10.0.0.88的访问比率要比10.0.0.77的访问比率高一倍。 upstream linuxidc{ server 10.0.0.77 weight=5; server 10.0.0.88 weight=10; } ip_hash(访问ip) 每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。 upstream favresin{ ip_hash; server 10.0.0.10:8080; server 10.0.0.11:8080; } fair(第三方) 按后端服务器的响应时间来分配请求,响应时间短的优先分配。与weight分配策略类似。 upstream favresin{ server 10.0.0.10:8080; server 10.0.0.11:8080; fair; } url_hash(第三方) 按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。 注意:在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法。 upstream resinserver{ server 10.0.0.10:7777; server 10.0.0.11:8888; hash $request_uri; hash_method crc32; } upstream还可以为每个设备设置状态值,这些状态值的含义分别如下: down 表示单前的server暂时不参与负载. weight 默认为1.weight越大,负载的权重就越大。 max_fails :允许请求失败的次数默认为1.当超过最大次数时,返回proxy_next_upstream 模块定义的错误. fail_timeout : max_fails次失败后,暂停的时间。 backup: 其它所有的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力会最轻。 upstream bakend{ #定义负载均衡设备的Ip及设备状态 ip_hash; server 10.0.0.11:9090 down; server 10.0.0.11:8080 weight=2; server 10.0.0.11:6060; server 10.0.0.11:7070 backup; }
在项目中总会遇到一些关于加载的优先级问题,近期也同样遇到过类似的,所以自己查找资料总结了下,下面有些是转载其他人的,毕竟人家写的不错,自己也就不重复造轮子了,只是略加点了自己的修饰。 首先可以肯定的是,加载顺序与它们在 web.xml 文件中的先后顺序无关。即不会因为 filter 写在 listener 的前面而会先加载 filter。最终得出的结论是:listener -> filter -> servlet 同时还存在着这样一种配置节:context-param,它用于向 ServletContext 提供键值对,即应用程序上下文信息。我们的 listener, filter 等在初始化时会用到这些上下文中的信息,那么 context-param 配置节是不是应该写在 listener 配置节前呢?实际上 context-param 配置节可写在任意位置,因此真正的加载顺序为:context-param -> listener -> filter -> servlet 对于某类配置节而言,与它们出现的顺序是有关的。以 filter 为例,web.xml 中当然可以定义多个 filter,与 filter 相关的一个配置节是 filter-mapping,这里一定要注意,对于拥有相同 filter-name 的 filter 和 filter-mapping 配置节而言,filter-mapping 必须出现在 filter 之后,否则当解析到 filter-mapping 时,它所对应的 filter-name 还未定义。web 容器启动时初始化每个 filter 时,是按照 filter 配置节出现的顺序来初始化的,当请求资源匹配多个 filter-mapping 时,filter 拦截资源是按照 filter-mapping 配置节出现的顺序来依次调用 doFilter() 方法的。 servlet 同 filter 类似 ,此处不再赘述。 由此,可以看出,web.xml 的加载顺序是:context-param -> listener -> filter -> servlet ,而同个类型之间的实际程序调用的时候的顺序是根据对应的 mapping 的顺序进行调用的。web.xml文件详解========================================================================Web.xml常用元素 <web-app> <display-name></display-name>定义了WEB应用的名字 <description></description> 声明WEB应用的描述信息 <context-param></context-param> context-param元素声明应用范围内的初始化参数。 <filter></filter> 过滤器元素将一个名字与一个实现javax.servlet.Filter接口的类相关联。 <filter-mapping></filter-mapping> 一旦命名了一个过滤器,就要利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。 <listener></listener>servlet API的版本2.3增加了对事件监听程序的支持,事件监听程序在建立、修改和删除会话或servlet环境时得到通知。 Listener元素指出事件监听程序类。 <servlet></servlet> 在向servlet或JSP页面制定初始化参数或定制URL时,必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。 <servlet-mapping></servlet-mapping> 服务器一般为servlet提供一个缺省的URL:http://host/webAppPrefix/servlet/ServletName。 但是,常常会更改这个URL,以便servlet可以访问初始化参数或更容易地处理相对URL。在更改缺省URL时,使用servlet-mapping元素。 <session-config></session-config> 如果某个会话在一定时间内未被访问,服务器可以抛弃它以节省内存。 可通过使用HttpSession的setMaxInactiveInterval方法明确设置单个会话对象的超时值,或者可利用session-config元素制定缺省超时值。 <mime-mapping></mime-mapping>如果Web应用具有想到特殊的文件,希望能保证给他们分配特定的MIME类型,则mime-mapping元素提供这种保证。 <welcome-file-list></welcome-file-list> 指示服务器在收到引用一个目录名而不是文件名的URL时,使用哪个文件。 <error-page></error-page> 在返回特定HTTP状态代码时,或者特定类型的异常被抛出时,能够制定将要显示的页面。 <taglib></taglib> 对标记库描述符文件(Tag Libraryu Descriptor file)指定别名。此功能使你能够更改TLD文件的位置, 而不用编辑使用这些文件的JSP页面。 <resource-env-ref></resource-env-ref>声明与资源相关的一个管理对象。 <resource-ref></resource-ref> 声明一个资源工厂使用的外部资源。 <security-constraint></security-constraint> 制定应该保护的URL。它与login-config元素联合使用 <login-config></login-config> 指定服务器应该怎样给试图访问受保护页面的用户授权。它与sercurity-constraint元素联合使用。 <security-role></security-role>给出安全角色的一个列表,这些角色将出现在servlet元素内的security-role-ref元素 的role-name子元素中。分别地声明角色可使高级IDE处理安全信息更为容易。 <env-entry></env-entry>声明Web应用的环境项。 <ejb-ref></ejb-ref>声明一个EJB的主目录的引用。 < ejb-local-ref></ ejb-local-ref>声明一个EJB的本地主目录的应用。 </web-app> 相应元素配置 1、Web应用图标:指出IDE和GUI工具用来表示Web应用的大图标和小图标 <icon> <small-icon>/images/app_small.gif</small-icon> <large-icon>/images/app_large.gif</large-icon> </icon> 2、Web 应用名称:提供GUI工具可能会用来标记这个特定的Web应用的一个名称 <display-name>Tomcat Example</display-name> 3、Web 应用描述: 给出于此相关的说明性文本 <disciption>Tomcat Example servlets and JSP pages.</disciption> 4、上下文参数:声明应用范围内的初始化参数。 <context-param> <param-name>ContextParameter</para-name> <param-value>test</param-value> <description>It is a test parameter.</description> </context-param> 在servlet里面可以通过getServletContext().getInitParameter("context/param")得到 5、过滤器配置:将一个名字与一个实现javaxs.servlet.Filter接口的类相关联。 <filter> <filter-name>setCharacterEncoding</filter-name> <filter-class>com.myTest.setCharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>GB2312</param-value> </init-param> </filter> <filter-mapping> <filter-name>setCharacterEncoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 6、监听器配置 <listener> <listerner-class>listener.SessionListener</listener-class> </listener> 7、Servlet配置 基本配置 <servlet> <servlet-name>snoop</servlet-name> <servlet-class>SnoopServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>snoop</servlet-name> <url-pattern>/snoop</url-pattern> </servlet-mapping> 高级配置 <servlet> <servlet-name>snoop</servlet-name> <servlet-class>SnoopServlet</servlet-class> <init-param> <param-name>foo</param-name> <param-value>bar</param-value> </init-param> <run-as> <description>Security role for anonymous access</description> <role-name>tomcat</role-name> </run-as> </servlet> <servlet-mapping> <servlet-name>snoop</servlet-name> <url-pattern>/snoop</url-pattern> </servlet-mapping> 元素说明 <servlet></servlet> 用来声明一个servlet的数据,主要有以下子元素: <servlet-name></servlet-name> 指定servlet的名称 <servlet-class></servlet-class> 指定servlet的类名称 <jsp-file></jsp-file> 指定web站台中的某个JSP网页的完整路径 <init-param></init-param> 用来定义参数,可有多个init-param。在servlet类中通过getInitParamenter(String name)方法访问初始化参数 <load-on-startup></load-on-startup>指定当Web应用启动时,装载Servlet的次序。 当值为正数或零时:Servlet容器先加载数值小的servlet,再依次加载其他数值大的servlet. 当值为负或未定义:Servlet容器将在Web客户首次访问这个servlet时加载它 <servlet-mapping></servlet-mapping> 用来定义servlet所对应的URL,包含两个子元素 <servlet-name></servlet-name> 指定servlet的名称 <url-pattern></url-pattern> 指定servlet所对应的URL 8、会话超时配置(单位为分钟) <session-config> <session-timeout>120</session-timeout> </session-config> 9、MIME类型配置 <mime-mapping> <extension>htm</extension> <mime-type>text/html</mime-type> </mime-mapping> 10、指定欢迎文件页配置 <welcome-file-list> <welcome-file>index.jsp</welcome-file> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> </welcome-file-list> 11、配置错误页面 一、 通过错误码来配置error-page <error-page> <error-code>404</error-code> <location>/NotFound.jsp</location> </error-page> 上面配置了当系统发生404错误时,跳转到错误处理页面NotFound.jsp。 二、通过异常的类型配置error-page <error-page> <exception-type>java.lang.NullException</exception-type> <location>/error.jsp</location> </error-page> 上面配置了当系统发生java.lang.NullException(即空指针异常)时,跳转到错误处理页面error.jsp 12、TLD配置 <taglib> <taglib-uri>http://jakarta.apache.org/tomcat/debug-taglib</taglib-uri> <taglib-location>/WEB-INF/jsp/debug-taglib.tld</taglib-location> </taglib> 如果MyEclipse一直在报错,应该把<taglib> 放到 <jsp-config>中 <jsp-config> <taglib> <taglib-uri>http://jakarta.apache.org/tomcat/debug-taglib</taglib-uri> <taglib-location>/WEB-INF/pager-taglib.tld</taglib-location> </taglib> </jsp-config> 13、资源管理对象配置 <resource-env-ref> <resource-env-ref-name>jms/StockQueue</resource-env-ref-name> </resource-env-ref> 14、资源工厂配置 <resource-ref> <res-ref-name>mail/Session</res-ref-name> <res-type>javax.mail.Session</res-type> <res-auth>Container</res-auth> </resource-ref> 配置数据库连接池就可在此配置: <resource-ref> <description>JNDI JDBC DataSource of shop</description> <res-ref-name>jdbc/sample_db</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> 15、安全限制配置 <security-constraint> <display-name>Example Security Constraint</display-name> <web-resource-collection> <web-resource-name>Protected Area</web-resource-name> <url-pattern>/jsp/security/protected/*</url-pattern> <http-method>DELETE</http-method> <http-method>GET</http-method> <http-method>POST</http-method> <http-method>PUT</http-method> </web-resource-collection> <auth-constraint> <role-name>tomcat</role-name> <role-name>role1</role-name> </auth-constraint> </security-constraint> 16、登陆验证配置 <login-config> <auth-method>FORM</auth-method> <realm-name>Example-Based Authentiation Area</realm-name> <form-login-config> <form-login-page>/jsp/security/protected/login.jsp</form-login-page> <form-error-page>/jsp/security/protected/error.jsp</form-error-page> </form-login-config> </login-config> 17、安全角色:security-role元素给出安全角色的一个列表,这些角色将出现在servlet元素内的security-role-ref元素的role-name子元素中。 分别地声明角色可使高级IDE处理安全信息更为容易。 <security-role> <role-name>tomcat</role-name> </security-role> 18、Web环境参数:env-entry元素声明Web应用的环境项 <env-entry> <env-entry-name>minExemptions</env-entry-name> <env-entry-value>1</env-entry-value> <env-entry-type>java.lang.Integer</env-entry-type> </env-entry> 19、EJB 声明 <ejb-ref> <description>Example EJB reference</decription> <ejb-ref-name>ejb/Account</ejb-ref-name> <ejb-ref-type>Entity</ejb-ref-type> <home>com.mycompany.mypackage.AccountHome</home> <remote>com.mycompany.mypackage.Account</remote> </ejb-ref> 20、本地EJB声明 <ejb-local-ref> <description>Example Loacal EJB reference</decription> <ejb-ref-name>ejb/ProcessOrder</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <local-home>com.mycompany.mypackage.ProcessOrderHome</local-home> <local>com.mycompany.mypackage.ProcessOrder</local> </ejb-local-ref> 21、配置DWR <servlet> <servlet-name>dwr-invoker</servlet-name> <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping> 22、配置Struts <display-name>Struts Blank Application</display-name> <servlet> <servlet-name>action</servlet-name> <servlet-class> org.apache.struts.action.ActionServlet </servlet-class> <init-param> <param-name>detail</param-name> <param-value>2</param-value> </init-param> <init-param> <param-name>debug</param-name> <param-value>2</param-value> </init-param> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>application</param-name> <param-value>ApplicationResources</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- Struts Tag Library Descriptors --> <taglib> <taglib-uri>struts-bean</taglib-uri> <taglib-location>/WEB-INF/tld/struts-bean.tld</taglib-location> </taglib> <taglib> <taglib-uri>struts-html</taglib-uri> <taglib-location>/WEB-INF/tld/struts-html.tld</taglib-location> </taglib> <taglib> <taglib-uri>struts-nested</taglib-uri> <taglib-location>/WEB-INF/tld/struts-nested.tld</taglib-location> </taglib> <taglib> <taglib-uri>struts-logic</taglib-uri> <taglib-location>/WEB-INF/tld/struts-logic.tld</taglib-location> </taglib> <taglib> <taglib-uri>struts-tiles</taglib-uri> <taglib-location>/WEB-INF/tld/struts-tiles.tld</taglib-location> </taglib> 23、配置Spring(基本上都是在Struts中配置的) <!-- 指定spring配置文件位置 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> <!--加载多个spring配置文件 --> /WEB-INF/applicationContext.xml, /WEB-INF/action-servlet.xml </param-value> </context-param> <!-- 定义SPRING监听器,加载spring --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener>
查看Eclipse版本号的方法:1、找到eclipse安装目录。2、进入readme文件夹,打开readme_eclipse.html。3、readme_eclipse.html呈现的第二行即数字版本号,如:Release 4.4.0Last revised June 5, 2014 附:Eclipse各个版本简介(http://zh.wikipedia.org/wiki/Eclipse)从2006年起,Eclipse基金会每年都会安排同步发布(simultaneous release)。同步发布主要在6月进行,并且会在接下来的9月及2月释放出SR1及SR2版本。 代号 平台版本 项目 主要版本发行日期 SR1发行日期 SR2发行日期 N/A 3.0 N/A 2004年6月21日 N/A N/A N/A 3.1 N/A 2005年6月28日 N/A N/A Callisto 3.2 Callisto projects 2006年6月30日 N/A N/A Europa 3.3 Europa projects 2007年6月29日 2007年9月28日 2008年2月29日 Ganymede 3.4 Ganymede projects 2008年6月25日 2008年9月24日 2009年2月25日 Galileo 3.5 Galileo projects 2009年6月24日 2009年9月25日 2010年2月26日 Helios 3.6 Helios projects 2010年6月23日 2010年9月24日 2011年2月25日 Indigo 3.7 Indigo projects 2011年6月22日 2011年9月23日 2012年2月24日 Juno 4.2 Juno projects 2012年6月27日 2012年9月28日 2013年2月22日 Kepler 4.3 Kepler projects 2013年6月26日 2013年9月28日 2014年2月28日 Luna 4.4 Luna projects 2014年6月25日 N/A N/A Mars 4.5 Mars projects 2015年6月24日 N/A N/A 老版本,已不支持 老版本,仍被支持 当前版本 未来版本
一. 解压安装jdk 在shell终端下进入jdk-6u14-linux-i586.bin文件所在目录,之后会在当前目录下生成一个jdk1.6.0_14目录二. 需要配置的环境变量 1. PATH环境变量。作用是指定命令搜索路径,在shell下面执行命令时,它会到PATH变量所指定的路径中查找看是否能找到相应的命令程序。我们需要把 jdk安装目录下的bin目录增加到现有的PATH变量中,bin目录中包含经常要用到的可执行文件如javac/java/javadoc等待,设置好 PATH变量后,就可以在任何目录下执行javac/java等工具了。 2. CLASSPATH环境变量。作用是指定类搜索路径,要使用已经编写好的类,前提当然是能够找到它们了,JVM就是通过CLASSPTH来寻找类的。我们 需要把jdk安装目录下的lib子目录中的dt.jar和tools.jar设置到CLASSPATH中,当然,当前目录“.”也必须加入到该变量中。 3. JAVA_HOME环境变量。它指向jdk的安装目录,Eclipse/NetBeans/Tomcat等软件就是通过搜索JAVA_HOME变量来找到并使用安装好的jdk。 三. 三种配置环境变量的方法1. 修改/etc/profile文件 不推荐如果你的计算机仅仅作为开发使用时推荐使用这种方法,因为所有用户的shell都有权使用这些环境变量,可能会给系统带来安全性问题。 ·用文本编辑器打开/etc/profile ·在profile文件末尾加入: export JAVA_HOME=/usr/share/jdk1.6.0_14 export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar ·重新登录 ·注解 a. 你要将 /usr/share/jdk1.6.0_14改为你的jdk安装目录 b. linux下用冒号“:”来分隔路径 c. $PATH / $CLASSPATH / $JAVA_HOME 是用来引用原来的环境变量的值 在设置环境变量时特别要注意不能把原来的值给覆盖掉了,这是一种 常见的错误。 d. CLASSPATH中当前目录“.”不能丢,把当前目录丢掉也是常见的错误。 e. export是把这三个变量导出为全局变量。 f. 大小写必须严格区分。 2. 修改.bash_profile文件 这是最靠谱的方法,也是一般在公司里你可以用的方法。这种方法更为安全,它可以把使用这些环境变量的权限控制到用户级别,如果你需要给某个用户权限使用这些环境变量,你只需要修改其个人用户主目录下的.bash_profile文件就可以了。 ·用文本编辑器打开用户目录下的.bash_profile文件 ·在.bash_profile文件末尾加入: export JAVA_HOME=/usr/share/jdk1.6.0_14 export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar ·重新登录 3. 直接在shell下设置变量 不赞成使用这种方法,因为换个shell,你的设置就无效了,因此这种方法仅仅是临时使用,以后要使用的时候又要重新设置,比较麻烦。 只需在shell终端执行下列命令: export JAVA_HOME=/usr/share/jdk1.6.0_14 export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar 四. 测试jdk 地球人都会,java -version五. 卸载jdk ·找到jdk安装目录的_uninst子目录 ·在shell终端执行命令./uninstall.sh即可卸载jdk。
如果要分析某条SQL的性能问题,通常我们要先看SQL的执行计划,看看SQL的每一步执行是否存在问题。 如果一条SQL平时执行的好好的,却有一天突然性能很差,如果排除了系统资源和阻塞的原因,那么基本可以断定是执行计划出了问题。 看懂执行计划也就成了SQL优化的先决条件。 这里的SQL优化指的是SQL性能问题的定位,定位后就可以解决问题。 一. 查看执行计划的三种方法 1.1 设置autotrace 序号 命令 解释 1 SET AUTOTRACE OFF 此为默认值,即关闭Autotrace 2 SET AUTOTRACE ON EXPLAIN 只显示执行计划 3 SET AUTOTRACE ON STATISTICS 只显示执行的统计信息 4 SET AUTOTRACE ON 包含2,3两项内容 5 SET AUTOTRACE TRACEONLY 与ON相似,但不显示语句的执行结果 SQL> set autotrace on SQL> select * from dave; ID NAME ---------- ---------- 8 安庆 1 dave 2 bl 1 bl 2 dave 3 dba 4 sf-express 5 dmm 已选择8行。 执行计划 ---------------------------------------------------------- Plan hash value: 3458767806 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 8 | 64 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS FULL| DAVE | 8 | 64 | 2 (0)| 00:00:01 | -------------------------------------------------------------------------- 统计信息 ---------------------------------------------------------- 0 recursive calls 0 db block gets 4 consistent gets 0 physical reads 0 redo size 609 bytes sent via SQL*Net to client 416 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 8 rows processed SQL> 1.2 使用SQL SQL>EXPLAIN PLAN FOR sql语句; SQL>SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE')); 示例: SQL> EXPLAIN PLAN FOR SELECT * FROM DAVE; 已解释。 SQL> SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE')); 或者: SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 3458767806 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 8 | 64 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS FULL| DAVE | 8 | 64 | 2 (0)| 00:00:01 | -------------------------------------------------------------------------- 已选择8行。 执行计划 ---------------------------------------------------------- Plan hash value: 2137789089 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 8168 | 16336 | 29 (0)| 00:00:01 | | 1 | COLLECTION ITERATOR PICKLER FETCH| DISPLAY | 8168 | 16336 | 29 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- 统计信息 ---------------------------------------------------------- 25 recursive calls 12 db block gets 168 consistent gets 0 physical reads 0 redo size 974 bytes sent via SQL*Net to client 416 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 8 rows processed SQL> 1.3 使用Toad,PL/SQL Developer工具 二. Cardinality(基数)/ rows Cardinality值表示CBO预期从一个行源(row source)返回的记录数,这个行源可能是一个表,一个索引,也可能是一个子查询。 在Oracle 9i中的执行计划中,Cardinality缩写成Card。 在10g中,Card值被rows替换。 这是9i的一个执行计划,我们可以看到关键字Card: 执行计划 ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=402) 1 0 TABLE ACCESS (FULL) OF 'TBILLLOG8' (Cost=2 Card=1 Bytes=402) Oracle 10g的执行计划,关键字换成了rows: 执行计划 ---------------------------------------------------------- Plan hash value: 2137789089 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 8168 | 16336 | 29 (0)| 00:00:01 | | 1 | COLLECTION ITERATOR PICKLER FETCH| DISPLAY | 8168 | 16336 | 29 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- Cardinality的值对于CBO做出正确的执行计划来说至关重要。 如果CBO获得的Cardinality值不够准确(通常是没有做分析或者分析数据过旧造成),在执行计划成本计算上就会出现偏差,从而导致CBO错误的制定出执行计划。 在多表关联查询或者SQL中有子查询时,每个关联表或子查询的Cardinality的值对主查询的影响都非常大,甚至可以说,CBO就是依赖于各个关联表或者子查询Cardinality值计算出最后的执行计划。 对于多表查询,CBO使用每个关联表返回的行数(Cardinality)决定用什么样的访问方式来做表关联(如Nested loops Join 或 hash Join)。 多表连接的三种方式详解 HASH JOIN MERGE JOIN NESTED LOOP http://blog.csdn.net/tianlesoftware/archive/2010/08/20/5826546.aspx 对于子查询,它的Cardinality将决定子查询是使用索引还是使用全表扫描的方式访问数据。 三. SQL 的执行计划 生成SQL的执行计划是Oracle在对SQL做硬解析时的一个非常重要的步骤,它制定出一个方案告诉Oracle在执行这条SQL时以什么样的方式访问数据:索引还是全表扫描,是Hash Join还是Nested loops Join等。 比如说某条SQL通过使用索引的方式访问数据是最节省资源的,结果CBO作出的执行计划是全表扫描,那么这条SQL的性能必然是比较差的。 Oracle SQL的硬解析和软解析 http://blog.csdn.net/tianlesoftware/archive/2010/04/08/5458896.aspx 示例: SQL> SET AUTOTRACE TRACEONLY; -- 只显示执行计划,不显示结果集 SQL> select * from scott.emp a,scott.emp b where a.empno=b.mgr; 已选择13行。 执行计划 ---------------------------------------------------------- Plan hash value: 992080948 --------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 13 | 988 | 6 (17)| 00:00:01 | | 1 | MERGE JOIN | | 13 | 988 | 6 (17)| 00:00:01 | | 2 | TABLE ACCESS BY INDEX ROWID| EMP | 14 | 532 | 2 (0)| 00:00:01 | | 3 | INDEX FULL SCAN | PK_EMP | 14 | | 1 (0)| 00:00:01 | |* 4 | SORT JOIN | | 13 | 494 | 4 (25)| 00:00:01 | |* 5 | TABLE ACCESS FULL | EMP | 13 | 494 | 3 (0)| 00:00:01 | --------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("A"."EMPNO"="B"."MGR") filter("A"."EMPNO"="B"."MGR") 5 - filter("B"."MGR" IS NOT NULL) 统计信息 ---------------------------------------------------------- 0 recursive calls 0 db block gets 11 consistent gets 0 physical reads 0 redo size 2091 bytes sent via SQL*Net to client 416 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 13 rows processed SQL> 图片是Toad工具查看的执行计划。 在Toad 里面,很清楚的显示了执行的顺序。 但是如果在SQLPLUS里面就不是那么直接。 但我们也可以判断:一般按缩进长度来判断,缩进最大的最先执行,如果有2行缩进一样,那么就先执行上面的。 3.1 执行计划中字段解释: ID: 一个序号,但不是执行的先后顺序。执行的先后根据缩进来判断。 Operation: 当前操作的内容。 Rows: 当前操作的Cardinality,Oracle估计当前操作的返回结果集。 Cost(CPU):Oracle 计算出来的一个数值(代价),用于说明SQL执行的代价。 Time:Oracle 估计当前操作的时间。 3.2 谓词说明: Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("A"."EMPNO"="B"."MGR") filter("A"."EMPNO"="B"."MGR") 5 - filter("B"."MGR" IS NOT NULL) Access: 表示这个谓词条件的值将会影响数据的访问路劲(表还是索引)。 Filter:表示谓词条件的值不会影响数据的访问路劲,只起过滤的作用。 在谓词中主要注意access,要考虑谓词的条件,使用的访问路径是否正确。 3.3 统计信息说明: db block gets : 从buffer cache中读取的block的数量 consistent gets: 从buffer cache中读取的undo数据的block的数量 physical reads: 从磁盘读取的block的数量 redo size: DML生成的redo的大小 sorts (memory) :在内存执行的排序量 sorts (disk) :在磁盘上执行的排序量 Physical Reads通常是我们最关心的,如果这个值很高,说明要从磁盘请求大量的数据到Buffer Cache里,通常意味着系统里存在大量全表扫描的SQL语句,这会影响到数据库的性能,因此尽量避免语句做全表扫描,对于全表扫描的SQL语句,建议增 加相关的索引,优化SQL语句来解决。 关于physical reads ,db block gets 和consistent gets这三个参数之间有一个换算公式: 数据缓冲区的使用命中率=1 - ( physical reads / (db block gets + consistent gets) )。 用以下语句可以查看数据缓冲区的命中率: SQL>SELECT name, value FROM v$sysstat WHERE name IN ('db block gets', 'consistent gets','physical reads'); 查询出来的结果Buffer Cache的命中率应该在90%以上,否则需要增加数据缓冲区的大小。 Recursive Calls: Number of recursive calls generated at both the user and system level. Oracle Database maintains tables used for internal processing. When it needs to change these tables, Oracle Database generates an internal SQL statement, which in turn generates a recursive call. In short, recursive calls are basically SQL performed on behalf of your SQL. So, if you had to parse the query, for example, you might have had to run some other queries to get data dictionary information. These would be recursive calls. Space management, security checks, calling PL/SQL from SQL—all incur recursive SQL calls。 DB Block Gets:Number of times a CURRENT block was requested. Current mode blocks are retrieved as they exist right now, not in a consistent read fashion. Normally, blocks retrieved for a query are retrieved as they existed when the query began. Current mode blocks are retrieved as they exist right now, not from a previous point in time. During a SELECT, you might see current mode retrievals due to reading the data dictionary to find the extent information for a table to do a full scan (because you need the "right now" information, not the consistent read). During a modification, you will access the blocks in current mode in order to write to them. (DB Block Gets:请求的数据块在buffer能满足的个数) 当前模式块意思就是在操作中正好提取的块数目,而不是在一致性读的情况下而产生的块数。正常的情况下,一个查询提取的块是在查询开始的那个时间点上存在的数据块,当前块是在这个时刻存在的数据块,而不是在这个时间点之前或者之后的数据块数目。 Consistent Gets: Number of times a consistent read was requested for a block. This is how many blocks you processed in "consistent read" mode. This will include counts of blocks read from the rollback segment in order to roll back a block. This is the mode you read blocks in with a SELECT, for example. Also, when you do a searched UPDATE/DELETE, you read the blocks in consistent read mode and then get the block in current mode to actually do the modification. (Consistent Gets: 数据请求总数在回滚段Buffer中的数据一致性读所需要的数据块) 这里的概念是在处理你这个操作的时候需要在一致性读状态上处理多少个块,这些块产生的主要原因是因为由于在你查询的过程中,由于其他会话对数据块进行操作,而对所要查询的块有了修改,但是由于我们的查询是在这些修改之前调用的,所以需要对回滚段中的数据块的前映像进行查询,以保证数据的一致性。这样就产生了一致性读。 Physical Reads:Total number of data blocks read from disk. This number equals the value of "physical reads direct" plus all reads into buffer cache. (Physical Reads:实例启动后,从磁盘读到Buffer Cache数据块数量) 就是从磁盘上读取数据块的数量,其产生的主要原因是: (1) 在数据库高速缓存中不存在这些块 (2) 全表扫描 (3) 磁盘排序它们三者之间的关系大致可概括为: 逻辑读指的是Oracle从内存读到的数据块数量。一般来说是'consistent gets' + 'db block gets'。当在内存中找不到所需的数据块的话就需要从磁盘中获取,于是就产生了'physical reads'。 Sorts(disk): Number of sort operations that required at least one disk write. Sorts that require I/O to disk are quite resource intensive. Try increasing the size of the initialization parameter SORT_AREA_SIZE. bytes sent via SQL*Net to client: Total number of bytes sent to the client from the foreground processes. bytes received via SQL*Net from client: Total number of bytes received from the client over Oracle Net. SQL*Net roundtrips to/from client: Total number of Oracle Net messages sent to and received from the client. 更多内容参考Oracle联机文档: Statistics Descriptions http://download.oracle.com/docs/cd/E11882_01/server.112/e10820/stats002.htm#i375475 3.4 动态分析 如果在执行计划中有如下提示: Note ------------ -dynamic sampling used for the statement 这提示用户CBO当前使用的技术,需要用户在分析计划时考虑到这些因素。 当出现这个提示,说明当前表使用了动态采样。 我们从而推断这个表可能没有做过分析。 这里会出现两种情况: (1) 如果表没有做过分析,那么CBO可以通过动态采样的方式来获取分析数据,也可以或者正确的执行计划。 (2) 如果表分析过,但是分析信息过旧,这时CBO就不会在使用动态采样,而是使用这些旧的分析数据,从而可能导致错误的执行计划。 总结: 在看执行计划的时候,除了看执行计划本身,还需要看谓词和提示信息。 通过整体信息来判断SQL 效率。
网游与手游的火爆,首要在于可玩性,而懒人模式和专属社交平台的构建,可以说是游戏吸引玩家的两把利器。而作为互联网展业平台之一的券商,如何在当下的“一人多户”时代发展出自己的特性,或许能从游戏的一些模式中获得启发。 现在不提互联网金融都不好意思自称业内人士,但是又有多少人知道互联网金融应该怎么玩,是拿了互联网证券资格就知道怎么玩了?想多了,其实,笔者也不知道到底该怎么玩,只知道玩法很多,因为它搭上了互联网。作为一个 80 后,有幸赶上这次浪潮,让我们敢想敢做。笔者也玩了不少网络游戏,如网游 LOL、梦幻西游手游版等。不是广告,只是这段时间这些游戏确实火爆,笔者也就紧跟时代潮流。所谓“学习工作两不误”,笔者在玩游戏的过程中也有颇多感受和体会,或许是互联网金融可以借鉴的。游戏的精髓在于可玩性毋庸置疑,不管是网游、手游,可玩性居于首位。但另一个重要因素是有一群认识的朋友在游戏中“拉帮结派”,同时也能在游戏里认识新的小伙伴成为战友,这在笔者看来是一款游戏成功的关键——建立社交属性。 以梦幻西游为例,因为它的无门槛吸引了大量的游戏者。具体来说,操作简单,闭上眼睛也能玩:点任务,自动寻路,打怪,乐意呢就选技能打,懒呢就自动打。另外,梦幻西游还提供正大光明的挂机功能,通过挂机,即使不玩游戏也能获得经验升级。可以说,这是一款懒人游戏,很多设定都基于让玩家增加经验值为目的。为了追赶上朋友的等级,很多玩家通宵组队,完成任务想方设法快速增加经验值。 笔者认为,这款游戏内容方面并无特别精彩之处,画面效果也差强人意,但整体社交运营模式是其中的亮点。在没有像腾讯般强大的社交平台来建立人人关系的情况下,他是如何营造社交模式的呢?答案是构建世界、帮派、队伍、好友 4 个核心体系。世界属于广播,帮派属于好友扎堆,队伍属于临时组队关系,可随时解散,好友自不用说。这 4 个层次构建了整体的社交面。在游戏过程中,当遇到难关,需要组队,需要帮派、好友帮助的时候,这种关系就逐渐建立起来了。 互联网展业中的券商创新——金融社交回过头来看,当下券商开展互联网金融的现状是:不少证券公司在与社交类的互联网公司谈合作,借助大型社交平台开创新未来,也就是一直所说的社交金融。其实金融本身也是一种金钱游戏,通过构建一系列的游戏规则,来达到一定的可玩性、可参与性,并获得一定的经济收益。笔者认为在与互联网公司合作的同时,证券公司也应考虑如何建立自己的微社交。作为互联网展业平台,证券公司似乎逐渐成为了一个产品或通道的提供商,随着 4 月 13 日“一人一户”限制放开后,客户拥有了更多的自由和选择空间,这对证券公司来说面临着更大的挑战。因此证券公司在联合互联网公司开展业务时,需要考虑建立自己的社交圈,围绕着自己的客户去用心经营。因为同在一个证券公司开户进行投资的人群彼此都没有交互往来,而通过投顾及一些活动可以逐渐建立同样的社交圈,构建起属于自己的客户圈子,不再让客户认为证券公司仅仅只是开户炒股的公司。证券公司应该是以一种服务的面貌,以理财助手的形象更好的帮助客户投资。以亲身经历为例,笔者除了使用证券公司的通道服务外,确实没有享受到更多的其他服务。或许是笔者关注度不够,但这从一个侧面说明证券公司的服务的确没有传递到每个投资者,亦没有通过其他方式或者活动吸引投资者关注。 社交体系与“懒人模式”四层社交体系笔者将梦幻西游的四层社交体系:世界、帮派、队伍、好友借用于证券公司。虽然这套体系不一定符合每家证券公司对于建立社交圈的规划,但大致可以套用。世界:所有证券公司的投资者,散布在各家券商及互联网公司,他们可以与本证券公司的投资者进行交流动。未来将是一个开放的世界,开放后证券公司的客户是否会被挖走或许不再那么重要,而更多的考虑是如何吸引客户。帮派:可理解为证券公司级别,是围绕“帮主”的团结群体。而“帮主”这个概念,其实可为证券公司提供非常多的想象力。 “弟子们”通过“帮主”指引,相互间建立起信用度。队伍:可以理解为各自建立的小团队,以队长为首带队进行活动。此小团体,为实现短期目标进行活动。好友:好友在四层社交体系中最为关键。如若在同一家证券公司开户的投资者本来即为好友,为何不能通过证券公司提供的平台进行联合行动呢?例如:对好友发起对话请求。现在很多沟通交流都是通过微信和 QQ等平台上进行,这其实就是需求,因为证券公司没有满足用户沟通的需求,所以用户只能另找平台去获得需求。这也从侧面反映出为什么现在有诸多类似QQ炒股群的社交群。 懒人模式可以说是懒人推动了科技进步。人都有惰性,而正是这种惰性,证券公司可加以利用。“你惰那我就推着你、你懒我就扶着你走”。对于非专业投资者而言,都希望简简单单就能赚钱。虽然证券公司不能答应这种要求,但是证券公司可以把复杂的金融简单化,让投资者的投资之路平坦化。当下模式是,投资者在完成风险测评问卷后,便可开户炒股;但这种风险评测收效甚微,对投资者难以起到很大的指导作用。那如何才能帮助投资者逐步提高投资炒股的水平呢?或许换一种方式:把初入股市的投资者假想为初生的婴儿,培养投资者就犹如哺育新生命一般,不同的成长阶段采取不同的培养教育方式,或许此种方式能收获惊喜。 1、贴心服务——新人助手游戏在这个方面做的就相当好,通过新手帮助,从简到难,逐步把游戏者不清楚、不明白的地方展示并且教会使用。从入门到进阶,等到完全掌握,就能靠他自己的实力和各种工具的应用来驰骋江湖了。遇到困难,也有多种渠道帮助解决,这才能使游戏者深入地玩下去。不知道有没有券商统计过,在炒股的投资终端上,除了交易功能,有多少投资者使用过,或者说有多少人会用。既然放到终端上去了,是否提供了渠道或路径来教会投资者使用?所以在笔者看来,放上去的东西必然是你认为投资者需要的,或者在投资中需要用到的工具,但是能够教会其使用,在投资中起到什么样的功能和角色呢?未来拼服务其实就是在拼这些细节。 2、基础需求——挂机模式主流的游戏模式是发现外挂会封停账号,但是梦幻西游却官方开启了挂机模式,这一思维的转换着实让人眼前一亮。为什么要挂机?挂机能带来经验值,能够完成之前尚未通过的任务,这就是基础需求。如果几次下来还是无法完成任务,那游戏者就会失去兴趣。同样,对于证券投资者而言,他们是不是也有类似挂机这样的需求,即投资者认为是合理的,但是证券公司目前还无法提供的最底层需求。举例来说,修改密码是投资者极为基础的需求,但证券公司手机 APP应用似乎还无法解决。又如,用户想要更改开户时使用的手机号码则只能通过互联网渠道,手机 APP 同样无法解决。诸如此类的需求,与用户息息相关,且不难实现,但目前少有券商能够满足用户的此类需求。但令人欣慰的是大部分证券公司正往这个方向在努力。 互联网金融没有可供参考的成熟模式, 所以需要借鉴成熟行业的模式。走前人之路似乎无可厚非,但一味地走别人的路或许会出现这样的局面:前面的羊走过,后来的羊只剩草根可嚼。因此找准自身的定位和方向尤为重要:一方面,借助平台,在众多互联网公司中找寻属于你的一盘菜;另一方面, 构建自己的平台体系, 别让 “券商成为产品及通道的提供商”的预言成真。
select count(*) from v$process --当前的连接数select value from v$parameter where name = 'processes' --数据库允许的最大连接数修改最大连接数:alter system set processes = 300 scope = spfile;重启数据库:shutdown immediate;startup;--查看当前有哪些用户正在使用数据SELECT osuser, a.username,cpu_time/executions/1000000||'s', sql_fulltext,machine from v$session a, v$sqlarea bwhere a.sql_address =b.address order by cpu_time/executions desc; 需要在SQL命令符下操作
java在图形处理时调用了本地的图形处理库。在利用Java作图形处理(比如:图片缩放,图片签名,生成报表)时,如果运行在windows上不会出问题。如果将程序移植到Linux/Unix上的时候有可能出现图形不能显示的错误。提示信息:"Can't connect to X11 window server"这是由于Linux的图形处理需要一个X Server服务器。 解决办法: 1.如果服务器上安装有图形界面,可以通过设置环境变量:DISPALY=127.0.0.1:0.0解决。 2. 如果没有安装图形界面,可以在Java运行时加上参数:-Djava.awt.headless=true。 3. 使用PJA库来代替本地图形处理库。 Tomcat的修改如下: 打开 %Tomcat_home%/bin/catalina.sh 文件在-Djava.io.tmpdir="$CATALINA_TMPDIR"后,启动Tomcat的脚本中加上:-Djava.awt.headless=true "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS"-classpath "$CLASSPATH" -Dcatalina.base="$CATALINA_BASE" -Dcatalina.home="$CATALINA_HOME" -Djava.io.tmpdir="$CATALINA_TMPDIR" -Djava.awt.headless=true org.apache.catalina.startup.Bootstrap "$@" start >> "$CATALINA_BASE"/logs/catalina.out 2>&1 &if [ ! -z "$CATALINA_PID" ]; thenecho $! > $CATALINA_PID fifielif [ "$1" = "stop" ] ; then 保存后,重新启动就可以处理图形了。
因为要对客户方的快30个项目进行特别有顺序的重启,所以不得不想办法写个脚本,网上看了不少段子。真是残缺的可以。没有一段是可以正常运行的。我来按顺序记录一下 脚本的本身 使用expect实现自动登录的脚本,网上有很多,可是都没有一个明白的说明,初学者一般都是照抄、收藏。可是为什么要这么写却不知其然。本文用一个最短的例子说明脚本的原理。 脚本代码如下: ############################################## #!/usr/bin/expect set timeout 30 spawn ssh -l username 192.168.1.1 expect "password:" send "ispass\r" interact ############################################## 1. [#!/usr/bin/expect] 这一行告诉操作系统脚本里的代码使用那一个shell来执行。这里的expect其实和linux下的bash、windows下的cmd是一类东西。 注意:这一行需要在脚本的第一行。 2. [set timeout 30] 基本上认识英文的都知道这是设置超时时间的,现在你只要记住他的计时单位是:秒 3. [spawn ssh -l username 192.168.1.1] spawn是进入expect环境后才可以执行的expect内部命令,如果没有装expect或者直接在默认的SHELL下执行是找不到spawn命令的。所以不要用 “which spawn“之类的命令去找spawn命令。好比windows里的dir就是一个内部命令,这个命令由shell自带,你无法找到一个dir.com 或 dir.exe 的可执行文件。 它主要的功能是给ssh运行进程加个壳,用来传递交互指令。 4. [expect "password:"] 这里的expect也是expect的一个内部命令,有点晕吧,expect的shell命令和内部命令是一样的,但不是一个功能,习惯就好了。这个命令的意思是判断上次输出结果里是否包含“password:”的字符串,如果有则立即返回,否则就等待一段时间后返回,这里等待时长就是前面设置的30秒 5. [send "ispass\r"] 这里就是执行交互动作,与手工输入密码的动作等效。 温馨提示: 命令字符串结尾别忘记加上“\r”,如果出现异常等待的状态可以核查一下。 6. [interact] 执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。如果你只是登录过去执行 #!/usr/bin/expect #注意安装的路径,不确定 whereis expect 一下 # Change a login shell to bash set user [lindex $argv 0] spawn bash $user expect "]:" send "/bin/bash " 然后问题就来了。 首先要安装 #apt-get install expect 或是 yum install expect 然后,上面的脚本使用sh命是无法运行的,这点一定要记住,不然会报如下: start.sh: line 3: spawn: command not found": no such file or directory:start.sh: line 5: send: command not foundstart.sh: line 6: interact: command not found 解决的办法如下: 含有expect的脚本不能用bash执行,bash无法解析。添加可执行权限后,直接./your_script即可。 然后问题又来了,它会报 /usr/bin/expect^M: bad interpreter: 没有那个文件或目录 这时要这么办: 运行脚本时出现了这样一个错误,打开之后并没有找到所谓的^M,查了之后才知道原来是文件格式的问题,也就是linux和windows之间的不完全兼容。。。具体细节不管,如果验证:vim test.sh:set ff?如果出现fileforma=dos那么就基本可以确定是这个问题了。:set fileformat=unix:wqOK了。。。。。。。 bash: ./eth0-access: /bin/bash^M: bad interpreter: 没有那个文件或目录 错误分析: 因为操作系统是windows,我在windows下编辑的脚本,所以有可能有不可见字符。 脚本文件是DOS格式的, 即每一行的行尾以 来标识, 其ASCII码分别是0x0D, 0x0A. 可以有很多种办法看这个文件是DOS格式的还是UNIX格式的, 还是MAC格式的 解决方法: vim filename 然后用命令 :set ff? #可以看到dos或unix的字样. 如果的确是dos格式的。 然后用 :set ff=unix #把它强制为unix格式的, 然后存盘退出。 再次运行脚本。 好累,终于写完了。
大早上正式库提示: Oracle提示错误消息ORA-28001: the password has expired 解决办法: 1、利用SYSDBA权限登陆; 2、查看账户信息:select username,account_status from dba_users 3、如果账户locked/expired, 得先解锁: alter user BJMMIS account unlock; 再修改密码 alter user BJMMIS identified by newpwd; 一劳永逸破解11G180天自动密码失效的办法: 查询密码有效期 SELECT * FROM dba_profiles WHERE profile='DEFAULT' AND resource_name='PASSWORD_LIFE_TIME' 修改为无限制: ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED
查看Memcached运行状态的命令是:echo stats | nc 127.0.0.1 11211 查看memcached状态的基本命令,通过这个命令可以看到如下信息: STAT pid 22459 进程ID STAT uptime 1027046 服务器运行秒数 STAT time 1273043062 服务器当前unix时间戳 STAT version 1.4.4 服务器版本 STAT pointer_size 64 操作系统字大小(这台服务器是64位的) STAT rusage_user 0.040000 进程累计用户时间 STAT rusage_system 0.260000 进程累计系统时间 STAT curr_connections 10 当前打开连接数 STAT total_connections 82 曾打开的连接总数 STAT connection_structures 13 服务器分配的连接结构数 STAT cmd_get 54 执行get命令总数 STAT cmd_set 34 执行set命令总数 STAT cmd_flush 3 指向flush_all命令总数 STAT get_hits 9 get命中次数 STAT get_misses 45 get未命中次数 STAT delete_misses 5 delete未命中次数 STAT delete_hits 1 delete命中次数 STAT incr_misses 0 incr未命中次数 STAT incr_hits 0 incr命中次数 STAT decr_misses 0 decr未命中次数 STAT decr_hits 0 decr命中次数 STAT cas_misses 0 cas未命中次数 STAT cas_hits 0 cas命中次数 STAT cas_badval 0 使用擦拭次数 STAT auth_cmds 0 STAT auth_errors 0 STAT bytes_read 15785 读取字节总数 STAT bytes_written 15222 写入字节总数 STAT limit_maxbytes 1048576 分配的内存数(字节) STAT accepting_conns 1 目前接受的链接数 STAT listen_disabled_num 0 STAT threads 4 线程数 STAT conn_yields 0 STAT bytes 0 存储item字节数 STAT curr_items 0 item个数 STAT total_items 34 item总数 STAT evictions 0 为获取空间删除item的总数 stats items 输出各个slab中的item信息。s stats slabs 输出slab中更详细的item信息 stats sizes 输出所有item的大小和个数 stats cachedump <slab_id> <limit_num> 根据<slab_id>输出相同的<slab_id>中的item信息。<limit_num>是输出的个数,当<limit_num>为0是输出所有的item。 利用shell命令操作Memcached 1、数据存储(假设key为g,value为12345) printf “set g 0 0 5\r\n12345\r\n”|nc 127.0.0.1 11211 STORED 2、数据取回(假设key为zhangyan) printf “get g\r\n”|nc 127.0.0.1 11211 VALUE g 0 5 12345 END 3、数值增加1(假设key为g,并且value为正整数) printf “incr g 1\r\n” | nc 127.0.0.1 11211 12346 4、数值减少3(假设key为g,并且value为正整数) printf “decr g 3\r\n” | nc 127.0.0.1 11211 12343 5、数据删除(假设key为g) printf “delete g\r\n” | nc 127.0.0.1 11211 DELETED 6、查看Memcached状态 printf “stats\r\n” | nc 127.0.0.1 11211 STAT pid 3025 STAT uptime 4120500 STAT time 1228021767 STAT version 1.2.6 STAT pointer_size 32 STAT rusage_user 433.463103 STAT rusage_system 1224.515845 STAT curr_items 1132460 STAT total_items 8980260 STAT bytes 1895325386 STAT curr_connections 252 STAT total_connections 547850 STAT connection_structures 1189 STAT cmd_get 13619685 STAT cmd_set 8980260 STAT get_hits 6851607 STAT get_misses 6768078 STAT evictions 0 STAT bytes_read 160396238246 STAT bytes_written 260080686529 STAT limit_maxbytes 2147483648 STAT threads 1 END 7、模拟top命令,查看Memcached状态: watch “printf ‘stats\r\n’ | nc 127.0.0.1 11211″ 或者 watch “echo stats | nc 127.0.0.1 11211″ 一、echo stats items | nc127.0.0.1 11211 STAT items:1:number 998 Slab Id=1 ; items数量:998(也就是已经存储了998个key值) STAT items:1:age 604348 Slab Id=1 ; 已经存在时间,单位秒 STAT items:1:evicted 0 Slab Id=1 ; 被踢出的数量 STAT items:1:evicted_nonzero 0 STAT items:1:evicted_time 0 STAT items:1:outofmemory 0 STAT items:1:tailrepairs 0 STAT items:1:reclaimed 0 STAT items:6:number 91897 Slab Id=6 ; items数量:91897(也就是已经存储了91897个key值) STAT items:6:age 604345 Slab Id=6 ; 已经存在时间,单位秒 STAT items:6:evicted 0 Slab Id=6 ; 被踢出的数量 STAT items:6:evicted_nonzero 0 STAT items:6:evicted_time 0 STAT items:6:outofmemory 0 STAT items:6:tailrepairs 0 STAT items:6:reclaimed 0
执行echo $LANG命令输出的是当前的编码方式,执行locale命令得到系统中所有可用的编码方式。要让Xshell不显示乱码,则要将编码方式改为UTF-8。 在Xshell中[file]-> [open] -> 在打开的session中选择连接的那个,点击properties ->[Terminal],在右边translation中选择UTF-8,再重新连接服务器即可。 或者也可以在xshell的工具栏里面点击“Encoding”按钮,选择utf-8编码即可。 更有效简单的方法是, 在终端执行命令: export LC_ALL=zh_CN.GB2312;export LANG=zh_CN.GB2312 就OK了
1. 在linux下,查看一个运行中的程序, 占用了多少内存, 一般的命令有 (1). ps aux: 其中 VSZ(或VSS)列 表示,程序占用了多少虚拟内存。 RSS列 表示, 程序占用了多少物理内存。 虚拟内存可以不用考虑,它并不占用实际物理内存。 (2). top 命令也可以 其中 VIRT(或VSS)列 表示,程序占用了多少虚拟内存。 同 ps aux 中的 VSZ列 RES列 表示, 程序占用了多少物理内存。同 ps aux 中的RSS列 2.在linux下, 查看当前系统占用了多少内存, 一般的命令是 free 其中, free就是系统还有多少内存可以使用。 但由于 linux 系统对内存使用有一个原则, 就是, 内存是宝贵的, 能使用多少就使用多少。 所以, linux会把已经调用过的包缓存起来,放在内存里。 这样,实际上,可以使用的内存,就可以理解为, free+buffers+cached 3.当你了解完这些命令以后, 再去使用ps aux 命令去查看的时候, 会发现一个奇怪的现象。 所有的 RSS 列的数据,加起来, 比物理内存的数要大很多。 比如, 物理内存为2G, 而RSS列的数据加起来,可能有5个G之多, 这是怎么回事了? 这是因为RSS列的值骗了我们。 linux的内存机制是这样的: 在运行一个程序时, linux会调用该程序依赖的链接库, 如lib.xx.so。 首先看该链接库是否被映射进内存中,如果没有被映射,则将代码段与数据段映射到内存中,否则只是将其加入进程的地址空间。 这样,当N个程序,依赖到lib.xx.so的时候, 实际上,内存中只有一个lib.xx.so ,而不是N个。 而RSS在显示一个程序占用的实际物理内存时, 将lib.xx.so也算了进来。 比如, X程序, 本身占用内存为5M, lib.xx.so 占用内存2M,lib.xx.so被N个程序共享依赖。 则RSS显示为,X程序运行,占用内存为7M。 实际上, X程序占用了5M空间。 多余的2m被讨入到RSS中了。 当你在用ps aux显示内存占用情况时, N个共享依赖lib.xx.so的N个程序,都把这2m空间,算在自己的RSS中了, 这样RSS的sum值,就比实际物理内存多了。 当然, linux的内存使用机制很复杂, 不是一句两句能说清楚的。这里只是简单的说明了一下, ps aux中的RSS值, 并不能真实反映物理内存的使用情况。 4. 如果查看更详细的内存使用情况, 可用以下几种方法, 或者几种方法结合使用: 这几种方法,都需要root账户的权限 (1). pmap -d $pid $pid 是正在运行的程序的pid (2). cat /proc/$pid/smaps smaps的数据比较详细,可简单的归纳一下,归纳的命令如下: cat /proc/$pid/smaps | awk '/Size|Rss|Pss|Shared|Private|Referenced|Swap/{val_name=gensub(/([a-zA-Z_]*).*/,"\\1",1,$1); list[val_name]+=$2; }END{for(val in list)print val,list[val];}' (3). cat /proc/$pid/maps (4). cat /proc/$pid/statm 输出解释 第一列 size:任务虚拟地址空间大小第二列 Resident:正在使用的物理内存大小第三列 Shared:共享页数第四列 Trs:程序所拥有的可执行虚拟内存大小第五列 Lrs:被映像倒任务的虚拟内存空间的库的大小第六列 Drs:程序数据段和用户态的栈的大小第七列 dt:脏页数量 (5). vmstat 这个命令据说也可以提供一些参考信息,具体还未研究 5.作为phper,尝试过使用php的函数memory_get_usage(), 该函数也不能得到php当前运行的程序,实际的,真正占用的内存数量。 如果真想得到,php真正占用的内存, 大概只能在, 程序运行的开始,执行一次memory_get_usage(). 在程序运行结束,执行一次memory_get_usage()。 将两者的值相减,得到的值, 应该是一个相对比较准确的,内存占用数量了。 这个方法还没有测试, 考虑到, 得到这个数量,也没有实际意义, 加上平时又比较忙,懒得试了。 也许php还有一个方法, 是使用shm_* 系列函数, 这也我也未深入研究,详见这篇文章(http://duckweeds.blog.sohu.com/166663796.html) 6.另外还有一些文章可以参考,如下: (1)一个C程序员, 眼中的Linux内存使用详解,写的比较详细,比较细致,也比较专业。 (2)对 /proc/pid/statm的详细说明 (3)简单解读linux的/proc下的statm、maps、memmap 内存信息文件分析 (4)php 共享内存的使用 (5)Memory Usage with smaps (6)Capturing Process Memory Usage Under Linux,这篇文章似乎是对一个产品的广告,但里面对USS,PSS,RSS 这几个概念有详细的解释 (7) ELC: How much memory are applications really using,跟(6)一样,是对同一个产品的广告,文章里有一些东西可以参考 (8) Linux Check Memory Usage,文章对 free, vmstat,top , gnome-system-monitor等命令有一些介绍 (9)Console Monitoring Tools for SUSE Linux,对top,free,uptime,pmap,smartctl,iostat,strace等命令有所介绍,并且介绍的比较详细,目前只是粗略的看了一下,有时间还要再看看。 (10)Solaris 9 Enhanced pmap,比较详细的介绍了pmap的应用,不过是基于Solaris 9的
有许多命令都可以查看文件,不同的命令有不同的优点,可以针对不同的需要分别选择命令以提高效率: cat 由第一行开始显示内容,并将所有内容输出 tac 从最后一行倒序显示内容,并将所有内容输出 more 根据窗口大小,一页一页的现实文件内容 less 和more类似,但其优点可以往前翻页,而且进行可以搜索字符 head 只显示头几行 tail 只显示最后几行 nl 类似于cat -n,显示时输出行号我使用最多的是more和less! 工具/原料 CENTOS 方法/步骤 1 cat 与 taccat的功能是将文件从第一行开始连续的将内容输出在屏幕上。但是cat并不常用,原因是当文件大,行数比较多时,屏幕无法全部容下时,只能看到一部分内容。 2 cat语法:cat [-n] 文件名参数说明:-n : 显示时,连行号一起输出例如:[root@redhat ~]# cat .bashrc # .bashrc# User specific aliases and functionsalias rm='rm -i'alias cp='cp -i'alias mv='mv -i'# Source global definitionsif [ -f /etc/bashrc ]; then. /etc/bashrcfi加入-n参数[root@redhat ~]# cat -n .bashrc 1 # .bashrc 2 3 # User specific aliases and functions 4 5 alias rm='rm -i' 6 alias cp='cp -i' 7 alias mv='mv -i' 8 9 # Source global definitions 10 if [ -f /etc/bashrc ]; then 11 . /etc/bashrc 12 fi 3 tac的功能是将文件从最后一行开始倒过来将内容数据输出到屏幕上。我们可以发现,tac实际上是cat反过来写。tac语法:tac 文件名例如:[root@redhat ~]# tac .bashrc fi. /etc/bashrcif [ -f /etc/bashrc ]; then# Source global definitionsalias mv='mv -i'alias cp='cp -i'alias rm='rm -i'# User specific aliases and functions# .bashrc发现没有,和cat输出的比较,完全是倒过来的。这个命令也不常用。 END more和less(推荐使用) 1 相对于cat和tac来说,more和less很好用。more的功能是将文件从第一行开始,根据输出窗口的大小,适当的输出文件内容。当一页无法全部输出时,可以用“回车键”向下翻3行(我的环境是3行,其他linux版本可能不同),或者使用“空格键”向下翻页。退出查看页面,请按“q”键。另外,more还可以配合管道符“|”(pipe)使用,例如:ls -al | moremore的语法:more 文件名 2 less的功能和more相似,但是使用more无法向前翻页,只能向后翻。less可以使用【pageup】和【pagedown】键进行前翻页和后翻页,这样看起来更方便。less的语法:less 文件名less还有一个功能,可以在文件中进行搜索你想找的内容,假设你想在passwd文件中查找有没有weblogic字符串,那么你可以这样来做:[root@redhat etc]# less passwd然后输入:/weblogic回车此时如果有weblogic字符串,linux会把该字符已高亮方式显示。退出查看页面,请按“q”键。 END head和tail 1 head和tail通常使用在只需要读取文件的前几行或者后几行的情况下使用。head的功能是显示文件的前几行内容head的语法:head 【-n number】 文件名例如:[root@redhat etc]# head -n 5 passwd --只显示5行内容root:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologinadm:x:3:4:adm:/var/adm:/sbin/nologinlp:x:4:7:lp:/var/spool/lpd:/sbin/nologin 2 tail的功能恰好和head相反,只显示最后几行内容tail的语法:tail [-n number] 文件名例如:[root@redhat etc]# tail -n 10 passwd --只显示最后5行userb:x:503:504::/home/userb:/bin/bashuserc:x:504:502::/home/userc:/bin/bashzgz:x:505:505::/home/zgz:/bin/bashmyy:x:506:505::/home/myy:/bin/bashweblogic:x:507:508::/home/weblogic:/bin/bash END nl nl的功能和cat -n一样,同样是从第一行输出全部内容,并且把行号显示出来nl的语法:nl 文件名例如:[root@redhat etc]# nl ~/.bashrc 1 # .bashrc 2 # User specific aliases and functions 3 alias rm='rm -i' 4 alias cp='cp -i' 5 alias mv='mv -i' 6 # Source global definitions 7 if [ -f /etc/bashrc ]; then 8 . /etc/bashrc 9 fi
最近做毕业设计用了一个叫做kindeditor的文本编辑工具,相信很多人都用过,这货和fckeditor差不多,个人感觉这个的皮肤更好看,而且对中文的支持更好,没那么容易出现中文乱码问题。下次记录一下自己的简单用法: 1,首先去官网下载http://www.kindsoft.net/ 2,解压之后如图所示: 由于本人做的是用的是JSP,所以ASP,PHP什么的就用不上了,直接把那些去掉然后将整个文件夹扔进Myeclipse,如图: 里面有个报错,估计是我自己IDE的问题,没有处理照常使用。 3,就可以开工写JSP了,下面把自己的一个JSP的代码贴出来,页面代码神马的不太规范,凑合着当示例,能跑通就行; <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <link rel="stylesheet" type="text/css" href="css/layout.css"> <link rel="stylesheet" href="kindeditor/themes/default/default.css" /> <link rel="stylesheet" href="kindeditor/plugins/code/prettify.css" /> <script charset="utf-8" src="kindeditor/kindeditor.js"> </script> <script charset="utf-8" src="kindeditor/lang/zh_CN.js"> </script> <script charset="utf-8" src="kindeditor/plugins/code/prettify.js"> </script> <script> KindEditor.ready(function(K) { var editor1 = K.create('textarea[name="article.content1"]', { cssPath : 'kindeditor/plugins/code/prettify.css', uploadJson : 'kindeditor/jsp/upload_json.jsp', fileManagerJson : 'kindeditor/jsp/file_manager_json.jsp', allowFileManager : true, afterCreate : function() { var self = this; K.ctrl(document, 13, function() { self.sync(); document.forms['example'].submit(); }); K.ctrl(self.edit.doc, 13, function() { self.sync(); document.forms['example'].submit(); }); } }); prettyPrint(); }); </script> </head> <body> <div id="container"> <div id="header"> </div> <div id="menu"> <ul> <li> <a href="main.jsp">首页</a> </li> <li> <a href="jsp/update_password.jsp">个人信息</a> </li> <li> <a href="jsp/article_add.jsp">提交论文</a> </li> <li> <a href="article_returnList.action">审批论文</a> </li> <li> <a href="#">后台管理</a> </li> </ul> </div> <div id="mainContent"> <form name="example" method="post" action="article_add.action"> 题目: <input type="text" name="article.title"> <br /> 内容: <textarea name="article.content1" cols="100" rows="8" ></textarea> <br /> <input type="submit" name="button" value="提交" /> (提交快捷键: Ctrl + Enter) </form> </div> <div id="footer"> Copyright 2012 by Lai Xuansi. </div> </div> </body> </html> 4,给张上述代码的效果图,本人不太会做界面,凑合看: 5,关于数据库和上传本地图片问题 Kindeditor对于上传的图片神马的会默认保存在attached文件夹中,至于数据库字段中存储的只是图片的地址,所以将内容读取出来的时候,只要读取数据库字段中的内容就会自动将文本和图片等一起显示出来了,很好很强大!要注意的就是读取数据库出来之后要将地址转换成HTML代码才能正确显示,这个工作很简单,只要:<s:property value="article.content1" escape="false" />将escape="false" 就OK了。
网上有很多的文章教大家使用定时任务,所以别的废话我就不多说了 我这里直接有SH来做定时,只是有一点大家不知道,一定要用Nohup,否则用户退出终端以后,SH任务会被自动终止掉 假设有一 tash.sh,注意以下,都是精华 启动是:nohup /home/spex/bin_prodDemo/task.sh & 一定要把路径打全,绝对地址 然就是退出,记得退出!!!用exit退出终端!!千万不要直接退出.不然还是没有用的. 然后查看一下 ps aux|grep task 你就能看到它乖乖在那里运行 root 279 0.0 0.0 0 0 ? S Sep26 0:00 [khungtaskd]spex 11488 0.0 0.0 63868 1096 ? S 14:52 0:00 /bin/sh /home/spex/bin_prodDemo/task.shspex 11544 0.0 0.0 61208 756 pts/3 S+ 14:53 0:00 grep task
(1) 最简单的修改方法,就是修改mysql的my.ini文件中的字符集键值, 如 default-character-set = utf8 character_set_server = utf8 修改完后,重启mysql的服务,service mysql restart 使用 mysql> SHOW VARIABLES LIKE 'character%';查看,发现数据库编码均已改成utf8 +--------------------------+---------------------------------+ | Variable_name | Value | +--------------------------+---------------------------------+ | character_set_client | utf8 | | character_set_connection | utf8 | | character_set_database | utf8 | | character_set_filesystem | binary | | character_set_results | utf8 | | character_set_server | utf8 | | character_set_system | utf8 | | character_sets_dir | D:"mysql-5.0.37"share"charsets" | +--------------------------+---------------------------------+ (2) 还有一种修改mysql默认字符集的方法,就是使用mysql的命令 mysql> SET character_set_client = utf8 ; mysql> SET character_set_connection = utf8 ; mysql> SET character_set_database = utf8 ; mysql> SET character_set_results = utf8 ; mysql> SET character_set_server = utf8 ; mysql> SET collation_connection = utf8 ; mysql> SET collation_database = utf8 ; mysql> SET collation_server = utf8 ; 一般就算设置了表的mysql默认字符集为utf8并且通过UTF-8编码发送查询,你会发现存入数据库的仍然是乱码。问题就出在这个connection连接层上。解决方法是在发送查询前执行一下下面这句: SET NAMES 'utf8'; 它相当于下面的三句指令: SET character_set_client = utf8; SET character_set_results = utf8; SET character_set_connection = utf8;
<constant name="struts.multipart.saveDir" value="D:\\AsimsTemp"></constant>黑体部分应为“\\”,否则回导致action中的自动封装数据失败,从而导致validate中验证出现空指针“NullPointer” 另外: <constant name="struts.multipart.saveDir" value="/temp" /> 将上传的临时文保存到D:/temp,而不是项目的WebAppRoot+/temp 下 原因解释: 当你配置"struts.multipart.saveDir"时,struts会将目录定向为: [html] view plaincopy if (saveDir != null) { fac.setRepository(new File(saveDir)); } [html] view plaincopy if (saveDir != null) { fac.setRepository(new File(saveDir)); } 当"struts.multipart.saveDir"为“/temp”时,很容易验证 : new File("/temp").getAbsolutePath(); 为当前的根目录下的temp目录。 当你使用相对路径比如 "myproject/temp"配置时,效果依然不行。因为 new File(相对路径) 得到的结果是: user.dir + 相对路径 在Tomcat下user.dir是 Tomcat目录下的bin目录,所以上述路径最终是: Tomcat/bin/myproject/temp ( 不要试图使用 ../webapps/myproject/temp , 这样是不成功的)。 解决办法2个,个人爱好自选: 1、更改user.dir的默认值。 在系统启动的时候,用ServletContextListener修改系统属性。 System.setProperty("user.dir","你的Tomcat的目录下的webapps"); 然后在配置: [html] view plaincopy <constant name="struts.multipart.saveDir" value="yourproject/temp"></constant> [html] view plaincopy <constant name="struts.multipart.saveDir" value="yourproject/temp"></constant> 2、直接了当: [html] view plaincopy <constant name="struts.multipart.saveDir" value="绝对路径"></constant>
debian下安装MySQL:1、构建源或使用光盘镜像,当然你插入光盘也没问题2、有源时本地文件的源配置:修改/etc/apt/sources.list文件, 示例:deb http://192.168.10.73/ sid main 以上设置如果待安装的电脑可以接入Internet,均可省略。3、准备工作做好了,现在开始安装: 1)输入:apt-get install mysql-server 2)接下来会有几个询问对话框,不管你能不能看懂,无特殊情况一路yes就OK了,主要就是搜寻源,识别文件等 3)当以上都OK了,就会弹出一个询问对话框,提示输入MySQL的root密码 4)接下来会弹出一个确认密码输入框,估计都看得懂,就不多说了。 5)再接下来就开始安装了,瞬间完成,你会发现比Red Hat简单多了,输入mysql –V(记得是大写的V,这里还不是mysql)查看一下,如果出现: mysql Ver 14.12 Distrib 5.0.51a, for debian-linux-gnu (i486) using readline 5.2 那么恭喜你,已经安装成功了。 访问设置:Mysql的操作工具个人觉得Navicat for MySQL最好用了,这时候就要解决Mysql数据库的远程访问问题了。为了便于初学者理解,可以先尝试使用如下几个命令了解下基本结构:testB:~# mysql -uroot -p123456进入Mysql,有如下提示:Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 33Server version: 5.0.51a-24 (Debian) Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> show database;查看数据库mysql> use mysql;基础库,数据库的用户和权限管理均在此库里操作mysql> show tables;查看该数据库里的表格,会发现有个user表,马上就会用到这个表mysql> select * from user;先用这个命令查看该表,会发现root用户只能通过本地访问 执行如下命令:mysql>grant all on *.* to root@192.168.1.22 identified by "123456";all即代表所有权限,*.*代表所有数据库,root代表访问时的用户名,@后面为允许访问的ip,使用%即赋予任意主机权限访问,最后一个字符串是与用户名匹配的访问密码。成功会有如下提示:Query OK, 0 rows affected (0.00 sec)再执行以下命令查看结果:mysql> select * from user;会发现新增了一条记录| 192.168.1.22 | root | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 |表示192.168.1.22可以使用root账户访问Mysql了如果要远程访问,还需要修改MySQL的配置文件/etc/mysql/my.cnf,因为默认3306端口只允许本地访问的,注释掉这行#bind-address = 127.0.0.1然后重启Mysql,/etc/init.d/mysql restart OK,现在可以远程访问数据了。
在Debian/Ubuntu系统中安装*.sh与*.bin文件的基本方法。一,安装*.sh文件运行命令行至文件目录下,执行:sudo sh *.sh直接运行在命令行中执行:sudo chmod +x *.sh再输入sudo ./*.sh可安装到任意目录,./*.sh可安装到当前用户有权限的目录。二,安装*.bin文件运行命令行至文件目录下在命令行中执行:sudo chmod +x *.bin再输入sudo ./*.bin可安装到任意目录,./*.bin可安装到当前用户有权限的目录。
JDK下载http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-javase6-419409.html#jdk-6u21-oth-JPR在 sun 下载了最新的 JDK,我下载的是 jdk-6u32-linux-i586.bin并把该文件的属性改成可执行,直接执行该文件# chmod +x jdk-6u32-linux-i586.bin# ./jdk-6u32-linux-i586.bin程序运行后会当前目录下生成一个名为 jdk1.6.0_32 的目录把该目录拷贝到/usr/local/jdk1.6.0_32 ,并在系统初始化脚本中增加以下两个环境变量cp -r /home/jdk1.6.0_32/ /usr/local/配置classpath,修改所有用户的环境变量root# vi /etc/profile在文件最后添加#set java environmentexport JAVA_HOME='/usr/local/jdk1.6.0_32'export JRE_HOME=/usr/local/jdk1.6.0_32/jreexport CLASSPATH=.:$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATHexport PATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH设置默认启动gedit /etc/rc.local加上:export JAVA_HOME=/usr/local/jdk1.6.0_32export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH:$CATALINA_HOME=/usr/local/apache-tomcat-6.0.26/binexport CLASSPATH=.:/usr/local/jdk1.6.0_32/lib:/usr/local/jdk1.6.0_32/jre/lib:$CLASSPATHexport CATALINA_BASE=/usr/local/apache-tomcat-6.0.26export CATALINA_HOME=/usr/local/apache-tomcat-6.0.26export JRE_HOME=/usr/local/jdk1.6.0_32/jre用java -version命令进行测试
json文件和脚本代码: jsonSrc/jsonTxt1.json, { "personInfoList": [ { "id": 0, "name": "A", "age": 12 }, { "id": 1, "name": "B", "age": 32 }, { "id": 2, "name": "C", "age": 2 } ] } extJS3.3/test4.html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>test4.html</title> <link rel="stylesheet" type="text/css" href="../ext-3.3.1/resources/css/ext-all.css"></link> <script type="text/javascript" src="../ext-3.3.1/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="../ext-3.3.1/ext-all.js"></script> <script type="text/javascript"> Ext.onReady(function(){ var myStore = new Ext.data.JsonStore({ url: '../jsonSrc/jsonTxt1.json', root: 'personInfoList', autoLoad: true, fields: [{name: 'id', type: 'int'}, {name: 'name', type: 'string'}, {name: 'age', type: 'int'}] }); var columns = new Ext.grid.ColumnModel({ columns: [ { header: 'ID', dataIndex: 'id' }, { header: 'Name', dataIndex: 'name' }, { header: 'Age', dataIndex: 'age' } ] }); var grid = new Ext.grid.GridPanel({ id: 'gridPanel', title : 'Person Info Panel', width : 250, height : 250, renderTo : 'personInfoListPanel', store : myStore, colModel : columns }); }); </script> </head> <body> <div id = "personInfoListPanel"></div> </body> </html>
今天在自己的电脑上安装了Tomcat6.0.14,是在Tomcat主页上直接下载的免安装版。但是把文件解压的之后,双击Tomcat6w.exe时,去出现了"指定的服务并未以已安装的服务存在,Unable to open the service"Tomcat5""。在地址栏输入http://localhost:8080,也无法显示Tomcat的主页。 后来在网上查了原因,原来是没有安装此项服务。解决的办法是进入命令行提示符cmd中,进入Tomcat\bin\安装目录,输入: service.bat install 即可。cmd-->services.msc,显示已经有Tomcat服务了。再次双击Tomcat6.exe,就可以运行了。 ====================================================================今天,在安装Tomcat6.0时遇到个问题:安装的时候,弹出错误信息:Failed to install Tomcat6 service. Check your settings and permissions Ignore and continue anyway 点击忽略后继续安装,但安装完成后启动Tomcat6时,又会提示:指定的服务并未以已安装的服务存在 Unable to open the service 'Tomcat6' 后来发现,这是因为原来的系统中已经装了Tomcat5.5,并将其添加到了系统的本地服务中了,所以在启动Tomcat6.0的时候就出错了,这时我们只需先讲系统中的Tomcat5服务卸载掉,然后再将Tomcat6的服务添加到系统中即可,无需卸载Tomcat5 如何卸载Tomcat的服务:运行cmd,进入到Tomcat5的bin目录下,然后运行命令“Service.bat remove” 卸载掉Tomcat5服务之后就可以安装Tomcat6服务了,同样的进入到Tomcat6的bin目录下,运行“Service.bat install“,这样,Tomcat6服务就安装好了,Tomcat6也就可以正常运行了 Tomcat 指定的服务并未以已安装的服务存在 Tomcat解压之后,双击Tomcat6w.exe时,出现"指定的服务并未以已安装的服务存在,Unable to open the service"Tomcat5""的错误,解决办法为: 进入命令行提示符cmd中,进入 安装目录\Tomcat\bin\,输入: service.bat install 即可。cmd-->services.msc,显示已经有Tomcat服务了。再次双击Tomcat6.exe,就可以运行了。但若是仅仅双击start.bat,还是不行的。 其次不要忘了设置JAVA_HOME:安装目录\Java\jdk~、PATH:安装目录\Java\jdk~\bin、CATALINA_HOME:安装目录\tomcat
//第一种 Iterator menus = menu.iterator(); while(menus.hasNext()) { Map userMap = (Map) menus.next(); System.out.print(userMap.get("link") + "\t"); // System.out.print(userMap.get("name") + "\t"); // System.out.print(userMap.get("sex") + "\t"); // System.out.println(userMap.get("age") + "\t"); } //第二种 for (int j = 0; j < menu.size(); j++) { String a = (String)(((Map) menu.get(0)).get("link")); System.out.println(a); // m.put("title", menu.get(j).getTitle()); // m.put("link",menu.get(j).getLink()); // list.add(m); // map.put("rows", list); }
jdbcTemplate的queryForList的使用方法如下,它不一样的地方是,它获得的结果,会再放到一个map里去: List rows = jdbcTemplate.queryForList("SELECT * FROM USER"); Iterator it = rows.iterator(); while(it.hasNext()) { Map userMap = (Map) it.next(); System.out.print(userMap.get("user_id") + "\t"); System.out.print(userMap.get("name") + "\t"); System.out.print(userMap.get("sex") + "\t"); System.out.println(userMap.get("age") + "\t"); } 我的一个例子中,得到的结果只有一列数字类型的数据(targer_id),但使用queryForList得到的结果集个数与数据库的内容一致,但每一项都是null。不清楚其原因,修改成 jdbcTemplate.query(sql, 才能正确得到结果。 附上带用的一些方法 使用jdbcTemplate查询数据的时候可以使用queryForXXX等方法。下面我们就一一解析一下:1、jdbcTemplate.queryForInt()和jdbcTemplate.queryForLong()--使用queryForInt返回user表中的记录数量,queryForInt搭配这样的sql可以在分页的时候计算总记录数jdbcTemplate.queryForInt("select count(*) from user");2、jdbcTemplate.queryForObject()--本质上和queryForInt相同,只是可以返回不同的对象,例如返回一个String对象String name = (String) jdbcTemplate.queryForObject( --3个参数,1、sql 2、要传递的参数数组 3、返回来的对象class"SELECT name FROM USER WHERE id = ?", new Object[] {id}, java.lang.String.class);3、jdbcTemplate.queryForList(???)--返回一个装有map的list,每一个map是一条记录,map里面的key是字段名List rows = jdbcTemplate.queryForList("SELECT * FROM user"); --得到装有map的listfor(int i=0;i<rows.size();i++){ --遍历Map userMap=rows.get(i);System.out.println(userMap.get("id")); System.out.println(userMap.get("name")); System.out.println(userMap.get("age"));}4、jdbcTemplate.queryForMap(SQL)--这个查询只能是查询一条记录的查询,返回一个map,key的值是column的值Map map = jdbcTemplate.queryForMap("select count(*) as keyval from user");map.get("keyval")5、jdbcTemplate.queryForRowSet(???)--返回一个RowSet 然后调用.getString或者getInt等去取值6、jdbc1.query(sql, new RowCallbackHandler()--返回一个ResultSet对象, processRow有自动循环的机制,它会自动执行processRow中的语句直到--rs的size执行完了为止。我们可以在这其中用list完成对象的转移,只不过list要用final来修饰jdbc1.query(sql, new RowCallbackHandler() { //editing public void processRow(ResultSet rs) throws SQLException { VideoSearch vs = new VideoSearch(); vs.setRECORDINGFILENAME(rs.getString("RECORDINGFILENAME")); vs.setCALLID(rs.getString("CALLID")); list.add(vs); } }说明:JDBCTemplate的使用方法:在ApplicationContext.xml中定义一个jdbcTemplate的节点,使用POJO注入,获得注入后可以执行操作不需要继承什么基类<bean id="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean>SqlRowSet rs = jdbcTemplate.queryForRowSet(sql, params);jdbcTemplate有很多的ORM化回调操作将返回结果转为对象列表,但很多时候还是需要返回ResultSet,Spring有提供一个类似ResultSet的,实现JDBC3.0 RowSet接口的Spring SqlRowSet注意jdbcTemplate尽量只执行查询操作,莫要进行更新,否则会破坏Hibernate的二级缓存体系
最简单看进程有没有: ps -ef | grep ora 其次用oracle的的命令查看,比如: su - oracle sqlplus / as sysdba 看能连进数据库不。 创建用户和表空间: 1、登录linux,以oracle用户登录(如果是root用户登录的,登录后用 su - oracle命令切换成oracle用户)2、以sysdba方式来打开sqlplus,命令如下: sqlplus / as sysdba3、创建临时表空间: --查询临时表空间文件的绝对路径。如果需要的话,可以通过查询来写定绝对路径。一般用${ORACLE_HOME}就可以了 select name from v$tempfile; create temporary tablespace NOTIFYDB_TEMP tempfile '${ORACLE_HOME}\oradata\NOTIFYDB_TEMP.bdf' size 100m reuse autoextend on next 20m maxsize unlimited; 4、创建表空间: --查询用户表空间文件的绝对路径: select name from v$datafile; create tablespace NOTIFYDB datafile '${ORACLE_HOME}\oradata\notifydb.dbf' size 100M reuse autoextend on next 40M maxsize unlimited default storage(initial 128k next 128k minextents 2 maxextents unlimited); 5、创建用户和密码,指定上边创建的临时表空间和表空间 create user hc_notify identified by hc_password default tablespace NOTIFYDB temporary tablespace NOTIFYDB_TEMP; 6、赋予权限 grant dba to hc_notify; grant connect,resource to hc_notify; grant select any table to hc_notify; grant delete any table to hc_notify; grant update any table to hc_notify; grant insert any table to hc_notify; 经过以上操作,就可以使用hc_notify/hc_password登录指定的实例,创建我们自己的表了。 删除表空间: 1、查看用户权限 --查看用户要具备drop tablespace的权限,如果没有,先用更高级的用户(如sys)给予授权 select a2.username,a1.privilege from dba_sys_privs a1 , user_role_privs a2 where a1.privilege = 'DROP TABLESPACE' and a1.grantee =a2.granted_role 2、删除临时表空间 --查看临时表空间文件 select name from v$tempfile; --查看用户和表空间的关系 select USERNAME,TEMPORARY_TABLESPACE from DBA_USERS; --如果有用户的默认临时表空间是NOTIFYDB_TEMP的话,建议进行更改 alter user xxx temporary tablespace tempdefault; ---设置tempdefault为默认临时表空间 alter database default temporary tablespace tempdefault; --删除表空间NOTIFYDB_TEMP及其包含数据对象以及数据文件 drop tablespace NOTIFYDB_TEMP including contents and datafiles; 3.删除用户表空间 --查看表空间文件 select name from v$datafile; --停止表空间的在线使用 alter tablespace 表空间名称 offline; --删除表空间NOTIFYDB_TEMP及其包含数据对象以及数据文件 drop tablespace NOTIFYDB_TEMP including contents and datafiles; Oracle用户权限查询相关操作: --查看所有的用户 select * from all_users; --查看当前用户信息 select * from user_users; --查看当前用户的角色 select * from user_role_privs; --查看当前用户的权限 select * from user_sys_privs; --查看当前用户的表可操作权限 select * from user_tab_privs; --查看某一个表的约束,注意表名要 大写 select * from user_constraints where table_name='TBL_XXX'; --查看某一个表的所有索引,注意表名要 大写 select index_name,index_type,status,blevel from user_indexes where table_name = 'TBL_XXX'; --查看索引的构成,注意表名要 大写 select table_name,index_name,column_name, column_position FROM user_ind_columns WHERE table_name='TBL_XXX'; --系统数据字典 DBA_TABLESPACES 中记录了关于表空间的详细信息 select * from sys.dba_tablespaces; --查看用户序列 select * from user_sequences; --查看数据库序列 select * from dba_sequences;
with cte as(select Id,Pid,DeptName,0 as lvl from Departmentwhere Id = 2union allselect d.Id,d.Pid,d.DeptName,lvl+1 from cte c inner join Department don c.Id = d.Pid)select * from cte 表结构: Id Pid DeptName----------- ----------- --------------------------------------------------1 0 总部2 1 研发部3 1 测试部4 1 质量部5 2 小组16 2 小组27 3 测试18 3 测试29 5 前端组10 5 美工 查询结果 查部门ID=2的所有下级部门和本级 Id Pid DeptName lvl----------- ----------- -------------------------------------------------- -----------2 1 研发部 05 2 小组1 16 2 小组2 19 5 前端组 210 5 美工 2 原理(摘自网上) 递归CTE最少包含两个查询(也被称为成员)。第一个查询为定点成员,定点成员只是一个返回有效表的查询,用于递归的基础或定位点。第二个查询被称为递归成员,使该查询称为递归成员的是对CTE名称的递归引用是触发。在逻辑上可以将CTE名称的内部应用理解为前一个查询的结果集。 递归查询没有显式的递归终止条件,只有当第二个递归查询返回空结果集或是超出了递归次数的最大限制时才停止递归。是指递归次数上限的方法是使用MAXRECURION。
实现linux定时任务有:cron、anacron、at等,这里主要介绍cron服务。 名词解释: cron是服务名称,crond是后台进程,crontab则是定制好的计划任务表。 软件包安装: 要使用cron服务,先要安装vixie-cron软件包和crontabs软件包,两个软件包作用如下: vixie-cron软件包是cron的主程序。crontabs软件包是用来安装、卸装、或列举用来驱动 cron 守护进程的表格的程序。查看是否安装了cron软件包: rpm -qa|grep vixie-cron 查看是否安装了crontabs软件包:rpm -qa|grep crontabs 如果没有安装,则执行如下命令安装软件包(软件包必须存在)rpm -ivh vixie-cron-4.1-54.FC5*rpm -ivh crontabs* 如果本地没有安装包,在能够连网的情况下可以在线安装 yum install vixie-cronyum install crontabs 查看crond服务是否运行: pgrep crond 或 /sbin/service crond status 或 ps -elf|grep crond|grep -v "grep" crond服务操作命令: /sbin/service crond start //启动服务 /sbin/service crond stop //关闭服务 /sbin/service crond restart //重启服务 /sbin/service crond reload //重新载入配置 cron是一个linux下的定时执行工具,可以在无需人工干预的情况下运行作业。由于Cron 是Linux的内置服务,但它不自动起来,可以用以下的方法启动、关闭这个服务: /sbin/service crond start //启动服务 /sbin/service crond stop //关闭服务 /sbin/service crond restart //重启服务 /sbin/service crond reload //重新载入配置 你也可以将这个服务在系统启动的时候自动启动: 在/etc/rc.d/rc.local这个脚本的末尾加上: /sbin/service crond start 现在Cron这个服务已经在进程里面了,我们就可以用这个服务了 ------------------------------------- 以Linux下定时备份mysql为例说明下 写一个简单的mysql备份shell脚本 vi #!/bin/shda=`date +%Y%m%d%H%M%S`mysqldump -u root -pdongjj --all-database>/root/mysqlbakup/$da 保存为 mysqlbak.sh 然后crontab-e 0 3 * * * /root/mysqlbak.sh 保存退出 相关命令---------------- crontab file [-u user]-用指定的文件替代目前的crontab。 crontab-[-u user]-用标准输入替代目前的crontab. crontab-1[user]-列出用户目前的crontab. crontab-e[user]-编辑用户目前的crontab. crontab-d[user]-删除用户目前的crontab. crontab-c dir- 指定crontab的目录。 crontab文件的格式:M H D m d cmd. M: 分钟(0-59)。 H:小时(0-23)。 D:天(1-31)。 m: 月(1-12)。 d: 一星期内的天(0~6,0 表示星期天) 除了数字还有几个个特殊的符号就是"*"、"/"和"-"、",",*代表所有的取值范围内的数字,"/"代表每的意思,"*/5"表示每5个单位,"-"代表从某个数字到某个数字,","分开几个离散的数字。 这里时间只是到分,有两种方案可以按秒来运行,如下: 第一种方案,当然是写一个后台运行的脚本一直循环,然后每次循环sleep一段时间。 while true ;docommandsleep XX //间隔秒数done 第二种方案,使用crontab。 我们都知道crontab的粒度最小是到分钟,但是我们还是可以通过变通的方法做到隔多少秒运行一次。 以下方法将每20秒执行一次 crontab -e * * * * * /bin/date* * * * * sleep 20; /bin/date * * * * * sleep 40; /bin/date 说明:需要将/bin/date更换成你的命令即可 这种做法去处理隔几十秒的定时任务还好,要是每1秒运行一次就得添加60条记录。。。如果每秒运行还是用方案一吧。 每次编辑完某个用户的cron设置后,cron自动在/var/spool/cron下生成一个与此用户同名的文件,此用户的cron信息都记录在这 个文件中,这个文件是不可以直接编辑的,只可以用crontab -e 来编辑。cron启动后每过一份钟读一次这个文件,检查是否要执行里面的命令。因此此文件修改后不需要重新启动cron服务。 查看crontab 执行的日志,可以在/var/log/cron* 查看,或者 0 3 * * * /root/mysqlbak.sh >/var/log/mysqlbak.log 2>&1 把日志定向出来查看。
Eclipse开发环境默认都是白底黑字的,看到同事的Xcode中设置的黑灰色背景挺好看的,就去网络上查了一下。发现Eclipse也可以设置主题。 http://eclipsecolorthemes.org/ 这个网站上提供了很多已经配置好的颜色主题,你喜欢哪个就下载下来。我下载的是.epf文件,下载下来后File->Import->General->Preferences->找到你的.epf文件->Finish。你会发现,Eclipse的编辑环境变成了你要的那种。但是问题也来了,你要如何恢复成原来的样子呢? 参考了下博文: http://my.oschina.net/ljhUncle/blog/42988 发现如果这样的话,太麻烦了,需要删除整个工程,不靠谱。 又参考了这个答案: http://stackoverflow.com/questions/186118/eclipse-fonts-and-background-color 发现有插件可以下载 You can install eclipse theme plugin then select default. Please visit here: http://eclipsecolorthemes.org/?view=plugin 进去以后可以看到这个界面 你可以从Eclipse Marketplace中下载,也可以 通过站点更新 我通过站点更新 Help->Install New Software->Work with:Update Site - http://eclipse-color-theme.github.com/update Next 安装的过程中发生了错误,不过貌似没事,顺利安装好了,有点奇怪。 安装后需要重启Eclipse。重启后打开Window->Preferences->General->Apperance->Color Theme(未安装插件前是没有这个选项的) 界面如下: 这里面有多个主题可以供你选择,也可以导入自己的主题文件,也可以恢复默认的样子,是不是很方便呢? 注意:如果Eclipse打不开,出现Fialed to create Java Virtural Machine的错误 参考:http://www.thecarneyeffect.co.uk/post-subcategory/configuration 解决方法是打开Eclipse目录下的eclipse.ini文件,将下面的两个-Xms删除,例如删除下面的两行。 -Xms384m -Xmx384m 保存后重新打开Eclipse即可。
为当前用户创建cron服务 1. 键入 crontab -e 编辑crontab服务文件 例如 文件内容如下: */2 * * * * /bin/sh /home/admin/jiaoben/buy/deleteFile.sh 保存文件并并退出 */2 * * * * /bin/sh /home/admin/jiaoben/buy/deleteFile.sh */2 * * * * 通过这段字段可以设定什么时候执行脚本 /bin/sh /home/admin/jiaoben/buy/deleteFile.sh 这一字段可以设定你要执行的脚本,这里要注意一下bin/sh 是指运行 脚本的命令 后面一段时指脚本存放的路径 2. 查看该用户下的crontab服务是否创建成功, 用 crontab -l 命令 3. 启动crontab服务 一般启动服务用 /sbin/service crond start 若是根用户的cron服务可以用 sudo service crond start, 这里还是要注意 下 不同版本linux系统启动的服务的命令也不同 ,像我的虚拟机里只需用 sudo service cron restart 即可,若是在根用下直接键入service cron start就能启动服务 4. 查看服务是否已经运行用 ps -ax | grep cron 5. crontab命令 cron服务提供crontab命令来设定cron服务的,以下是这个命令的一些参数与说明: crontab -u //设定某个用户的cron服务,一般root用户在执行这个命令的时候需要此参数 crontab -l //列出某个用户cron服务的详细内容 crontab -r //删除没个用户的cron服务 crontab -e //编辑某个用户的cron服务 比如说root查看自己的cron设置:crontab -u root -l 再例如,root想删除fred的cron设置:crontab -u fred -r 在编辑cron服务时,编辑的内容有一些格式和约定,输入:crontab -u root -e 进入vi编辑模式,编辑的内容一定要符合下面的格式:*/1 * * * * ls >> /tmp/ls.txt 任务调度的crond常驻命令 crond 是linux用来定期执行程序的命令。当安装完成操作系统之后,默认便会启动此 任务调度命令。crond命令每分锺会定期检查是否有要执行的工作,如果有要执行的工 作便会自动执行该工作。 6. crontab命令选项: -u指定一个用户 -l列出某个用户的任务计划 -r删除某个用户的任务 -e编辑某个用户的任务 7. cron文件语法: 分 小时 日 月 星期 命令 0-59 0-23 1-31 1-12 0-6 command (取值范围,0表示周日一般一行对应一个任务) 记住几个特殊符号的含义: “*”代表取值范围内的数字, “/”代表”每”, “-”代表从某个数字到某个数字, “,”分开几个离散的数字 8. 任务调度设置文件的写法 可用crontab -e命令来编辑,编辑的是/var/spool/cron下对应用户的cron文件,也可以直接修改/etc/crontab文件 具体格式如下: Minute Hour Day Month Dayofweek command 分钟 小时 天 月 天每星期 命令 每个字段代表的含义如下: Minute 每个小时的第几分钟执行该任务 Hour 每天的第几个小时执行该任务 Day 每月的第几天执行该任务 Month 每年的第几个月执行该任务 DayOfWeek 每周的第几天执行该任务 Command 指定要执行的程序 在这些字段里,除了“Command”是每次都必须指定的字段以外,其它字段皆为可选 字段,可视需要决定。对于不指定的字段,要用“*”来填补其位置。 举例如下: 5 * * * * ls 指定每小时的第5分钟执行一次ls命令 30 5 * * * ls 指定每天的 5:30 执行ls命令 30 7 8 * * ls 指定每月8号的7:30分执行ls命令 30 5 8 6 * ls 指定每年的6月8日5:30执行ls命令 30 6 * * 0 ls 指定每星期日的6:30执行ls命令[注:0表示星期天,1表示星期1, 以此类推,也可以用英文来表示,sun表示星期天,mon表示星期一等。] 30 3 10,20 * * ls 每月10号及20号的3:30执行ls命令[注:“,”用来连接多个不连续的时段] 25 8-11 * * * ls 每天8-11点的第25分钟执行ls命令[注:“-”用来连接连续的时段] */15 * * * * ls 每15分钟执行一次ls命令 [即每个小时的第0 15 30 45 60分钟执行ls命令 ] 30 6 */10 * * ls 每个月中,每隔10天6:30执行一次ls命令[即每月的1、11、21、31日是的6:30执行一次ls 命令。 ] 每天7:50以root 身份执行/etc/cron.daily目录中的所有可执行文件 50 7 * * * root run-parts /etc/cron.daily [ 注:run-parts参数表示,执行后面目录中的所有可执行文件。 ] 9. 新增调度任务 新增调度任务可用两种方法: 1)、在命令行输入: crontab -e 然后添加相应的任务,wq存盘退出。 2)、直接编辑/etc/crontab 文件,即vi /etc/crontab,添加相应的任务。 10. 查看调度任务 crontab -l //列出当前的所有调度任务 crontab -l -u jp //列出用户jp的所有调度任务 11. 删除任务调度工作 crontab -r //删除所有任务调度工作 12. 任务调度执行结果的转向 例1:每天5:30执行ls命令,并把结果输出到/jp/test文件中 30 5 * * * ls >/jp/test 2>&1 注:2>&1 表示执行结果及错误信息。 编辑/etc/crontab 文件配置cron cron服务每分钟不仅要读一次/var/spool/cron内的所有文件,还需要读一次 /etc/crontab,因此我们配置这个文件也能运用cron服务做一些事情。用crontab配置是针对某个用户的,而编辑/etc/crontab是针对系统的任务。此文件的文件格式是: SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root //如果出现错误,或者有数据输出,数据作为邮件发给这个帐号 HOME=/ //使用者运行的路径,这里是根目录 # run-parts 01 * * * * root run-parts /etc/cron.hourly //每小时执行 /etc/cron.hourly内的脚本 02 4 * * * root run-parts /etc/cron.daily //每天执行/etc/cron.daily内的脚本 22 4 * * 0 root run-parts /etc/cron.weekly //每星期执行 /etc/cron.weekly内的脚本 42 4 1 * * root run-parts /etc/cron.monthly //每月去执行/etc/cron.monthly内的脚本 大家注意”run-parts”这个参数了,如果去掉这个参数的话,后面就可以写要运行的某个脚本名,而不是文件夹名了 例如: 1) 在命令行输入: crontab -e 然后添加相应的任务,wq存盘退出。 2)直接编辑/etc/crontab 文件,即vi /etc/crontab,添加相应的任务 11 2 21 10 * rm -rf /mnt/fb
今天码代码的时候遇到了这个问题,因为oracle用的比较少,所在查询了一下。 顿时傻眼,有很多的贴子说是因为nls_date_language的问题,还要改会话级的NLS_DATE_LANGUAGE设置为简体中文,还有些别的,等等。我当时就无语了,我觉得大部分楼主都是在自己玩玩oracle的吧,虽然也算是因素,但如果是在正经项目中,谁会让你去改这种东西?! 后来发现了正确的做法,如下: to_date('2014-06-24 00:00:00','yyyy-mm-dd hh24:mi:ss')