程序员珍藏的东西会是什么?呵呵,除了平时写的代码,就是那些百看不厌的电子书了。
昨天很郁闷,我用了5年的移动硬盘,莫名奇妙的坏掉了。里面40G的资料全部报销了。
为了不再重蹈覆辙,我决定把重要的电子书都放到网络硬盘上去备份。索性将这些资料的下载方式公布出来,与大家分享,一定会有你想要的!
下载的兄弟注意了,点击下载后,可以在url中看到后缀名:),如果把后缀名改错了就看不了了,所有的资料都有人下载过了,应该都能看。
Python相关的资料还可以看:http://jythoner.javaeye.com/blog/569987
新书区
Python源码剖析: 下載文件 Python源码剖析.chm (670.21 KB)
Python黑客: 下載文件 Gray Hat Python Python Programming for Hackers and Reverse Engineers.pdf (3.30 MB)
上一本书的源码: 下載文件 ghpython_src.zip (606.56 KB)
php5手册: 下載文件 php5手册.chm (5.70 MB)
Google Adsense的秘密: 下載文件 《Google Adsense的秘密》中文版.pdf (905.51 KB)
《Google_Cash》快速致富手册,主要讲如何利用Google Adwords网赚: 下載文件 《Google_Cash》快速致富手册.pdf (2.23 MB)
Google cash补充,Day job killer中文版: 下載文件 跟打工生涯说再见.pdf (749.96 KB)
Java
Java API 1.6中文版: 下載文件 JDK API 1.6.0 zh_CN.CHM (35.12 MB)
下載文件 《java编程思想》第三版 第四版包括习题答案(8.92 MB)
下載文件 Java Collections.pdf (1.33 MB)
下載文件 Java_Collections-src.zip (150.21 KB)
下載文件 Java IO.chm (2.76 MB)
下載文件 Java NIO.pdf (1.40 MB)
下載文件 IO.gif (36.00 KB)
下載文件 JDBC Recipes A Problem Solution Approach.pdf (5.23 MB)
下載文件 Java Network Programming.chm (1.97 MB)
下載文件 Servlets and JavaServer Pages The J2EE Tier.pdf (4.73 MB)
英文版: 下載文件 Java.Threads.3rd.Edition.chm (685.45 KB)
中文版: 下載文件 java线程.pdf (20.26 MB)
Java与模式绝对高清版: 下載文件 Java与模式.pdf (39.64 MB)
为什么要看上面的那些书?请看我以前转 的 帖子:Java 推荐读物与源代码阅读: http://jythoner.javaeye.com/blog/311434
Java程序员必备的书籍,比API更有用: 下載文件 The Java Developers Almanac 1.4.chm (679.90 KB)
找工作必备: 下載文件 125条常见的java面试笔试题大汇总.pdf (95.17 KB
一本Java实现的数据结构书籍: 下載文件 Java数据结构与算法.pdf (25.35 MB)
学Struts,Hibernate,Spring,如果不懂反射的原理,就不用谈了: 下載文件 Java Reflection In Action.pdf (1.70 MB)
JMX,Jboss的基础,jdk5后被纳入进来: 下載文件 JMX In Action.pdf (4.76 MB)
Junit绝对经典书籍: 下載文件 Junit in action.pdf (15.64 MB)
一本专门讲jdk1.5新特性的书,英文版: 下載文件 Java 1.5 Tiger A Developer's Notebook.chm(411.57 KB)
中文版: 下載文件 Java 1.5 Tiger A Developer's Notebook.pdf (7.23 MB)
源代码: 下載文件 Java 1.5 Tiger A Developer's Notebook.zip (38.18 KB)
下載文件 Java极限编程.pdf (23.96 MB)
下載文件 uml distilled.chm (1.52 MB)
下載文件 Expert One-on-One J2EE Design and Development.chm (10.66 MB)
下載文件 测试驱动开发.pdf (6.89 MB)
如果不知道上面4本书的作者,那么基本上你的Java算白学了
Ant手册: 下載文件 Apache Ant Manual 1.7.chm (949.48 KB)
英文版: 下載文件 UML for Java Programmers.pdf (1.77 MB)
中文版: 下載文件 UML for Java Programmers中文版.pdf (2.51 MB)
源代码: 下載文件 UML for Java Programmers.rar (465.36 KB)
下載文件 Java实用系统开发指南.pdf (57.82 MB)
下載文件 JAVA优化编程.pdf (13.58 MB)
下載文件 Java and XML(英文第三版).chm (3.79 MB)
下載文件 Apress.Pro.XML.Development.with.Java.Technology.pdf (12.83 MB)
Eclipse插件开发
一个高手推荐的4本必读书:
中文版: 下載文件 Contributing to Eclipse Principles, Patterns and PlugIns.pdf (23.29 MB)
英文版: 下載文件 Contributing to Eclipse Principles, Patt ... (4.93 MB)
下載文件 Eclipse Building Commercial-Quality Plug-ins.chm (20.11 MB)
下載文件 Eclipse Modeling Framework.chm (3.20 MB)
下載文件 Eclipse Rich Client Platform Designing Coding and Packaging Java Applications.chm(4.90 MB)
下載文件 SWT-A Developer's Note Book.chm (1.53 MB)
API: 下載文件 swt jface API.pdf (7.59 MB)
Linux
中国Linuxer应该没人不知道鸟哥,这本书是基础版与网络版的合集: 下載文件 鸟哥的Linux私房菜.pdf (36.43 MB)
命令大全,支持查找: 下載文件 Linux命令大全(修改版).chm (345.84 KB)
无意间发现的,里面记载了很多实用的命令: 下載文件 Unix Toolbox.pdf (350.31 KB)
学习shell编程必读,里面包括一份详细的学习笔记: 下載文件 LINUX与UNIX SHELL编程指南.zip (19.22 MB)
Sed和Awk唯一的书: 下載文件 Sed and Awk.pdf 中文版(6.89 MB)
下載文件 AWK单行脚本快速参考.doc (52.50 KB)
下載文件 SED单行脚本快速参考.doc (91.00 KB)
一张RE图表: 下載文件 regular-expressions-cheat-sheet-v2.pdf (647.55 KB)
Linux+认证书籍: 下載文件 McGraw.Hill.Linux.plus.Certification.Study Guide.pdf (28.60 MB)
下載文件 O'Reilly.Bash.Cookbook.pdf (3.16 MB)
下載文件 A Practical Guide to Linux Commands, Edi ... (3.61 MB)
Oracle
Oracle9i OCP官方培训教程:
下載文件 sql.zip (3.34 MB)
下載文件 dba1.zip (1.78 MB)
下載文件 dba2.zip (2.95 MB)
下載文件 Perf.zip (6.90 MB)
Oracle 10教程:
下載文件 Oracle.Database.10g.Administration.Workshop.II.Student.Guide.pdf (4.74 MB)
下載文件 Oracle.Database.10g.Administration.Workshop.I.Student.Guide.pdf (5.66 MB)
下載文件 Sybex.OCA.Oracle.10g.Administration.I.Study.Guide.pdf (18.20 MB)
下載文件 Sybex.OCP.Oracle.10g.Administration.II.Study.Guide.pdf (15.39 MB)
Oracle 11g官方培训教程:
下載文件 d49996gc10_ppt Oracle Database 11g SQL Fundamentals I.rar (2.26 MB)
下載文件 d49994gc10_ppt Oracle Database 11g SQL Fundamentals II.rar (1.78 MB)
下載文件 d50102gc10_ppt Oracle Database 11g Administration Workshop I.rar (11.00 MB)
下載文件 d50079gc10_ppt Oracle Database 11g Administration Workshop II.rar (6.31 MB)
下載文件 d50317gc10_ppt Oracle Database 11g Performance Tuning.rar (3.63 MB)
英文版: 下載文件 d50081gc10_ppt Oracle Database 11g New Features for Administrators.rar(6.64 MB)
中文版: 下載文件 d50081cn11_ppt Oracle Database 11g - New Features for Administrators.rar (6.58 MB)
下載文件 d46592gc11_ppt Oracle Database 10g Managing Oracle on Linux for System Administrators.rar (1.25 MB)
下載文件 d50311gc10_ppt Oracle Database 11g RAC Administration.rar (7.80 MB)
下載文件 Oracle Database 10g.chm (2.43 MB)
下載文件 ORACLE.10G入门与实践.pdf (39.58 MB)
下載文件 Oracle.Database.10g实用培训教程.pdf (47.49 MB)
下載文件 精通ORACLE.10G备份与恢复.pdf (23.90 MB)
Python
BASIC:
A Byte of Python(Python 3.0) 下載文件 A Byte of Python.pdf (564.61 KB)
A Byte of Python中文版 下載文件 Python简明教程.pdf (784.85 KB)
下載文件 Dive into Python.zip (763.71 KB)
下載文件 Dive into Python 中文版.zip (3.51 MB)
下載文件 Python Essential Reference 4th Edition.pdf (4.80 MB)
下載文件 Python精要参考.pdf (678.65 KB)
下載文件 Learning Python.pdf (4.80 MB)
下載文件 Core Python Programming 2nd Edition.chm (3.45 MB)
高清完整版 下載文件 Python核心编程第二版.pdf (5.16 MB)
下載文件 Python Standard Library.chm (355.63 KB)
下載文件 Python Standard Library中文版.pdf (1.00 MB)
下載文件 Python Cookbook.chm (1.00 MB)
下載文件 Python Cookbook Collection.chm (2.53 MB)
Guido 2007年Python大会ppt,关于python3.0新特性 下載文件 Py3k2007PyCon.ppt (134.00 KB)
GUI:
下載文件 wxPython In Action.rar (9.24 MB)
wxPython in Action中文版 下載文件 wxPython实战(中文版).pdf (3.54 MB)
下載文件 wxPythonInAction_src.zip (333.41 KB)
WEB:
Django Book 收费版 下載文件 The Definitive Guide to Django 2nd Edition.pdf (5.92 MB)
下載文件 Practical Django Projects 2nd Edition.pdf (4.89 MB)
GAE:
下載文件 Google App Engine 入门.pdf (227.23 KB)
下載文件 Google App Engine 开发人员指南.pdf (855.70 KB)
下載文件 Using Google App Engine.pdf (3.20 MB)
下載文件 Developing With Google App Engine.pdf (3.35 MB)
Other:
下載文件 Twisted Network Programming Essentials Python.chm (1.24 MB)
下載文件 Python for Unix and Linux System Administration.pdf (3.41 MB)
下載文件 Text Processing in Python.chm (871.68 KB)
下載文件 Python Programming on Win32.chm (2.10 MB)
下載文件 Jython for Java Programmers.chm (713.20 KB)
English
下載文件 古典 1677超核心词表.pdf (1.34 MB)
下載文件 古典 1677超核心词表.rm (3.41 MB)
下載文件 新东方李玉技老师的734条高频词组.pdf (77.00 KB)
下載文件 英语常用短语词典.chm (2.31 MB)
Perl
下載文件 Perl Cook Book.pdf (9.22 MB)
英文版: 下載文件 Learning.Perl.4ed.En.chm (699.96 KB)
中文版: 下載文件 Learning.Perl.4ed.Cn.pdf (1.19 MB)
一本讲用perl进行automation测试的好书: 下載文件 Perl Testing.chm (640.57 KB)
Other
下載文件 Agile Web Development with Rails 3rdEdition.pdf (10.87 MB)
谭浩强那本: 下載文件 C语言程序设计.pdf (9.95 MB)
下載文件 C语言趣味编程百例.pdf (4.72 MB)
下載文件 数据库系统概论(第三版).pdf (10.16 MB)
下載文件 php5手册.chm (5.70 MB)
精通正则表达式: 下載文件 Mastering Regular Expressions.chm (1.45 MB)
昨天很郁闷,我用了5年的移动硬盘,莫名奇妙的坏掉了。里面40G的资料全部报销了。
为了不再重蹈覆辙,我决定把重要的电子书都放到网络硬盘上去备份。索性将这些资料的下载方式公布出来,与大家分享,一定会有你想要的!
下载的兄弟注意了,点击下载后,可以在url中看到后缀名:),如果把后缀名改错了就看不了了,所有的资料都有人下载过了,应该都能看。
Python相关的资料还可以看:http://jythoner.javaeye.com/blog/569987
新书区
Python源码剖析: 下載文件 Python源码剖析.chm (670.21 KB)
Python黑客: 下載文件 Gray Hat Python Python Programming for Hackers and Reverse Engineers.pdf (3.30 MB)
上一本书的源码: 下載文件 ghpython_src.zip (606.56 KB)
php5手册: 下載文件 php5手册.chm (5.70 MB)
Google Adsense的秘密: 下載文件 《Google Adsense的秘密》中文版.pdf (905.51 KB)
《Google_Cash》快速致富手册,主要讲如何利用Google Adwords网赚: 下載文件 《Google_Cash》快速致富手册.pdf (2.23 MB)
Google cash补充,Day job killer中文版: 下載文件 跟打工生涯说再见.pdf (749.96 KB)
Java
Java API 1.6中文版: 下載文件 JDK API 1.6.0 zh_CN.CHM (35.12 MB)
下載文件 《java编程思想》第三版 第四版包括习题答案(8.92 MB)
下載文件 Java Collections.pdf (1.33 MB)
下載文件 Java_Collections-src.zip (150.21 KB)
下載文件 Java IO.chm (2.76 MB)
下載文件 Java NIO.pdf (1.40 MB)
下載文件 IO.gif (36.00 KB)
下載文件 JDBC Recipes A Problem Solution Approach.pdf (5.23 MB)
下載文件 Java Network Programming.chm (1.97 MB)
下載文件 Servlets and JavaServer Pages The J2EE Tier.pdf (4.73 MB)
英文版: 下載文件 Java.Threads.3rd.Edition.chm (685.45 KB)
中文版: 下載文件 java线程.pdf (20.26 MB)
Java与模式绝对高清版: 下載文件 Java与模式.pdf (39.64 MB)
为什么要看上面的那些书?请看我以前转 的 帖子:Java 推荐读物与源代码阅读: http://jythoner.javaeye.com/blog/311434
Java程序员必备的书籍,比API更有用: 下載文件 The Java Developers Almanac 1.4.chm (679.90 KB)
找工作必备: 下載文件 125条常见的java面试笔试题大汇总.pdf (95.17 KB
一本Java实现的数据结构书籍: 下載文件 Java数据结构与算法.pdf (25.35 MB)
学Struts,Hibernate,Spring,如果不懂反射的原理,就不用谈了: 下載文件 Java Reflection In Action.pdf (1.70 MB)
JMX,Jboss的基础,jdk5后被纳入进来: 下載文件 JMX In Action.pdf (4.76 MB)
Junit绝对经典书籍: 下載文件 Junit in action.pdf (15.64 MB)
一本专门讲jdk1.5新特性的书,英文版: 下載文件 Java 1.5 Tiger A Developer's Notebook.chm(411.57 KB)
中文版: 下載文件 Java 1.5 Tiger A Developer's Notebook.pdf (7.23 MB)
源代码: 下載文件 Java 1.5 Tiger A Developer's Notebook.zip (38.18 KB)
下載文件 Java极限编程.pdf (23.96 MB)
下載文件 uml distilled.chm (1.52 MB)
下載文件 Expert One-on-One J2EE Design and Development.chm (10.66 MB)
下載文件 测试驱动开发.pdf (6.89 MB)
如果不知道上面4本书的作者,那么基本上你的Java算白学了
Ant手册: 下載文件 Apache Ant Manual 1.7.chm (949.48 KB)
英文版: 下載文件 UML for Java Programmers.pdf (1.77 MB)
中文版: 下載文件 UML for Java Programmers中文版.pdf (2.51 MB)
源代码: 下載文件 UML for Java Programmers.rar (465.36 KB)
下載文件 Java实用系统开发指南.pdf (57.82 MB)
下載文件 JAVA优化编程.pdf (13.58 MB)
下載文件 Java and XML(英文第三版).chm (3.79 MB)
下載文件 Apress.Pro.XML.Development.with.Java.Technology.pdf (12.83 MB)
Eclipse插件开发
一个高手推荐的4本必读书:
中文版: 下載文件 Contributing to Eclipse Principles, Patterns and PlugIns.pdf (23.29 MB)
英文版: 下載文件 Contributing to Eclipse Principles, Patt ... (4.93 MB)
下載文件 Eclipse Building Commercial-Quality Plug-ins.chm (20.11 MB)
下載文件 Eclipse Modeling Framework.chm (3.20 MB)
下載文件 Eclipse Rich Client Platform Designing Coding and Packaging Java Applications.chm(4.90 MB)
下載文件 SWT-A Developer's Note Book.chm (1.53 MB)
API: 下載文件 swt jface API.pdf (7.59 MB)
Linux
中国Linuxer应该没人不知道鸟哥,这本书是基础版与网络版的合集: 下載文件 鸟哥的Linux私房菜.pdf (36.43 MB)
命令大全,支持查找: 下載文件 Linux命令大全(修改版).chm (345.84 KB)
无意间发现的,里面记载了很多实用的命令: 下載文件 Unix Toolbox.pdf (350.31 KB)
学习shell编程必读,里面包括一份详细的学习笔记: 下載文件 LINUX与UNIX SHELL编程指南.zip (19.22 MB)
Sed和Awk唯一的书: 下載文件 Sed and Awk.pdf 中文版(6.89 MB)
下載文件 AWK单行脚本快速参考.doc (52.50 KB)
下載文件 SED单行脚本快速参考.doc (91.00 KB)
一张RE图表: 下載文件 regular-expressions-cheat-sheet-v2.pdf (647.55 KB)
Linux+认证书籍: 下載文件 McGraw.Hill.Linux.plus.Certification.Study Guide.pdf (28.60 MB)
下載文件 O'Reilly.Bash.Cookbook.pdf (3.16 MB)
下載文件 A Practical Guide to Linux Commands, Edi ... (3.61 MB)
Oracle
Oracle9i OCP官方培训教程:
下載文件 sql.zip (3.34 MB)
下載文件 dba1.zip (1.78 MB)
下載文件 dba2.zip (2.95 MB)
下載文件 Perf.zip (6.90 MB)
Oracle 10教程:
下載文件 Oracle.Database.10g.Administration.Workshop.II.Student.Guide.pdf (4.74 MB)
下載文件 Oracle.Database.10g.Administration.Workshop.I.Student.Guide.pdf (5.66 MB)
下載文件 Sybex.OCA.Oracle.10g.Administration.I.Study.Guide.pdf (18.20 MB)
下載文件 Sybex.OCP.Oracle.10g.Administration.II.Study.Guide.pdf (15.39 MB)
Oracle 11g官方培训教程:
下載文件 d49996gc10_ppt Oracle Database 11g SQL Fundamentals I.rar (2.26 MB)
下載文件 d49994gc10_ppt Oracle Database 11g SQL Fundamentals II.rar (1.78 MB)
下載文件 d50102gc10_ppt Oracle Database 11g Administration Workshop I.rar (11.00 MB)
下載文件 d50079gc10_ppt Oracle Database 11g Administration Workshop II.rar (6.31 MB)
下載文件 d50317gc10_ppt Oracle Database 11g Performance Tuning.rar (3.63 MB)
英文版: 下載文件 d50081gc10_ppt Oracle Database 11g New Features for Administrators.rar(6.64 MB)
中文版: 下載文件 d50081cn11_ppt Oracle Database 11g - New Features for Administrators.rar (6.58 MB)
下載文件 d46592gc11_ppt Oracle Database 10g Managing Oracle on Linux for System Administrators.rar (1.25 MB)
下載文件 d50311gc10_ppt Oracle Database 11g RAC Administration.rar (7.80 MB)
下載文件 Oracle Database 10g.chm (2.43 MB)
下載文件 ORACLE.10G入门与实践.pdf (39.58 MB)
下載文件 Oracle.Database.10g实用培训教程.pdf (47.49 MB)
下載文件 精通ORACLE.10G备份与恢复.pdf (23.90 MB)
Python
BASIC:
A Byte of Python(Python 3.0) 下載文件 A Byte of Python.pdf (564.61 KB)
A Byte of Python中文版 下載文件 Python简明教程.pdf (784.85 KB)
下載文件 Dive into Python.zip (763.71 KB)
下載文件 Dive into Python 中文版.zip (3.51 MB)
下載文件 Python Essential Reference 4th Edition.pdf (4.80 MB)
下載文件 Python精要参考.pdf (678.65 KB)
下載文件 Learning Python.pdf (4.80 MB)
下載文件 Core Python Programming 2nd Edition.chm (3.45 MB)
高清完整版 下載文件 Python核心编程第二版.pdf (5.16 MB)
下載文件 Python Standard Library.chm (355.63 KB)
下載文件 Python Standard Library中文版.pdf (1.00 MB)
下載文件 Python Cookbook.chm (1.00 MB)
下載文件 Python Cookbook Collection.chm (2.53 MB)
Guido 2007年Python大会ppt,关于python3.0新特性 下載文件 Py3k2007PyCon.ppt (134.00 KB)
GUI:
下載文件 wxPython In Action.rar (9.24 MB)
wxPython in Action中文版 下載文件 wxPython实战(中文版).pdf (3.54 MB)
下載文件 wxPythonInAction_src.zip (333.41 KB)
WEB:
Django Book 收费版 下載文件 The Definitive Guide to Django 2nd Edition.pdf (5.92 MB)
下載文件 Practical Django Projects 2nd Edition.pdf (4.89 MB)
GAE:
下載文件 Google App Engine 入门.pdf (227.23 KB)
下載文件 Google App Engine 开发人员指南.pdf (855.70 KB)
下載文件 Using Google App Engine.pdf (3.20 MB)
下載文件 Developing With Google App Engine.pdf (3.35 MB)
Other:
下載文件 Twisted Network Programming Essentials Python.chm (1.24 MB)
下載文件 Python for Unix and Linux System Administration.pdf (3.41 MB)
下載文件 Text Processing in Python.chm (871.68 KB)
下載文件 Python Programming on Win32.chm (2.10 MB)
下載文件 Jython for Java Programmers.chm (713.20 KB)
English
下載文件 古典 1677超核心词表.pdf (1.34 MB)
下載文件 古典 1677超核心词表.rm (3.41 MB)
下載文件 新东方李玉技老师的734条高频词组.pdf (77.00 KB)
下載文件 英语常用短语词典.chm (2.31 MB)
Perl
下載文件 Perl Cook Book.pdf (9.22 MB)
英文版: 下載文件 Learning.Perl.4ed.En.chm (699.96 KB)
中文版: 下載文件 Learning.Perl.4ed.Cn.pdf (1.19 MB)
一本讲用perl进行automation测试的好书: 下載文件 Perl Testing.chm (640.57 KB)
Other
下載文件 Agile Web Development with Rails 3rdEdition.pdf (10.87 MB)
谭浩强那本: 下載文件 C语言程序设计.pdf (9.95 MB)
下載文件 C语言趣味编程百例.pdf (4.72 MB)
下載文件 数据库系统概论(第三版).pdf (10.16 MB)
下載文件 php5手册.chm (5.70 MB)
精通正则表达式: 下載文件 Mastering Regular Expressions.chm (1.45 MB)
程序员珍藏的东西会是什么?呵呵,除了平时写的代码,就是那些百看不厌的电子书了。
昨天很郁闷,我用了5年的移动硬盘,莫名奇妙的坏掉了。里面40G的资料全部报销了。
为了不再重蹈覆辙,我决定把重要的电子书都放到网络硬盘上去备份。索性将这些资料的下载方式公布出来,与大家分享,一定会有你想要的!
Java Linux Oracle Perl相关的资料请看:http://jythoner.javaeye.com/blog/570792
BASIC:
A Byte of Python(Python 3.0) 下載文件 A Byte of Python.pdf (564.61 KB)
A Byte of Python中文版 下載文件 Python简明教程.pdf (784.85 KB)
下載文件 Dive into Python.zip (763.71 KB)
下載文件 Dive into Python 中文版.zip (3.51 MB)
下載文件 Python Essential Reference 4th Edition.pdf (4.80 MB)
下載文件 Python精要参考.pdf (678.65 KB)
下載文件 Learning Python.pdf (4.80 MB)
下載文件 Core Python Programming 2nd Edition.chm (3.45 MB)
高清完整版 下載文件 Python核心编程第二版.pdf (5.16 MB)
下載文件 Python Standard Library.chm (355.63 KB)
下載文件 Python Standard Library中文版.pdf (1.00 MB)
下載文件 Python Cookbook.chm (1.00 MB)
下載文件 Python Cookbook Collection.chm (2.53 MB)
Guido 2007年Python大会ppt,关于python3.0新特性 下載文件 Py3k2007PyCon.ppt (134.00 KB)
GUI:
下載文件 wxPython In Action.rar (9.24 MB)
wxPython in Action中文版 下載文件 wxPython实战(中文版).pdf (3.54 MB)
下載文件 wxPythonInAction_src.zip (333.41 KB)
WEB:
Django Book 收费版 下載文件 The Definitive Guide to Django 2nd Edition.pdf (5.92 MB)
下載文件 Practical Django Projects 2nd Edition.pdf (4.89 MB)
GAE:
下載文件 Google App Engine 入门.pdf (227.23 KB)
下載文件 Google App Engine 开发人员指南.pdf (855.70 KB)
下載文件 Using Google App Engine.pdf (3.20 MB)
下載文件 Developing With Google App Engine.pdf (3.35 MB)
Other:
下載文件 Twisted Network Programming Essentials Python.chm (1.24 MB)
下載文件 Python for Unix and Linux System Administration.pdf (3.41 MB)
下載文件 Text Processing in Python.chm (871.68 KB)
下載文件 Python Programming on Win32.chm (2.10 MB)
下載文件 Jython for Java Programmers.chm (713.20 KB)
昨天很郁闷,我用了5年的移动硬盘,莫名奇妙的坏掉了。里面40G的资料全部报销了。
为了不再重蹈覆辙,我决定把重要的电子书都放到网络硬盘上去备份。索性将这些资料的下载方式公布出来,与大家分享,一定会有你想要的!
Java Linux Oracle Perl相关的资料请看:http://jythoner.javaeye.com/blog/570792
BASIC:
A Byte of Python(Python 3.0) 下載文件 A Byte of Python.pdf (564.61 KB)
A Byte of Python中文版 下載文件 Python简明教程.pdf (784.85 KB)
下載文件 Dive into Python.zip (763.71 KB)
下載文件 Dive into Python 中文版.zip (3.51 MB)
下載文件 Python Essential Reference 4th Edition.pdf (4.80 MB)
下載文件 Python精要参考.pdf (678.65 KB)
下載文件 Learning Python.pdf (4.80 MB)
下載文件 Core Python Programming 2nd Edition.chm (3.45 MB)
高清完整版 下載文件 Python核心编程第二版.pdf (5.16 MB)
下載文件 Python Standard Library.chm (355.63 KB)
下載文件 Python Standard Library中文版.pdf (1.00 MB)
下載文件 Python Cookbook.chm (1.00 MB)
下載文件 Python Cookbook Collection.chm (2.53 MB)
Guido 2007年Python大会ppt,关于python3.0新特性 下載文件 Py3k2007PyCon.ppt (134.00 KB)
GUI:
下載文件 wxPython In Action.rar (9.24 MB)
wxPython in Action中文版 下載文件 wxPython实战(中文版).pdf (3.54 MB)
下載文件 wxPythonInAction_src.zip (333.41 KB)
WEB:
Django Book 收费版 下載文件 The Definitive Guide to Django 2nd Edition.pdf (5.92 MB)
下載文件 Practical Django Projects 2nd Edition.pdf (4.89 MB)
GAE:
下載文件 Google App Engine 入门.pdf (227.23 KB)
下載文件 Google App Engine 开发人员指南.pdf (855.70 KB)
下載文件 Using Google App Engine.pdf (3.20 MB)
下載文件 Developing With Google App Engine.pdf (3.35 MB)
Other:
下載文件 Twisted Network Programming Essentials Python.chm (1.24 MB)
下載文件 Python for Unix and Linux System Administration.pdf (3.41 MB)
下載文件 Text Processing in Python.chm (871.68 KB)
下載文件 Python Programming on Win32.chm (2.10 MB)
下載文件 Jython for Java Programmers.chm (713.20 KB)
1、HTML静态化其实大家都知道,效率最高、消耗最小的就是纯静态化的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统CMS,像我们常访问的各个门户站点的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,像Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现,比如论坛中论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这部分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求。
2、图片服务器分离大家知道,对于Web服务器来说,不管是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他们都有独立的图片服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃,在应用服务器和图片服务器上,可以进行不同的配置优化,比如apache在配置ContentType的时候可以尽量少支持,尽可能少的LoadModule,保证更高的系统消耗和执行效率。
3、数据库集群和库表散列大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是类似的方案,您使用了什么样的DB,就参考相应的解决方案来实施即可。上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用DB类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并且最有效的解决方案。我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。sohu的论坛就是采用了这样的架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系统随时增加一台低成本的数据库进来补充系统性能。
4、缓存缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。架构方面的缓存,对Apache比较熟悉的人都能知道Apache提供了自己的缓存模块,也可以使用外加的Squid模块进行缓存,这两种方式均可以有效的提高Apache的访问响应能力。网站程序开发方面的缓存,Linux上提供的Memory Cache是常用的缓存接口,可以在web开发中使用,比如用Java开发的时候就可以调用MemoryCache对一些数据进行缓存和通讯共享,一些大型社区使用了这样的架构。另外,在使用web语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP有Pear的Cache模块,Java就更多了,.net不是很熟悉,相信也肯定有。
5、镜像镜像是大型网站常采用的提高性能和数据安全性的方式,镜像的技术可以解决不同网络接入商和地域带来的用户访问速度差异,比如 ChinaNet和EduNet之间的差异就促使了很多网站在教育网内搭建镜像站点,数据进行定时更新或者实时更新。在镜像的细节技术方面,这里不阐述太深,有很多专业的现成的解决架构和产品可选。也有廉价的通过软件实现的思路,比如Linux上的rsync等工具。
6、负载均衡负载均衡将是大型网站解决高负荷访问和大量并发请求采用的终极解决办法。负载均衡技术发展了多年,有很多专业的服务提供商和产品可以选择,我个人接触过一些解决方法,其中有两个架构可以给大家做参考。
7、硬件四层交换第四层交换使用第三层和第四层信息包的报头信息,根据应用区间识别业务流,将整个区间段的业务流分配到合适的应用服务器进行处理。 第四层交换功能就象是虚 IP,指向物理服务器。它传输的业务服从的协议多种多样,有HTTP、FTP、NFS、Telnet或其他协议。这些业务在物理服务器基础上,需要复杂的载量平衡算法。在IP世界,业务类型由终端TCP或UDP端口地址来决定,在第四层交换中的应用区间则由源端和终端IP地址、TCP和UDP端口共同决定。在硬件四层交换产品领域,有一些知名的产品可以选择,比如Alteon、F5等,这些产品很昂贵,但是物有所值,能够提供非常优秀的性能和很灵活的管理能力。Yahoo中国当初接近2000台服务器使用了三四台Alteon就搞定了
。8、软件四层交换大家知道了硬件四层交换机的原理后,基于OSI模型来实现的软件四层交换也就应运而生,这样的解决方案实现的原理一致,不过性能稍差。但是满足一定量的压力还是游刃有余的,有人说软件实现方式其实更灵活,处理能力完全看你配置的熟悉能力。软件四层交换我们可以使用Linux上常用的LVS来解决,LVS就是Linux Virtual Server,他提供了基于心跳线heartbeat的实时灾难应对解决方案,提高系统的鲁棒性,同时可供了灵活的虚拟VIP配置和管理功能,可以同时满足多种应用需求,这对于分布式的系统来说必不可少。一个典型的使用负载均衡的策略就是,在软件或者硬件四层交换的基础上搭建squid集群,这种思路在很多大型网站包括搜索引擎上被采用,这样的架构低成本、高性能还有很强的扩张性,随时往架构里面增减节点都非常容易。这样的架构我准备空了专门详细整理一下和大家探讨。对于大型网站来说,前面提到的每个方法可能都会被同时使用到,我这里介绍得比较浅显,具体实现过程中很多细节还需要大家慢慢熟悉和体会,有时一个很小的squid参数或者apache参数设置,对于系统性能的影响就会很大,希望大家一起讨论,达到抛砖引玉之效。
用squid做web cache server,而apache在squid的后面提供真正的web服务。当然使用这样的架构必须要保证主页上大部分都是静态页面。这就需要程序员的配合将页面在反馈给客户端之前将页面全部转换成静态页面。
基本看出sina和sohu对于频道等栏目都用了相同的技术,即squid来监听这些IP的80端口,而真正的web server来监听另外一个端口。从用户的感觉上来说不会有任何的区别,而相对于将web server直接和客户端连在一起的方式,这样的方式明显的节省的带宽和服务器。用户访问的速度感觉也会更快。
http://www.dbanotes.net/arch/yupoo_arch.html
带宽:4000M/S (参考)
服务器数量:60 台左右
Web服务器:Lighttpd, Apache, nginx
应用服务器:Tomcat
其他:Python, Java, MogileFS 、ImageMagick 等
关于 Squid 与 Tomcat
Squid 与 Tomcat 似乎在 Web 2.0 站点的架构中较少看到。我首先是对 Squid 有点疑问,对此阿华的解释是"目前暂时还没找到效率比 Squid 高的缓存系统,原来命中率的确很差,后来在 Squid 前又装了层 Lighttpd, 基于 url 做 hash, 同一个图片始终会到同一台 squid 去,所以命中率彻底提高了"
对于应用服务器层的 Tomcat,现在 Yupoo! 技术人员也在逐渐用其他轻量级的东西替代,而 YPWS/YPFS 现在已经用 Python 进行开发了。
名次解释:
· YPWS--Yupoo Web Server YPWS 是用 Python开发的一个小型 Web 服务器,提供基本的 Web 服务外,可以增加针对用户、图片、外链网站显示的逻辑判断,可以安装于任何有空闲资源的服务器中,遇到性能瓶颈时方便横向扩展。
· YPFS--Yupoo File System 与 YPWS 类似,YPFS 也是基于这个 Web 服务器上开发的图片上传服务器。
【Updated: 有网友留言质疑 Python 的效率,Yupoo 老大刘平阳在 del.icio.us 上写到 "YPWS用Python自己写的,每台机器每秒可以处理294个请求, 现在压力几乎都在10%以下"】
图片处理层
接下来的 Image Process Server 负责处理用户上传的图片。使用的软件包也是 ImageMagick,在上次存储升级的同时,对于锐化的比率也调整过了(我个人感觉,效果的确好了很多)。”Magickd“ 是图像处理的一个远程接口服务,可以安装在任何有空闲 CPU资源的机器上,类似 Memcached的服务方式。
我们知道 Flickr 的缩略图功能原来是用 ImageMagick 软件包的,后来被雅虎收购后出于版权原因而不用了(?);EXIF 与 IPTC Flicke 是用 Perl 抽取的,我是非常建议 Yupoo! 针对 EXIF 做些文章,这也是潜在产生受益的一个重点。
图片存储层
原来 Yupoo! 的存储采用了磁盘阵列柜,基于 NFS 方式的,随着数据量的增大,”Yupoo! 开发部从07年6月份就开始着手研究一套大容量的、能满足 Yupoo! 今后发展需要的、安全可靠的存储系统“,看来 Yupoo! 系统比较有信心,也是满怀期待的,毕竟这要支撑以 TB 计算的海量图片的存储和管理。我们知道,一张图片除了原图外,还有不同尺寸的,这些图片统一存储在 MogileFS 中。
对于其他部分,常见的 Web 2.0 网站必须软件都能看到,如 MySQL、Memcached 、Lighttpd 等。Yupoo! 一方面采用不少相对比较成熟的开源软件,一方面也在自行开发定制适合自己的架构组件。这也是一个 Web 2.0 公司所必需要走的一个途径。
非常感谢一下 Yupoo! 阿华对于技术信息的分享,技术是共通的。下一个能爆料是哪家?
--EOF--
lighttpd+squid这套缓存是放在另外一个机房作为cdn的一个节点使用的,图中没描绘清楚,给大家带来不便了。
squid前端用lighttpd没用nginx,主要是用了这么久,没出啥大问题,所以就没想其他的了。
URL Hash的扩展性的确不好,能做的就是不轻易去增减服务器,我们目前是5台服务器做一组hash.
我们现在用Python写的Web Server,在效率方面,我可以给个测试数据,根据目前的访问日志模拟访问测试的结果是1台ypws,平均每秒处理294个请求(加载所有的逻辑判断)。
在可靠性上,还不没具体的数据,目前运行1个多月还没有任何异常。
lvs每个节点上都装nginx,主要是为了反向代理及处理静态内容,不过apache已显得不是那么必需,准备逐渐去掉。
我们处理图片都是即时的,我们目前半数以上的服务器都装了magickd服务,用来分担图片处理请求。
http://www.dbanotes.net/review/tailrank_arch.html
每天数以千万计的 Blog 内容中,实时的热点是什么? Tailrank 这个 Web 2.0 Startup 致力于回答这个问题。
专门爆料网站架构的 Todd Hoff 对 Kevin Burton 进行了采访。于是我们能了解一下 Tailrank 架构的一些信息。每小时索引 2400 万的 Blog 与 Feed,内容处理能力为 160-200Mbps,IO 写入大约在10-15MBps。每个月要处理 52T 之多的原始数据。Tailrank 所用的爬虫现在已经成为一个独立产品:spinn3r。
服务器硬件
目前大约 15 台服务器,CPU 是 64 位的 Opteron。每台主机上挂两个 SATA 盘,做 RAID 0。据我所知,国内很多 Web 2.0 公司也用的是类似的方式,SATA 盘容量达,低廉价格,堪称不二之选。操作系统用的是 Debian Linux 。Web 服务器用 Apache 2.0,Squid 做反向代理服务器。
数据库
Tailrank 用 MySQL 数据库,联邦数据库形式。存储引擎用 InnoDB, 数据量 500GB。Kevin Burton 也指出了 MySQL 5 在修了一些 多核模式下互斥锁的问题(This Bug?)。到数据库的JDBC 驱动连接池用 lbpool 做负载均衡。MySQL Slave 或者 Master的复制用 MySQLSlaveSync 来轻松完成。不过即使这样,还要花费 20%的时间来折腾 DB。
其他开放的软件
任何一套系统都离不开合适的 Profiling 工具,Tailrank 也不利外,针对 Java 程序的 Benchmark 用 Benchmark4j。Log 工具用 Log5j(不是 Log4j)。Tailrank 所用的大部分工具都是开放的。
Tailrank 的一个比较大的竞争对手是 Techmeme,虽然二者暂时看面向内容的侧重点有所不同。其实,最大的对手还是自己,当需要挖掘的信息量越来越大,如果精准并及时的呈现给用户内容的成本会越来越高。从现在来看,Tailrank 离预期目标还差的很远。期待罗马早日建成
YouTube架构学习
关键字: YouTube
原文: YouTube Architecture
YouTube发展迅速,每天超过1亿的视频点击量,但只有很少人在维护站点和确保伸缩性。
平台
Apache
Python
Linux(SuSe)
MySQL
psyco,一个动态的Python到C的编译器
lighttpd代替Apache做视频查看
状态
支持每天超过1亿的视频点击量
成立于2005年2月
于2006年3月达到每天3千万的视频点击量
于2006年7月达到每天1亿的视频点击量
2个系统管理员,2个伸缩性软件架构师
2个软件开发工程师,2个网络工程师,1个DBA
处理飞速增长的流量
Java代码
1. while (true)
2. {
3. identify_and_fix_bottlenecks();
4. drink();
5. sleep();
6. notice_new_bottleneck();
7. }
while (true)
{
identify_and_fix_bottlenecks();
drink();
sleep();
notice_new_bottleneck();
}
每天运行该循环多次
Web服务器
1,NetScaler用于负载均衡和静态内容缓存
2,使用mod_fast_cgi运行Apache
3,使用一个Python应用服务器来处理请求的路由
4,应用服务器与多个数据库和其他信息源交互来获取数据和格式化html页面
5,一般可以通过添加更多的机器来在Web层提高伸缩性
6,Python的Web层代码通常不是性能瓶颈,大部分时间阻塞在RPC
7,Python允许快速而灵活的开发和部署
8,通常每个页面服务少于100毫秒的时间
9,使用psyco(一个类似于JIT编译器的动态的Python到C的编译器)来优化内部循环
10,对于像加密等密集型CPU活动,使用C扩展
11,对于一些开销昂贵的块使用预先生成并缓存的html
12,数据库里使用行级缓存
13,缓存完整的Python对象
14,有些数据被计算出来并发送给各个程序,所以这些值缓存在本地内存中。这是个使用不当的策略。应用服务器里最快的缓存将预先计算的值发送给所有服务器也花不了多少时间。只需弄一个代理来监听更改,预计算,然后发送。
视频服务
1,花费包括带宽,硬件和能源消耗
2,每个视频由一个迷你集群来host,每个视频被超过一台机器持有
3,使用一个集群意味着:
-更多的硬盘来持有内容意味着更快的速度
-failover。如果一台机器出故障了,另外的机器可以继续服务
-在线备份
4,使用lighttpd作为Web服务器来提供视频服务:
-Apache开销太大
-使用epoll来等待多个fds
-从单进程配置转变为多进程配置来处理更多的连接
5,大部分流行的内容移到CDN:
-CDN在多个地方备份内容,这样内容离用户更近的机会就会更高
-CDN机器经常内存不足,因为内容太流行以致很少有内容进出内存的颠簸
6,不太流行的内容(每天1-20浏览次数)在许多colo站点使用YouTube服务器
-长尾效应。一个视频可以有多个播放,但是许多视频正在播放。随机硬盘块被访问
-在这种情况下缓存不会很好,所以花钱在更多的缓存上可能没太大意义。
-调节RAID控制并注意其他低级问题
-调节每台机器上的内存,不要太多也不要太少
视频服务关键点
1,保持简单和廉价
2,保持简单网络路径,在内容和用户间不要有太多设备
3,使用常用硬件,昂贵的硬件很难找到帮助文档
4,使用简单而常见的工具,使用构建在Linux里或之上的大部分工具
5,很好的处理随机查找(SATA,tweaks)
缩略图服务
1,做到高效令人惊奇的难
2,每个视频大概4张缩略图,所以缩略图比视频多很多
3,缩略图仅仅host在几个机器上
4,持有一些小东西所遇到的问题:
-OS级别的大量的硬盘查找和inode和页面缓存问题
-单目录文件限制,特别是Ext3,后来移到多分层的结构。内核2.6的最近改进可能让Ext3允许大目录,但在一个文件系统里存储大量文件不是个好主意
-每秒大量的请求,因为Web页面可能在页面上显示60个缩略图
-在这种高负载下Apache表现的非常糟糕
-在Apache前端使用squid,这种方式工作了一段时间,但是由于负载继续增加而以失败告终。它让每秒300个请求变为20个
-尝试使用lighttpd但是由于使用单线程它陷于困境。遇到多进程的问题,因为它们各自保持自己单独的缓存
-如此多的图片以致一台新机器只能接管24小时
-重启机器需要6-10小时来缓存
5,为了解决所有这些问题YouTube开始使用Google的BigTable,一个分布式数据存储:
-避免小文件问题,因为它将文件收集到一起
-快,错误容忍
-更低的延迟,因为它使用分布式多级缓存,该缓存与多个不同collocation站点工作
-更多信息参考Google Architecture,GoogleTalk Architecture和BigTable
数据库
1,早期
-使用MySQL来存储元数据,如用户,tags和描述
-使用一整个10硬盘的RAID 10来存储数据
-依赖于信用卡所以YouTube租用硬件
-YouTube经过一个常见的革命:单服务器,然后单master和多read slaves,然后数据库分区,然后sharding方式
-痛苦与备份延迟。master数据库是多线程的并且运行在一个大机器上所以它可以处理许多工作,slaves是单线程的并且通常运行在小一些的服务器上并且备份是异步的,所以slaves会远远落后于master
-更新引起缓存失效,硬盘的慢I/O导致慢备份
-使用备份架构需要花费大量的money来获得增加的写性能
-YouTube的一个解决方案是通过把数据分成两个集群来将传输分出优先次序:一个视频查看池和一个一般的集群
2,后期
-数据库分区
-分成shards,不同的用户指定到不同的shards
-扩散读写
-更好的缓存位置意味着更少的IO
-导致硬件减少30%
-备份延迟降低到0
-现在可以任意提升数据库的伸缩性
数据中心策略
1,依赖于信用卡,所以最初只能使用受管主机提供商
2,受管主机提供商不能提供伸缩性,不能控制硬件或使用良好的网络协议
3,YouTube改为使用colocation arrangement。现在YouTube可以自定义所有东西并且协定自己的契约
4,使用5到6个数据中心加CDN
5,视频来自任意的数据中心,不是最近的匹配或其他什么。如果一个视频足够流行则移到CDN
6,依赖于视频带宽而不是真正的延迟。可以来自任何colo
7,图片延迟很严重,特别是当一个页面有60张图片时
8,使用BigTable将图片备份到不同的数据中心,代码查看谁是最近的
学到的东西
1,Stall for time。创造性和风险性的技巧让你在短期内解决问题而同时你会发现长期的解决方案
2,Proioritize。找出你的服务中核心的东西并对你的资源分出优先级别
3,Pick your battles。别怕将你的核心服务分出去。YouTube使用CDN来分布它们最流行的内容。创建自己的网络将花费太多时间和太多money
4,Keep it simple!简单允许你更快的重新架构来回应问题
5,Shard。Sharding帮助隔离存储,CPU,内存和IO,不仅仅是获得更多的写性能
6,Constant iteration on bottlenecks:
-软件:DB,缓存
-OS:硬盘I/O
-硬件:内存,RAID
7,You succeed as a team。拥有一个跨越条律的了解整个系统并知道系统内部是什么样的团队,如安装打印机,安装机器,安装网络等等的人。With a good team all things are possible。
http://hideto.javaeye.com/blog/130815
Google架构学习
关键字: Google
原文:Google Architecture
Google是伸缩性的王者。Google一直的目标就是构建高性能高伸缩性的基础组织来支持它们的产品。
平台
Linux
大量语言:Python,Java,C++
状态
在2006年大约有450,000台廉价服务器
在2005年Google索引了80亿Web页面,现在没有人知道数目
目前在Google有超过200个GFS集群。一个集群可以有1000或者甚至5000台机器。成千上万的机器从运行着5000000000000000字节存储的GFS集群获取数据,集群总的读写吞吐量可以达到每秒40兆字节
目前在Google有6000个MapReduce程序,而且每个月都写成百个新程序
BigTable伸缩存储几十亿的URL,几百千千兆的卫星图片和几亿用户的参数选择
堆栈
Google形象化它们的基础组织为三层架构:
1,产品:搜索,广告,email,地图,视频,聊天,博客
2,分布式系统基础组织:GFS,MapReduce和BigTable
3,计算平台:一群不同的数据中心里的机器
4,确保公司里的人们部署起来开销很小
5,花费更多的钱在避免丢失日志数据的硬件上,其他类型的数据则花费较少
可信赖的存储机制GFS(Google File System)
1,可信赖的伸缩性存储是任何程序的核心需求。GFS就是Google的核心存储平台
2,Google File System - 大型分布式结构化日志文件系统,Google在里面扔了大量的数据
3,为什么构建GFS而不是利用已有的东西?因为可以自己控制一切并且这个平台与别的不一样,Google需要:
-跨数据中心的高可靠性
-成千上万的网络节点的伸缩性
-大读写带宽的需求
-支持大块的数据,可能为上千兆字节
-高效的跨节点操作分发来减少瓶颈
4,系统有Master和Chunk服务器
-Master服务器在不同的数据文件里保持元数据。数据以64MB为单位存储在文件系统中。客户端与Master服务器交流来在文件上做元数据操作并且找到包含用户需要数据的那些Chunk服务器
-Chunk服务器在硬盘上存储实际数据。每个Chunk服务器跨越3个不同的Chunk服务器备份以创建冗余来避免服务器崩溃。一旦被Master服务器指明,客户端程序就会直接从Chunk服务器读取文件
6,一个上线的新程序可以使用已有的GFS集群或者可以制作自己的GFS集群
7,关键点在于有足够的基础组织来让人们对自己的程序有所选择,GFS可以调整来适应个别程序的需求
使用MapReduce来处理数据
1,现在你已经有了一个很好的存储系统,你该怎样处理如此多的数据呢?比如你有许多TB的数据存储在1000台机器上。数据库不能伸缩或者伸缩到这种级别花费极大,这就是 MapReduce出现的原因
2, MapReduce是一个处理和生成大量数据集的编程模型和相关实现。用户指定一个 map方法来处理一个键/值对来生成一个中间的键/值对,还有一个reduce方法来合并所有关联到同样的中间键的中间值。许多真实世界的任务都可以使用这种模型来表现。以这种风格来写的程序会自动并行的在一个大量机器的集群里运行。运行时系统照顾输入数据划分、程序在机器集之间执行的调度、机器失败处理和必需的内部机器交流等细节。这允许程序员没有多少并行和分布式系统的经验就可以很容易使用一个大型分布式系统资源
3,为什么使用 MapReduce?
-跨越大量机器分割任务的好方式
-处理机器失败
-可以与不同类型的程序工作,例如搜索和广告。几乎任何程序都有 map和reduce类型的操作。你可以预先计算有用的数据、查询字数统计、对TB的数据排序等等
4, MapReduce系统有三种不同类型的服务器
-Master服务器分配用户任务到 Map和Reduce服务器。它也跟踪任务的状态
- Map服务器接收用户输入并在其基础上处理 map操作。结果写入中间文件
-Reduce服务器接收 Map服务器产生的中间文件并在其基础上处理reduce操作
5,例如,你想在所有Web页面里的字数。你将存储在GFS里的所有页面抛入 MapReduce。这将在成千上万台机器上同时进行并且所有的调整、工作调度、失败处理和数据传输将自动完成
-步骤类似于:GFS -> Map -> Shuffle -> Reduction -> Store Results back into GFS
-在 MapReduce里一个 map操作将一些数据映射到另一个中,产生一个键值对,在我们的例子里就是字和字数
-Shuffling操作聚集键类型
-Reduction操作计算所有键值对的综合并产生最终的结果
6,Google索引操作管道有大约20个不同的 map和reduction。
7,程序可以非常小,如20到50行代码
8,一个问题是掉队者。掉队者是一个比其他程序慢的计算,它阻塞了其他程序。掉队者可能因为缓慢的IO或者临时的CPU不能使用而发生。解决方案是运行多个同样的计算并且当一个完成后杀死所有其他的
9,数据在 Map和Reduce服务器之间传输时被压缩了。这可以节省带宽和I/O。
在BigTable里存储结构化数据
1,BigTable是一个大伸缩性、错误容忍、自管理的系统,它包含千千兆的内存和1000000000000000的存储。它可以每秒钟处理百万的读写
2,BigTable是一个构建于GFS之上的分布式哈希机制。它不是关系型数据库。它不支持join或者SQL类型查询
3,它提供查询机制来通过键访问结构化数据。GFS存储存储不透明的数据而许多程序需求有结构化数据
4,商业数据库不能达到这种级别的伸缩性并且不能在成千上万台机器上工作
5,通过控制它们自己的低级存储系统Google得到更多的控制权来改进它们的系统。例如,如果它们想让跨数据中心的操作更简单这个特性,它们可以内建它
6,系统运行时机器可以自由的增删而整个系统保持工作
7,每个数据条目存储在一个格子里,它可以通过一个行key和列key或者时间戳来访问
8,每一行存储在一个或多个tablet中。一个tablet是一个64KB块的数据序列并且格式为SSTable
9,BigTable有三种类型的服务器:
-Master服务器分配tablet服务器,它跟踪tablet在哪里并且如果需要则重新分配任务
-Tablet服务器为tablet处理读写请求。当tablet超过大小限制(通常是100MB-200MB)时它们拆开tablet。当一个Tablet服务器失败时,则100个Tablet服务器各自挑选一个新的tablet然后系统恢复。
-Lock服务器形成一个分布式锁服务。像打开一个tablet来写、Master调整和访问控制检查等都需要互斥
10,一个locality组可以用来在物理上将相关的数据存储在一起来得到更好的locality选择
11,tablet尽可能的缓存在RAM里
硬件
1,当你有很多机器时你怎样组织它们来使得使用和花费有效?
2,使用非常廉价的硬件
3,A 1,000-fold computer power increase can be had for a 33 times lower cost if you you use a failure-prone infrastructure rather than an infrastructure built on highly reliable components. You must build reliability on top of unreliability for this strategy to work.
4,Linux,in-house rack design,PC主板,低端存储
5,Price per wattage on performance basis isn't getting better. Have huge power and cooling issues
6,使用一些collocation和Google自己的数据中心
其他
1,迅速更改而不是等待QA
2,库是构建程序的卓越方式
3,一些程序作为服务提供
4,一个基础组织处理程序的版本,这样它们可以发布而不用害怕会破坏什么东西
Google将来的方向
1,支持地理位置分布的集群
2,为所有数据创建一个单独的全局名字空间。当前的数据由集群分离
3,更多和更好的自动化数据迁移和计算
4,解决当使用网络划分来做广阔区域的备份时的一致性问题(例如保持服务即使一个集群离线维护或由于一些损耗问题)
学到的东西
1,基础组织是有竞争性的优势。特别是对Google而言。Google可以很快很廉价的推出新服务,并且伸缩性其他人很难达到。许多公司采取完全不同的方式。许多公司认为基础组织开销太大。Google认为自己是一个系统工程公司,这是一个新的看待软件构建的方式
2,跨越多个数据中心仍然是一个未解决的问题。大部分网站都是一个或者最多两个数据中心。我们不得不承认怎样在一些数据中心之间完整的分布网站是很需要技巧的
3,如果你自己没有时间从零开始重新构建所有这些基础组织你可以看看Hadoop。Hadoop是这里很多同样的主意的一个开源实现
4,平台的一个优点是初级开发人员可以在平台的基础上快速并且放心的创建健全的程序。如果每个项目都需要发明同样的分布式基础组织的轮子,那么你将陷入困境因为知道怎样完成这项工作的人相对较少
5,协同工作不一直是掷骰子。通过让系统中的所有部分一起工作则一个部分的改进将帮助所有的部分。改进文件系统则每个人从中受益而且是透明的。如果每个项目使用不同的文件系统则在整个堆栈中享受不到持续增加的改进
6,构建自管理系统让你没必要让系统关机。这允许你更容易在服务器之间平衡资源、动态添加更大的容量、让机器离线和优雅的处理升级
7,创建可进化的基础组织,并行的执行消耗时间的操作并采取较好的方案
8,不要忽略学院。学院有许多没有转变为产品的好主意。Most of what Google has done has prior art, just not prior large scale deployment.
9,考虑压缩。当你有许多CPU而IO有限时压缩是一个好的选择。
http://blog.daviesliu.net/2006/09/09/010620/
Lighttpd+Squid+Apache搭建高效率Web服务器
架构原理
Apache通常是开源界的首选Web服务器,因为它的强大和可靠,已经具有了品牌效应,可以适用于绝大部分的应用场合。但是它的强大有时候却显得笨重,配置文件得让人望而生畏,高并发情况下效率不太高。而轻量级的Web服务器Lighttpd却是后起之秀,其静态文件的响应能力远高于 Apache,据说是Apache的2-3倍。Lighttpd的高性能和易用性,足以打动我们,在它能够胜任的领域,尽量用它。Lighttpd对 PHP的支持也很好,还可以通过Fastcgi方式支持其他的语言,比如Python。
毕竟Lighttpd是轻量级的服务器,功能上不能跟Apache比,某些应用无法胜任。比如Lighttpd还不支持缓存,而现在的绝大部分站点都是用程序生成动态内容,没有缓存的话即使程序的效率再高也很难满足大访问量的需求,而且让程序不停的去做同一件事情也实在没有意义。首先,Web程序是需要做缓存处理的,即把反复使用的数据做缓存。即使这样也还不够,单单是启动Web处理程序的代价就不少,缓存最后生成的静态页面是必不可少的。而做这个是 Squid的强项,它本是做代理的,支持高效的缓存,可以用来给站点做反向代理加速。把Squid放在Apache或者Lighttpd的前端来缓存 Web服务器生成的动态内容,而Web应用程序只需要适当地设置页面实效时间即可。
即使是大部分内容动态生成的网站,仍免不了会有一些静态元素,比如图片、JS脚本、CSS等等,将Squid放在Apache或者Lighttp 前端后,反而会使性能下降,毕竟处理HTTP请求是Web服务器的强项。而且已经存在于文件系统中的静态内容再在Squid中缓存一下,浪费内存和硬盘空间。因此可以考虑将Lighttpd再放在Squid的前面,构成 Lighttpd+Squid+Apache的一条处理链,Lighttpd在最前面,专门用来处理静态内容的请求,把动态内容请求通过proxy模块转发给Squid,如果Squid中有该请求的内容且没有过期,则直接返回给Lighttpd。新请求或者过期的页面请求交由Apache中Web程序来处理。经过Lighttpd和Squid的两级过滤,Apache需要处理的请求将大大减少,减少了Web应用程序的压力。同时这样的构架,便于把不同的处理分散到多台计算机上进行,由Lighttpd在前面统一把关。
在这种架构下,每一级都是可以进行单独优化的,比如Lighttpd可以采用异步IO方式,Squid可以启用内存来缓存,Apache可以启用MPM 等,并且每一级都可以使用多台机器来均衡负载,伸缩性很好。
2、图片服务器分离大家知道,对于Web服务器来说,不管是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他们都有独立的图片服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃,在应用服务器和图片服务器上,可以进行不同的配置优化,比如apache在配置ContentType的时候可以尽量少支持,尽可能少的LoadModule,保证更高的系统消耗和执行效率。
3、数据库集群和库表散列大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是类似的方案,您使用了什么样的DB,就参考相应的解决方案来实施即可。上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用DB类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并且最有效的解决方案。我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。sohu的论坛就是采用了这样的架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系统随时增加一台低成本的数据库进来补充系统性能。
4、缓存缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。架构方面的缓存,对Apache比较熟悉的人都能知道Apache提供了自己的缓存模块,也可以使用外加的Squid模块进行缓存,这两种方式均可以有效的提高Apache的访问响应能力。网站程序开发方面的缓存,Linux上提供的Memory Cache是常用的缓存接口,可以在web开发中使用,比如用Java开发的时候就可以调用MemoryCache对一些数据进行缓存和通讯共享,一些大型社区使用了这样的架构。另外,在使用web语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP有Pear的Cache模块,Java就更多了,.net不是很熟悉,相信也肯定有。
5、镜像镜像是大型网站常采用的提高性能和数据安全性的方式,镜像的技术可以解决不同网络接入商和地域带来的用户访问速度差异,比如 ChinaNet和EduNet之间的差异就促使了很多网站在教育网内搭建镜像站点,数据进行定时更新或者实时更新。在镜像的细节技术方面,这里不阐述太深,有很多专业的现成的解决架构和产品可选。也有廉价的通过软件实现的思路,比如Linux上的rsync等工具。
6、负载均衡负载均衡将是大型网站解决高负荷访问和大量并发请求采用的终极解决办法。负载均衡技术发展了多年,有很多专业的服务提供商和产品可以选择,我个人接触过一些解决方法,其中有两个架构可以给大家做参考。
7、硬件四层交换第四层交换使用第三层和第四层信息包的报头信息,根据应用区间识别业务流,将整个区间段的业务流分配到合适的应用服务器进行处理。 第四层交换功能就象是虚 IP,指向物理服务器。它传输的业务服从的协议多种多样,有HTTP、FTP、NFS、Telnet或其他协议。这些业务在物理服务器基础上,需要复杂的载量平衡算法。在IP世界,业务类型由终端TCP或UDP端口地址来决定,在第四层交换中的应用区间则由源端和终端IP地址、TCP和UDP端口共同决定。在硬件四层交换产品领域,有一些知名的产品可以选择,比如Alteon、F5等,这些产品很昂贵,但是物有所值,能够提供非常优秀的性能和很灵活的管理能力。Yahoo中国当初接近2000台服务器使用了三四台Alteon就搞定了
。8、软件四层交换大家知道了硬件四层交换机的原理后,基于OSI模型来实现的软件四层交换也就应运而生,这样的解决方案实现的原理一致,不过性能稍差。但是满足一定量的压力还是游刃有余的,有人说软件实现方式其实更灵活,处理能力完全看你配置的熟悉能力。软件四层交换我们可以使用Linux上常用的LVS来解决,LVS就是Linux Virtual Server,他提供了基于心跳线heartbeat的实时灾难应对解决方案,提高系统的鲁棒性,同时可供了灵活的虚拟VIP配置和管理功能,可以同时满足多种应用需求,这对于分布式的系统来说必不可少。一个典型的使用负载均衡的策略就是,在软件或者硬件四层交换的基础上搭建squid集群,这种思路在很多大型网站包括搜索引擎上被采用,这样的架构低成本、高性能还有很强的扩张性,随时往架构里面增减节点都非常容易。这样的架构我准备空了专门详细整理一下和大家探讨。对于大型网站来说,前面提到的每个方法可能都会被同时使用到,我这里介绍得比较浅显,具体实现过程中很多细节还需要大家慢慢熟悉和体会,有时一个很小的squid参数或者apache参数设置,对于系统性能的影响就会很大,希望大家一起讨论,达到抛砖引玉之效。
用squid做web cache server,而apache在squid的后面提供真正的web服务。当然使用这样的架构必须要保证主页上大部分都是静态页面。这就需要程序员的配合将页面在反馈给客户端之前将页面全部转换成静态页面。
基本看出sina和sohu对于频道等栏目都用了相同的技术,即squid来监听这些IP的80端口,而真正的web server来监听另外一个端口。从用户的感觉上来说不会有任何的区别,而相对于将web server直接和客户端连在一起的方式,这样的方式明显的节省的带宽和服务器。用户访问的速度感觉也会更快。
http://www.dbanotes.net/arch/yupoo_arch.html
带宽:4000M/S (参考)
服务器数量:60 台左右
Web服务器:Lighttpd, Apache, nginx
应用服务器:Tomcat
其他:Python, Java, MogileFS 、ImageMagick 等
关于 Squid 与 Tomcat
Squid 与 Tomcat 似乎在 Web 2.0 站点的架构中较少看到。我首先是对 Squid 有点疑问,对此阿华的解释是"目前暂时还没找到效率比 Squid 高的缓存系统,原来命中率的确很差,后来在 Squid 前又装了层 Lighttpd, 基于 url 做 hash, 同一个图片始终会到同一台 squid 去,所以命中率彻底提高了"
对于应用服务器层的 Tomcat,现在 Yupoo! 技术人员也在逐渐用其他轻量级的东西替代,而 YPWS/YPFS 现在已经用 Python 进行开发了。
名次解释:
· YPWS--Yupoo Web Server YPWS 是用 Python开发的一个小型 Web 服务器,提供基本的 Web 服务外,可以增加针对用户、图片、外链网站显示的逻辑判断,可以安装于任何有空闲资源的服务器中,遇到性能瓶颈时方便横向扩展。
· YPFS--Yupoo File System 与 YPWS 类似,YPFS 也是基于这个 Web 服务器上开发的图片上传服务器。
【Updated: 有网友留言质疑 Python 的效率,Yupoo 老大刘平阳在 del.icio.us 上写到 "YPWS用Python自己写的,每台机器每秒可以处理294个请求, 现在压力几乎都在10%以下"】
图片处理层
接下来的 Image Process Server 负责处理用户上传的图片。使用的软件包也是 ImageMagick,在上次存储升级的同时,对于锐化的比率也调整过了(我个人感觉,效果的确好了很多)。”Magickd“ 是图像处理的一个远程接口服务,可以安装在任何有空闲 CPU资源的机器上,类似 Memcached的服务方式。
我们知道 Flickr 的缩略图功能原来是用 ImageMagick 软件包的,后来被雅虎收购后出于版权原因而不用了(?);EXIF 与 IPTC Flicke 是用 Perl 抽取的,我是非常建议 Yupoo! 针对 EXIF 做些文章,这也是潜在产生受益的一个重点。
图片存储层
原来 Yupoo! 的存储采用了磁盘阵列柜,基于 NFS 方式的,随着数据量的增大,”Yupoo! 开发部从07年6月份就开始着手研究一套大容量的、能满足 Yupoo! 今后发展需要的、安全可靠的存储系统“,看来 Yupoo! 系统比较有信心,也是满怀期待的,毕竟这要支撑以 TB 计算的海量图片的存储和管理。我们知道,一张图片除了原图外,还有不同尺寸的,这些图片统一存储在 MogileFS 中。
对于其他部分,常见的 Web 2.0 网站必须软件都能看到,如 MySQL、Memcached 、Lighttpd 等。Yupoo! 一方面采用不少相对比较成熟的开源软件,一方面也在自行开发定制适合自己的架构组件。这也是一个 Web 2.0 公司所必需要走的一个途径。
非常感谢一下 Yupoo! 阿华对于技术信息的分享,技术是共通的。下一个能爆料是哪家?
--EOF--
lighttpd+squid这套缓存是放在另外一个机房作为cdn的一个节点使用的,图中没描绘清楚,给大家带来不便了。
squid前端用lighttpd没用nginx,主要是用了这么久,没出啥大问题,所以就没想其他的了。
URL Hash的扩展性的确不好,能做的就是不轻易去增减服务器,我们目前是5台服务器做一组hash.
我们现在用Python写的Web Server,在效率方面,我可以给个测试数据,根据目前的访问日志模拟访问测试的结果是1台ypws,平均每秒处理294个请求(加载所有的逻辑判断)。
在可靠性上,还不没具体的数据,目前运行1个多月还没有任何异常。
lvs每个节点上都装nginx,主要是为了反向代理及处理静态内容,不过apache已显得不是那么必需,准备逐渐去掉。
我们处理图片都是即时的,我们目前半数以上的服务器都装了magickd服务,用来分担图片处理请求。
http://www.dbanotes.net/review/tailrank_arch.html
每天数以千万计的 Blog 内容中,实时的热点是什么? Tailrank 这个 Web 2.0 Startup 致力于回答这个问题。
专门爆料网站架构的 Todd Hoff 对 Kevin Burton 进行了采访。于是我们能了解一下 Tailrank 架构的一些信息。每小时索引 2400 万的 Blog 与 Feed,内容处理能力为 160-200Mbps,IO 写入大约在10-15MBps。每个月要处理 52T 之多的原始数据。Tailrank 所用的爬虫现在已经成为一个独立产品:spinn3r。
服务器硬件
目前大约 15 台服务器,CPU 是 64 位的 Opteron。每台主机上挂两个 SATA 盘,做 RAID 0。据我所知,国内很多 Web 2.0 公司也用的是类似的方式,SATA 盘容量达,低廉价格,堪称不二之选。操作系统用的是 Debian Linux 。Web 服务器用 Apache 2.0,Squid 做反向代理服务器。
数据库
Tailrank 用 MySQL 数据库,联邦数据库形式。存储引擎用 InnoDB, 数据量 500GB。Kevin Burton 也指出了 MySQL 5 在修了一些 多核模式下互斥锁的问题(This Bug?)。到数据库的JDBC 驱动连接池用 lbpool 做负载均衡。MySQL Slave 或者 Master的复制用 MySQLSlaveSync 来轻松完成。不过即使这样,还要花费 20%的时间来折腾 DB。
其他开放的软件
任何一套系统都离不开合适的 Profiling 工具,Tailrank 也不利外,针对 Java 程序的 Benchmark 用 Benchmark4j。Log 工具用 Log5j(不是 Log4j)。Tailrank 所用的大部分工具都是开放的。
Tailrank 的一个比较大的竞争对手是 Techmeme,虽然二者暂时看面向内容的侧重点有所不同。其实,最大的对手还是自己,当需要挖掘的信息量越来越大,如果精准并及时的呈现给用户内容的成本会越来越高。从现在来看,Tailrank 离预期目标还差的很远。期待罗马早日建成
YouTube架构学习
关键字: YouTube
原文: YouTube Architecture
YouTube发展迅速,每天超过1亿的视频点击量,但只有很少人在维护站点和确保伸缩性。
平台
Apache
Python
Linux(SuSe)
MySQL
psyco,一个动态的Python到C的编译器
lighttpd代替Apache做视频查看
状态
支持每天超过1亿的视频点击量
成立于2005年2月
于2006年3月达到每天3千万的视频点击量
于2006年7月达到每天1亿的视频点击量
2个系统管理员,2个伸缩性软件架构师
2个软件开发工程师,2个网络工程师,1个DBA
处理飞速增长的流量
Java代码
1. while (true)
2. {
3. identify_and_fix_bottlenecks();
4. drink();
5. sleep();
6. notice_new_bottleneck();
7. }
while (true)
{
identify_and_fix_bottlenecks();
drink();
sleep();
notice_new_bottleneck();
}
每天运行该循环多次
Web服务器
1,NetScaler用于负载均衡和静态内容缓存
2,使用mod_fast_cgi运行Apache
3,使用一个Python应用服务器来处理请求的路由
4,应用服务器与多个数据库和其他信息源交互来获取数据和格式化html页面
5,一般可以通过添加更多的机器来在Web层提高伸缩性
6,Python的Web层代码通常不是性能瓶颈,大部分时间阻塞在RPC
7,Python允许快速而灵活的开发和部署
8,通常每个页面服务少于100毫秒的时间
9,使用psyco(一个类似于JIT编译器的动态的Python到C的编译器)来优化内部循环
10,对于像加密等密集型CPU活动,使用C扩展
11,对于一些开销昂贵的块使用预先生成并缓存的html
12,数据库里使用行级缓存
13,缓存完整的Python对象
14,有些数据被计算出来并发送给各个程序,所以这些值缓存在本地内存中。这是个使用不当的策略。应用服务器里最快的缓存将预先计算的值发送给所有服务器也花不了多少时间。只需弄一个代理来监听更改,预计算,然后发送。
视频服务
1,花费包括带宽,硬件和能源消耗
2,每个视频由一个迷你集群来host,每个视频被超过一台机器持有
3,使用一个集群意味着:
-更多的硬盘来持有内容意味着更快的速度
-failover。如果一台机器出故障了,另外的机器可以继续服务
-在线备份
4,使用lighttpd作为Web服务器来提供视频服务:
-Apache开销太大
-使用epoll来等待多个fds
-从单进程配置转变为多进程配置来处理更多的连接
5,大部分流行的内容移到CDN:
-CDN在多个地方备份内容,这样内容离用户更近的机会就会更高
-CDN机器经常内存不足,因为内容太流行以致很少有内容进出内存的颠簸
6,不太流行的内容(每天1-20浏览次数)在许多colo站点使用YouTube服务器
-长尾效应。一个视频可以有多个播放,但是许多视频正在播放。随机硬盘块被访问
-在这种情况下缓存不会很好,所以花钱在更多的缓存上可能没太大意义。
-调节RAID控制并注意其他低级问题
-调节每台机器上的内存,不要太多也不要太少
视频服务关键点
1,保持简单和廉价
2,保持简单网络路径,在内容和用户间不要有太多设备
3,使用常用硬件,昂贵的硬件很难找到帮助文档
4,使用简单而常见的工具,使用构建在Linux里或之上的大部分工具
5,很好的处理随机查找(SATA,tweaks)
缩略图服务
1,做到高效令人惊奇的难
2,每个视频大概4张缩略图,所以缩略图比视频多很多
3,缩略图仅仅host在几个机器上
4,持有一些小东西所遇到的问题:
-OS级别的大量的硬盘查找和inode和页面缓存问题
-单目录文件限制,特别是Ext3,后来移到多分层的结构。内核2.6的最近改进可能让Ext3允许大目录,但在一个文件系统里存储大量文件不是个好主意
-每秒大量的请求,因为Web页面可能在页面上显示60个缩略图
-在这种高负载下Apache表现的非常糟糕
-在Apache前端使用squid,这种方式工作了一段时间,但是由于负载继续增加而以失败告终。它让每秒300个请求变为20个
-尝试使用lighttpd但是由于使用单线程它陷于困境。遇到多进程的问题,因为它们各自保持自己单独的缓存
-如此多的图片以致一台新机器只能接管24小时
-重启机器需要6-10小时来缓存
5,为了解决所有这些问题YouTube开始使用Google的BigTable,一个分布式数据存储:
-避免小文件问题,因为它将文件收集到一起
-快,错误容忍
-更低的延迟,因为它使用分布式多级缓存,该缓存与多个不同collocation站点工作
-更多信息参考Google Architecture,GoogleTalk Architecture和BigTable
数据库
1,早期
-使用MySQL来存储元数据,如用户,tags和描述
-使用一整个10硬盘的RAID 10来存储数据
-依赖于信用卡所以YouTube租用硬件
-YouTube经过一个常见的革命:单服务器,然后单master和多read slaves,然后数据库分区,然后sharding方式
-痛苦与备份延迟。master数据库是多线程的并且运行在一个大机器上所以它可以处理许多工作,slaves是单线程的并且通常运行在小一些的服务器上并且备份是异步的,所以slaves会远远落后于master
-更新引起缓存失效,硬盘的慢I/O导致慢备份
-使用备份架构需要花费大量的money来获得增加的写性能
-YouTube的一个解决方案是通过把数据分成两个集群来将传输分出优先次序:一个视频查看池和一个一般的集群
2,后期
-数据库分区
-分成shards,不同的用户指定到不同的shards
-扩散读写
-更好的缓存位置意味着更少的IO
-导致硬件减少30%
-备份延迟降低到0
-现在可以任意提升数据库的伸缩性
数据中心策略
1,依赖于信用卡,所以最初只能使用受管主机提供商
2,受管主机提供商不能提供伸缩性,不能控制硬件或使用良好的网络协议
3,YouTube改为使用colocation arrangement。现在YouTube可以自定义所有东西并且协定自己的契约
4,使用5到6个数据中心加CDN
5,视频来自任意的数据中心,不是最近的匹配或其他什么。如果一个视频足够流行则移到CDN
6,依赖于视频带宽而不是真正的延迟。可以来自任何colo
7,图片延迟很严重,特别是当一个页面有60张图片时
8,使用BigTable将图片备份到不同的数据中心,代码查看谁是最近的
学到的东西
1,Stall for time。创造性和风险性的技巧让你在短期内解决问题而同时你会发现长期的解决方案
2,Proioritize。找出你的服务中核心的东西并对你的资源分出优先级别
3,Pick your battles。别怕将你的核心服务分出去。YouTube使用CDN来分布它们最流行的内容。创建自己的网络将花费太多时间和太多money
4,Keep it simple!简单允许你更快的重新架构来回应问题
5,Shard。Sharding帮助隔离存储,CPU,内存和IO,不仅仅是获得更多的写性能
6,Constant iteration on bottlenecks:
-软件:DB,缓存
-OS:硬盘I/O
-硬件:内存,RAID
7,You succeed as a team。拥有一个跨越条律的了解整个系统并知道系统内部是什么样的团队,如安装打印机,安装机器,安装网络等等的人。With a good team all things are possible。
http://hideto.javaeye.com/blog/130815
Google架构学习
关键字: Google
原文:Google Architecture
Google是伸缩性的王者。Google一直的目标就是构建高性能高伸缩性的基础组织来支持它们的产品。
平台
Linux
大量语言:Python,Java,C++
状态
在2006年大约有450,000台廉价服务器
在2005年Google索引了80亿Web页面,现在没有人知道数目
目前在Google有超过200个GFS集群。一个集群可以有1000或者甚至5000台机器。成千上万的机器从运行着5000000000000000字节存储的GFS集群获取数据,集群总的读写吞吐量可以达到每秒40兆字节
目前在Google有6000个MapReduce程序,而且每个月都写成百个新程序
BigTable伸缩存储几十亿的URL,几百千千兆的卫星图片和几亿用户的参数选择
堆栈
Google形象化它们的基础组织为三层架构:
1,产品:搜索,广告,email,地图,视频,聊天,博客
2,分布式系统基础组织:GFS,MapReduce和BigTable
3,计算平台:一群不同的数据中心里的机器
4,确保公司里的人们部署起来开销很小
5,花费更多的钱在避免丢失日志数据的硬件上,其他类型的数据则花费较少
可信赖的存储机制GFS(Google File System)
1,可信赖的伸缩性存储是任何程序的核心需求。GFS就是Google的核心存储平台
2,Google File System - 大型分布式结构化日志文件系统,Google在里面扔了大量的数据
3,为什么构建GFS而不是利用已有的东西?因为可以自己控制一切并且这个平台与别的不一样,Google需要:
-跨数据中心的高可靠性
-成千上万的网络节点的伸缩性
-大读写带宽的需求
-支持大块的数据,可能为上千兆字节
-高效的跨节点操作分发来减少瓶颈
4,系统有Master和Chunk服务器
-Master服务器在不同的数据文件里保持元数据。数据以64MB为单位存储在文件系统中。客户端与Master服务器交流来在文件上做元数据操作并且找到包含用户需要数据的那些Chunk服务器
-Chunk服务器在硬盘上存储实际数据。每个Chunk服务器跨越3个不同的Chunk服务器备份以创建冗余来避免服务器崩溃。一旦被Master服务器指明,客户端程序就会直接从Chunk服务器读取文件
6,一个上线的新程序可以使用已有的GFS集群或者可以制作自己的GFS集群
7,关键点在于有足够的基础组织来让人们对自己的程序有所选择,GFS可以调整来适应个别程序的需求
使用MapReduce来处理数据
1,现在你已经有了一个很好的存储系统,你该怎样处理如此多的数据呢?比如你有许多TB的数据存储在1000台机器上。数据库不能伸缩或者伸缩到这种级别花费极大,这就是 MapReduce出现的原因
2, MapReduce是一个处理和生成大量数据集的编程模型和相关实现。用户指定一个 map方法来处理一个键/值对来生成一个中间的键/值对,还有一个reduce方法来合并所有关联到同样的中间键的中间值。许多真实世界的任务都可以使用这种模型来表现。以这种风格来写的程序会自动并行的在一个大量机器的集群里运行。运行时系统照顾输入数据划分、程序在机器集之间执行的调度、机器失败处理和必需的内部机器交流等细节。这允许程序员没有多少并行和分布式系统的经验就可以很容易使用一个大型分布式系统资源
3,为什么使用 MapReduce?
-跨越大量机器分割任务的好方式
-处理机器失败
-可以与不同类型的程序工作,例如搜索和广告。几乎任何程序都有 map和reduce类型的操作。你可以预先计算有用的数据、查询字数统计、对TB的数据排序等等
4, MapReduce系统有三种不同类型的服务器
-Master服务器分配用户任务到 Map和Reduce服务器。它也跟踪任务的状态
- Map服务器接收用户输入并在其基础上处理 map操作。结果写入中间文件
-Reduce服务器接收 Map服务器产生的中间文件并在其基础上处理reduce操作
5,例如,你想在所有Web页面里的字数。你将存储在GFS里的所有页面抛入 MapReduce。这将在成千上万台机器上同时进行并且所有的调整、工作调度、失败处理和数据传输将自动完成
-步骤类似于:GFS -> Map -> Shuffle -> Reduction -> Store Results back into GFS
-在 MapReduce里一个 map操作将一些数据映射到另一个中,产生一个键值对,在我们的例子里就是字和字数
-Shuffling操作聚集键类型
-Reduction操作计算所有键值对的综合并产生最终的结果
6,Google索引操作管道有大约20个不同的 map和reduction。
7,程序可以非常小,如20到50行代码
8,一个问题是掉队者。掉队者是一个比其他程序慢的计算,它阻塞了其他程序。掉队者可能因为缓慢的IO或者临时的CPU不能使用而发生。解决方案是运行多个同样的计算并且当一个完成后杀死所有其他的
9,数据在 Map和Reduce服务器之间传输时被压缩了。这可以节省带宽和I/O。
在BigTable里存储结构化数据
1,BigTable是一个大伸缩性、错误容忍、自管理的系统,它包含千千兆的内存和1000000000000000的存储。它可以每秒钟处理百万的读写
2,BigTable是一个构建于GFS之上的分布式哈希机制。它不是关系型数据库。它不支持join或者SQL类型查询
3,它提供查询机制来通过键访问结构化数据。GFS存储存储不透明的数据而许多程序需求有结构化数据
4,商业数据库不能达到这种级别的伸缩性并且不能在成千上万台机器上工作
5,通过控制它们自己的低级存储系统Google得到更多的控制权来改进它们的系统。例如,如果它们想让跨数据中心的操作更简单这个特性,它们可以内建它
6,系统运行时机器可以自由的增删而整个系统保持工作
7,每个数据条目存储在一个格子里,它可以通过一个行key和列key或者时间戳来访问
8,每一行存储在一个或多个tablet中。一个tablet是一个64KB块的数据序列并且格式为SSTable
9,BigTable有三种类型的服务器:
-Master服务器分配tablet服务器,它跟踪tablet在哪里并且如果需要则重新分配任务
-Tablet服务器为tablet处理读写请求。当tablet超过大小限制(通常是100MB-200MB)时它们拆开tablet。当一个Tablet服务器失败时,则100个Tablet服务器各自挑选一个新的tablet然后系统恢复。
-Lock服务器形成一个分布式锁服务。像打开一个tablet来写、Master调整和访问控制检查等都需要互斥
10,一个locality组可以用来在物理上将相关的数据存储在一起来得到更好的locality选择
11,tablet尽可能的缓存在RAM里
硬件
1,当你有很多机器时你怎样组织它们来使得使用和花费有效?
2,使用非常廉价的硬件
3,A 1,000-fold computer power increase can be had for a 33 times lower cost if you you use a failure-prone infrastructure rather than an infrastructure built on highly reliable components. You must build reliability on top of unreliability for this strategy to work.
4,Linux,in-house rack design,PC主板,低端存储
5,Price per wattage on performance basis isn't getting better. Have huge power and cooling issues
6,使用一些collocation和Google自己的数据中心
其他
1,迅速更改而不是等待QA
2,库是构建程序的卓越方式
3,一些程序作为服务提供
4,一个基础组织处理程序的版本,这样它们可以发布而不用害怕会破坏什么东西
Google将来的方向
1,支持地理位置分布的集群
2,为所有数据创建一个单独的全局名字空间。当前的数据由集群分离
3,更多和更好的自动化数据迁移和计算
4,解决当使用网络划分来做广阔区域的备份时的一致性问题(例如保持服务即使一个集群离线维护或由于一些损耗问题)
学到的东西
1,基础组织是有竞争性的优势。特别是对Google而言。Google可以很快很廉价的推出新服务,并且伸缩性其他人很难达到。许多公司采取完全不同的方式。许多公司认为基础组织开销太大。Google认为自己是一个系统工程公司,这是一个新的看待软件构建的方式
2,跨越多个数据中心仍然是一个未解决的问题。大部分网站都是一个或者最多两个数据中心。我们不得不承认怎样在一些数据中心之间完整的分布网站是很需要技巧的
3,如果你自己没有时间从零开始重新构建所有这些基础组织你可以看看Hadoop。Hadoop是这里很多同样的主意的一个开源实现
4,平台的一个优点是初级开发人员可以在平台的基础上快速并且放心的创建健全的程序。如果每个项目都需要发明同样的分布式基础组织的轮子,那么你将陷入困境因为知道怎样完成这项工作的人相对较少
5,协同工作不一直是掷骰子。通过让系统中的所有部分一起工作则一个部分的改进将帮助所有的部分。改进文件系统则每个人从中受益而且是透明的。如果每个项目使用不同的文件系统则在整个堆栈中享受不到持续增加的改进
6,构建自管理系统让你没必要让系统关机。这允许你更容易在服务器之间平衡资源、动态添加更大的容量、让机器离线和优雅的处理升级
7,创建可进化的基础组织,并行的执行消耗时间的操作并采取较好的方案
8,不要忽略学院。学院有许多没有转变为产品的好主意。Most of what Google has done has prior art, just not prior large scale deployment.
9,考虑压缩。当你有许多CPU而IO有限时压缩是一个好的选择。
http://blog.daviesliu.net/2006/09/09/010620/
Lighttpd+Squid+Apache搭建高效率Web服务器
架构原理
Apache通常是开源界的首选Web服务器,因为它的强大和可靠,已经具有了品牌效应,可以适用于绝大部分的应用场合。但是它的强大有时候却显得笨重,配置文件得让人望而生畏,高并发情况下效率不太高。而轻量级的Web服务器Lighttpd却是后起之秀,其静态文件的响应能力远高于 Apache,据说是Apache的2-3倍。Lighttpd的高性能和易用性,足以打动我们,在它能够胜任的领域,尽量用它。Lighttpd对 PHP的支持也很好,还可以通过Fastcgi方式支持其他的语言,比如Python。
毕竟Lighttpd是轻量级的服务器,功能上不能跟Apache比,某些应用无法胜任。比如Lighttpd还不支持缓存,而现在的绝大部分站点都是用程序生成动态内容,没有缓存的话即使程序的效率再高也很难满足大访问量的需求,而且让程序不停的去做同一件事情也实在没有意义。首先,Web程序是需要做缓存处理的,即把反复使用的数据做缓存。即使这样也还不够,单单是启动Web处理程序的代价就不少,缓存最后生成的静态页面是必不可少的。而做这个是 Squid的强项,它本是做代理的,支持高效的缓存,可以用来给站点做反向代理加速。把Squid放在Apache或者Lighttpd的前端来缓存 Web服务器生成的动态内容,而Web应用程序只需要适当地设置页面实效时间即可。
即使是大部分内容动态生成的网站,仍免不了会有一些静态元素,比如图片、JS脚本、CSS等等,将Squid放在Apache或者Lighttp 前端后,反而会使性能下降,毕竟处理HTTP请求是Web服务器的强项。而且已经存在于文件系统中的静态内容再在Squid中缓存一下,浪费内存和硬盘空间。因此可以考虑将Lighttpd再放在Squid的前面,构成 Lighttpd+Squid+Apache的一条处理链,Lighttpd在最前面,专门用来处理静态内容的请求,把动态内容请求通过proxy模块转发给Squid,如果Squid中有该请求的内容且没有过期,则直接返回给Lighttpd。新请求或者过期的页面请求交由Apache中Web程序来处理。经过Lighttpd和Squid的两级过滤,Apache需要处理的请求将大大减少,减少了Web应用程序的压力。同时这样的构架,便于把不同的处理分散到多台计算机上进行,由Lighttpd在前面统一把关。
在这种架构下,每一级都是可以进行单独优化的,比如Lighttpd可以采用异步IO方式,Squid可以启用内存来缓存,Apache可以启用MPM 等,并且每一级都可以使用多台机器来均衡负载,伸缩性很好。
1.Java语言的特点:面向对象,跨平台,多线程
2.Java运行环境:JVM+Java API
3.数据类型:
boolean 1
char 16
byte 8
short 16
int 32
long 64
float 32
double 64
4.面向对象:
封装:类
继承:单一继承
多态:重写(Overridding)与重载(Overloading)
在子类中,方法的参数个数和返回值都与父类相同,称为重写
在同一个类中,方法的参数不同,称为重载
5.作用域:
private 子类不能继承
default 如果不在同一包中,子类不能继承
protected
public
6.垃圾回收原理:有向图原则(另一种为计数器类型),可能会有内存泄漏,计数器类型与有向图类型相比:效率更高,但是精度偏低,因为它很难发现变量循环引用的问题
会有内存泄漏的代码:
Vector v=new Vector(10)
for (int i=0;i<10;i++){
Object o=new Object();
v.add(o);
o=null;
}
解决方法:
v=null;
7.栈与堆:原始数据类型存放在栈里,对象数据类型存放在堆中,栈中存放堆中的地址。
8.变量比较:==比较的是栈,原始数据类型相同,但是引用类型值不等,equals比较的是堆,特例是string,java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。
9.变量复制:原始数据类型复制的是值,引用数据类型复制的是栈中的引用,所以当一个修改的时候,会影响另一个。对象复制:Object.clone()方法
10.引用传递和值传递:原始数据类型传递的是值,引用数据类型传递的是栈中的引用
11.异常:(Java.lang.Exception)检查时异常,(Java.lang.RuntimeException)运行时异常,((java.lang.Error)错误,检查时异常必须处理
12.集合:collection: set:HashSet TreeSet
list:ArrayList LinkedList Vector Stack
map:Hash Map Tree Map HashTable Properties
13.Set里的元素是不能重复的,什么方法来区分重复与否呢? 是用==还是
equals()? 它们有何区别?
Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两
个Set是否相等。
equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个
分离的对象的内容和类型相配的话,返回真值
14.ArrayList, Vector, LinkedList的存储性能和特性
ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据
以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元
素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方
法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序
号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即
可,所以插入速度较快。
15、Hash Map和Hashtable的区别。
Hash Map是Hashtable的轻量级实现(非线程安全的实现),他们都完成了 Map接口,
主要区别在于Hash Map允许空(null)键值(key),由于非线程安全,效率上可能高于
Hashtable。
Hash Map允许将null作为一个entry的key或者value,而Hashtable不允许。
Hash Map把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为
contains方法容易让人引起误解。
Hashtable继承自Dictionary类,而Hash Map是Java1.2引进的 Map interface的一个实现。
最大的不同是,Hashtable的方法是Synchronize的,而Hash Map不是,在多个线程访问
Hashtable时,不需要自己为它的方法实现同步,而Hash Map 就必须为之提供外同步。
Hashtable和Hash Map采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。
2.Java运行环境:JVM+Java API
3.数据类型:
boolean 1
char 16
byte 8
short 16
int 32
long 64
float 32
double 64
4.面向对象:
封装:类
继承:单一继承
多态:重写(Overridding)与重载(Overloading)
在子类中,方法的参数个数和返回值都与父类相同,称为重写
在同一个类中,方法的参数不同,称为重载
5.作用域:
private 子类不能继承
default 如果不在同一包中,子类不能继承
protected
public
6.垃圾回收原理:有向图原则(另一种为计数器类型),可能会有内存泄漏,计数器类型与有向图类型相比:效率更高,但是精度偏低,因为它很难发现变量循环引用的问题
会有内存泄漏的代码:
Vector v=new Vector(10)
for (int i=0;i<10;i++){
Object o=new Object();
v.add(o);
o=null;
}
解决方法:
v=null;
7.栈与堆:原始数据类型存放在栈里,对象数据类型存放在堆中,栈中存放堆中的地址。
8.变量比较:==比较的是栈,原始数据类型相同,但是引用类型值不等,equals比较的是堆,特例是string,java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。
9.变量复制:原始数据类型复制的是值,引用数据类型复制的是栈中的引用,所以当一个修改的时候,会影响另一个。对象复制:Object.clone()方法
10.引用传递和值传递:原始数据类型传递的是值,引用数据类型传递的是栈中的引用
11.异常:(Java.lang.Exception)检查时异常,(Java.lang.RuntimeException)运行时异常,((java.lang.Error)错误,检查时异常必须处理
12.集合:collection: set:HashSet TreeSet
list:ArrayList LinkedList Vector Stack
map:Hash Map Tree Map HashTable Properties
13.Set里的元素是不能重复的,什么方法来区分重复与否呢? 是用==还是
equals()? 它们有何区别?
Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两
个Set是否相等。
equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个
分离的对象的内容和类型相配的话,返回真值
14.ArrayList, Vector, LinkedList的存储性能和特性
ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据
以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元
素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方
法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序
号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即
可,所以插入速度较快。
15、Hash Map和Hashtable的区别。
Hash Map是Hashtable的轻量级实现(非线程安全的实现),他们都完成了 Map接口,
主要区别在于Hash Map允许空(null)键值(key),由于非线程安全,效率上可能高于
Hashtable。
Hash Map允许将null作为一个entry的key或者value,而Hashtable不允许。
Hash Map把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为
contains方法容易让人引起误解。
Hashtable继承自Dictionary类,而Hash Map是Java1.2引进的 Map interface的一个实现。
最大的不同是,Hashtable的方法是Synchronize的,而Hash Map不是,在多个线程访问
Hashtable时,不需要自己为它的方法实现同步,而Hash Map 就必须为之提供外同步。
Hashtable和Hash Map采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。
定时执行任务的三种方法:
1)java.util.Timer.
2)ServletContextListener.
3)org.springframework.scheduling.timer.ScheduledTimerTask
1)java.util.Timer
这个方法应该是最常用的,不过这个方法需要手工启动你的任务:
Timer timer=new Timer();
timer.schedule(new ListByDayTimerTask(),10000,86400000);
这里的ListByDayTimerTask类必须extends TimerTask里面的run()方法。
2)ServletContextListener
这个方法在web容器环境比较方便,这样,在web server启动后就可以自动运行该任务,不需要手工操作。
将ListByDayListener implements ServletContextListener接口,在contextInitialized方法中加入启动Timer的代码,在 contextDestroyed方法中加入cancel该Timer的代码;然后在web.xml中,加入listener:
<-listener>
<-listener-class>com.qq.customer.ListByDayListener</listener-class>
<-/listener>
3)org.springframework.scheduling.timer.ScheduledTimerTask如果你用spring,那么你不需要写Timer类了,在schedulingContext-timer.xml中加入下面的内容就可以了:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="timer" class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<list>
<ref local="MyTimeTask1"/>
</list>
</property>
</bean>
<bean id="MyTimeTask" class="com.qq.timer.ListByDayTimerTask"/>
<bean id="MyTimeTask1" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="timerTask">
<ref bean="MyTimeTask"/>
</property>
<property name="delay">
<value>10000</value>
</property>
<property name="period">
<value>86400000</value>
</property>
</bean>
</beans>
以上内容转载自:http://dev.csdn.net/author/xtpdcsse/ec8e8080a5b04fa79e7d4828bc807d3f.html
下面是我的实现。
1)利用java.util.Timer. 代码如下
<1>StartThread.java
<2>StatPlan.java
<3>StatWeekPlan .java
2)ServletContextListener.实现
<1> SysStatListener .java
<2> StatTask .java
<3>在web.xml中添加下面的内容(注:下面的内容放到<filter- mapping>的后面)
<listener>
<listener-class>com.jview.auto.stat.SysStatListener </listener-class>
</listener>
1)java.util.Timer.
2)ServletContextListener.
3)org.springframework.scheduling.timer.ScheduledTimerTask
1)java.util.Timer
这个方法应该是最常用的,不过这个方法需要手工启动你的任务:
Timer timer=new Timer();
timer.schedule(new ListByDayTimerTask(),10000,86400000);
这里的ListByDayTimerTask类必须extends TimerTask里面的run()方法。
2)ServletContextListener
这个方法在web容器环境比较方便,这样,在web server启动后就可以自动运行该任务,不需要手工操作。
将ListByDayListener implements ServletContextListener接口,在contextInitialized方法中加入启动Timer的代码,在 contextDestroyed方法中加入cancel该Timer的代码;然后在web.xml中,加入listener:
<-listener>
<-listener-class>com.qq.customer.ListByDayListener</listener-class>
<-/listener>
3)org.springframework.scheduling.timer.ScheduledTimerTask如果你用spring,那么你不需要写Timer类了,在schedulingContext-timer.xml中加入下面的内容就可以了:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="timer" class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<list>
<ref local="MyTimeTask1"/>
</list>
</property>
</bean>
<bean id="MyTimeTask" class="com.qq.timer.ListByDayTimerTask"/>
<bean id="MyTimeTask1" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="timerTask">
<ref bean="MyTimeTask"/>
</property>
<property name="delay">
<value>10000</value>
</property>
<property name="period">
<value>86400000</value>
</property>
</bean>
</beans>
以上内容转载自:http://dev.csdn.net/author/xtpdcsse/ec8e8080a5b04fa79e7d4828bc807d3f.html
下面是我的实现。
1)利用java.util.Timer. 代码如下
<1>StartThread.java
- package com.jview.main;<BR>
- import java.util.Date;<BR>
- import java.util.Timer;<BR>
- import java.util.TimerTask;<BR>
- import org.apache.log4j.Logger;<BR>
- public class StartThreadextends Thread {<BR>
- private static Logger logger = Logger.getLogger("StartThread");<BR>
- public static final int EXECUTE_CYC = 86400000; //24*60*60*1000毫秒<BR>
- int startH = 9;<BR>
- int startM = 52;<BR>
- private Timer _timer ;<BR>
- private Date _statDate;<BR>
- private Date _nowDate;<BR>
- public StartThread(){<BR>
- _nowDate = new Date();<BR>
- _timer = new Timer();<BR>
- _statDate = newDate(_nowDate.getYear(),_nowDate.getMonth(),_nowDate.getDate(),startH,startM);<BR>
- }<BR>
- public void StartUp(){<BR>
- _timer.schedule(<BR>
- new TimerTask() {<BR>
- public void run()<BR>
- {<BR>
- logger.info("开始统计...");<BR>
- try {<BR>
- Class.forName("com.jview.stat.StatPlan").newInstance();<BR>
- } catch (InstantiationException e) {<BR>
- // TODO Auto-generated catch block<BR>
- e.printStackTrace();<BR>
- } catch (IllegalAccessException e) {<BR>
- // TODO Auto-generated catch block<BR>
- e.printStackTrace();<BR>
- } catch (ClassNotFoundException e) {<BR>
- // TODO Auto-generated catch block<BR>
- e.printStackTrace();<BR>
- }<BR>
- }<BR>
- },_statDate,EXECUTE_CYC);<BR>
- }<BR>
- public static void main(String[] args) {<BR>
- StartThread _statUp = new StartThread();<BR>
- _statUp.StartUp();<BR>
- } <BR>
- }
- package com.jview.main;
- import java.util.Date;
- import java.util.Timer;
- import java.util.TimerTask;
- import org.apache.log4j.Logger;
- public class StartThread extends Thread {
- private static Logger logger = Logger.getLogger("StartThread");
- public static final int EXECUTE_CYC = 86400000; //24*60*60*1000毫秒
- int startH = 9;
- int startM = 52;
- private Timer _timer ;
- private Date _statDate;
- private Date _nowDate;
- public StartThread(){
- _nowDate = new Date();
- _timer = new Timer();
- _statDate = new Date(_nowDate.getYear(),_nowDate.getMonth(),_nowDate.getDate(),startH,startM);
- }
- public void StartUp(){
- _timer.schedule(
- new TimerTask() {
- public void run()
- {
- logger.info("开始统计...");
- try {
- Class.forName("com.jview.stat.StatPlan").newInstance();
- } catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- },_statDate,EXECUTE_CYC);
- }
- public static void main(String[] args) {
- StartThread _statUp = new StartThread();
- _statUp.StartUp();
- }
- }
<2>StatPlan.java
- package com.jview.stat;<BR>
- import java.util.Calendar;<BR>
- import org.apache.log4j.Logger;<BR>
- public class StatPlan {<BR>
- <BR>
- <BR>
- private static Logger logger = Logger.getLogger("StatPlan");<BR>
- private int i = 0;<BR>
- private StatPlanService _sps ;<BR>
- public StatPlan(){<BR>
- _sps = new StatWeekPlan();<BR>
- statPlan();<BR>
- }<BR>
- <BR>
- public void statPlan(){<BR>
- Calendar _c = Calendar.getInstance();<BR>
- logger.info("stat plan ... 执行"+ i +"次,时间:"+_c.getTime());<BR>
- _sps.StatPlan();<BR>
- i++;<BR>
- for(int i = 0; i<9999;i++){<BR>
- if(i==0 || i==9998){<BR>
- logger.info(""+i);<BR>
- }<BR>
- }<BR>
- }<BR>
- }
- package com.jview.stat;
- import java.util.Calendar;
- import org.apache.log4j.Logger;
- public class StatPlan {
- private static Logger logger = Logger.getLogger("StatPlan");
- private int i = 0;
- private StatPlanService _sps ;
- public StatPlan(){
- _sps = new StatWeekPlan();
- statPlan();
- }
- public void statPlan(){
- Calendar _c = Calendar.getInstance();
- logger.info("stat plan ... 执行"+ i +"次,时间:"+_c.getTime());
- _sps.StatPlan();
- i++;
- for(int i = 0; i<9999;i++){
- if(i==0 || i== 9998){
- logger.info(""+i);
- }
- }
- }
- }
<3>StatWeekPlan .java
- import org.apache.log4j.Logger;<BR>
- <BR>
- public class StatWeekPlanextends StatPlanService {<BR>
- private static Logger logger = Logger.getLogger("StatWeekPlan");<BR>
- public void StatPlan(){<BR>
- logger.info("this is statWeekPlan");<BR>
- }<BR>
- }<BR>
- <4>StatPlanService .java<BR>
- public class StatPlanService {<BR>
- public void StatPlan(){ <BR>
- }<BR>
- }
- import org.apache.log4j.Logger;
- public class StatWeekPlan extends StatPlanService {
- private static Logger logger = Logger.getLogger("StatWeekPlan");
- public void StatPlan(){
- logger.info("this is statWeekPlan");
- }
- }
- <4>StatPlanService .java
- public class StatPlanService {
- public void StatPlan(){
- }
- }
2)ServletContextListener.实现
<1> SysStatListener .java
- import java.util.Date;<BR>
- import java.util.Timer;<BR>
- import java.util.TimerTask;<BR>
- import javax.servlet.ServletContextEvent;<BR>
- import javax.servlet.ServletContextListener;<BR>
- import org.apache.log4j.Logger;<BR>
- public class SysStatListenerimplements ServletContextListener{<BR>
- private static Logger logger = Logger.getLogger("ListByDayListener");<BR>
- private StatTask _sTask;<BR>
- public void contextDestroyed(ServletContextEvent scevent) {<BR>
- String status = "停止系统统计线程";<BR>
- scevent.getServletContext().log(status);<BR>
- logger.info(status);<BR>
- _sTask.shutDown();<BR>
- }<BR>
- public void contextInitialized(ServletContextEvent scevent) {<BR>
- String status = "启动系统统计线程";<BR>
- scevent.getServletContext().log(status);<BR>
- logger.info(status);<BR>
- _sTask = new StatTask();<BR>
- _sTask.startUp();<BR>
- }<BR>
- }
- import java.util.Date;
- import java.util.Timer;
- import java.util.TimerTask;
- import javax.servlet.ServletContextEvent;
- import javax.servlet.ServletContextListener;
- import org.apache.log4j.Logger;
- public class SysStatListener implements ServletContextListener{
- private static Logger logger = Logger.getLogger("ListByDayListener");
- private StatTask _sTask;
- public void contextDestroyed(ServletContextEvent scevent) {
- String status = "停止系统统计线程";
- scevent.getServletContext().log(status);
- logger.info(status);
- _sTask.shutDown();
- }
- public void contextInitialized(ServletContextEvent scevent) {
- String status = "启动系统统计线程";
- scevent.getServletContext().log(status);
- logger.info(status);
- _sTask = new StatTask();
- _sTask.startUp();
- }
- }
<2> StatTask .java
- import java.util.Date;<BR>
- import java.util.Timer;<BR>
- import java.util.TimerTask;<BR>
- import org.apache.log4j.Logger;<BR>
- public class StatTaskextends Thread {<BR>
- private static Logger logger = Logger.getLogger("StartThread");<BR>
- public staticfinal int EXECUTE_CYC =86400000;<BR>
- int startH = 9;<BR>
- int startM = 52;<BR>
- private Timer _timer ;<BR>
- private Date _statDate;<BR>
- private Date _nowDate;<BR>
- public StatTask(){<BR>
- _nowDate = new Date();<BR>
- _timer = new Timer();<BR>
- _statDate = newDate(_nowDate.getYear(),_nowDate.getMonth(),_nowDate.getDate(),startH,startM);<BR>
- }<BR>
- public void startUp(){<BR>
- _timer.schedule(<BR>
- new TimerTask() {<BR>
- public void run()<BR>
- {<BR>
- logger.info("开始统计...");<BR>
- try {<BR>
- Class.forName("com.jview.stat.StatPlan").newInstance();<BR>
- } catch (InstantiationException e) {<BR>
- // TODO Auto-generated catch block<BR>
- e.printStackTrace();<BR>
- } catch (IllegalAccessException e) {<BR>
- // TODO Auto-generated catch block<BR>
- e.printStackTrace();<BR>
- } catch (ClassNotFoundException e) {<BR>
- // TODO Auto-generated catch block<BR>
- e.printStackTrace();<BR>
- }<BR>
- }<BR>
- },_statDate,EXECUTE_CYC);<BR>
- }<BR>
- public void shutDown(){<BR>
- _timer.cancel();<BR>
- }
- import java.util.Date;
- import java.util.Timer;
- import java.util.TimerTask;
- import org.apache.log4j.Logger;
- public class StatTask extends Thread {
- private static Logger logger = Logger.getLogger("StartThread");
- public static final int EXECUTE_CYC = 86400000;
- int startH = 9;
- int startM = 52;
- private Timer _timer ;
- private Date _statDate;
- private Date _nowDate;
- public StatTask(){
- _nowDate = new Date();
- _timer = new Timer();
- _statDate = new Date(_nowDate.getYear(),_nowDate.getMonth(),_nowDate.getDate(),startH,startM);
- }
- public void startUp(){
- _timer.schedule(
- new TimerTask() {
- public void run()
- {
- logger.info("开始统计...");
- try {
- Class.forName("com.jview.stat.StatPlan").newInstance();
- } catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- },_statDate,EXECUTE_CYC);
- }
- public void shutDown(){
- _timer.cancel();
- }
<3>在web.xml中添加下面的内容(注:下面的内容放到<filter- mapping>的后面)
<listener>
<listener-class>com.jview.auto.stat.SysStatListener </listener-class>
</listener>
这篇文章是由José M. Aguilar在他卓越的博客中以西班牙语的形式首发,其后Timm Martin在获得Aguilar先生的授权下,对该文章进行翻译、修改,并且在DevTopics上发布。
以下13个小技巧可以使得你的代码在长时间内依然能够保持容易理解和维护。
1. 对不同级别的代码进行注释
对于不同级别的代码块,要使用统一的方法来进行注释。例如:
对于每一个类,需要包含一段简明扼要的描述,作者和上一次修改的时间
对于每一个方法,需要包含这个方法的用途,功能,参数以及返回结果
当你在一个团队里面的时候,采用一套注释的标准是非常重要的。当然,使用一种大家都认可的注释约定和工具(例如C#的XML注释和Java的Javadoc)在一定程度上能推动这项任务。
2. 使用段落注释
首先把代码块分解成多个“段落”,每一个段落都执行单一的任务;然后在每一个“段落”开始之前添加注释,告诉阅读代码的人接下来的这段代码是干什么用的
// 检查所有记录都是正确的
foreach (Record record in records)
{
if (rec.checkStatus()==Status.OK)
{
. . .
}
}
// 现在开始进行处理
Context ctx = new ApplicationContext();
ctx.BeginTransaction();
. . .
3. 对齐注释行
对于那些在行末写有注释的代码,应该对齐注释行来使得方便阅读
const MAX_ITEMS = 10; // maximum number of packets
const MASK = 0x1F; // mask bit TCP
有些开发人员使用tab来对齐注释,而另外一些人会用空格来对齐。由于tab在不同的编辑器和集成开发环境中会有所不同,所以最佳的方法是使用空格来对齐注释行。
4. 不要侮辱阅读者的智慧
要避免没用的注释,例如
if (a == 5) //如果a等于5
counter = 0 //把counte设为0
这不单把时间浪费在写没用的注释上面,同时也在分散读者的注意力。
5. 要有礼貌
应当避免没有礼貌的注释,例如“要注意一些愚蠢的用户会输入一个负数”,或者“修正由菜鸟工程师写的愚蠢得可怜的代码而导致的副作用”。这样的注释对于代码的写注释的人来说并没有任何好处,同时你永远都不会知道将来这些注释会被谁来阅读,你的老板,一个客户或者是刚才被你数落的愚蠢得可怜的工程师。
6. 直截了当
不要在注释里面写过多的废话。避免在注释里面卖弄ASCII艺术,写笑话,作诗和过于冗长。简而言之就是保持注释的简单和直接。
7. 使用统一的风格
有些人觉得注释应该让非程序员也能看懂。另外一些人觉得注释需要面对的读者只是程序员。无论如何,正如Successful Strategies for Commenting Code中所说的,最重要的是注释的风格需要统一,并且总是面向相同的读者。就自己而论,我怀疑非程序员是否会去读代码,所以我觉得注释应该面向程序员来写。
8. 在内部使用特殊的标签
当你在一个团队里工作的时候,采用一组一致的标签能帮助不同的程序员沟通。例如,很多团队会采用“TODO”标签来表示一段尚未完成的代码
int Estimate(int x, int y)
{
// TODO: implement the calculations
return 0;
}
标签注释并不会解释代码,它们寻求注意或者是传递信息。但是如果适当地使用这种技术,要记住跟进这段代码并且完成该标签传递的任务。
9. 在写代码的同时添加注释
当你在写代码而且记忆犹新的同时就添加注释。如果等到项目后期才添加注释,会让你事倍功半。“我没有时间写注释”,“我的时间很紧迫”和“项目已经延迟了”,这些都是不写注释的常见借口。有些工程师觉最佳的解决方法是“注释先行”。例如:
public void ProcessOrder()
{
// Make sure the products are available
// Check that the customer is valid
// Send the order to the store
// Generate bill
}
10. 把自己想象为注释的读者(事实上就是如此)
当你正在给代码写注释的时候,不仅仅为日后维护你的代码的开发者考虑,同时也设想一下如果自己就是注释的读者。Phil Haack曾经说过:
“一旦一行代码被敲到文件中, 你就已经要开始维护那一行代码了。”
所以,我们自己就是好(或者坏)注释的第一个受益者(或者受害者)。
11. 更新代码的时候要更新注释
如果注释没有随着代码的修改而更新,那么这些注释将是毫无意义的。代码和注释需要同步,否则注释只会让维护代码的开发者更加痛苦。需要特别注意的是,一些重构的工具会自动更新代码,但是却没有自动更新注释,那么注释就自然而然地过期作废了。
12. 良好可读性代码是注释的金科玉律
对于很多开发者来说,一个基本的原则就是:让代码自己描述自己。虽然有人怀疑这是由不喜欢写注释的程序员所倡导的一场运动,但是无需解释的代码有很大的好处,这些代码更加容易理解甚至让注释变得没有必要。例如,在我的文章Fluid Interfaces中就给大家展示了什么是清晰的无需解释的代码。
Calculator calc = new Calculator();
calc.Set(0);
calc.Add(10);
calc.Multiply(2);
calc.Subtract(4);
Console.WriteLine( “Result: {0}”, calc.Get() );
在这个例子里面,注释就像是违反了第4条技巧那样,变得毫无必要。要写出可读性好的代码,你需要使用适当的命名方式(在经典的 Ottinger’s Rules中有阐述),保证恰当的缩进,并且采用编码风格指导。如果代码不遵守这条技巧,那么注释看起来就好像是为自己不好的代码的写道歉信一样。
13. 跟你的同事分享这些技巧
虽然从第10条技巧中我们已经知道了自己就是好注释的得益者,但是这些技巧对于所有的开发者来说都是很有帮助的,尤其是整个团队都有相同共识的情况下。因此,大方地跟你的同事去分享这些技巧,让我们写出更加容易理解和维护的代码。
以下13个小技巧可以使得你的代码在长时间内依然能够保持容易理解和维护。
1. 对不同级别的代码进行注释
对于不同级别的代码块,要使用统一的方法来进行注释。例如:
对于每一个类,需要包含一段简明扼要的描述,作者和上一次修改的时间
对于每一个方法,需要包含这个方法的用途,功能,参数以及返回结果
当你在一个团队里面的时候,采用一套注释的标准是非常重要的。当然,使用一种大家都认可的注释约定和工具(例如C#的XML注释和Java的Javadoc)在一定程度上能推动这项任务。
2. 使用段落注释
首先把代码块分解成多个“段落”,每一个段落都执行单一的任务;然后在每一个“段落”开始之前添加注释,告诉阅读代码的人接下来的这段代码是干什么用的
// 检查所有记录都是正确的
foreach (Record record in records)
{
if (rec.checkStatus()==Status.OK)
{
. . .
}
}
// 现在开始进行处理
Context ctx = new ApplicationContext();
ctx.BeginTransaction();
. . .
3. 对齐注释行
对于那些在行末写有注释的代码,应该对齐注释行来使得方便阅读
const MAX_ITEMS = 10; // maximum number of packets
const MASK = 0x1F; // mask bit TCP
有些开发人员使用tab来对齐注释,而另外一些人会用空格来对齐。由于tab在不同的编辑器和集成开发环境中会有所不同,所以最佳的方法是使用空格来对齐注释行。
4. 不要侮辱阅读者的智慧
要避免没用的注释,例如
if (a == 5) //如果a等于5
counter = 0 //把counte设为0
这不单把时间浪费在写没用的注释上面,同时也在分散读者的注意力。
5. 要有礼貌
应当避免没有礼貌的注释,例如“要注意一些愚蠢的用户会输入一个负数”,或者“修正由菜鸟工程师写的愚蠢得可怜的代码而导致的副作用”。这样的注释对于代码的写注释的人来说并没有任何好处,同时你永远都不会知道将来这些注释会被谁来阅读,你的老板,一个客户或者是刚才被你数落的愚蠢得可怜的工程师。
6. 直截了当
不要在注释里面写过多的废话。避免在注释里面卖弄ASCII艺术,写笑话,作诗和过于冗长。简而言之就是保持注释的简单和直接。
7. 使用统一的风格
有些人觉得注释应该让非程序员也能看懂。另外一些人觉得注释需要面对的读者只是程序员。无论如何,正如Successful Strategies for Commenting Code中所说的,最重要的是注释的风格需要统一,并且总是面向相同的读者。就自己而论,我怀疑非程序员是否会去读代码,所以我觉得注释应该面向程序员来写。
8. 在内部使用特殊的标签
当你在一个团队里工作的时候,采用一组一致的标签能帮助不同的程序员沟通。例如,很多团队会采用“TODO”标签来表示一段尚未完成的代码
int Estimate(int x, int y)
{
// TODO: implement the calculations
return 0;
}
标签注释并不会解释代码,它们寻求注意或者是传递信息。但是如果适当地使用这种技术,要记住跟进这段代码并且完成该标签传递的任务。
9. 在写代码的同时添加注释
当你在写代码而且记忆犹新的同时就添加注释。如果等到项目后期才添加注释,会让你事倍功半。“我没有时间写注释”,“我的时间很紧迫”和“项目已经延迟了”,这些都是不写注释的常见借口。有些工程师觉最佳的解决方法是“注释先行”。例如:
public void ProcessOrder()
{
// Make sure the products are available
// Check that the customer is valid
// Send the order to the store
// Generate bill
}
10. 把自己想象为注释的读者(事实上就是如此)
当你正在给代码写注释的时候,不仅仅为日后维护你的代码的开发者考虑,同时也设想一下如果自己就是注释的读者。Phil Haack曾经说过:
“一旦一行代码被敲到文件中, 你就已经要开始维护那一行代码了。”
所以,我们自己就是好(或者坏)注释的第一个受益者(或者受害者)。
11. 更新代码的时候要更新注释
如果注释没有随着代码的修改而更新,那么这些注释将是毫无意义的。代码和注释需要同步,否则注释只会让维护代码的开发者更加痛苦。需要特别注意的是,一些重构的工具会自动更新代码,但是却没有自动更新注释,那么注释就自然而然地过期作废了。
12. 良好可读性代码是注释的金科玉律
对于很多开发者来说,一个基本的原则就是:让代码自己描述自己。虽然有人怀疑这是由不喜欢写注释的程序员所倡导的一场运动,但是无需解释的代码有很大的好处,这些代码更加容易理解甚至让注释变得没有必要。例如,在我的文章Fluid Interfaces中就给大家展示了什么是清晰的无需解释的代码。
Calculator calc = new Calculator();
calc.Set(0);
calc.Add(10);
calc.Multiply(2);
calc.Subtract(4);
Console.WriteLine( “Result: {0}”, calc.Get() );
在这个例子里面,注释就像是违反了第4条技巧那样,变得毫无必要。要写出可读性好的代码,你需要使用适当的命名方式(在经典的 Ottinger’s Rules中有阐述),保证恰当的缩进,并且采用编码风格指导。如果代码不遵守这条技巧,那么注释看起来就好像是为自己不好的代码的写道歉信一样。
13. 跟你的同事分享这些技巧
虽然从第10条技巧中我们已经知道了自己就是好注释的得益者,但是这些技巧对于所有的开发者来说都是很有帮助的,尤其是整个团队都有相同共识的情况下。因此,大方地跟你的同事去分享这些技巧,让我们写出更加容易理解和维护的代码。
今天试着用了一下log4j来处理java中的日志,感觉良好,顺便记录一下log4j的配置文件log4j.properties各语句的含义。
这是一个数据库配置文件
#这是一个配置文件实例,PropertyConfigurator将使用这个文件 :
#声明一个appender变量名为JDBC
log4j.rootLogger=DEBUG, JDBC
#JDBC是一个JDBCAppender类,这个类可以写消息到数据库
log4j.appender.JDBC=com.benqguru.palau.log.jdbc.test.JDBCAppender
#1.连接数据库的数据库选项
log4j.appender.JDBC.url=jdbc:mysql://localhost:3306/logtest
log4j.appender.JDBC.username=root
log4j.appender.JDBC.password=
#2.指定你自己的JDBCConnectionHandler的连接器选项
log4j.appender.JDBC.connection_class=com.benqguru.palau.log.jdbc.test.MyConnectionHandler
#3.指定一个静态的SQL语句的SQL选项,这个语句将在每次消息事件发生时被执行
log4j.appender.JDBC.sql=INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')
#4. 指定数据库中一个表的表选项。
log4j.appender.JDBC.table=logtest
#5.描述表的重要列的列选项(非空列是必须被描述的)
log4j.appender.JDBC.columns=id_seq~EMPTY id~ID~MyIDHandler msg~MSG created_on~TIMESTAMP created_by~STATIC~Thomas Fenner
#6.定义消息布局器的布局器选项(可选)
log4j.appender.JDBC.layout=org.apache.log4j.PatternLayout
log4j.appender.JDBC.layout.ConversionPattern=%m
#7.定义消息事件缓冲器的大小的缓冲器选项(可选)
log4j.appender.JDBC.buffer_size=1
#8.定义自动提交的提交选项(可选)
log4j.appender.JDBC.docommit=N
##########下面是英文说明#############
#Date - %d{DATE}[slf5s.DATE]
#Priority - %p[slf5s.PRIORITY]
#NDC - %x[slf5s.NDC]
#Thread - %t[slf5s.THREAD]
#Category - %c[slf5s.CATEGORY]
#Location - %l[slf5s.LOCATION]
#Message - %m[slf5s.MESSAGE]
#
#log4j.appender.R.layout.ConversionPattern=[slf5s.start]%d{DATE}[slf5s.DATE]%n/
# %p[slf5s.PRIORITY]%n%x[slf5s.NDC]%n%t[slf5s.THREAD]%n/
# %c[slf5s.CATEGORY]%n%l[slf5s.LOCATION]%n%m[slf5s.MESSAGE]%n%n
##########下面是中文说明#############
#%m 输出代码中指定的消息
#%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
#%r 输出自应用启动到输出该log信息耗费的毫秒数
#%c 输出所属的类目,通常就是所在类的全名
#%t 输出产生该日志事件的线程名
#%n 输出一个回车换行符,Windows平台为“/r/n”,Unix平台为“/n”
#%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,
#比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
#%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)
这是一个普通的配置文件
#log4j会解析这个文件
log4j.debug=false
#暂时还不清楚这两个属性的作用
log4j.disableOverride=true
log4j.disable=INFO
#设置记录所有类的日志的优先级别
log4j.rootLogger=DEBUG,dest1,dest2
#设置这个包记录日志为假的话
#dist1,dist2就不会记录com.benqguru.commons.logging.test.LoggingSample的日志,只有dist3会记录.
#反之,会记录,这样就会重复记录
log4j.additivity.com.benqguru.commons.logging.test.LoggingSample=false
#特别指定某个特殊包的日志级别和目标设备
log4j.category.com.benqguru.commons.logging.test.LoggingSample=WARN, dest3
#这个目标设备用来debug
log4j.appender.dest1=org.apache.log4j.ConsoleAppender
#log4j.appender.dest1.layout=org.apache.log4j.SimpleLayout
log4j.appender.dest1.layout=org.apache.log4j.PatternLayout
#%-4r %-5p [%t] %37c %3x - %m%n
log4j.appender.dest1.layout.ConversionPattern=%d %p %c - <%m> (%F.%M:%L) %t%n
#这个目标设备用来trace
log4j.appender.dest2=org.apache.log4j.RollingFileAppender
#该文件记录日志的级别(INFO以下的级别不记录)
log4j.appender.dest2.Threshold=INFO
#文件保存路径
log4j.appender.dest2.File=c:/log4jlog.htm
#是否往文件追加信息
log4j.appender.dest2.Append=true
#设置文件最大值
log4j.appender.dest2.MaxFileSize=5KB
#设置备份文件的最大数量
log4j.appender.dest2.MaxBackupIndex=10
#使用一个html格式来记录日志
log4j.appender.dest2.layout=org.apache.log4j.HTMLLayout
#是否打印该消息的代码行
log4j.appender.dest2.layout.LocationInfo=true
#设置该日志的html的标题
log4j.appender.dest2.layout.Title=My app title
#这个目标设备用来trace指定类或包
log4j.appender.dest3=org.apache.log4j.DailyRollingFileAppender
#log4j.appender.dest3.Threshold=DEBUG
log4j.appender.dest3.File=c:/SpecPackageLog.htm
log4j.appender.dest3.Append=false
log4j.appender.dest3.layout=org.apache.log4j.HTMLLayout
log4j.appender.dest3.layout.LocationInfo=true
log4j.appender.dest3.layout.Title=My app title
这是一个数据库配置文件
#这是一个配置文件实例,PropertyConfigurator将使用这个文件 :
#声明一个appender变量名为JDBC
log4j.rootLogger=DEBUG, JDBC
#JDBC是一个JDBCAppender类,这个类可以写消息到数据库
log4j.appender.JDBC=com.benqguru.palau.log.jdbc.test.JDBCAppender
#1.连接数据库的数据库选项
log4j.appender.JDBC.url=jdbc:mysql://localhost:3306/logtest
log4j.appender.JDBC.username=root
log4j.appender.JDBC.password=
#2.指定你自己的JDBCConnectionHandler的连接器选项
log4j.appender.JDBC.connection_class=com.benqguru.palau.log.jdbc.test.MyConnectionHandler
#3.指定一个静态的SQL语句的SQL选项,这个语句将在每次消息事件发生时被执行
log4j.appender.JDBC.sql=INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')
#4. 指定数据库中一个表的表选项。
log4j.appender.JDBC.table=logtest
#5.描述表的重要列的列选项(非空列是必须被描述的)
log4j.appender.JDBC.columns=id_seq~EMPTY id~ID~MyIDHandler msg~MSG created_on~TIMESTAMP created_by~STATIC~Thomas Fenner
#6.定义消息布局器的布局器选项(可选)
log4j.appender.JDBC.layout=org.apache.log4j.PatternLayout
log4j.appender.JDBC.layout.ConversionPattern=%m
#7.定义消息事件缓冲器的大小的缓冲器选项(可选)
log4j.appender.JDBC.buffer_size=1
#8.定义自动提交的提交选项(可选)
log4j.appender.JDBC.docommit=N
##########下面是英文说明#############
#Date - %d{DATE}[slf5s.DATE]
#Priority - %p[slf5s.PRIORITY]
#NDC - %x[slf5s.NDC]
#Thread - %t[slf5s.THREAD]
#Category - %c[slf5s.CATEGORY]
#Location - %l[slf5s.LOCATION]
#Message - %m[slf5s.MESSAGE]
#
#log4j.appender.R.layout.ConversionPattern=[slf5s.start]%d{DATE}[slf5s.DATE]%n/
# %p[slf5s.PRIORITY]%n%x[slf5s.NDC]%n%t[slf5s.THREAD]%n/
# %c[slf5s.CATEGORY]%n%l[slf5s.LOCATION]%n%m[slf5s.MESSAGE]%n%n
##########下面是中文说明#############
#%m 输出代码中指定的消息
#%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
#%r 输出自应用启动到输出该log信息耗费的毫秒数
#%c 输出所属的类目,通常就是所在类的全名
#%t 输出产生该日志事件的线程名
#%n 输出一个回车换行符,Windows平台为“/r/n”,Unix平台为“/n”
#%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,
#比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
#%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)
这是一个普通的配置文件
#log4j会解析这个文件
log4j.debug=false
#暂时还不清楚这两个属性的作用
log4j.disableOverride=true
log4j.disable=INFO
#设置记录所有类的日志的优先级别
log4j.rootLogger=DEBUG,dest1,dest2
#设置这个包记录日志为假的话
#dist1,dist2就不会记录com.benqguru.commons.logging.test.LoggingSample的日志,只有dist3会记录.
#反之,会记录,这样就会重复记录
log4j.additivity.com.benqguru.commons.logging.test.LoggingSample=false
#特别指定某个特殊包的日志级别和目标设备
log4j.category.com.benqguru.commons.logging.test.LoggingSample=WARN, dest3
#这个目标设备用来debug
log4j.appender.dest1=org.apache.log4j.ConsoleAppender
#log4j.appender.dest1.layout=org.apache.log4j.SimpleLayout
log4j.appender.dest1.layout=org.apache.log4j.PatternLayout
#%-4r %-5p [%t] %37c %3x - %m%n
log4j.appender.dest1.layout.ConversionPattern=%d %p %c - <%m> (%F.%M:%L) %t%n
#这个目标设备用来trace
log4j.appender.dest2=org.apache.log4j.RollingFileAppender
#该文件记录日志的级别(INFO以下的级别不记录)
log4j.appender.dest2.Threshold=INFO
#文件保存路径
log4j.appender.dest2.File=c:/log4jlog.htm
#是否往文件追加信息
log4j.appender.dest2.Append=true
#设置文件最大值
log4j.appender.dest2.MaxFileSize=5KB
#设置备份文件的最大数量
log4j.appender.dest2.MaxBackupIndex=10
#使用一个html格式来记录日志
log4j.appender.dest2.layout=org.apache.log4j.HTMLLayout
#是否打印该消息的代码行
log4j.appender.dest2.layout.LocationInfo=true
#设置该日志的html的标题
log4j.appender.dest2.layout.Title=My app title
#这个目标设备用来trace指定类或包
log4j.appender.dest3=org.apache.log4j.DailyRollingFileAppender
#log4j.appender.dest3.Threshold=DEBUG
log4j.appender.dest3.File=c:/SpecPackageLog.htm
log4j.appender.dest3.Append=false
log4j.appender.dest3.layout=org.apache.log4j.HTMLLayout
log4j.appender.dest3.layout.LocationInfo=true
log4j.appender.dest3.layout.Title=My app title
在Sun的API中对在对 “最大匹配Greedy”“最小匹配Reluctant”“完全匹配Possessive”的描述,不能让我明白他们有什么区别,现在将我对这三种匹配模式的理解写出来,供大家参考。
1、Greediness(贪心)匹配:
X?、X*、X+、X{n,}都是最大匹配。例如你要用“<.+>”去匹配“a<tr>aava </tr>abb”,也许你所期待的结果是想匹配“<tr>”,但是实际结果却会匹配到“<tr>aava </tr>”。这是为什么呢?下面我们跟踪下最大匹配的匹配过程。
①“<”匹配字符串的“<”。②“.+”匹配字符串的“tr>aava </tr>ab”,在进行最大匹配时,它把两个“>”都匹配了,它匹配了所有字符,直到文本的最后字符“b” ③这时,发现不能成功匹配“>”,开始按原路回退,用“a”与“>”匹配,直到“ab”前面的“>”匹配成功。
2、Reluctant(Laziness)最小匹配
X?、X*、X+、X{n,}都是最大匹配。好,加个?就成了Laziness匹配。例如X??、X*?、X+?、X{n,}?都是最小匹配,其实X{n,m}?和X{n }?有些多余。
最小匹配意味者,.+? 匹配一个字符后,马上试一试>的匹配可能,失败了,则.+? 再匹配一个字符,再马上试一试>的匹配可能。JDK文档中Greedy 和 Reluctant,它是以eat一口来隐喻的,所以翻译成贪吃和(勉强的)厌食最贴切了。不过我喜欢最大匹配、最小匹配的说法。
3、Possessive完全匹配
与最大匹配不同,还有一种匹配形式:X?+、X*+、X++、X{n,}+等,成为完全匹配。它和最大匹配一样,一直匹配所有的字符,直到文本的最后,但它不由原路返回。也就是说,一口匹配,搞不定就算了,到也干脆,偶喜欢。
Use a possessive quantifier for situations where you want to seize all of something without ever backing off; it will outperform the equivalent greedy quantifier in cases where the match is not immediately found.
1、Greediness(贪心)匹配:
X?、X*、X+、X{n,}都是最大匹配。例如你要用“<.+>”去匹配“a<tr>aava </tr>abb”,也许你所期待的结果是想匹配“<tr>”,但是实际结果却会匹配到“<tr>aava </tr>”。这是为什么呢?下面我们跟踪下最大匹配的匹配过程。
①“<”匹配字符串的“<”。②“.+”匹配字符串的“tr>aava </tr>ab”,在进行最大匹配时,它把两个“>”都匹配了,它匹配了所有字符,直到文本的最后字符“b” ③这时,发现不能成功匹配“>”,开始按原路回退,用“a”与“>”匹配,直到“ab”前面的“>”匹配成功。
2、Reluctant(Laziness)最小匹配
X?、X*、X+、X{n,}都是最大匹配。好,加个?就成了Laziness匹配。例如X??、X*?、X+?、X{n,}?都是最小匹配,其实X{n,m}?和X{n }?有些多余。
最小匹配意味者,.+? 匹配一个字符后,马上试一试>的匹配可能,失败了,则.+? 再匹配一个字符,再马上试一试>的匹配可能。JDK文档中Greedy 和 Reluctant,它是以eat一口来隐喻的,所以翻译成贪吃和(勉强的)厌食最贴切了。不过我喜欢最大匹配、最小匹配的说法。
3、Possessive完全匹配
与最大匹配不同,还有一种匹配形式:X?+、X*+、X++、X{n,}+等,成为完全匹配。它和最大匹配一样,一直匹配所有的字符,直到文本的最后,但它不由原路返回。也就是说,一口匹配,搞不定就算了,到也干脆,偶喜欢。
Use a possessive quantifier for situations where you want to seize all of something without ever backing off; it will outperform the equivalent greedy quantifier in cases where the match is not immediately found.
- import java.util.regex.Matcher;<BR>
- import java.util.regex.Pattern;<BR>
- <BR>
- public class RegularExpression {<BR>
- <BR>
- public static void main(String[] args) {<BR>
- // 简单认识正则表达式的概念<BR>
- <BR>
- // p("abc".matches("..."));<BR>
- // p("a8729a".replaceAll("//d", "-"));<BR>
- // Pattern p = Pattern.compile("[a-z]{3}");<BR>
- // Matcher m = p.matcher("fgh");<BR>
- // p(m.matches());<BR>
- // p("aaas".matches("[a-z]{3,}"));<BR>
- <BR>
- // 初步认识. * + ?<BR>
- <BR>
- // p("a".matches("."));<BR>
- // p("aa".matches("aa"));<BR>
- // p("aaaa".matches("a*"));<BR>
- // p("aaaa".matches("a+"));<BR>
- // p("".matches("a*"));<BR>
- // p("aaaa".matches("a?"));<BR>
- // p("".matches("a?"));<BR>
- // p("a".matches("a?"));<BR>
- // p("214523145234532".matches("//d{3,100}"));<BR>
- // p("192.168.0.231".matches("//d{1,3}//.//d{1,3}//.//d{1,3}//.//d{1,3}"));<BR>
- // p("192".matches("[0-2][0-9][0-9]"));<BR>
- <BR>
- // 范围<BR>
- <BR>
- // p("a".matches("[abc]"));<BR>
- // p("a".matches("[^abc]"));<BR>
- // p("A".matches("[a-zA-Z]"));<BR>
- // p("a".matches("[a-z]|[A-Z]"));<BR>
- // p("A".matches("[a-z[A-Z]]"));<BR>
- // p("R".matches("[A-Z&&[RFG]]"));<BR>
- <BR>
- // 认识/s /w /d /<BR>
- <BR>
- // p(" /n/r/t".matches("//s{4}"));<BR>
- // p(" ".matches("//S"));<BR>
- // p("a_8".matches("//w{3}"));<BR>
- // p("abc888&^%".matches("[a-z]{1,3}//d+[&^#%]+"));<BR>
- // p("//".matches(""));<BR>
- <BR>
- // POSIX Style<BR>
- <BR>
- // p("a".matches("//p{Lower}"));<BR>
- <BR>
- // boundary<BR>
- <BR>
- // p("hello sir".matches("^h.*"));<BR>
- // p("hello sir".matches(".*ir$"));<BR>
- // p("hello sir".matches("^h[a-z]{1,3}o//b.*"));<BR>
- // p("hellosir".matches("^h[a-z]{1,3}o//b.*")); // whilte lines<BR>
- // p("/n".matches("^[//s&&[^//n<BR>
- // p("aaa 8888c".matches(".*//d{4}."));<BR>
- // p("aaa8888c".matches(".*//b//d{4}."));<BR>
- // p("aaa8888c".matches(".*//d{4}."));<BR>
- // p("aaa 8888c".matches(".*//b//d{4}."));<BR>
- <BR>
- // email<BR>
- <BR>
- // p("asdfasdfsafsf@dsdfsdf.com".matches("[//w[.-]]+@[//w[.-]]+//.[//w]+"));<BR>
- <BR>
- // matches find lookingAt<BR>
- <BR>
- // Pattern p = Pattern.compile("//d{3,5}");<BR>
- // String s = "123-34345-234-00";<BR>
- // Matcher m = p.matcher(s);<BR>
- // p(m.matches());<BR>
- // m.reset();<BR>
- // p(m.find());<BR>
- // p(m.start() + "-" + m.end());<BR>
- // p(m.find());<BR>
- // p(m.start() + "-" + m.end());<BR>
- // p(m.find());<BR>
- // p(m.start() + "-" + m.end());<BR>
- // p(m.find());<BR>
- // //p(m.start() + "-" + m.end());<BR>
- // p(m.lookingAt());<BR>
- // p(m.lookingAt());<BR>
- // p(m.lookingAt());<BR>
- // p(m.lookingAt());<BR>
- <BR>
- // replacement<BR>
- <BR>
- // Pattern p = Pattern.compile("java", Pattern.CASE_INSENSITIVE);<BR>
- // Matcher m = p<BR>
- // .matcher("java Java JAVa JaVa IloveJAVA you hateJava afasdfasdf");<BR>
- // StringBuffer buf = new StringBuffer();<BR>
- // int i = 0;<BR>
- // while (m.find()) {<BR>
- // i++;<BR>
- // if (i % 2 == 0) {<BR>
- // m.appendReplacement(buf, "java");<BR>
- // } else {<BR>
- // m.appendReplacement(buf, "JAVA");<BR>
- // }<BR>
- // }<BR>
- // m.appendTail(buf);<BR>
- // p(buf);<BR>
- <BR>
- // group<BR>
- <BR>
- // Pattern p = Pattern.compile("(//d{3,5})([a-z]{2})");<BR>
- // String s = "123aa-34345bb-234cc-00";<BR>
- // Matcher m = p.matcher(s);<BR>
- // while (m.find()) {<BR>
- // p(m.group());<BR>
- // }<BR>
- <BR>
- // qulifiers<BR>
- <BR>
- // Pattern p = Pattern.compile(".{3,10}+[0-9]");<BR>
- // String s = "aaaa5bbbb68";<BR>
- // Matcher m = p.matcher(s);<BR>
- // if (m.find())<BR>
- // p(m.start() + "-" + m.end());<BR>
- // else<BR>
- // p("not match!");<BR>
- <BR>
- // non-capturing groups<BR>
- <BR>
- // Pattern p = Pattern.compile(".{3}(?=a)");<BR>
- // String s = "444a66b";<BR>
- // Matcher m = p.matcher(s);<BR>
- // while (m.find()) {<BR>
- // p(m.group());<BR>
- // }<BR>
- <BR>
- // back refenrences<BR>
- <BR>
- // Pattern p = Pattern.compile("(//d(//d))//2");<BR>
- // String s = "122";<BR>
- // Matcher m = p.matcher(s);<BR>
- // p(m.matches());<BR>
- <BR>
- // flags的简写<BR>
- Pattern p = Pattern.compile("java", Pattern.CASE_INSENSITIVE);<BR>
- p("Java".matches("(?i)(java)"));<BR>
- }<BR>
- <BR>
- public staticvoid p(Object o) {<BR>
- System.out.println(o);<BR>
- }<BR>
- <BR>
- }<BR>
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- public class RegularExpression {
- public static void main(String[] args) {
- // 简单认识正则表达式的概念
- // p("abc".matches("..."));
- // p("a8729a".replaceAll("//d", "-"));
- // Pattern p = Pattern.compile("[a-z]{3}");
- // Matcher m = p.matcher("fgh");
- // p(m.matches());
- // p("aaas".matches("[a-z]{3,}"));
- // 初步认识. * + ?
- // p("a".matches("."));
- // p("aa".matches("aa"));
- // p("aaaa".matches("a*"));
- // p("aaaa".matches("a+"));
- // p("".matches("a*"));
- // p("aaaa".matches("a?"));
- // p("".matches("a?"));
- // p("a".matches("a?"));
- // p("214523145234532".matches("//d{3,100}"));
- // p("192.168.0.231".matches("//d{1,3}//.//d{1,3}//.//d{1,3}//.//d{1,3}"));
- // p("192".matches("[0-2][0-9][0-9]"));
- // 范围
- // p("a".matches("[abc]"));
- // p("a".matches("[^abc]"));
- // p("A".matches("[a-zA-Z]"));
- // p("a".matches("[a-z]|[A-Z]"));
- // p("A".matches("[a-z[A-Z]]"));
- // p("R".matches("[A-Z&&[RFG]]"));
- // 认识/s /w /d /
- // p(" /n/r/t".matches("//s{4}"));
- // p(" ".matches("//S"));
- // p("a_8".matches("//w{3}"));
- // p("abc888&^%".matches("[a-z]{1,3}//d+[&^#%]+"));
- // p("//".matches(""));
- // POSIX Style
- // p("a".matches("//p{Lower}"));
- // boundary
- // p("hello sir".matches("^h.*"));
- // p("hello sir".matches(".*ir$"));
- // p("hello sir".matches("^h[a-z]{1,3}o//b.*"));
- // p("hellosir".matches("^h[a-z]{1,3}o//b.*")); // whilte lines
- // p("/n".matches("^[//s&&[^//n
- // p("aaa 8888c".matches(".*//d{4}."));
- // p("aaa8888c".matches(".*//b//d{4}."));
- // p("aaa8888c".matches(".*//d{4}."));
- // p("aaa 8888c".matches(".*//b//d{4}."));
- // p("asdfasdfsafsf@dsdfsdf.com".matches("[//w[.-]]+@[//w[.-]]+//.[//w]+"));
- // matches find lookingAt
- // Pattern p = Pattern.compile("//d{3,5}");
- // String s = "123-34345-234-00";
- // Matcher m = p.matcher(s);
- // p(m.matches());
- // m.reset();
- // p(m.find());
- // p(m.start() + "-" + m.end());
- // p(m.find());
- // p(m.start() + "-" + m.end());
- // p(m.find());
- // p(m.start() + "-" + m.end());
- // p(m.find());
- // //p(m.start() + "-" + m.end());
- // p(m.lookingAt());
- // p(m.lookingAt());
- // p(m.lookingAt());
- // p(m.lookingAt());
- // replacement
- // Pattern p = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
- // Matcher m = p
- // .matcher("java Java JAVa JaVa IloveJAVA you hateJava afasdfasdf");
- // StringBuffer buf = new StringBuffer();
- // int i = 0;
- // while (m.find()) {
- // i++;
- // if (i % 2 == 0) {
- // m.appendReplacement(buf, "java");
- // } else {
- // m.appendReplacement(buf, "JAVA");
- // }
- // }
- // m.appendTail(buf);
- // p(buf);
- // group
- // Pattern p = Pattern.compile("(//d{3,5})([a-z]{2})");
- // String s = "123aa-34345bb-234cc-00";
- // Matcher m = p.matcher(s);
- // while (m.find()) {
- // p(m.group());
- // }
- // qulifiers
- // Pattern p = Pattern.compile(".{3,10}+[0-9]");
- // String s = "aaaa5bbbb68";
- // Matcher m = p.matcher(s);
- // if (m.find())
- // p(m.start() + "-" + m.end());
- // else
- // p("not match!");
- // non-capturing groups
- // Pattern p = Pattern.compile(".{3}(?=a)");
- // String s = "444a66b";
- // Matcher m = p.matcher(s);
- // while (m.find()) {
- // p(m.group());
- // }
- // back refenrences
- // Pattern p = Pattern.compile("(//d(//d))//2");
- // String s = "122";
- // Matcher m = p.matcher(s);
- // p(m.matches());
- // flags的简写
- Pattern p = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
- p("Java".matches("(?i)(java)"));
- }
- public static void p(Object o) {
- System.out.println(o);
- }
- }
- if (Character.isLowerCase(ch[i])) {<BR>
- char temp = Character.toUpperCase(ch[i]);<BR>
- ch[i] = temp;<BR>
- } else if (Character.isUpperCase(ch[i])) {<BR>
- char temp = Character.toLowerCase(ch[i]);<BR>
- ch[i] = temp;<BR>
- }
- if (Character.isLowerCase(ch[i])) {
- char temp = Character.toUpperCase(ch[i]);
- ch[i] = temp;
- } else if (Character.isUpperCase(ch[i])) {
- char temp = Character.toLowerCase(ch[i]);
- ch[i] = temp;
- }
- while (true) {<BR>
- flag = 0;<BR>
- result = tc.getNum();<BR>
- >
- input = Integer.parseInt(br.readLine());<BR>
- >
- while (flag == 0) {<BR>
- if (input == result) {<BR>
- System.out.println("very good!");<BR>
- flag = 1;<BR>
- } else {<BR>
- flag = 0;<BR>
- System.out.println("no,please try again.");<BR>
- input = Integer.parseInt(br.readLine());<BR>
- }<BR>
- }<BR>
- }
- while (true) {
- flag = 0;
- result = tc.getNum();
- input = Integer.parseInt(br.readLine());
- while (flag == 0) {
- if (input == result) {
- System.out.println("very good!");
- flag = 1;
- } else {
- flag = 0;
- System.out.println("no,please try again.");
- input = Integer.parseInt(br.readLine());
- }
- }
- }
- L1: while (true) {<BR>
- int question = new Times().question();<BR>
- L2: while (true) {<BR>
- int answer = scanner.nextInt();<BR>
- if (answer == question) {<BR>
- System.out.println("very good!");<BR>
- continue L1;<BR>
- } else {<BR>
- System.out.println("no,please try again.");<BR>
- continue L2;<BR>
- }<BR>
- }<BR>
- }
- L1: while (true) {
- int question = new Times().question();
- L2: while (true) {
- int answer = scanner.nextInt();
- if (answer == question) {
- System.out.println("very good!");
- continue L1;
- } else {
- System.out.println("no,please try again.");
- continue L2;
- }
- }
- }
- char[] c1 = s1.toCharArray();
- char[] c1 = s1.toCharArray();
- Map<Integer, String> m =new HashMap<Integer, String>();<BR>
- m.put(1, "one");<BR>
- m.put(2, "two");<BR>
- m.put(3, "three");<BR>
- Object o[] = m.values().toArray();
- Map<Integer, String> m = new HashMap<Integer, String>();
- m.put(1, "one");
- m.put(2, "two");
- m.put(3, "three");
- Object o[] = m.values().toArray();
- GregorianCalendar ca = new GregorianCalendar();<BR>
- System.out.println(ca.get(GregorianCalendar.AM_PM));
- GregorianCalendar ca = new GregorianCalendar();
- System.out.println(ca.get(GregorianCalendar.AM_PM));
- for (int i =0; i < number.length; i++) {<BR>
- for (int j = 0; j < number.length - i - 1; j++) {<BR>
- if (number[j] < number[j + 1]) {<BR>
- int temp = number[j];<BR>
- number[j] = number[j + 1];<BR>
- number[j + 1] = temp;<BR>
- }<BR>
- }<BR>
- }<BR>
- for (int i = 0; i < number.length; i++) {
- for (int j = 0; j < number.length - i - 1; j++) {
- if (number[j] < number[j + 1]) {
- int temp = number[j];
- number[j] = number[j + 1];
- number[j + 1] = temp;
- }
- }
- }
- BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));<BR>
- String str = buffer.readLine();
- BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));
- String str = buffer.readLine();
- Scanner scanner = new Scanner(System.in);<BR>
- String line = scanner.nextLine();
- Scanner scanner = new Scanner(System.in);
- String line = scanner.nextLine();
- InputStreamReader in = null;<BR>
- OutputStreamWriter out = null;<BR>
- BufferedReader br = null;<BR>
- <BR>
- in = new InputStreamReader(new FileInputStream(System<BR>
- .getProperty("user.dir")<BR>
- + "//old.txt"), "UTF-8");<BR>
- br = new BufferedReader(in);<BR>
- out = new OutputStreamWriter(new FileOutputStream(<BR>
- "C://WINDOWS//new.txt"), "UTF-8");
- InputStreamReader in = null;
- OutputStreamWriter out = null;
- BufferedReader br = null;
- in = new InputStreamReader(new FileInputStream(System
- .getProperty("user.dir")
- + "//old.txt"), "UTF-8");
- br = new BufferedReader(in);
- out = new OutputStreamWriter(new FileOutputStream(
- "C://WINDOWS//new.txt"), "UTF-8");
- String[] temp;<BR>
- temp = text.split("//s{1,}");// 按照空格分割字符串,多个空格作为一个空格对字符串进行分割
- String[] temp;
- temp = text.split("//s{1,}");// 按照空格分割字符串,多个空格作为一个空格对字符串进行分割
- String s = String.valueOf(1);<BR>
- if (s.charAt(i) == '1'){}<BR>
- String s = String.valueOf(1);
- if (s.charAt(i) == '1'){}
- System.nanoTime();
- System.nanoTime();
- SimpleDateFormat sdt = new SimpleDateFormat("yyyy-MM-DD HH:mm:ss");<BR>
- String date = sdt.format(System.currentTimeMillis());
- SimpleDateFormat sdt = new SimpleDateFormat("yyyy-MM-DD HH:mm:ss");
- String date = sdt.format(System.currentTimeMillis());
- DecimalFormat df = (DecimalFormat) NumberFormat.getInstance();<BR>
- df.applyPattern("00");
- DecimalFormat df = (DecimalFormat) NumberFormat.getInstance();
- df.applyPattern("00");
- Calendar current_time = new GregorianCalendar();<BR>
- Calendar target_time = new GregorianCalendar(2009, Calendar.MAY,1);<BR>
- long diffMillis = target_time.getTimeInMillis()<BR>
- - current_time.getTimeInMillis();<BR>
- long diffDays = diffMillis / (24 *60 * 60 * 1000) + 1;<BR>
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-DD");<BR>
- <BR>
- System.out.println("距" + sdf.format(target_time.getTimeInMillis())<BR>
- + "还有:" + diffDays + "天");
- Calendar current_time = new GregorianCalendar();
- Calendar target_time = new GregorianCalendar(2009, Calendar.MAY, 1);
- long diffMillis = target_time.getTimeInMillis()
- - current_time.getTimeInMillis();
- long diffDays = diffMillis / (24 * 60 * 60 * 1000) + 1;
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-DD");
- System.out.println("距" + sdf.format(target_time.getTimeInMillis())
- + "还有:" + diffDays + "天");
- Arrays.sort(array);
- Arrays.sort(array);
- StringBuilder s1 = new StringBuilder(temp);<BR>
- // StringBuffer s1 = new StringBuffer(temp);<BR>
- System.out.println(s1.reverse());
- StringBuilder s1 = new StringBuilder(temp);
- // StringBuffer s1 = new StringBuffer(temp);
- System.out.println(s1.reverse());
我是个彩民,特别喜欢买双色球。最近看了几种算法,写了一个很简单的双色球的算号器,分享给大家,仅供娱乐,呵呵
- package com.tester.luckly;<BR>
- <BR>
- import java.util.Random;<BR>
- import java.util.Scanner;<BR>
- import java.util.Set;<BR>
- import java.util.TreeSet;<BR>
- <BR>
- public class DoubleBall {<BR>
- private Set<Integer> hs;<BR>
- private static boolean flag = true;<BR>
- <BR>
- public enum oddOrEven {<BR>
- Odd, Even<BR>
- }<BR>
- <BR>
- public enum aOrBOrC {<BR>
- A, B, C<BR>
- }<BR>
- <BR>
- public static void main(String[] args) {<BR>
- DoubleBall db = new DoubleBall();<BR>
- System.out<BR>
- .println("====================Red Balls============================");<BR>
- String url1 = "http://map.zhcw.com/ssq//html/h7fenqu_ascstr=20.html";<BR>
- System.out.println();<BR>
- System.err.println(url1);<BR>
- Scanner scanner1 = new Scanner(System.in);<BR>
- System.out.println("Please select:1.Input your own two numbers");<BR>
- System.out<BR>
- .println(" 2.Input the range of the two numbers");<BR>
- int select = scanner1.nextInt();<BR>
- if (select == 1) {<BR>
- flag = false;<BR>
- } else {<BR>
- flag = true;<BR>
- }<BR>
- System.out.println("Please input two numbers like this: 1 2");<BR>
- scanner1.nextLine();<BR>
- String[] str = scanner1.nextLine().split(" ");<BR>
- Set<Integer> redBall = db.calcRedBall(Integer.parseInt(str[0]), Integer<BR>
- .parseInt(str[1]));<BR>
- <BR>
- System.out<BR>
- .println("====================Blue Ball============================");<BR>
- String url2 = "http://sports.sohu.com/s2007/0445/s252476794/";<BR>
- System.out.println();<BR>
- System.err.println(url2);<BR>
- Scanner scanner2 = new Scanner(System.in);<BR>
- <BR>
- System.out.println("1.Please select:" + oddOrEven.Odd +" or "<BR>
- + oddOrEven.Even);<BR>
- String select1 = scanner2.nextLine();<BR>
- <BR>
- System.out.println("2.Please select:" + aOrBOrC.A +" or " + aOrBOrC.B<BR>
- + " or " + aOrBOrC.C);<BR>
- String select2 = scanner2.nextLine();<BR>
- <BR>
- int blueBall = db.calcBlueBall(select1, select2);<BR>
- <BR>
- System.out<BR>
- .println("====================The Result============================");<BR>
- System.out.println();<BR>
- for (int i : redBall) {<BR>
- System.err.print(i + " ");<BR>
- }<BR>
- System.err.println("| " + blueBall);<BR>
- String url3 = "http://www.bwlc.net/dhtz/";<BR>
- System.err.println(url3);<BR>
- }<BR>
- <BR>
- public Set<Integer> calcRedBall(int begin,int end) {<BR>
- hs = new TreeSet<Integer>();<BR>
- <BR>
- /*<BR>
- * 固定恒码:就是每期都必备的号码,通常为2-3个,如02,13,27。 这2-3个号码长期备选(至少50期)。<BR>
- */<BR>
- hs.add(10);<BR>
- hs.add(27);<BR>
- <BR>
- /*<BR>
- * 边缘捡“胆”:就是在边缘码“05、10、15、20、25、30”及“01、<BR>
- * 06、11、16、21、26、31”共13个号码中巧妙地捡出胆码。之所以 把边缘码作为“胆”码的一种选取方法,是因为从历史中奖号码来看,<BR>
- * 几乎每一期都会在具备这种特性的号码中出现2-3个。<BR>
- */<BR>
- int[] array = { 5, 15, 20, 25, 30, 01, 06, 11, 16, 21, 26, 31 };<BR>
- for (int i = 0; i < 2; i++) {<BR>
- Random random = new Random();<BR>
- hs.add(array[random.nextInt(array.length)]);<BR>
- }<BR>
- <BR>
- /*<BR>
- * 重码追邻:在上上期出号的左右选取的上期号码,这话不好理解,现举例说明:双色球2007109期开01 04 07 08 13 14;<BR>
- * 110期开02 04 07 15 24 28;其中这期的04<BR>
- * 07,刚好是109期二、三位的重码,那么111期的重码该选取哪个呢?重码追邻就是在110期的04 07 的左右选取,110期04 07<BR>
- * 的左右是02 15。所以111期的备选号就是02 15 了。实际上111期重码刚好开的是02。重码追邻一般选取2-3个号码。<BR>
- */<BR>
- /* ========= 或者========= */<BR>
- /* 旺区落“胆”。就是在最近几期热号区选择胆码。比如,如果最近的5期内,在中区12~22出号比较密集,那么就要在这个区域里选取3个胆码。这样选择的理由是热码恒热原理,即号码总是在某个区域相对集中出现。 */<BR>
- if (flag == true) {<BR>
- hs.add(begin);<BR>
- hs.add(end);<BR>
- } else {<BR>
- Random random = new Random();<BR>
- hs.add(begin + random.nextInt(end - begin + 1));<BR>
- }<BR>
- /* Luck number! */<BR>
- while (hs.size() < 6) {<BR>
- Random random = new Random();<BR>
- hs.add(1 + random.nextInt(33));<BR>
- }<BR>
- <BR>
- return hs;<BR>
- }<BR>
- <BR>
- public int calcBlueBall(String select1, String select2) {<BR>
- int begin = 1;<BR>
- int end = 16;<BR>
- <BR>
- if (select2.equals(aOrBOrC.A.toString())) {<BR>
- begin = 1;<BR>
- end = 5;<BR>
- } else if (select2.equals(aOrBOrC.B.toString())) {<BR>
- begin = 6;<BR>
- end = 10;<BR>
- } else {<BR>
- begin = 11;<BR>
- end = 16;<BR>
- }<BR>
- <BR>
- Random random = new Random();<BR>
- int blueBall = begin + random.nextInt(end - begin +1);<BR>
- if (select1.equals(oddOrEven.Odd.toString())) {<BR>
- while (blueBall % 2 == 0) {<BR>
- blueBall = begin + random.nextInt(end - begin + 1);<BR>
- }<BR>
- } else {<BR>
- while (blueBall % 2 == 1) {<BR>
- blueBall = begin + random.nextInt(end - begin + 1);<BR>
- }<BR>
- }<BR>
- <BR>
- return blueBall;<BR>
- }<BR>
- }
- package com.tester.luckly;
- import java.util.Random;
- import java.util.Scanner;
- import java.util.Set;
- import java.util.TreeSet;
- public class DoubleBall {
- private Set<Integer> hs;
- private static boolean flag = true;
- public enum oddOrEven {
- Odd, Even
- }
- public enum aOrBOrC {
- A, B, C
- }
- public static void main(String[] args) {
- DoubleBall db = new DoubleBall();
- System.out
- .println("====================Red Balls============================");
- String url1 = "http://map.zhcw.com/ssq//html/h7fenqu_ascstr=20.html";
- System.out.println();
- System.err.println(url1);
- Scanner scanner1 = new Scanner(System.in);
- System.out.println("Please select:1.Input your own two numbers");
- System.out
- .println(" 2.Input the range of the two numbers");
- int select = scanner1.nextInt();
- if (select == 1) {
- flag = false;
- } else {
- flag = true;
- }
- System.out.println("Please input two numbers like this: 1 2");
- scanner1.nextLine();
- String[] str = scanner1.nextLine().split(" ");
- Set<Integer> redBall = db.calcRedBall(Integer.parseInt(str[0]), Integer
- .parseInt(str[1]));
- System.out
- .println("====================Blue Ball============================");
- String url2 = "http://sports.sohu.com/s2007/0445/s252476794/";
- System.out.println();
- System.err.println(url2);
- Scanner scanner2 = new Scanner(System.in);
- System.out.println("1.Please select:" + oddOrEven.Odd + " or "
- + oddOrEven.Even);
- String select1 = scanner2.nextLine();
- System.out.println("2.Please select:" + aOrBOrC.A + " or " + aOrBOrC.B
- + " or " + aOrBOrC.C);
- String select2 = scanner2.nextLine();
- int blueBall = db.calcBlueBall(select1, select2);
- System.out
- .println("====================The Result============================");
- System.out.println();
- for (int i : redBall) {
- System.err.print(i + " ");
- }
- System.err.println("| " + blueBall);
- String url3 = "http://www.bwlc.net/dhtz/";
- System.err.println(url3);
- }
- public Set<Integer> calcRedBall(int begin, int end) {
- hs = new TreeSet<Integer>();
- /*
- * 固定恒码:就是每期都必备的号码,通常为2-3个,如02,13,27。 这2-3个号码长期备选(至少50期)。
- */
- hs.add(10);
- hs.add(27);
- /*
- * 边缘捡“胆”:就是在边缘码“05、10、15、20、25、30”及“01、
- * 06、11、16、21、26、31”共13个号码中巧妙地捡出胆码。之所以 把边缘码作为“胆”码的一种选取方法,是因为从历史中奖号码来看,
- * 几乎每一期都会在具备这种特性的号码中出现2-3个。
- */
- int[] array = { 5, 15, 20, 25, 30, 01, 06, 11, 16, 21, 26, 31 };
- for (int i = 0; i < 2; i++) {
- Random random = new Random();
- hs.add(array[random.nextInt(array.length)]);
- }
- /*
- * 重码追邻:在上上期出号的左右选取的上期号码,这话不好理解,现举例说明:双色球2007109期开01 04 07 08 13 14;
- * 110期开02 04 07 15 24 28;其中这期的04
- * 07,刚好是109期二、三位的重码,那么111期的重码该选取哪个呢?重码追邻就是在110期的04 07 的左右选取,110期04 07
- * 的左右是02 15。所以111期的备选号就是02 15 了。实际上111期重码刚好开的是02。重码追邻一般选取2-3个号码。
- */
- /* ========= 或者========= */
- /* 旺区落“胆”。就是在最近几期热号区选择胆码。比如,如果最近的5期内,在中区12~22出号比较密集,那么就要在这个区域里选取3个胆码。这样选择的理由是热码恒热原理,即号码总是在某个区域相对集中出现。 */
- if (flag == true) {
- hs.add(begin);
- hs.add(end);
- } else {
- Random random = new Random();
- hs.add(begin + random.nextInt(end - begin + 1));
- }
- /* Luck number! */
- while (hs.size() < 6) {
- Random random = new Random();
- hs.add(1 + random.nextInt(33));
- }
- return hs;
- }
- public int calcBlueBall(String select1, String select2) {
- int begin = 1;
- int end = 16;
- if (select2.equals(aOrBOrC.A.toString())) {
- begin = 1;
- end = 5;
- } else if (select2.equals(aOrBOrC.B.toString())) {
- begin = 6;
- end = 10;
- } else {
- begin = 11;
- end = 16;
- }
- Random random = new Random();
- int blueBall = begin + random.nextInt(end - begin + 1);
- if (select1.equals(oddOrEven.Odd.toString())) {
- while (blueBall % 2 == 0) {
- blueBall = begin + random.nextInt(end - begin + 1);
- }
- } else {
- while (blueBall % 2 == 1) {
- blueBall = begin + random.nextInt(end - begin + 1);
- }
- }
- return blueBall;
- }
- }
- 12:29
- 浏览 (2864)
- 论坛浏览 (4047)
- 评论 (7)
- 分类: Java
- 收藏
今天遇到了一个极其郁闷的问题,想写一段代码,可以给windows自动安装一种字体。
原理就是将4个ttf字体文件复制到C://WINDOWS//Fonts//目录下。本来以为很简单,但用java I/O复制过去的字体不能使用(将记事本的字体改成DejaVuSansMono,如果有效果变化,就是正常的),直接手动复制同样的文件过去,就可以使用。不知道问题出在哪里?
哪位朋友帮忙看看,万分感谢,字体文件在附件中,代码如下:
用另一种写法试了下,也是不行,复制过去的文件大小是相同的,用比较工具比较也没问题。
原理就是将4个ttf字体文件复制到C://WINDOWS//Fonts//目录下。本来以为很简单,但用java I/O复制过去的字体不能使用(将记事本的字体改成DejaVuSansMono,如果有效果变化,就是正常的),直接手动复制同样的文件过去,就可以使用。不知道问题出在哪里?
哪位朋友帮忙看看,万分感谢,字体文件在附件中,代码如下:
- package com.test;<BR>
- import java.io.FileInputStream;<BR>
- import java.io.FileOutputStream;<BR>
- import java.io.IOException;<BR>
- import java.nio.channels.FileChannel;<BR>
- <BR>
- public class FontInstall {<BR>
- <BR>
- public staticvoid main(String[] args) {<BR>
- try {<BR>
- String[] fonts = { "DejaVuSansMono-Oblique.ttf",<BR>
- "DejaVuSansMono-BoldOblique.ttf", "DejaVuSansMono.ttf",<BR>
- "DejaVuSansMono-Bold.ttf" };<BR>
- System.out.println();<BR>
- for (int i =0; i < fonts.length; i++) {<BR>
- // Create channel on the source<BR>
- FileChannel srcChannel = new FileInputStream(System<BR>
- .getProperty("user.dir")<BR>
- + "//" + fonts[i]).getChannel();<BR>
- <BR>
- // Create channel on the destination<BR>
- FileChannel dstChannel = new FileOutputStream(<BR>
- "C://WINDOWS//Fonts//" + fonts[i]).getChannel();<BR>
- <BR>
- // Copy file contents from source to destination<BR>
- dstChannel.transferFrom(srcChannel, 0, srcChannel.size());<BR>
- <BR>
- // Close the channels<BR>
- srcChannel.close();<BR>
- dstChannel.close();<BR>
- }<BR>
- } catch (IOException e) {<BR>
- }<BR>
- }<BR>
- }<BR>
- package com.test;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.nio.channels.FileChannel;
- public class FontInstall {
- public static void main(String[] args) {
- try {
- String[] fonts = { "DejaVuSansMono-Oblique.ttf",
- "DejaVuSansMono-BoldOblique.ttf", "DejaVuSansMono.ttf",
- "DejaVuSansMono-Bold.ttf" };
- System.out.println();
- for (int i = 0; i < fonts.length; i++) {
- // Create channel on the source
- FileChannel srcChannel = new FileInputStream(System
- .getProperty("user.dir")
- + "//" + fonts[i]).getChannel();
- // Create channel on the destination
- FileChannel dstChannel = new FileOutputStream(
- "C://WINDOWS//Fonts//" + fonts[i]).getChannel();
- // Copy file contents from source to destination
- dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
- // Close the channels
- srcChannel.close();
- dstChannel.close();
- }
- } catch (IOException e) {
- }
- }
- }
用另一种写法试了下,也是不行,复制过去的文件大小是相同的,用比较工具比较也没问题。
- package com.test;<BR>
- <BR>
- import java.io.FileInputStream;<BR>
- import java.io.FileOutputStream;<BR>