├── Hexo搭建博客.md ├── Java8系列.md ├── README.md ├── SUMMARY.md ├── _book ├── Hexo搭建博客.html ├── Java8系列.html ├── articles │ ├── electrical-business-architecture.html │ ├── request_message.html │ └── software_architecture_patterns.html ├── cache │ ├── 1.concept.md │ ├── 2.spring.md │ ├── 3.myioc.md │ └── index.md ├── designpatterns │ ├── proxy.html │ ├── singleton.html │ └── strategy.html ├── git │ └── guide.html ├── gitbook │ ├── fonts │ │ └── fontawesome │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ ├── gitbook-plugin-fontsettings │ │ ├── fontsettings.js │ │ └── website.css │ ├── gitbook-plugin-highlight │ │ ├── ebook.css │ │ └── website.css │ ├── gitbook-plugin-livereload │ │ └── plugin.js │ ├── gitbook-plugin-lunr │ │ ├── lunr.min.js │ │ └── search-lunr.js │ ├── gitbook-plugin-search │ │ ├── lunr.min.js │ │ ├── search-engine.js │ │ ├── search.css │ │ └── search.js │ ├── gitbook-plugin-sharing │ │ └── buttons.js │ ├── gitbook.js │ ├── images │ │ ├── apple-touch-icon-precomposed-152.png │ │ └── favicon.ico │ ├── style.css │ └── theme.js ├── hexo │ ├── config.html │ ├── hello.html │ └── writing.html ├── http │ └── request_message.md ├── index.html ├── ioc │ ├── 1.concept.html │ ├── 2.spring.html │ ├── 3.myioc.html │ ├── 4.principle.html │ ├── IOC容器实现篇 │ ├── IOC容器实现篇.html │ └── index.md ├── java8 │ ├── foreach.html │ └── java8-guide.html ├── learn_server │ ├── config-nginx-proxy.md │ ├── config-tomcat-service.md │ ├── index.html │ ├── init-os.md │ ├── install-jdk.md │ ├── install-mysql.md │ ├── install-nginx.md │ ├── install-redis.md │ ├── install-svn.md │ ├── install-tomcat.md │ ├── optimization-nginx.md │ ├── optimization-tomcat8.md │ ├── use-jemeter-test-tomcat.md │ └── virtual-machine-install-centos6.md ├── mvc │ ├── 1.plan.html │ ├── 2.route.html │ ├── 3.controller.html │ ├── 4.config.html │ ├── 5.view.html │ ├── 6.dbutil.html │ ├── 7.crud.html │ ├── MVC框架实现篇.html │ └── index.html ├── search_index.json ├── shell │ ├── download-jdk.md │ ├── install_jdk_tomcat.sh │ ├── tomcat.md │ └── uninstall_jdk_tomcat.sh ├── simple-java │ ├── classes-and-interfaces │ │ └── README.md │ ├── collections │ │ └── README.md │ ├── common-methods │ │ └── README.md │ ├── compiler-and-jvm │ │ └── README.md │ ├── concurrency │ │ └── README.md │ ├── exceptions │ │ └── README.md │ ├── generics │ │ └── README.md │ ├── io-and-database │ │ └── README.md │ ├── reflection-tutorial.md │ └── string-and-array │ │ ├── README.md │ │ ├── diagram-to-show-java-strings-immutability.md │ │ └── the-substring-method-in-jdk-6-and-jdk-7.md ├── user_guide │ └── google-java8-guide.html ├── web │ └── test_tool.html ├── 实用工具.html ├── 开发者指南.html ├── 开源组件实现.html ├── 服务器相关.html ├── 经典文章.html ├── 设计模式系列.html └── 运维相关.html ├── articles ├── electrical-business-architecture.md ├── request_message.md └── software_architecture_patterns.md ├── cache ├── 1.concept.md ├── 2.spring.md ├── 3.myioc.md └── index.md ├── designpatterns ├── proxy.md ├── singleton.md └── strategy.md ├── git └── guide.md ├── hexo ├── config.md ├── hello.md └── writing.md ├── http └── request_message.md ├── ioc ├── 1.concept.md ├── 2.spring.md ├── 3.myioc.md ├── 4.principle.md ├── IOC容器实现篇 ├── IOC容器实现篇.md └── index.md ├── java8 ├── foreach.md └── java8-guide.md ├── learn_server ├── README.md ├── config-nginx-proxy.md ├── config-tomcat-service.md ├── init-os.md ├── install-jdk.md ├── install-mysql.md ├── install-nginx.md ├── install-redis.md ├── install-svn.md ├── install-tomcat.md ├── optimization-nginx.md ├── optimization-tomcat8.md ├── use-jemeter-test-tomcat.md └── virtual-machine-install-centos6.md ├── mvc ├── 1.plan.md ├── 2.route.md ├── 3.controller.md ├── 4.config.md ├── 5.view.md ├── 6.dbutil.md ├── 7.crud.md ├── MVC框架实现篇.md └── index.md ├── shell ├── download-jdk.md ├── install_jdk_tomcat.sh ├── tomcat.md └── uninstall_jdk_tomcat.sh ├── simple-java ├── classes-and-interfaces │ └── README.md ├── collections │ └── README.md ├── common-methods │ └── README.md ├── compiler-and-jvm │ └── README.md ├── concurrency │ └── README.md ├── exceptions │ └── README.md ├── generics │ └── README.md ├── io-and-database │ └── README.md ├── reflection-tutorial.md └── string-and-array │ ├── README.md │ ├── diagram-to-show-java-strings-immutability.md │ └── the-substring-method-in-jdk-6-and-jdk-7.md ├── user_guide └── google-java8-guide.md ├── web └── test_tool.md ├── 实用工具.md ├── 开发者指南.md ├── 开源组件实现.md ├── 服务器相关.md ├── 经典文章.md ├── 设计模式系列.md └── 运维相关.md /Hexo搭建博客.md: -------------------------------------------------------------------------------- 1 | # Hexo搭建博客 2 | 3 | -------------------------------------------------------------------------------- /Java8系列.md: -------------------------------------------------------------------------------- 1 | # Java8系列 2 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # java-bible 2 | 3 | 这里记录了一些技术摘要,部分文章来自网络,本项目的目的力求分享精品技术干货,以Java为主。 4 | 5 | 如果你喜欢,`star` 便是,持续更新ing,还有Fork项目的同学,貌似没有什么卵用。。。 6 | 7 | ## 目录 8 | 9 | * [开源组件实现](#开源组件实现) 10 | * [MVC框架实现篇](#MVC框架实现篇) 11 | * [IOC容器实现篇](#IOC容器实现篇) 12 | * [设计模式系列](#设计模式系列) 13 | * [Java8系列](#Java8系列) 14 | * [Hexo搭建博客](#Hexo搭建博客) 15 | * [开发者指南](#开发者指南) 16 | * [运维相关](#运维相关) 17 | * [经典文章](#经典文章) 18 | 19 | ## 开源组件实现 20 | 21 | ### [MVC框架实现篇](mvc/index.md) 22 | 23 | * [项目规划](mvc/1.plan.md) 24 | * [路由设计](mvc/2.route.md) 25 | * [控制器设计](mvc/3.controller.md) 26 | * [配置设计](mvc/4.config.md) 27 | * [视图设计](mvc/5.view.md) 28 | * [数据库操作](mvc/6.dbutil.md) 29 | * [增删改查](mvc/7.crud.md) 30 | 31 | 32 | ### [IOC容器实现篇](ioc/index.md) 33 | 34 | * [IOC的概念](ioc/1.concept.md) 35 | * [Spring中怎么用](ioc/2.spring.md) 36 | * [设计一个IOC](ioc/3.myioc.md) 37 | * [原理分析](ioc/4.principle.md) 38 | 39 | ## 设计模式系列 40 | 41 | * [如何正确地写出单例模式](designpatterns/singleton.md) 42 | * [代理模式剖析](designpatterns/proxy.md) 43 | * [什么是策略模式](designpatterns/strategy.md) 44 | 45 | ## Java8系列 46 | 47 | * [Java8简明教程](java8/java8-guide.md) 48 | * [Java8 Foreach](java8/foreach.md) 49 | 50 | ## 架构博文专栏 51 | 52 | - [朱晔的互联网架构实践心得](https://juejin.im/user/5ba37b9de51d450e7428affe/posts) 53 | 54 | ## Hexo搭建博客 55 | 56 | * [分分钟部署一个Hexo环境](hexo/hello.md) 57 | * [各种配置详解](hexo/config.md) 58 | * [开始写作吧](hexo/writing.md) 59 | 60 | ## 开发者指南 61 | 62 | * [git - 简明指南](git/guide.md) 63 | * [Jersey-2.x用户指南](https://waylau.gitbooks.io/jersey-2-user-guide/content/index.html) 64 | * [REST 实战](https://waylau.gitbooks.io/rest-in-action/content/) 65 | * [Java Servlet 3.1 规范](https://github.com/waylau/servlet-3.1-specification) 66 | * [MyBatis中文指南](http://mybatis.github.io/mybatis-3/zh/index.html) 67 | * [Apache Shiro 用户指南](https://github.com/waylau/apache-shiro-1.2.x-reference) 68 | * [Spring Boot参考指南](https://github.com/qibaoguang/Spring-Boot-Reference-Guide/blob/master/SUMMARY.md) 69 | * [Netty4 用户指南](https://github.com/waylau/netty-4-user-guide/blob/master/SUMMARY.md) 70 | * [Google Java编程风格指南](user_guide/google-java8-guide.md) 71 | 72 | ## 运维相关 73 | 74 | * [linux安装jdk、tomcat脚本](shell/install_jdk_tomcat.sh) 75 | * [Web性能测试工具](web/test_tool.md) 76 | * [Java 程序员眼中的 Linux](https://github.com/judasn/Linux-Tutorial) 77 | * [写给java开发的运维笔记](learn_server/README.md) 78 | 79 | ## 经典文章 80 | 81 | * [HTTP请求报文解剖](articles/request_message.md) 82 | * [软件架构模式](articles/software_architecture_patterns.md) 83 | * [电商网站架构案例](articles/electrical-business-architecture.md) 84 | 85 | ## 服务器/域名/SSL证书 86 | 87 | * [可用域名](https://domainr.com) | [秋玉米](http://www.qiuyumi.com/) 88 | * [优惠域名](http://hk.hostsir.com/Affiliates/i/d/976) | [非主流域名]() 89 | * [vultr VPS](http://www.vultr.com/?ref=6886447) | [DO VPS](https://m.do.co/c/e2ceeb8b08fe) 90 | * [免费VPN/SSH服务](https://sshdropbear.net/) 91 | * [免费SS](http://get.ishadow.website/) 92 | * [1小时免费VPN]() 93 | * [SSL.DO](https://saki.ssl.do/aff.php?aff=11) | [Let's Encrypt](http://frontenddev.org/article/using-certbot-deployment-let-s-encrypt-free-ssl-certificate-implementation-https.html) 94 | * [免费虚拟主机](http://api.hostinger.com.hk/redir/22018310) 95 | 96 | ## 实用工具/API 97 | 98 | * [PNG图片无损压缩](https://tinypng.com/) 99 | * [在线给图片加水印](http://tool.c7sky.com/image-watermark/) 100 | * [随机密码生成](http://tool.c7sky.com/password/) 101 | * [随机头像生成](https://randomuser.me/) 102 | * [微博一键清理工具](http://tool.c7sky.com/tcleaner/) 103 | * [CSS压缩](http://csscompressor.com/) 104 | * [在线工具](http://www.atool.org/) 105 | 106 | ## 联系 107 | 108 | - Blog:https://biezhi.me 109 | - Mail:biezhi.me@gmail.com 110 | 111 | 有兴趣分享技术的 可以发邮件给我 :confused: 112 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [介绍](README.md) 4 | * [开源组件实现](开源组件实现.md) 5 | * [MVC框架实现篇](mvc/MVC框架实现篇.md) 6 | * [MVC框架实现篇](mvc/index.md) 7 | * [项目规划](mvc/1.plan.md) 8 | * [路由设计](mvc/2.route.md) 9 | * [控制器设计](mvc/3.controller.md) 10 | * [配置设计](mvc/4.config.md) 11 | * [视图设计](mvc/5.view.md) 12 | * [数据库操作](mvc/6.dbutil.md) 13 | * [增删改查](mvc/7.crud.md) 14 | * [IOC容器实现篇](ioc/IOC容器实现篇.md) 15 | * [IOC的概念](ioc/1.concept.md) 16 | * [Spring中怎么用](ioc/2.spring.md) 17 | * [设计一个IOC](ioc/3.myioc.md) 18 | * [原理分析](ioc/4.principle.md) 19 | * [设计模式系列](设计模式系列.md) 20 | * [如何正确地写出单例模式](designpatterns/singleton.md) 21 | * [代理模式剖析](designpatterns/proxy.md) 22 | * [什么是策略模式](designpatterns/strategy.md) 23 | * [Java8系列](Java8系列.md) 24 | * [Java8简明教程](java8/java8-guide.md) 25 | * [Java8 Foreach](java8/foreach.md) 26 | * [Hexo搭建博客](Hexo搭建博客.md) 27 | * [分分钟部署一个Hexo环境](hexo/hello.md) 28 | * [各种配置详解](hexo/config.md) 29 | * [开始写作吧](hexo/writing.md) 30 | * [开发者指南](开发者指南.md) 31 | * [git - 简明指南](git/guide.md) 32 | * [Jersey-2.x用户指南](https://waylau.gitbooks.io/jersey-2-user-guide/content/index.html) 33 | * [REST 实战](https://waylau.gitbooks.io/rest-in-action/content/) 34 | * [Java Servlet 3.1 规范](https://github.com/waylau/servlet-3.1-specification) 35 | * [MyBatis中文指南](http://mybatis.github.io/mybatis-3/zh/index.html) 36 | * [Apache Shiro 用户指南](https://github.com/waylau/apache-shiro-1.2.x-reference) 37 | * [Spring Boot参考指南](https://github.com/qibaoguang/Spring-Boot-Reference-Guide/blob/master/SUMMARY.md) 38 | * [Netty4 用户指南](https://github.com/waylau/netty-4-user-guide/blob/master/SUMMARY.md) 39 | * [Google Java编程风格指南](user_guide/google-java8-guide.md) 40 | * [运维相关](运维相关.md) 41 | * [Web性能测试工具](web/test_tool.md) 42 | * [Java 程序员眼中的 Linux](https://github.com/judasn/Linux-Tutorial) 43 | * [写给java开发的运维笔记](learn_server/README.md) 44 | * [经典文章](经典文章.md) 45 | * [HTTP请求报文解剖](articles/request_message.md) 46 | * [软件架构模式](articles/software_architecture_patterns.md) 47 | * [电商网站架构案例](articles/electrical-business-architecture.md) 48 | * [服务器/域名/SSL证书](服务器相关.md) 49 | * [可用域名](https://domainr.com) 50 | * [优惠域名](http://hk.hostsir.com/Affiliates/i/d/976) 51 | * [vultr VPS](http://www.vultr.com/?ref=6886447) 52 | * [免费VPN/SSH服务](https://sshdropbear.net/) 53 | * [免费SS](http://get.ishadow.website/) 54 | * 1小时免费VPN 55 | * [SSL.DO](https://saki.ssl.do/aff.php?aff=11) 56 | * [免费虚拟主机](http://api.hostinger.com.hk/redir/22018310) 57 | * [实用工具/API](实用工具.md) 58 | * [PNG图片无损压缩](https://tinypng.com/) 59 | * [在线给图片加水印](http://tool.c7sky.com/image-watermark/) 60 | * [随机密码生成](http://tool.c7sky.com/password/) 61 | * [随机头像生成](https://randomuser.me/) 62 | * [微博一键清理工具](http://tool.c7sky.com/tcleaner/) 63 | * [CSS压缩](http://csscompressor.com/) 64 | * [在线工具](http://www.atool.org/) 65 | 66 | -------------------------------------------------------------------------------- /_book/cache/1.concept.md: -------------------------------------------------------------------------------- 1 | # IOC的概念 2 | 3 | ## 什么是IOC? 4 | 5 | IoC(Inversion of Control),意为控制反转,不是什么技术,而是一种设计思想。Ioc意味着**将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制**。 6 | 7 | 如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下: 8 | 9 | - **谁控制谁,控制什么**:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。 10 | 11 | - **为何是反转,哪些方面反转了**:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。 12 | 13 | 下面举个例子说明说明是IOC: 14 | 15 | 假设我们要设计一个Girl和一个Boy类,其中Girl有kiss方法,即Girl想要Kiss一个Boy。那么,我们的问题是,Girl如何能够认识这个Boy? 16 | 17 | 在我们中国,常见的MM与GG的认识方式有以下几种: 18 | 19 | 20 | 1. 青梅竹马 21 | 2. 亲友介绍 22 | 3. 父母包办 23 | 24 | 那么哪一种才是最好呢? 25 |    26 | 1. **青梅竹马:Girl从小就知道自己的Boy。** 27 | 28 | ```java 29 | public class Girl {  30 | void kiss(){ 31 |     Boy boy = new Boy(); 32 |   } 33 | } 34 | ``` 35 | 36 | 然而从开始就创建的Boy缺点就是无法在更换。并且要负责Boy的整个生命周期。如果我们的Girl想要换一个怎么办?(笔者严重不支持Girl经常更换Boy) 37 | 38 | 2. **亲友介绍:由中间人负责提供Boy来见面** 39 | 40 | ```java 41 | public class Girl { 42 |   void kiss(){ 43 |     Boy boy = BoyFactory.createBoy();    44 |   } 45 | } 46 | ``` 47 | 48 | 亲友介绍,固然是好。如果不满意,尽管另外换一个好了。但是,亲友BoyFactory经常是以Singleton的形式出现,不然就是,存在于Globals,无处不在,无处不能。实在是太繁琐了一点,不够灵活。我为什么一定要这个亲友掺和进来呢?为什么一定要付给她介绍费呢?万一最好的朋友爱上了我的男朋友呢? 49 | 50 | 3. **父母包办:一切交给父母,自己不用费吹灰之力,只需要等着Kiss就好了。** 51 | 52 | ```java 53 | public class Girl { 54 |   void kiss(Boy boy){ 55 |     // kiss boy  56 |    boy.kiss(); 57 |   } 58 | } 59 | ``` 60 | 61 | Well,这是对Girl最好的方法,只要想办法贿赂了Girl的父母,并把Boy交给他。那么我们就可以轻松的和Girl来Kiss了。看来几千年传统的父母之命还真是有用哦。至少Boy和Girl不用自己瞎忙乎了。 62 | 63 | 这就是IOC,将对象的创建和获取提取到外部。由外部容器提供需要的组件。 64 | 65 | ## IoC能做什么 66 | 67 | IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。 68 | 69 | 其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。 70 | 71 | IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。 72 | 73 | ## IoC和DI 74 | 75 | DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。 76 | 77 | 理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下: 78 | 79 | - 谁依赖于谁:当然是应用程序依赖于IoC容器; 80 | 81 | - 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源; 82 | 83 | - 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象; 84 | 85 | - 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。 86 | 87 | IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。 88 | 89 | 对于Spring Ioc这个核心概念,我相信每一个学习Spring的人都会有自己的理解。这种概念上的理解没有绝对的标准答案,仁者见仁智者见智。 90 | 理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在框架中堆积木而已,下一节来看看Spring是怎么用的 91 | 92 | ## links 93 | * [目录]() 94 | * 下一节: [Spring中怎么用](<2.spring.md>) -------------------------------------------------------------------------------- /_book/cache/2.spring.md: -------------------------------------------------------------------------------- 1 | # Spring中怎么用 2 | 3 | 我们在Spring中是这样获取对象的: 4 | 5 | ```java 6 | public static void main(String[] args) { 7 | ApplicationContext context = new FileSystemXmlApplicationContext("applicationContext.xml"); 8 | Lol lol = (Lol) context.getBean("lol"); 9 | lol.gank(); 10 | } 11 | ``` 12 | 13 | 一起看看Spring如何让它生效呢,在 `applicationContext.xml` 配置文件中是酱紫的: 14 | 15 | ```xml 16 | 17 | 18 | 19 | ``` 20 | 21 | `Person` 类是这样的: 22 | 23 | ```java 24 | public class Lol { 25 | 26 | private String name; 27 | 28 | public Lol() { 29 | } 30 | 31 | public void gank(){ 32 | System.out.println(this.name + "在gank!!"); 33 | } 34 | 35 | public String getName() { 36 | return name; 37 | } 38 | 39 | public void setName(String name) { 40 | this.name = name; 41 | } 42 | } 43 | ``` 44 | 45 | 上面的代码运行结果自然是 `剑圣在gank!!`。 46 | 47 | Spring更高级的用法,在3.0版本之后有了基于Annotation的注入实现,为毛每次都要配置 `Xml` 看到都蛋疼。。 48 | 49 | 首先还是要在 `xml` 中配置启用注解方式 50 | 51 | ```xml 52 | 53 | ``` 54 | 55 | 这样就能使用注解驱动依赖注入了,下面是一个使用场景 56 | 57 | ```java 58 | public class Lol { 59 | 60 | @Autowired 61 | private DuangService duangService ; 62 | 63 | public void buyDuang(String name, int money) { 64 | duangService.buy(name, money); 65 | } 66 | } 67 | ``` 68 | 69 | ```java 70 | @Service("duangService") 71 | public class DuangService { 72 | 73 | public void buy(String name, int money){ 74 | if(money > 0){ 75 | System.out.println(name + "买了" + money + "毛钱的特效,装逼成功!"); 76 | } else{ 77 | System.out.println(name + "没钱还想装逼,真是匪夷所思"); 78 | } 79 | } 80 | } 81 | ``` 82 | 83 | 这只是一个简单的例子,剑圣打野的时候想要买5毛钱的三杀特效,嗯。。虽然不符合逻辑 84 | 85 | 此时 `DuangService` 已经注入到 `Lol` 对象中,运行代码的结果(这里是例子,代码不能运行的)就是: 86 | 87 | ```sh 88 | 德玛买了5毛钱的特效,装逼成功! 89 | ``` 90 | 91 | 好了,深入的不说了,我们不是学spring的,只是知道一下ioc在spring中高大上的形象,接下来步入正轨,开始设计一个IOC容器 92 | 93 | ## links 94 | * [目录]() 95 | * 上一节: [IOC的概念](<1.concept.md>) 96 | * 下一节: [设计一个IOC](<3.myioc.md>) -------------------------------------------------------------------------------- /_book/cache/index.md: -------------------------------------------------------------------------------- 1 | # 缓存的理解与实现 2 | 3 | ![](http://i.imgur.com/HLyJOSv.png) 4 | 5 | IOC是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、微信号...(balabala),想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从 `JNDI` 中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。 6 | 下面的章节带你理解并实现一个IOC容器。 7 | 8 | IOC源码:[https://github.com/junicorn/easy-ioc](https://github.com/junicorn/easy-ioc) 9 | 10 | ## 目录 11 | 12 | * [IOC的概念](1.concept.md) 13 | * [Spring中怎么用](2.spring.md) 14 | * [设计一个IOC](3.myioc.md) 15 | -------------------------------------------------------------------------------- /_book/gitbook/fonts/fontawesome/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellokaton/java-bible/68b3e4fa1d5eaf08003d480802f2884c4243b41e/_book/gitbook/fonts/fontawesome/FontAwesome.otf -------------------------------------------------------------------------------- /_book/gitbook/fonts/fontawesome/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellokaton/java-bible/68b3e4fa1d5eaf08003d480802f2884c4243b41e/_book/gitbook/fonts/fontawesome/fontawesome-webfont.eot -------------------------------------------------------------------------------- /_book/gitbook/fonts/fontawesome/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellokaton/java-bible/68b3e4fa1d5eaf08003d480802f2884c4243b41e/_book/gitbook/fonts/fontawesome/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /_book/gitbook/fonts/fontawesome/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellokaton/java-bible/68b3e4fa1d5eaf08003d480802f2884c4243b41e/_book/gitbook/fonts/fontawesome/fontawesome-webfont.woff -------------------------------------------------------------------------------- /_book/gitbook/fonts/fontawesome/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellokaton/java-bible/68b3e4fa1d5eaf08003d480802f2884c4243b41e/_book/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /_book/gitbook/gitbook-plugin-highlight/ebook.css: -------------------------------------------------------------------------------- 1 | pre, 2 | code { 3 | /* http://jmblog.github.io/color-themes-for-highlightjs */ 4 | /* Tomorrow Comment */ 5 | /* Tomorrow Red */ 6 | /* Tomorrow Orange */ 7 | /* Tomorrow Yellow */ 8 | /* Tomorrow Green */ 9 | /* Tomorrow Aqua */ 10 | /* Tomorrow Blue */ 11 | /* Tomorrow Purple */ 12 | } 13 | pre .hljs-comment, 14 | code .hljs-comment, 15 | pre .hljs-title, 16 | code .hljs-title { 17 | color: #8e908c; 18 | } 19 | pre .hljs-variable, 20 | code .hljs-variable, 21 | pre .hljs-attribute, 22 | code .hljs-attribute, 23 | pre .hljs-tag, 24 | code .hljs-tag, 25 | pre .hljs-regexp, 26 | code .hljs-regexp, 27 | pre .hljs-deletion, 28 | code .hljs-deletion, 29 | pre .ruby .hljs-constant, 30 | code .ruby .hljs-constant, 31 | pre .xml .hljs-tag .hljs-title, 32 | code .xml .hljs-tag .hljs-title, 33 | pre .xml .hljs-pi, 34 | code .xml .hljs-pi, 35 | pre .xml .hljs-doctype, 36 | code .xml .hljs-doctype, 37 | pre .html .hljs-doctype, 38 | code .html .hljs-doctype, 39 | pre .css .hljs-id, 40 | code .css .hljs-id, 41 | pre .css .hljs-class, 42 | code .css .hljs-class, 43 | pre .css .hljs-pseudo, 44 | code .css .hljs-pseudo { 45 | color: #c82829; 46 | } 47 | pre .hljs-number, 48 | code .hljs-number, 49 | pre .hljs-preprocessor, 50 | code .hljs-preprocessor, 51 | pre .hljs-pragma, 52 | code .hljs-pragma, 53 | pre .hljs-built_in, 54 | code .hljs-built_in, 55 | pre .hljs-literal, 56 | code .hljs-literal, 57 | pre .hljs-params, 58 | code .hljs-params, 59 | pre .hljs-constant, 60 | code .hljs-constant { 61 | color: #f5871f; 62 | } 63 | pre .ruby .hljs-class .hljs-title, 64 | code .ruby .hljs-class .hljs-title, 65 | pre .css .hljs-rules .hljs-attribute, 66 | code .css .hljs-rules .hljs-attribute { 67 | color: #eab700; 68 | } 69 | pre .hljs-string, 70 | code .hljs-string, 71 | pre .hljs-value, 72 | code .hljs-value, 73 | pre .hljs-inheritance, 74 | code .hljs-inheritance, 75 | pre .hljs-header, 76 | code .hljs-header, 77 | pre .hljs-addition, 78 | code .hljs-addition, 79 | pre .ruby .hljs-symbol, 80 | code .ruby .hljs-symbol, 81 | pre .xml .hljs-cdata, 82 | code .xml .hljs-cdata { 83 | color: #718c00; 84 | } 85 | pre .css .hljs-hexcolor, 86 | code .css .hljs-hexcolor { 87 | color: #3e999f; 88 | } 89 | pre .hljs-function, 90 | code .hljs-function, 91 | pre .python .hljs-decorator, 92 | code .python .hljs-decorator, 93 | pre .python .hljs-title, 94 | code .python .hljs-title, 95 | pre .ruby .hljs-function .hljs-title, 96 | code .ruby .hljs-function .hljs-title, 97 | pre .ruby .hljs-title .hljs-keyword, 98 | code .ruby .hljs-title .hljs-keyword, 99 | pre .perl .hljs-sub, 100 | code .perl .hljs-sub, 101 | pre .javascript .hljs-title, 102 | code .javascript .hljs-title, 103 | pre .coffeescript .hljs-title, 104 | code .coffeescript .hljs-title { 105 | color: #4271ae; 106 | } 107 | pre .hljs-keyword, 108 | code .hljs-keyword, 109 | pre .javascript .hljs-function, 110 | code .javascript .hljs-function { 111 | color: #8959a8; 112 | } 113 | pre .hljs, 114 | code .hljs { 115 | display: block; 116 | background: white; 117 | color: #4d4d4c; 118 | padding: 0.5em; 119 | } 120 | pre .coffeescript .javascript, 121 | code .coffeescript .javascript, 122 | pre .javascript .xml, 123 | code .javascript .xml, 124 | pre .tex .hljs-formula, 125 | code .tex .hljs-formula, 126 | pre .xml .javascript, 127 | code .xml .javascript, 128 | pre .xml .vbscript, 129 | code .xml .vbscript, 130 | pre .xml .css, 131 | code .xml .css, 132 | pre .xml .hljs-cdata, 133 | code .xml .hljs-cdata { 134 | opacity: 0.5; 135 | } 136 | -------------------------------------------------------------------------------- /_book/gitbook/gitbook-plugin-livereload/plugin.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var newEl = document.createElement('script'), 3 | firstScriptTag = document.getElementsByTagName('script')[0]; 4 | 5 | if (firstScriptTag) { 6 | newEl.async = 1; 7 | newEl.src = '//' + window.location.hostname + ':35729/livereload.js'; 8 | firstScriptTag.parentNode.insertBefore(newEl, firstScriptTag); 9 | } 10 | 11 | })(); 12 | -------------------------------------------------------------------------------- /_book/gitbook/gitbook-plugin-lunr/search-lunr.js: -------------------------------------------------------------------------------- 1 | require([ 2 | 'gitbook', 3 | 'jquery' 4 | ], function(gitbook, $) { 5 | // Define global search engine 6 | function LunrSearchEngine() { 7 | this.index = null; 8 | this.store = {}; 9 | this.name = 'LunrSearchEngine'; 10 | } 11 | 12 | // Initialize lunr by fetching the search index 13 | LunrSearchEngine.prototype.init = function() { 14 | var that = this; 15 | var d = $.Deferred(); 16 | 17 | $.getJSON(gitbook.state.basePath+'/search_index.json') 18 | .then(function(data) { 19 | // eslint-disable-next-line no-undef 20 | that.index = lunr.Index.load(data.index); 21 | that.store = data.store; 22 | d.resolve(); 23 | }); 24 | 25 | return d.promise(); 26 | }; 27 | 28 | // Search for a term and return results 29 | LunrSearchEngine.prototype.search = function(q, offset, length) { 30 | var that = this; 31 | var results = []; 32 | 33 | if (this.index) { 34 | results = $.map(this.index.search(q), function(result) { 35 | var doc = that.store[result.ref]; 36 | 37 | return { 38 | title: doc.title, 39 | url: doc.url, 40 | body: doc.summary || doc.body 41 | }; 42 | }); 43 | } 44 | 45 | return $.Deferred().resolve({ 46 | query: q, 47 | results: results.slice(0, length), 48 | count: results.length 49 | }).promise(); 50 | }; 51 | 52 | // Set gitbook research 53 | gitbook.events.bind('start', function(e, config) { 54 | var engine = gitbook.search.getEngine(); 55 | if (!engine) { 56 | gitbook.search.setEngine(LunrSearchEngine, config); 57 | } 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /_book/gitbook/gitbook-plugin-search/search-engine.js: -------------------------------------------------------------------------------- 1 | require([ 2 | 'gitbook', 3 | 'jquery' 4 | ], function(gitbook, $) { 5 | // Global search objects 6 | var engine = null; 7 | var initialized = false; 8 | 9 | // Set a new search engine 10 | function setEngine(Engine, config) { 11 | initialized = false; 12 | engine = new Engine(config); 13 | 14 | init(config); 15 | } 16 | 17 | // Initialize search engine with config 18 | function init(config) { 19 | if (!engine) throw new Error('No engine set for research. Set an engine using gitbook.research.setEngine(Engine).'); 20 | 21 | return engine.init(config) 22 | .then(function() { 23 | initialized = true; 24 | gitbook.events.trigger('search.ready'); 25 | }); 26 | } 27 | 28 | // Launch search for query q 29 | function query(q, offset, length) { 30 | if (!initialized) throw new Error('Search has not been initialized'); 31 | return engine.search(q, offset, length); 32 | } 33 | 34 | // Get stats about search 35 | function getEngine() { 36 | return engine? engine.name : null; 37 | } 38 | 39 | function isInitialized() { 40 | return initialized; 41 | } 42 | 43 | // Initialize gitbook.search 44 | gitbook.search = { 45 | setEngine: setEngine, 46 | getEngine: getEngine, 47 | query: query, 48 | isInitialized: isInitialized 49 | }; 50 | }); -------------------------------------------------------------------------------- /_book/gitbook/gitbook-plugin-search/search.css: -------------------------------------------------------------------------------- 1 | /* 2 | This CSS only styled the search results section, not the search input 3 | It defines the basic interraction to hide content when displaying results, etc 4 | */ 5 | #book-search-results .search-results { 6 | display: none; 7 | } 8 | #book-search-results .search-results ul.search-results-list { 9 | list-style-type: none; 10 | padding-left: 0; 11 | } 12 | #book-search-results .search-results ul.search-results-list li { 13 | margin-bottom: 1.5rem; 14 | padding-bottom: 0.5rem; 15 | /* Highlight results */ 16 | } 17 | #book-search-results .search-results ul.search-results-list li p em { 18 | background-color: rgba(255, 220, 0, 0.4); 19 | font-style: normal; 20 | } 21 | #book-search-results .search-results .no-results { 22 | display: none; 23 | } 24 | #book-search-results.open .search-results { 25 | display: block; 26 | } 27 | #book-search-results.open .search-noresults { 28 | display: none; 29 | } 30 | #book-search-results.no-results .search-results .has-results { 31 | display: none; 32 | } 33 | #book-search-results.no-results .search-results .no-results { 34 | display: block; 35 | } 36 | -------------------------------------------------------------------------------- /_book/gitbook/gitbook-plugin-sharing/buttons.js: -------------------------------------------------------------------------------- 1 | require(['gitbook', 'jquery'], function(gitbook, $) { 2 | var SITES = { 3 | 'facebook': { 4 | 'label': 'Facebook', 5 | 'icon': 'fa fa-facebook', 6 | 'onClick': function(e) { 7 | e.preventDefault(); 8 | window.open('http://www.facebook.com/sharer/sharer.php?s=100&p[url]='+encodeURIComponent(location.href)); 9 | } 10 | }, 11 | 'twitter': { 12 | 'label': 'Twitter', 13 | 'icon': 'fa fa-twitter', 14 | 'onClick': function(e) { 15 | e.preventDefault(); 16 | window.open('http://twitter.com/home?status='+encodeURIComponent(document.title+' '+location.href)); 17 | } 18 | }, 19 | 'google': { 20 | 'label': 'Google+', 21 | 'icon': 'fa fa-google-plus', 22 | 'onClick': function(e) { 23 | e.preventDefault(); 24 | window.open('https://plus.google.com/share?url='+encodeURIComponent(location.href)); 25 | } 26 | }, 27 | 'weibo': { 28 | 'label': 'Weibo', 29 | 'icon': 'fa fa-weibo', 30 | 'onClick': function(e) { 31 | e.preventDefault(); 32 | window.open('http://service.weibo.com/share/share.php?content=utf-8&url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)); 33 | } 34 | }, 35 | 'instapaper': { 36 | 'label': 'Instapaper', 37 | 'icon': 'fa fa-instapaper', 38 | 'onClick': function(e) { 39 | e.preventDefault(); 40 | window.open('http://www.instapaper.com/text?u='+encodeURIComponent(location.href)); 41 | } 42 | }, 43 | 'vk': { 44 | 'label': 'VK', 45 | 'icon': 'fa fa-vk', 46 | 'onClick': function(e) { 47 | e.preventDefault(); 48 | window.open('http://vkontakte.ru/share.php?url='+encodeURIComponent(location.href)); 49 | } 50 | } 51 | }; 52 | 53 | 54 | 55 | gitbook.events.bind('start', function(e, config) { 56 | var opts = config.sharing; 57 | 58 | // Create dropdown menu 59 | var menu = $.map(opts.all, function(id) { 60 | var site = SITES[id]; 61 | 62 | return { 63 | text: site.label, 64 | onClick: site.onClick 65 | }; 66 | }); 67 | 68 | // Create main button with dropdown 69 | if (menu.length > 0) { 70 | gitbook.toolbar.createButton({ 71 | icon: 'fa fa-share-alt', 72 | label: 'Share', 73 | position: 'right', 74 | dropdown: [menu] 75 | }); 76 | } 77 | 78 | // Direct actions to share 79 | $.each(SITES, function(sideId, site) { 80 | if (!opts[sideId]) return; 81 | 82 | gitbook.toolbar.createButton({ 83 | icon: site.icon, 84 | label: site.text, 85 | position: 'right', 86 | onClick: site.onClick 87 | }); 88 | }); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /_book/gitbook/images/apple-touch-icon-precomposed-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellokaton/java-bible/68b3e4fa1d5eaf08003d480802f2884c4243b41e/_book/gitbook/images/apple-touch-icon-precomposed-152.png -------------------------------------------------------------------------------- /_book/gitbook/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellokaton/java-bible/68b3e4fa1d5eaf08003d480802f2884c4243b41e/_book/gitbook/images/favicon.ico -------------------------------------------------------------------------------- /_book/ioc/IOC容器实现篇: -------------------------------------------------------------------------------- 1 | # IOC容器实现篇 2 | 3 | -------------------------------------------------------------------------------- /_book/ioc/index.md: -------------------------------------------------------------------------------- 1 | # 理解并实现一个IOC容器 2 | 3 | ![](http://i.imgur.com/HLyJOSv.png) 4 | 5 | IOC是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、微信号...(balabala),想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从 `JNDI` 中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。 6 | 下面的章节带你理解并实现一个IOC容器。 7 | 8 | IOC源码:[https://github.com/junicorn/easy-ioc](https://github.com/junicorn/easy-ioc) 9 | 10 | ## 目录 11 | 12 | * [IOC的概念](1.concept.md) 13 | * [Spring中怎么用](2.spring.md) 14 | * [设计一个IOC](3.myioc.md) 15 | * [原理分析](4.principle.md) 16 | -------------------------------------------------------------------------------- /_book/learn_server/config-nginx-proxy.md: -------------------------------------------------------------------------------- 1 | # 配置tomcat+nginx反向代理 2 | 3 | 一般我们服务器对外只暴力22, 443, 80端口,其他的尽量都在内网访问,那么tomcat的8080端口是不应该对外访问的, 4 | nginx作为一个性能卓越的web服务器提供了反向代理的功能,可以做到转发。 5 | 6 | 假设我们现在有一个域名绑定在服务器的80端口上,使用tomcat搭建的程序,但是我又不想修改tomcat端口,该怎么办呢? 7 | 8 | nginx默认监听了80端口,配置文件在 `/usr/local/nginx/conf`文件夹下的 `nginx.conf`。 9 | 10 | ## 取消默认站点 11 | 12 | ```bash 13 | [root@localhost]# cd /usr/local/nginx/conf 14 | [root@localhost conf]# vim nginx.conf 15 | ``` 16 | 17 | 将 `server` 块注释即可。然后我们在 `conf` 文件夹下创建一个 `vhost` 目录存储虚拟主机配置文件。 18 | 19 | ```bash 20 | [root@localhost conf]# mkdir vhost 21 | ``` 22 | 23 | 创建一个tomcat的虚拟主机配置文件。 24 | 25 | ```bash 26 | [root@localhost conf]# vim vhost/tomcat8.conf 27 | ``` 28 | 29 | 加入以下配置 30 | 31 | ```bash 32 | server { 33 | listen 80; 34 | server_name localhost; 35 | 36 | location / { 37 | proxy_pass http://127.0.0.1:8080; 38 | } 39 | } 40 | ``` 41 | 42 | 在 `nginx.conf` 中将 `vhost` 文件夹下的配置文件引入,只需在 `http` 块中加入一行 `include vhost/*.conf` 保存即可。 43 | 44 | 重启nginx 45 | 46 | ```bash 47 | [root@localhost conf]# service nginx restart 48 | Stopping Nginx: [ OK ] 49 | Starting Nginx: [ OK ] 50 | ``` 51 | 52 | 查看tomcat是否已经启动,如果关闭将它开启,然后访问 [http://192.168.100.128/](http://192.168.100.128/) 53 | 54 | ![](https://ooo.0o0.ooo/2016/09/09/57d260a9a1004.png) 55 | 56 | 这样tomcat的8080端口就被nginx转发了,我们此时用域名直接绑定到80端口即可! 57 | 58 | ## links 59 | * [目录]() 60 | * 上一节: [配置tomcat为服务]() 61 | * 下一节: [使用jemeter测试tomcat性能]() -------------------------------------------------------------------------------- /_book/learn_server/init-os.md: -------------------------------------------------------------------------------- 1 | # 初始化操作系统 2 | 3 | Ok,我们安装好了CentOS系统,可以使用SSH工具连接上去进行尝试了,我推荐使用 [XShell] 这款工具,支持中文,还有一些主题使用。 4 | 5 | 现在我们还不知道CentOS的IP是无法连接的,所以先在虚拟机中启动CentOS。 6 | 7 | ![](https://ooo.0o0.ooo/2016/09/09/57d225f429a16.png) 8 | 9 | 这里账户输入 `root` 密码是你在安装的时候设置的。 10 | 11 | ## 网络配置 12 | 13 | 这时候我们键入 `ifconfig` 查看ip 14 | 15 | ```bash 16 | [root@localhost ~]# ifconfig 17 | 18 | lo Link encap:Local Loopback 19 | inet addr:127.0.0.1 Mask:255.0.0.0 20 | inet6 addr: ::1/128 Scope:Host 21 | UP LOOPBACK RUNNING MTU:65536 Metric:1 22 | RX packets:0 errors:0 dropped:0 overruns:0 frame:0 23 | TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 24 | collisions:0 txqueuelen:0 25 | RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) 26 | ``` 27 | 28 | 发现还没有,我们需要设置一下网卡配置。 29 | 30 | ```bash 31 | [root@localhost ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0 32 | ``` 33 | 34 | 使用 `vi` 命令编辑第一块网卡的配置 35 | 36 | ```bash 37 | DEVICE=eth0 38 | HWADDR=00:0C:29:50:58:BE 39 | TYPE=Ethernet 40 | UUID=58f93b51-314d-49bb-9db2-036bf91161fb 41 | ONBOOT=no 42 | NM_CONTROLLED=yes 43 | BOOTPROTO=dhcp 44 | ``` 45 | 46 | 只需要将 `ONBOOT` 修改为 `yes` ,然后保存。 47 | 48 | ```bash 49 | [root@localhost ~]# service network restart 50 | Shutting down interface eth0: [ OK ] 51 | Shutting down loopback interface: [ OK ] 52 | Bringing up loopback interface: [ OK ] 53 | Bringing up interface eth0: 54 | Determining IP information for eth0... done. 55 | [ OK ] 56 | ``` 57 | 58 | 这时候我们再查看一下ip 59 | 60 | ```bash 61 | [root@localhost ~]# ifconfig 62 | eth0 Link encap:Ethernet HWaddr 00:0C:29:50:58:BE 63 | inet addr:192.168.100.128 Bcast:192.168.100.255 Mask:255.255.255.0 64 | inet6 addr: fe80::20c:29ff:fe50:58be/64 Scope:Link 65 | UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 66 | RX packets:302 errors:0 dropped:0 overruns:0 frame:0 67 | TX packets:189 errors:0 dropped:0 overruns:0 carrier:0 68 | collisions:0 txqueuelen:1000 69 | RX bytes:33591 (32.8 KiB) TX bytes:29591 (28.8 KiB) 70 | 71 | lo Link encap:Local Loopback 72 | inet addr:127.0.0.1 Mask:255.0.0.0 73 | inet6 addr: ::1/128 Scope:Host 74 | UP LOOPBACK RUNNING MTU:65536 Metric:1 75 | RX packets:0 errors:0 dropped:0 overruns:0 frame:0 76 | TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 77 | collisions:0 txqueuelen:0 78 | RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) 79 | ``` 80 | 81 | 网卡的配置已经被应用到了,我们CentOS的IP是 `192.168.100.128` 你的可能和我不一样,那这个192.168.100开头的网段是在哪里设置的呢? 82 | 83 | ![](https://ooo.0o0.ooo/2016/09/09/57d2293d28fe5.png) 84 | 85 | 点击虚拟网络编辑器 86 | 87 | ![](https://ooo.0o0.ooo/2016/09/09/57d229a2311f4.png) 88 | 89 | 如果你在执行 `service network restart` 的时候失败可以在这里修改一个网段试试。 90 | 91 | 此时你已经可以使用SSH工具连接到你的CenOS主机了。 92 | 93 | 关于更详细的网络设置大家可以参考这2篇文章: 94 | 95 | - [虚拟机下CentOS 6.5配置IP地址的三种方法](http://www.centoscn.com/CentOS/config/2014/1112/4112.html) 96 | - [Vmware安装Centos NAT方式设置静态IP](http://www.centoscn.com/CentosBug/osbug/2015/1224/6568.html) 97 | 98 | 99 | ## yum源设置 100 | 101 | 先安装 `wget` 工具,我们安装的操作系统mini版的,默认没有wget命令,执行以下命令: 102 | 103 | ```bash 104 | yum install -y wget 105 | ``` 106 | 107 | 然后设置yum源,我选择的是网易的源,你也可以设置阿里的或者其他。 108 | 109 | ```bash 110 | cd /etc/yum.repos.d 111 | mv CentOS-Base.repo bak-CentOS-Base.repo 112 | wget http://mirrors.163.com/.help/CentOS6-Base-163.repo 113 | yum clean all 114 | yum makecache 115 | ``` 116 | 117 | 安全性的配置在这里先不讲解,我们先用 `root`账户来操作。 118 | 119 | ## links 120 | * [目录]() 121 | * 上一节: [在虚拟机里安装centos6]() 122 | * 下一节: [安装jdk环境]() -------------------------------------------------------------------------------- /_book/learn_server/install-jdk.md: -------------------------------------------------------------------------------- 1 | # 安装jdk环境 2 | 3 | 服务器上如果不需要编码实际应该不安装JDK只安装JRE,我们考虑到以后可能安装其他软件就直接装JDK了。 4 | 5 | ## 下载JDK 6 | 7 | [下载jdk](http://stackoverflow.com/questions/10268583/downloading-java-jdk-on-linux-via-wget-is-shown-license-page-instead) 8 | 9 | 上面的连接是stackoverflow有开发者写的不使用cookie下载jdk和jre的命令。 10 | 11 | ```bash 12 | [root@localhost ~]# wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u102-b14/jdk-8u102-linux-x64.tar.gz 13 | --2016-09-09 19:57:01-- http://download.oracle.com/otn-pub/java/jdk/8u102-b14/jdk-8u102-linux-x64.tar.gz 14 | Resolving download.oracle.com... 23.4.240.57, 23.4.240.59 15 | Connecting to download.oracle.com|23.4.240.57|:80... connected. 16 | HTTP request sent, awaiting response... 302 Found 17 | Location: http://120.52.72.24:80/download.oracle.com/c3pr90ntc0td/otn-pub/java/jdk/8u102-b14/jdk-8u102-linux-x64.tar.gz [following] 18 | --2016-09-09 19:57:01-- http://120.52.72.24/download.oracle.com/c3pr90ntc0td/otn-pub/java/jdk/8u102-b14/jdk-8u102-linux-x64.tar.gz 19 | Connecting to 120.52.72.24:80... connected. 20 | HTTP request sent, awaiting response... 200 OK 21 | Length: 181435897 (173M) [application/x-gzip] 22 | Saving to: “jdk-8u102-linux-x64.tar.gz” 23 | 24 | 100%[==================================================================================================================================>] 181,435,897 2.07M/s in 85s 25 | 26 | 2016-09-09 19:58:26 (2.04 MB/s) - “jdk-8u102-linux-x64.tar.gz” saved [181435897/181435897] 27 | ``` 28 | 29 | ## 解压 30 | 31 | ```bash 32 | [root@localhost ~]# tar -zxvf jdk-8u102-linux-x64.tar.gz 33 | [root@localhost ~]# mkdir /usr/local/java 34 | [root@localhost ~]# mv jdk1.8.0_102/ /usr/local/java/ 35 | ``` 36 | 37 | ## 配置环境变量 38 | 39 | ```bash 40 | [root@localhost ~]# vim /etc/profile 41 | ``` 42 | 43 | 在最后一行添加 44 | 45 | ```bash 46 | # java 47 | export JAVA_HOME=/usr/local/java/jdk1.8.0_102 48 | export JRE_HOME=/usr/local/java/jdk1.8.0_102/jre 49 | export CLASSPATH=.:$JRE_HOME/lib/dt.jar:$JRE_HOME/lib/tools.jar 50 | export PATH=$JRE_HOME/bin:$JRE_HOME/bin:$PATH 51 | ``` 52 | 53 | ## 生效 54 | 55 | ```bash 56 | [root@localhost ~]# source /etc/profile 57 | [root@localhost ~]# java -version 58 | java version "1.8.0_102" 59 | Java(TM) SE Runtime Environment (build 1.8.0_102-b14) 60 | Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14, mixed mode) 61 | ``` 62 | 这里我安装的是最新版的JDK。 63 | 64 | ## links 65 | * [目录]() 66 | * 上一节: [初始化操作系统]() 67 | * 下一节: [安装tomcat]() -------------------------------------------------------------------------------- /_book/learn_server/install-mysql.md: -------------------------------------------------------------------------------- 1 | # 安装mysql及配置 2 | 3 | ## 卸载旧版本 4 | 5 | 查找本机是否已经安装mysql 6 | 7 | ```bash 8 | [root@localhost ~]# rpm -qa | grep mysql 9 | mysql-libs-5.1.73-7.el6.x86_64 10 | ``` 11 | 12 | 卸载 13 | 14 | ```bash 15 | [root@localhost ~]# rpm -e --nodeps mysql-libs-5.1.73-7.el6.x86_64 16 | ``` 17 | 18 | ## 安装MySQL 19 | 20 | 这里我们使用yum方式进行安装,编译安装比较慢也很繁琐,查看系统里面有没有mysql的repo 21 | 22 | ```bash 23 | [root@localhost ~]# yum repolist all | grep mysql 24 | Repository base is listed more than once in the configuration 25 | Repository updates is listed more than once in the configuration 26 | Repository extras is listed more than once in the configuration 27 | Repository centosplus is listed more than once in the configuration 28 | Repository contrib is listed more than once in the configuration 29 | ``` 30 | 31 | 先执行如下语句,安装相关依赖 32 | 33 | ```bash 34 | [root@localhost ~]# yum install gcc-c++ jemalloc-devel openssl-devel openssl -y 35 | ``` 36 | 37 | 安装mysql的yum源 38 | 39 | ```bash 40 | [root@localhost ~]# wget http://dev.mysql.com/get/mysql57-community-release-el6-8.noarch.rpm 41 | ``` 42 | 43 | 然后更新 44 | 45 | ```bash 46 | [root@localhost ~]# sudo rpm -Uvh mysql57-community-release-el6-8.noarch.rpm 47 | warning: mysql57-community-release-el6-8.noarch.rpm: Header V3 DSA/SHA1 Signature, key ID 5072e1f5: NOKEY 48 | Preparing... ########################################### [100%] 49 | 1:mysql57-community-relea########################################### [100%] 50 | ``` 51 | 52 | 更新源,将mysql56的 `enable` 置为1,其余置为0 53 | 54 | ```bash 55 | [root@localhost ~]# vim /etc/yum.repos.d/mysql-community.repo 56 | ``` 57 | 58 | 修改后是这样 59 | 60 | ```bash 61 | [root@localhost ~]# cat /etc/yum.repos.d/mysql-community.repo 62 | [mysql-connectors-community] 63 | name=MySQL Connectors Community 64 | baseurl=http://repo.mysql.com/yum/mysql-connectors-community/el/6/$basearch/ 65 | enabled=0 66 | gpgcheck=1 67 | gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql 68 | 69 | [mysql-tools-community] 70 | name=MySQL Tools Community 71 | baseurl=http://repo.mysql.com/yum/mysql-tools-community/el/6/$basearch/ 72 | enabled=0 73 | gpgcheck=1 74 | gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql 75 | 76 | # Enable to use MySQL 5.5 77 | [mysql55-community] 78 | name=MySQL 5.5 Community Server 79 | baseurl=http://repo.mysql.com/yum/mysql-5.5-community/el/6/$basearch/ 80 | enabled=0 81 | gpgcheck=1 82 | gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql 83 | 84 | # Enable to use MySQL 5.6 85 | [mysql56-community] 86 | name=MySQL 5.6 Community Server 87 | baseurl=http://repo.mysql.com/yum/mysql-5.6-community/el/6/$basearch/ 88 | enabled=1 89 | gpgcheck=1 90 | gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql 91 | 92 | [mysql57-community] 93 | name=MySQL 5.7 Community Server 94 | baseurl=http://repo.mysql.com/yum/mysql-5.7-community/el/6/$basearch/ 95 | enabled=0 96 | gpgcheck=1 97 | gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql 98 | 99 | [mysql-tools-preview] 100 | name=MySQL Tools Preview 101 | baseurl=http://repo.mysql.com/yum/mysql-tools-preview/el/6/$basearch/ 102 | enabled=0 103 | gpgcheck=1 104 | gpgkey=file:/etc/pki/rpm-gpg/RPM-GPG-KEY-mysql 105 | ``` 106 | 107 | 执行安装 108 | 109 | ```bash 110 | [root@localhost ~]# yum install -y mysql-community-server 111 | ``` 112 | 113 | ## 启动Mysql 114 | 115 | ok,安装完成了,我们启动mysql 116 | 117 | ```bash 118 | [root@localhost ~]# service mysqld start 119 | ``` 120 | 121 | ## 配置MySQL 122 | 123 | yum安装的时候会把mysql的配置文件存放在 `/etc/my.cnf` 这个位置,在第一次启动的时候可以看到。 124 | 125 | ### 设置mysql root密码 126 | 127 | 有两种方式可以设置mysql的root密码 128 | 129 | ```bash 130 | [root@localhost ~]# /usr/bin/mysqladmin -u root password 'new-password' 131 | ``` 132 | 133 | 或者通过该命令给root账号设置密码 134 | 135 | ```bash 136 | [root@localhost ~]# mysqladmin -u root password 'new-password' 137 | ``` 138 | 139 | 此时我们就可以使用刚才设置的密码进行登录了 140 | 141 | ```bash 142 | [root@localhost ~]# mysql -uroot -p 143 | Enter password: 144 | Welcome to the MySQL monitor. Commands end with ; or \g. 145 | Your MySQL connection id is 4 146 | Server version: 5.6.33 MySQL Community Server (GPL) 147 | 148 | Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. 149 | 150 | Oracle is a registered trademark of Oracle Corporation and/or its 151 | affiliates. Other names may be trademarks of their respective 152 | owners. 153 | 154 | Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. 155 | 156 | mysql> 157 | ``` 158 | 159 | ### 设置开机启动 160 | 161 | 我们可以 通过 `chkconfig mysqld on` 命令来设置mysql开机启动 162 | 163 | ```bash 164 | [root@localhost ~]# chkconfig mysqld on 165 | ``` 166 | 167 | 看一下 168 | 169 | ```bash 170 | [root@localhost ~]# chkconfig --list | grep mysqld 171 | mysqld 0:off 1:off 2:on 3:on 4:on 5:on 6:off 172 | ``` 173 | 174 | 在这一步Mysql的安装就已经完成了,我们先步入下一个软件的安装,在之后的章节中还会继续讲解Mysql的配置。 175 | 176 | ## links 177 | * [目录]() 178 | * 上一节: [安装tomcat]() 179 | * 下一节: [安装nginx]() -------------------------------------------------------------------------------- /_book/learn_server/install-nginx.md: -------------------------------------------------------------------------------- 1 | # 安装nginx 2 | 3 | 在安装nginx前,需要确保系统安装了g++、gcc、openssl-devel、pcre-devel和zlib-devel软件。 4 | 5 | ```bash 6 | [root@localhost ~]# yum -y install gcc-c++ zlib zlib-devel openssl openssl-devel pcre pcre-devel 7 | ``` 8 | 9 | ## 下载nginx 10 | 11 | ```bash 12 | [root@localhost ~]# wget http://nginx.org/download/nginx-1.10.1.tar.gz 13 | ``` 14 | 15 | ```bash 16 | [root@localhost ~]# tar -zxvf nginx-1.10.1.tar.gz 17 | [root@localhost ~]# cd nginx-1.10.1 18 | [root@localhost nginx-1.10.1]# ./configure --prefix=/usr/local/nginx --with-http_ssl_module 19 | ``` 20 | 21 | 上面 `--prefix` 配置nginx所在目录,`--with-http_ssl_module`配置nginx支持ssl,配置https会用到。 22 | 23 | ## 编译安装 24 | 25 | ```bash 26 | [root@localhost nginx-1.10.1]# make && make install 27 | ``` 28 | 29 | 来看看 30 | 31 | ```bash 32 | [root@localhost nginx-1.10.1]# ll /usr/local/nginx/ 33 | total 16 34 | drwxr-xr-x. 2 root root 4096 Sep 9 22:07 conf 35 | drwxr-xr-x. 2 root root 4096 Sep 9 22:07 html 36 | drwxr-xr-x. 2 root root 4096 Sep 9 22:07 logs 37 | drwxr-xr-x. 2 root root 4096 Sep 9 22:07 sbin 38 | ``` 39 | 40 | ## 启动nginx 41 | 42 | ```bash 43 | [root@localhost nginx-1.10.1]# cd /usr/local/nginx/sbin/ 44 | [root@localhost sbin]# ./nginx 45 | ``` 46 | 47 | 这样就启动nginx,nginx默认监听在80端口,但是我们不要忘了把80端口对外开放。 48 | 49 | 在 `/etc/sysconfig/iptables` 中添加80端口 50 | 51 | ```bash 52 | -A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT 53 | ``` 54 | 55 | 保存后重启一下防火墙 56 | 57 | ```bash 58 | [root@localhost sbin]# service iptables restart 59 | iptables: Setting chains to policy ACCEPT: filter [ OK ] 60 | iptables: Flushing firewall rules: [ OK ] 61 | iptables: Unloading modules: [ OK ] 62 | iptables: Applying firewall rules: [ OK ] 63 | ``` 64 | 65 | 访问 [http://192.168.100.128/](http://192.168.100.128/) 你将看到 66 | 67 | ![](https://ooo.0o0.ooo/2016/09/09/57d253381cff7.png) 68 | 69 | ## 关闭nginx 70 | 71 | ```bash 72 | #查询nginx主进程号 73 | [root@localhost sbin]# ps -ef | grep nginx 74 | #停止进程 75 | [root@localhost sbin]# kill -QUIT 主进程号 76 | #快速停止 77 | [root@localhost sbin]# kill -TERM 主进程号 78 | #强制停止 79 | [root@localhost sbin]# pkill -9 nginx 80 | ``` 81 | 82 | ## 重启nginx 83 | 84 | ```bash 85 | [root@localhost ~]# /usr/local/nginx/sbin/nginx -s reload 86 | ``` 87 | 88 | ## 配置nginx为服务 89 | 90 | ```bash 91 | [root@localhost ~]# vim /etc/init.d/nginx 92 | ``` 93 | 94 | 将服务脚本粘贴进去 95 | 96 | _服务脚本_ 97 | 98 | ```bash 99 | #!/bin/sh 100 | # chkconfig: 2345 85 15 101 | # description:Nginx Server 102 | 103 | NGINX_HOME=/usr/local/nginx 104 | NGINX_SBIN=$NGINX_HOME/sbin/nginx 105 | NGINX_CONF=$NGINX_HOME/conf/nginx.conf 106 | NGINX_PID=$NGINX_HOME/logs/nginx.pid 107 | 108 | NGINX_NAME="Nginx" 109 | 110 | . /etc/rc.d/init.d/functions 111 | 112 | if [ ! -f $NGINX_SBIN ] 113 | then 114 | echo "$NGINX_NAME startup: $NGINX_SBIN not exists! " 115 | exit 116 | fi 117 | 118 | start() { 119 | $NGINX_SBIN -c $NGINX_CONF 120 | ret=$? 121 | if [ $ret -eq 0 ]; then 122 | action $"Starting $NGINX_NAME: " /bin/true 123 | else 124 | action $"Starting $NGINX_NAME: " /bin/false 125 | fi 126 | } 127 | 128 | stop() { 129 | kill `cat $NGINX_PID` 130 | ret=$? 131 | if [ $ret -eq 0 ]; then 132 | action $"Stopping $NGINX_NAME: " /bin/true 133 | else 134 | action $"Stopping $NGINX_NAME: " /bin/false 135 | fi 136 | } 137 | 138 | restart() { 139 | stop 140 | start 141 | } 142 | 143 | check() { 144 | $NGINX_SBIN -c $NGINX_CONF -t 145 | } 146 | 147 | 148 | reload() { 149 | kill -HUP `cat $NGINX_PID` && echo "reload success!" 150 | } 151 | 152 | relog() { 153 | kill -USR1 `cat $NGINX_PID` && echo "relog success!" 154 | } 155 | 156 | case "$1" in 157 | start) 158 | start 159 | ;; 160 | stop) 161 | stop 162 | ;; 163 | restart) 164 | restart 165 | ;; 166 | check|chk) 167 | check 168 | ;; 169 | status) 170 | status -p $NGINX_PID 171 | ;; 172 | reload) 173 | reload 174 | ;; 175 | relog) 176 | relog 177 | ;; 178 | *) 179 | echo $"Usage: $0 {start|stop|restart|reload|status|check|relog}" 180 | exit 1 181 | esac 182 | ``` 183 | 184 | 给脚本可执行权限 185 | 186 | ```bash 187 | [root@localhost ~]# chmod +x /etc/init.d/nginx 188 | ``` 189 | 190 | 然后你就可以使用 `service nginx start` 的方式启动nginx了 191 | 192 | ```bash 193 | [root@localhost ~]# service nginx 194 | Usage: /etc/init.d/nginx {start|stop|restart|reload|status|check|relog} 195 | ``` 196 | 197 | ## 添加到开机项 198 | 199 | ```bash 200 | [root@localhost ~]# chkconfig --add nginx 201 | [root@localhost ~]# chkconfig 202 | auditd 0:off 1:off 2:on 3:on 4:on 5:on 6:off 203 | blk-availability 0:off 1:on 2:on 3:on 4:on 5:on 6:off 204 | crond 0:off 1:off 2:on 3:on 4:on 5:on 6:off 205 | ip6tables 0:off 1:off 2:on 3:on 4:on 5:on 6:off 206 | iptables 0:off 1:off 2:on 3:on 4:on 5:on 6:off 207 | iscsi 0:off 1:off 2:off 3:on 4:on 5:on 6:off 208 | iscsid 0:off 1:off 2:off 3:on 4:on 5:on 6:off 209 | lvm2-monitor 0:off 1:on 2:on 3:on 4:on 5:on 6:off 210 | mdmonitor 0:off 1:off 2:on 3:on 4:on 5:on 6:off 211 | multipathd 0:off 1:off 2:off 3:off 4:off 5:off 6:off 212 | mysqld 0:off 1:off 2:on 3:on 4:on 5:on 6:off 213 | netconsole 0:off 1:off 2:off 3:off 4:off 5:off 6:off 214 | netfs 0:off 1:off 2:off 3:on 4:on 5:on 6:off 215 | network 0:off 1:off 2:on 3:on 4:on 5:on 6:off 216 | nginx 0:off 1:off 2:on 3:on 4:on 5:on 6:off 217 | postfix 0:off 1:off 2:on 3:on 4:on 5:on 6:off 218 | rdisc 0:off 1:off 2:off 3:off 4:off 5:off 6:off 219 | restorecond 0:off 1:off 2:off 3:off 4:off 5:off 6:off 220 | rsyslog 0:off 1:off 2:on 3:on 4:on 5:on 6:off 221 | saslauthd 0:off 1:off 2:off 3:off 4:off 5:off 6:off 222 | sshd 0:off 1:off 2:on 3:on 4:on 5:on 6:off 223 | udev-post 0:off 1:on 2:on 3:on 4:on 5:on 6:off 224 | ``` 225 | 我们可以看到nginx已经被添加到开机启动了。 226 | 227 | ## links 228 | * [目录]() 229 | * 上一节: [安装mysql及配置]() 230 | * 下一节: [安装redis3]() -------------------------------------------------------------------------------- /_book/learn_server/install-redis.md: -------------------------------------------------------------------------------- 1 | # 安装redis3 2 | 3 | ## 安装依赖软件 4 | 5 | ```bash 6 | yum install -y gcc* 7 | yum install -y tcl 8 | ``` 9 | 10 | ## 安装redis 11 | 12 | ```bash 13 | [root@localhost ~]# wget http://download.redis.io/releases/redis-3.2.3.tar.gz 14 | [root@localhost ~]# tar -zxvf redis-3.2.3.tar.gz 15 | [root@localhost ~]# cd redis-3.2.3 16 | [root@localhost redis-3.2.3]# make 17 | [root@localhost redis-3.2.3]# make test 18 | [root@localhost redis-3.2.3]# make install 19 | [root@localhost redis-3.2.3]# cd utils 20 | [root@localhost redis-3.2.3]# chmod +x install_server.sh 21 | [root@localhost redis-3.2.3]# ./install_server.sh 22 | ``` 23 | 24 | 在install的时候提示选项,全部选择默认即可,你看到如下画面表示安装成功 25 | 26 | ```bash 27 | Please select the redis port for this instance: [6379] 28 | Selecting default: 6379 29 | Please select the redis config file name [/etc/redis/6379.conf] 30 | Selected default - /etc/redis/6379.conf 31 | Please select the redis log file name [/var/log/redis_6379.log] 32 | Selected default - /var/log/redis_6379.log 33 | Please select the data directory for this instance [/var/lib/redis/6379] 34 | Selected default - /var/lib/redis/6379 35 | Please select the redis executable path [/usr/local/bin/redis-server] 36 | Selected config: 37 | Port : 6379 38 | Config file : /etc/redis/6379.conf 39 | Log file : /var/log/redis_6379.log 40 | Data dir : /var/lib/redis/6379 41 | Executable : /usr/local/bin/redis-server 42 | Cli Executable : /usr/local/bin/redis-cli 43 | Is this ok? Then press ENTER to go on or Ctrl-C to abort. 44 | Copied /tmp/6379.conf => /etc/init.d/redis_6379 45 | Installing service... 46 | Successfully added to chkconfig! 47 | Successfully added to runlevels 345! 48 | Starting Redis server... 49 | Installation successful! 50 | ``` 51 | 52 | ## 测试一下 53 | 54 | ```bash 55 | [root@localhost ~]# redis-cli 56 | 127.0.0.1:6379> set name jack 57 | OK 58 | 127.0.0.1:6379> get name 59 | "jack" 60 | ``` 61 | 62 | ## 查看redis状态 63 | 64 | ```bash 65 | [root@localhost ~]# service redis_6379 status 66 | Redis is running (14927) 67 | ``` 68 | 69 | ## 启动/关闭redis 70 | 71 | ```bash 72 | [root@localhost ~]# service redis_6379 stop 73 | Stopping ... 74 | Waiting for Redis to shutdown ... 75 | Redis stopped 76 | [root@localhost ~]# service redis_6379 start 77 | Starting Redis server... 78 | ``` 79 | 80 | ## 设置redis认证密码 81 | 82 | ```bash 83 | [root@localhost ~]# vim /etc/redis/6379.conf 84 | ``` 85 | 86 | 找到 `# requirepass foobared` 将 `#` 去掉,设置一个密码。 87 | 88 | 然后重启redis 89 | 90 | ```bash 91 | [root@localhost ~]# service redis_6379 restart 92 | Stopping ... 93 | Redis stopped 94 | Starting Redis server... 95 | ``` 96 | 97 | wow,你已经完成初级篇的所有任务了,接下里我们会玩点有趣的 :) 98 | 99 | ## links 100 | * [目录]() 101 | * 上一节: [安装nginx]() 102 | * 下一节: [配置tomcat为服务]() -------------------------------------------------------------------------------- /_book/learn_server/install-svn.md: -------------------------------------------------------------------------------- 1 | # 安装svn服务 2 | 3 | Subversion是一个自由,开源的版本控制系统。Subversion将文件存放在中心版本库里。这个版本库很像一个普通的文件服务器,不同的是,它可以记录每一次文件和目录的修改情况。这样就可以籍此将数据恢复到以前的版本,并可以查看数据的更改细节。Subversion是Apache基金会下的一个项目,官网 [https://subversion.apache.org](https://subversion.apache.org) 4 | 5 | ## 安装依赖 6 | 7 | ```bash 8 | [root@localhost ~]# yum install sqlite sqlite-devel apr-util apr-util-devel -y 9 | ``` 10 | 11 | ## 安装subversion 12 | 13 | ```bash 14 | [root@localhost ~]# wget http://mirrors.cnnic.cn/apache/subversion/subversion-1.8.16.tar.gz 15 | [root@localhost ~]# tar -zxvf subversion-1.8.16.tar.gz 16 | [root@localhost ~]# cd subversion-1.8.16 17 | [root@localhost subversion-1.8.16]# ./configure --prefix=/usr/local/subversion 18 | [root@localhost subversion-1.8.16]# make && make install 19 | ``` 20 | 21 | ## 配置环境 22 | 23 | ```bash 24 | [root@localhost ~]# vim /etc/profile 25 | ``` 26 | 27 | 加入 `PATH=$PATH:/usr/local/subversion/bin` 28 | 29 | ## 查看版本 30 | 31 | ```bash 32 | [root@localhost ~]# svn --version 33 | svn, version 1.8.16 (r1740329) 34 | compiled Sep 26 2016, 06:42:53 on x86_64-unknown-linux-gnu 35 | 36 | Copyright (C) 2016 The Apache Software Foundation. 37 | This software consists of contributions made by many people; 38 | see the NOTICE file for more information. 39 | Subversion is open source software, see http://subversion.apache.org/ 40 | 41 | The following repository access (RA) modules are available: 42 | 43 | * ra_svn : Module for accessing a repository using the svn network protocol. 44 | - with Cyrus SASL authentication 45 | - handles 'svn' scheme 46 | * ra_local : Module for accessing a repository on local disk. 47 | - handles 'file' scheme 48 | ``` 49 | 50 | ## 开始配置 51 | 52 | ### 建立仓库目录 53 | 54 | ```bash 55 | [root@localhost ~]# mkdir -p /data/svn/repos 56 | ``` 57 | 58 | ### 创建版本 59 | 60 | ```bash 61 | [root@localhost ~]# svnadmin create /data/svn/repos/ 62 | ``` 63 | 64 | ### 修改配置 65 | 66 | ```bash 67 | [root@localhost ~]# vim /data/svn/repos/conf/svnserve.conf 68 | ``` 69 | 70 | ```bash 71 | [general] 72 | anon-access = none 73 | auth-access = write 74 | password-db = passwd #用户密码文件 75 | authz-db = authz #授权登录文件 76 | realm = repos 77 | ``` 78 | 79 | 修改`/data/svn/repos/conf/passwd`文件,添加用户及密码: 80 | 81 | ```bash 82 | [root@localhost ~]# vim /data/svn/repos/conf/passwd 83 | 84 | [users] 85 | username=password #用户名=密码   一行一个 86 | ``` 87 | 88 | 修改`/data/svn/repos/conf/authz`文件,控制用户权限 89 | 90 | ```bash 91 | [root@localhost ~]# vim /data/svn/repos/conf/authz 92 | ``` 93 | 94 | > 注意: 95 | 96 | * 权限配置文件中出现的用户名必须已在用户配置文件中定义。 97 | * 对权限配置文件的修改立即生效,不必重启svn。 98 | 99 | 用户组格式: 100 | 101 | ```bash 102 | [groups] 103 | = , 104 | ``` 105 | 106 | 其中,1个用户组可以包含1个或多个用户,用户间以逗号分隔。 107 | 版本库目录格式: 108 | 109 | ```bash 110 | [<版本库>:/项目/目录] 111 | @<用户组名> = <权限> 112 | <用户名> = <权限> 113 | ``` 114 | 115 | 其中,方框号内部分可以有多种写法: 116 | 117 | - [/],表示根目录及以下,根目录是svnserve启动时指定的,我们指定为/home/svndata,[/]就是表示对全部版本库设置权限。 118 | - [repos:/] 表示对版本库repos设置权限; 119 | - [repos:/abc] 表示对版本库repos中的abc项目设置权限; 120 | - [repos:/abc/aaa] 表示对版本库repos中的abc项目的aaa目录设置权限; 121 | - 122 | 权限主体可以是`用户组`、`用户`或`*`,用户组在前面加`@`,`*`表示全部用户。 123 | 权限可以是`w`、`r`、`wr`和空,空表示没有任何权限。 124 | 125 | ## 启动SVN 126 | 127 | ```bash 128 | [root@localhost ~]# svnserve -d --listen-port 10901 -r /data/svn 129 | ``` 130 | 131 | - -d :表示以daemon方式(后台运行)运行; 132 | - --listen-port 10901 :表示使用10901端口,可以换成你需要的端口。但注意,使用1024以下的端口需要root权限; 133 | - -r /data/svn :指定根目录是/data/svn。 134 | 135 | ## 将svn作为服务 136 | 137 | ```bash 138 | #!/bin/bash 139 | # build this file in /etc/init.d/svn 140 | # chmod 755 /etc/init.d/svn 141 | # centos下可以用如下命令管理svn: service svn start(restart/stop) 142 | SVN_HOME=/home/svn 143 | if [ ! -f "/usr/local/subversion/bin/svnserve" ] 144 | then 145 | echo "svnserver startup: cannot start" 146 | exit 147 | fi 148 | case "$1" in 149 | start) 150 | echo "Starting svnserve..." 151 | /usr/local/subversion/bin/svnserve -d --listen-port 10901 -r $SVN_HOME 152 | echo "Finished!" 153 | ;; 154 | stop) 155 | echo "Stoping svnserve..." 156 | killall svnserve 157 | echo "Finished!" 158 | ;; 159 | restart) 160 | $0 stop 161 | $0 start 162 | ;; 163 | *) 164 | echo "Usage: svn { start | stop | restart } " 165 | exit 1 166 | esac 167 | ``` 168 | 169 | wow,你已经完成初级篇的所有任务了,接下里我们会玩点有趣的 :) 170 | 171 | ## links 172 | * [目录]() 173 | * 上一节: [安装nginx]() 174 | * 下一节: [配置tomcat为服务]() 175 | -------------------------------------------------------------------------------- /_book/learn_server/install-tomcat.md: -------------------------------------------------------------------------------- 1 | # 安装tomcat 2 | 3 | 上一章节我们安装了JDK的环境,Tomcat运行的前提是要有JDK环境。 4 | 5 | ## 下载Tomcat 6 | 7 | ```bash 8 | [root@localhost ~]# wget http://mirror.bit.edu.cn/apache/tomcat/tomcat-8/v8.5.5/bin/apache-tomcat-8.5.5.tar.gz 9 | [root@localhost ~]# tar -zxvf apache-tomcat-8.5.5.tar.gz 10 | [root@localhost ~]# mv apache-tomcat-8.5.5 /usr/local/tomcat8 11 | ``` 12 | 13 | ## 启动tomcat 14 | 15 | ```bash 16 | [root@localhost ~]# cd /usr/local/tomcat8/bin/ 17 | [root@localhost bin]# ./startup.sh 18 | Using CATALINA_BASE: /usr/local/tomcat8 19 | Using CATALINA_HOME: /usr/local/tomcat8 20 | Using CATALINA_TMPDIR: /usr/local/tomcat8/temp 21 | Using JRE_HOME: /usr/local/java/jdk1.8.0_102/jre 22 | Using CLASSPATH: /usr/local/tomcat8/bin/bootstrap.jar:/usr/local/tomcat8/bin/tomcat-juli.jar 23 | Tomcat started. 24 | ``` 25 | 26 | 现在打开 [http://192.168.100.128:8080](http://192.168.100.128:8080) 应该就可以看到Tomcat的汤姆猫页面。 27 | 而事实是你看到这个: 28 | 29 | ![](https://ooo.0o0.ooo/2016/09/09/57d23ea8c353e.png) 30 | 31 | 哦草。。。为什么,机智的同学已经想到了,防火墙啊。对我们没有对防火墙进行任何配置,实际上8080端口是不对外开放的, 32 | 那么如何解决呢? 33 | 34 | - 关闭防火墙 35 | - 开放8080端口 36 | 37 | ## 配置防火墙 38 | 39 | 在CentOS上关闭防火墙是非常简单的 40 | 41 | ```bash 42 | [root@localhost bin]# service iptables stop 43 | iptables: Setting chains to policy ACCEPT: filter [ OK ] 44 | iptables: Flushing firewall rules: [ OK ] 45 | iptables: Unloading modules: [ OK ] 46 | ``` 47 | 48 | 这时候你再访问 [http://192.168.100.128:8080](http://192.168.100.128:8080) 就可以看到 49 | 50 | ![](https://ooo.0o0.ooo/2016/09/09/57d23f752bfce.png) 51 | 52 | 当然这种方式是简单粗暴的,我们在真实服务器上不可能这么做,怎么做呢? 53 | 54 | ```bash 55 | [root@localhost bin]# vim /etc/sysconfig/iptables 56 | ``` 57 | 58 | 我们看到 `iptables` 的默认配置是这样的: 59 | 60 | ```bash 61 | # Firewall configuration written by system-config-firewall 62 | # Manual customization of this file is not recommended. 63 | *filter 64 | :INPUT ACCEPT [0:0] 65 | :FORWARD ACCEPT [0:0] 66 | :OUTPUT ACCEPT [0:0] 67 | -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 68 | -A INPUT -p icmp -j ACCEPT 69 | -A INPUT -i lo -j ACCEPT 70 | -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT 71 | -A INPUT -j REJECT --reject-with icmp-host-prohibited 72 | -A FORWARD -j REJECT --reject-with icmp-host-prohibited 73 | COMMIT 74 | ``` 75 | 76 | 只需要添加一行和 `22` 端口一样的配置: 77 | 78 | ```bash 79 | -A INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -j ACCEPT 80 | ``` 81 | 82 | 这样既把8080的TCP端口对外开放了,然后重启防火墙 83 | 84 | ```bash 85 | [root@localhost bin]# service iptables restart 86 | iptables: Setting chains to policy ACCEPT: filter [ OK ] 87 | iptables: Flushing firewall rules: [ OK ] 88 | iptables: Unloading modules: [ OK ] 89 | iptables: Applying firewall rules: [ OK ] 90 | ``` 91 | 92 | 可以达到同样的效果。 93 | 94 | ## links 95 | * [目录]() 96 | * 上一节: [安装jdk环境]() 97 | * 下一节: [安装mysql及配置]() -------------------------------------------------------------------------------- /_book/learn_server/optimization-nginx.md: -------------------------------------------------------------------------------- 1 | # 优化nginx配置 -------------------------------------------------------------------------------- /_book/learn_server/optimization-tomcat8.md: -------------------------------------------------------------------------------- 1 | # 优化tomcat8 2 | 3 | 我们优化tomcat的目的是提高并发性,即在多线程环境下能够快速响应,提高吞吐量。 4 | 5 | 首先在tomcat的bin目录下新建一个名为 `setenv.sh` 的文件,tomcat启动时会自动加载该文件。 6 | 7 | ```bash 8 | [root@localhost bin]# vim setenv.sh 9 | ``` 10 | 11 | 加入tomcat基础配置 12 | 13 | ```bash 14 | #!/usr/bin 15 | 16 | export CATALINA_HOME=/usr/local/tomcat8 17 | export CATALINA_BASE=/usr/local/tomcat8 18 | ``` 19 | 20 | ## JAVA_OPTS 21 | 22 | 加入如下配置,我们服务器的内存是1G。这里我设置最大占用768 23 | 24 | ```bash 25 | export JAVA_OPTS="$JAVA_OPTS\ 26 | -server\ 27 | -Xms768m\ 28 | -Xmx768m\ 29 | -Xss512k\ 30 | -Djava.awt.headless=true\ 31 | -Dfile.encoding=utf-8\ 32 | -Djava.net.preferIPv4Stack=true\ 33 | -Djava.security.egd=file:/dev/./urandom" 34 | ``` 35 | 36 | - -server:表示这是应用于服务器的配置,JVM 内部会有特殊处理的 37 | - -Xms768m:设置JVM最大可用内存为768MB 38 | - -Xmx768m:设置JVM最小内存为768MB。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。 39 | - -Dfile.encoding:默认文件编码 40 | - -Djava.net.preferIPv4Stack:使用IPV4 41 | - -Djava.security.egd:[详细解释](http://fengbin2005.iteye.com/blog/2313845) 42 | 43 | ## 优化`server.xml` 44 | 45 | ```xml 46 | 56 | ``` 57 | 58 | **maxThreads 连接数限制** 59 | 60 | maxThreads 是 Tomcat 所能接受最大连接数。一般设置不要超过8000以上,如果你的网站访问量非常大可能使用运行多个Tomcat实例的方法。 61 | 62 | ## 安装apr 63 | 64 | 安装依赖 65 | 66 | ```bash 67 | [root@localhost ~]# yum install -y openssl-devel 68 | ``` 69 | 70 | 下载apr相关包 71 | 72 | ```bash 73 | [root@localhost ~]# wget http://mirrors.tuna.tsinghua.edu.cn/apache//apr/apr-1.5.2.tar.gz 74 | [root@localhost ~]# wget http://mirrors.tuna.tsinghua.edu.cn/apache//apr/apr-util-1.5.4.tar.gz 75 | [root@localhost ~]# wget http://mirrors.tuna.tsinghua.edu.cn/apache//apr/apr-iconv-1.2.1.tar.gz 76 | 77 | # 安装apr 78 | [root@localhost ~]# tar -zxvf apr-1.5.2.tar.gz 79 | 80 | [root@localhost ~]# cd apr-1.5.2 81 | [root@localhost apr-1.5.2]# ./configure && make && make install 82 | 83 | # 安装apr-util 84 | [root@localhost ~]# tar -zxvf apr-util-1.5.4.tar.gz 85 | [root@localhost ~]# cd apr-util-1.5.4 86 | [root@localhost apr-util-1.5.4]# ./configure --with-apr=/usr/local/apr && make && make install 87 | 88 | # 安装apr-iconv 89 | [root@localhost ~]# cd apr-iconv-1.2.1 90 | [root@localhost apr-iconv-1.2.1]# ./configure --with-apr=/usr/local/apr && make && make install 91 | ``` 92 | 93 | 配置tomcat 94 | 95 | ```bash 96 | [root@localhost apr-iconv-1.2.1]# cd /usr/local/tomcat8/bin/ 97 | [root@localhost bin]# tar -zxf tomcat-native.tar.gz 98 | [root@localhost bin]# cd tomcat-native-1.2.8-src/native/ 99 | [root@localhost native]# ./configure --with-apr=/usr/local/apr && make && make install 100 | ``` 101 | 102 | 这是提示我 103 | 104 | ```bash 105 | configure: error: Your version of OpenSSL is not compatible with this version of tcnative 106 | ``` 107 | 108 | 由于centos 当前的yum 库只有1.0.1 的OpenSSL,所以我们需要手工安装1.0.2 109 | 110 | ```bash 111 | [root@localhost ~]# wget https://www.openssl.org/source/openssl-1.0.2-latest.tar.gz 112 | [root@localhost ~]# tar -zxf openssl-1.0.2-latest.tar.gz 113 | [root@localhost ~]# cd openssl-1.0.2h 114 | [root@localhost openssl-1.0.2h]# ./config --prefix=/usr/local/openssl -fPIC 115 | ``` 116 | 117 | > 注意这里需要加入 -fPIC参数,否则后面在安装tomcat native 组件会出错 118 | > 注意:不要按照提示去运行 make depend 119 | 120 | ```bash 121 | [root@localhost openssl-1.0.2h]# make 122 | [root@localhost openssl-1.0.2h]# make install 123 | [root@localhost openssl-1.0.2h]# mv /usr/bin/openssl ~ 124 | [root@localhost openssl-1.0.2h]# ln -s /usr/local/openssl/bin/openssl /usr/bin/openssl 125 | [root@localhost openssl-1.0.2h]# openssl version 126 | OpenSSL 1.0.2h 3 May 2016 127 | ``` 128 | 129 | 重新安装 tomcat-native组件 130 | 131 | ```bash 132 | [root@localhost openssl-1.0.2h]# cd /usr/local/tomcat8/bin/tomcat-native-1.2.8-src/native/ 133 | [root@localhost native]# ./configure --with-apr=/usr/local/apr --with-ssl=/usr/local/openssl 134 | [root@localhost native]# make && make install 135 | ``` 136 | 137 | 在 `setenv.sh` 文件中添加 138 | 139 | ```bash 140 | LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/apr/lib 141 | export LD_LIBRARY_PATH 142 | ``` 143 | 144 | 在 `server.xml` 中加 145 | 146 | ```xml 147 | 150 | ``` 151 | 152 | 启动tomcat,打开控制台日志可以看到如图所示的日志 153 | 154 | ![](https://ooo.0o0.ooo/2016/09/09/57d281093d907.png) 155 | 156 | ## 性能测试 -------------------------------------------------------------------------------- /_book/learn_server/use-jemeter-test-tomcat.md: -------------------------------------------------------------------------------- 1 | # 使用jemeter测试tomcat性能 2 | 3 | JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域。 4 | 5 | 下载地址:[http://jmeter.apache.org/download_jmeter.cgi](http://jmeter.apache.org/download_jmeter.cgi) 6 | 7 | 安装启动即可。 8 | 9 | ![](https://ooo.0o0.ooo/2016/09/09/57d26181a3bbc.png) 10 | 11 | ## 服务器环境 12 | 13 | - CPU:Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz 14 | - 内存:1G 15 | - 操作系统:CentOS6.8_x64 16 | - JDK:1.8.0_102 17 | - Tomcat:8.5.5 18 | 19 | 下面所有测试都是基于1000个请求做的,且都是访问Tomcat默认的ROOT首页 20 | 21 | ## 创建测试计划 22 | 23 | ![](https://ooo.0o0.ooo/2016/09/09/57d262594f2ec.png) 24 | ![](https://ooo.0o0.ooo/2016/09/09/57d262640c284.png) 25 | ![](https://ooo.0o0.ooo/2016/09/09/57d2626eddf57.png) 26 | 27 | 配置参数,这里我们进行多次测试. 28 | 29 | ![](https://ooo.0o0.ooo/2016/09/09/57d262af81eaf.png) 30 | ![](https://ooo.0o0.ooo/2016/09/09/57d262b9a7fe3.png) 31 | 32 | | 并发用户数 | 吞吐量/每秒 | 请求等待时间/毫秒 | 错误请求数/百分比 | 33 | | :----: | :----: | :----: | :----: | 34 | | 10 | 1999 | 8 | 0.00 | 35 | | 20 | 2667 | 11 | 0.00 | 36 | | 30 | 2746 | 13 | 0.00 | 37 | | 40 | 2730 | 16 | 0.00 | 38 | | 50 | 2682 | 20 | 0.00 | 39 | | 60 | 2756 | 23 | 0.00 | 40 | | 70 | 2764 | 27 | 0.00 | 41 | | 80 | 2714 | 32 | 0.00 | 42 | | 90 | 2131 | 35 | 0.00 | 43 | | 100 | 2739 | 38 | 0.00 | 44 | | 200 | 1404 | 43 | 0.34% | 45 | | 300 | 1066 | 50 | 0.77% | 46 | | 400 | 995 | 52 | 1.23% | 47 | | 500 | 1086 | 46 | 1.42% | 48 | | 1000 | 1163 | 59 | 2.83% | 49 | 50 | ![](https://ooo.0o0.ooo/2016/09/09/57d276759cccc.png) 51 | 52 | ![](https://ooo.0o0.ooo/2016/09/09/57d2768276105.png) 53 | 54 | 从上面的测试结果来看,在90-100个并发的时候出现不稳定,其他都比较平缓,请求时间一直在上涨。CPU负载均在60%左右。 55 | 56 | 在聚合报告中,会显示一行数据,共有10个字段,含义分别如下。 57 | 58 | - Label:每个 JMeter 的 element(例如 HTTP Request)都有一个 Name 属性,这里显示的就是 Name 属性的值 59 | - #Samples:表示你这次测试中一共发出了多少个请求,如果模拟10个用户,每个用户迭代10次,那么这里显示100 60 | - Average:平均响应时间——默认情况下是单个 Request 的平均响应时间,当使用了 Transaction Controller 时,也可以以Transaction 为单位显示平均响应时间 61 | - Median:中位数,也就是 50% 用户的响应时间 62 | - 90% Line:90% 用户的响应时间 63 | - Min:最小响应时间 64 | - Max:最大响应时间 65 | - Error%:本次测试中出现错误的请求的数量/请求的总数 66 | - Throughput:吞吐量——默认情况下表示每秒完成的请求数(Request per Second) 67 | - KB/Sec:每秒从服务器端接收到的数据量,相当于LoadRunner中的Throughput/Sec 68 | 69 | 在下一章节我们介绍对tomcat8的优化。 70 | 71 | ## links 72 | * [目录]() 73 | * 上一节: [配置tomcat+nginx反向代理]() 74 | * 下一节: [优化tomcat8]() -------------------------------------------------------------------------------- /_book/learn_server/virtual-machine-install-centos6.md: -------------------------------------------------------------------------------- 1 | # 在虚拟机里安装centos6 2 | 3 | ## 基础环境 4 | 5 | - 操作系统:Win7操作系统 6 | - 虚拟机:VMware® Workstation 12 Pro 7 | - Linux系统:CentOS 64位 8 | 9 | 接下里我们创建一个Linux虚拟机。 10 | 11 | ![](https://ooo.0o0.ooo/2016/09/09/57d21ba70219e.png) 12 | 13 | 这里选择自定义配置 14 | 15 | ![](https://ooo.0o0.ooo/2016/09/09/57d21bcabe68c.png) 16 | 17 | 我们选择了Workstation 8x, 为了兼容低版本的vmvware 18 | 19 | ![](https://ooo.0o0.ooo/2016/09/09/57d21bd500c46.png) 20 | 21 | 稍后安装操作系统 22 | 23 | ![](https://ooo.0o0.ooo/2016/09/09/57d21bdd67f7f.png) 24 | 25 | 选择Linux -> centos64位 26 | 27 | ![](https://ooo.0o0.ooo/2016/09/09/57d21be618ba3.png) 28 | 29 | 保存虚拟机到本地文件夹 30 | 31 | ![](https://ooo.0o0.ooo/2016/09/09/57d21bf02cb1f.png) 32 | 33 | 选择处理器数量和核心数,这里我选择默认的,根据你的机器情况可适当调整。 34 | 35 | ![](https://ooo.0o0.ooo/2016/09/09/57d21bf903216.png) 36 | 37 | 设置Centos内存,我设置1G 38 | 39 | ![](https://ooo.0o0.ooo/2016/09/09/57d21c048caba.png) 40 | 41 | 如果你在局域网环境并且希望其他人可以访问到你的centos,可以选择桥接模式, 42 | 这里我只有宿主机访问虚拟机,就设置了NAT模式,桥接的时候会和宿主机处于同一IP段。 43 | 44 | ![](https://ooo.0o0.ooo/2016/09/09/57d21c5b16bc8.png) 45 | ![](https://ooo.0o0.ooo/2016/09/09/57d21c63c49fa.png) 46 | 47 | 这里选择默认即可。 48 | 49 | ![](https://ooo.0o0.ooo/2016/09/09/57d21c70a4f1e.png) 50 | ![](https://ooo.0o0.ooo/2016/09/09/57d21c7a64405.png) 51 | 52 | 磁盘大小设置20G,用到更多可以累加上去,然后将虚拟磁盘存储为单文件,防止磁盘碎片。 53 | 54 | ![](https://ooo.0o0.ooo/2016/09/09/57d21ccc2db15.png) 55 | 56 | 点击完成。 57 | 58 | ![](https://ooo.0o0.ooo/2016/09/09/57d21cd385b5a.png) 59 | 60 | 选择你的IOS镜像文件,如果没有可以在 [这里](http://isoredirect.centos.org/centos/6/isos/x86_64/) 下载 61 | 62 | ![](https://ooo.0o0.ooo/2016/09/09/57d21cd94a75e.png) 63 | 64 | ![](https://ooo.0o0.ooo/2016/09/09/57d21ce210877.png) 65 | 66 | 安装操作系统 67 | 68 | ![](https://ooo.0o0.ooo/2016/09/09/57d21cf139606.png) 69 | 70 | 这里要检查硬件,可以直接跳过。 71 | 72 | ![](https://ooo.0o0.ooo/2016/09/09/57d21cf8bbfb7.png) 73 | 74 | 选择语言环境,我选择英文,避免在以后的操作中遇到未知的错误。 75 | 76 | ![](https://ooo.0o0.ooo/2016/09/09/57d21de293453.png) 77 | ![](https://ooo.0o0.ooo/2016/09/09/57d21de9279bf.png) 78 | 79 | 确定将配置写入到磁盘 80 | 81 | ![](https://ooo.0o0.ooo/2016/09/09/57d21def353cf.png) 82 | 83 | 这里就默认把,暂时用不到 84 | 85 | ![](https://ooo.0o0.ooo/2016/09/09/57d21dfa1624b.png) 86 | 87 | 选择时区,我们选择Asia/shanghai 88 | 89 | ![](https://ooo.0o0.ooo/2016/09/09/57d21dffbd0ac.png) 90 | 91 | 设置你的ROOT用户密码,请牢记以后会经常用到。 92 | 93 | ![](https://ooo.0o0.ooo/2016/09/09/57d21e06b65c4.png) 94 | 95 | 使用全部空间,就不分区了。 96 | 97 | ![](https://ooo.0o0.ooo/2016/09/09/57d21e0c94266.png) 98 | 99 | 将修改写入到磁盘。 100 | 101 | ![](https://ooo.0o0.ooo/2016/09/09/57d21e21e909f.png) 102 | 103 | 等待CentOS为你安装基础软件环境。 104 | 105 | ![](https://ooo.0o0.ooo/2016/09/09/57d21e687d0de.png) 106 | 107 | 看到这个界面你的CentOS就安装完成了,点击REBOOT即重启机器。 108 | 可以进行下一关了,上车! 109 | 110 | ## links 111 | * [目录]() 112 | * 下一节: [初始化操作系统]() -------------------------------------------------------------------------------- /_book/shell/download-jdk.md: -------------------------------------------------------------------------------- 1 | ## Download Oracle Java JRE & JDK using a script 2 | 3 | Oracle has recently disallowed direct downloads of java from their servers (without going through the browser and agreeing to their terms, which you can look at here: Oracle terms). So, if you try: 4 | 5 | ```sh 6 | wget "http://download.oracle.com/otn-pub/java/jdk/7u4-b20/jdk-7u4-linux-x64.tar.gz" 7 | ``` 8 | 9 | you will receive a page with "In order to download products from Oracle Technology Network you must agree to the OTN license terms" error message. 10 | 11 | This can be rather troublesome for setting up servers with automated scripts. 12 | 13 | Luckily, it seems that a single cookie is all that is needed to bypass this (you still have to agree to the terms to install): 14 | 15 | ```sh 16 | Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie 17 | ``` 18 | 19 | So, if you want to download jdk7u4 for 64-bit Linux (e.g., Ubuntu) using wget, you can use: 20 | 21 | ```sh 22 | wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/7u4-b20/jdk-7u4-linux-x64.tar.gz" 23 | ``` 24 | 25 | Just for reference, here are the links to the current (at the time of posting) downloads of JDK and JRE 26 | 27 | **JDK 8u66** 28 | 29 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-linux-i586.rpm 30 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-linux-i586.tar.gz 31 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-linux-x64.rpm 32 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-linux-x64.tar.gz 33 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-macosx-x64.dmg 34 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-solaris-sparcv9.tar.Z 35 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-solaris-sparcv9.tar.gz 36 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-solaris-x64.tar.Z 37 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-solaris-x64.tar.gz 38 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-windows-i586.exe 39 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-windows-x64.exe 40 | 41 | **JRE 8u66** 42 | 43 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-linux-i586.rpm 44 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-linux-i586.tar.gz 45 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-linux-x64.rpm 46 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-linux-x64.tar.gz 47 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-macosx-x64.dmg 48 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-macosx-x64.tar.gz 49 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-solaris-sparcv9.tar.gz 50 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-solaris-x64.tar.gz 51 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-windows-i586-iftw.exe 52 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-windows-i586.exe 53 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-windows-i586.tar.gz 54 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-windows-x64.exe 55 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-windows-x64.tar.gz 56 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/server-jre-8u66-linux-x64.tar.gz 57 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/server-jre-8u66-solaris-sparcv9.tar.gz 58 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/server-jre-8u66-solaris-x64.tar.gz 59 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/server-jre-8u66-windows-x64.tar.gz 60 | -------------------------------------------------------------------------------- /_book/shell/install_jdk_tomcat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | ############################################### 4 | #author: biezhi 5 | #email:i@biezhi.me 6 | #date: 2015-09-16 7 | ############################################### 8 | 9 | base_dir=$(cd "$(dirname "$0")";pwd) 10 | 11 | JDK_FILE=$(ls | grep jdk-*-linux-*.tar.gz) 12 | TOMCAT_FILE=$(ls | grep apache-tomcat-*.tar.gz) 13 | #下载JDK 14 | download(){ 15 | os_version=`uname -a` 16 | echo $os_version 17 | architecture="64" 18 | echo "$os_version" | grep -q "$architecture" 19 | 20 | if [ $? -eq 0 ] 21 | then 22 | # 不存在即去外网下载jdk文件 23 | if [ ! -f "$JDK_FILE" ]; then 24 | echo "您正在使用64位操作系统,为您选择64位JDK" 25 | wget http://7xls9k.dl1.z0.glb.clouddn.com/jdk-8u60-linux-x64.tar.gz 26 | fi 27 | else 28 | # 不存在即去外网下载jdk文件 29 | if [ ! -f "$JDK_FILE" ]; then 30 | echo "您正在使用32位操作系统,为您选择32位JDK" 31 | wget http://7xls9k.dl1.z0.glb.clouddn.com/jdk-8u60-linux-i586.tar.gz 32 | fi 33 | fi 34 | #下载tomcat 35 | if [ ! -f "$TOMCAT_FILE" ]; then 36 | wget http://7xls9k.dl1.z0.glb.clouddn.com/apache-tomcat-8.0.26.tar.gz 37 | fi 38 | JDK_FILE=$(ls | grep jdk-*-linux-*.tar.gz) 39 | TOMCAT_FILE=$(ls | grep apache-tomcat-*.tar.gz) 40 | } 41 | 42 | #安装JDK 43 | install_jdk(){ 44 | JAVA_DIR=/usr/local/java 45 | JDK_DIR="jdk1.8.0_60" 46 | JDK_PATH="$JAVA_DIR"/"$JDK_DIR" 47 | 48 | tar xzf $JDK_FILE 49 | 50 | mkdir -p $JAVA_DIR 51 | mv $JDK_DIR $JAVA_DIR 52 | #配置环境变量 53 | cp ~/.bashrc ~/.bashrc.backup.java 54 | if [ ! -n "$JAVA_HOME" ]; then 55 | echo "export JAVA_HOME=\"$JDK_PATH\"" >> ~/.bashrc 56 | fi 57 | if [ ! -n "$JRE_HOME" ]; then 58 | echo "export JRE_HOME=\"\$JAVA_HOME/jre\"" >> ~/.bashrc 59 | fi 60 | if [ ! -n "$CLASSPATH" ]; then 61 | echo "export CLASSPATH=.:\$JDK_PATH/lib/dt.jar:\$JDK_PATH/lib/tools.jar" >> ~/.bashrc 62 | fi 63 | echo "export PATH=\$JAVA_HOME/bin:\$JRE_HOME/bin:\$PATH" >> ~/.bashrc 64 | source ~/.bashrc 65 | echo "JDK install success!" 66 | } 67 | #安装tomcat 68 | install_tomcat(){ 69 | TOMCAT_DIR=/usr/local/tomcat8 70 | 71 | mkdir -p $TOMCAT_DIR 72 | 73 | tar xzf $TOMCAT_FILE 74 | mv apache-tomcat-8.0.26 tomcat8 75 | mv tomcat8 /usr/local/ 76 | 77 | cp ~/.bashrc ~/.bashrc.backup.tomcat8 78 | if [ ! -n "$TOMCAT_HOME" ]; then 79 | echo "export TOMCAT_HOME=$TOMCAT_DIR" >> ~/.bashrc 80 | fi 81 | if [ ! -n "$CATALINA_HOME" ]; then 82 | echo "export CATALINA_HOME=$TOMCAT_DIR" >> ~/.bashrc 83 | fi 84 | source ~/.bashrc 85 | echo "Tomact install success!" 86 | } 87 | 88 | main(){ 89 | download 90 | if [ $? != 0 ]; then 91 | echo "tomcat & JDK download failed" 92 | exit 1 93 | fi 94 | install_jdk 95 | if [ $? != 0 ]; then 96 | echo "JDK install failed" 97 | exit 1 98 | fi 99 | install_tomcat 100 | if [ $? != 0 ]; then 101 | echo "Tomcat install failed" 102 | exit 1 103 | fi 104 | } 105 | main 106 | -------------------------------------------------------------------------------- /_book/shell/tomcat.md: -------------------------------------------------------------------------------- 1 | ```sh 2 | #!/bin/bash 3 | # author: Sean Chow (seanlook7@gmail.com) 4 | # 5 | # 6 | # chkconfig: 345 80 15 7 | # description: use service tomcat xintr 8 | 9 | # Source function library. 10 | . /etc/rc.d/init.d/functions 11 | 12 | export JAVA_HOME= 13 | export JRE_HOME= 14 | 15 | # tomcat名字 16 | tcName=tomcat-$1 17 | basedir=/data/program/tomcat/$tcName 18 | tclog=${basedir}/logs/catalina.out 19 | 20 | RETVAL=0 21 | 22 | start(){ 23 | checkrun 24 | if [ $RETVAL -eq 0 ]; then 25 | echo "###### Tomcat正在启动 ######" 26 | $basedir/bin/startup.sh 27 | touch /var/lock/subsys/${tcNo} 28 | checklog 29 | status 30 | else 31 | echo "###### Tomcat启动成功 ######" 32 | fi 33 | } 34 | 35 | # 停止某一台tomcat,如果是重启则带re参数,表示不查看日志,等待启动时再提示查看 36 | stop(){ 37 | checkrun 38 | if [ $RETVAL -eq 1 ]; then 39 | echo "###### Tomcat正在关闭 ######" 40 | $basedir/bin/shutdown.sh 41 | if [ "$1" != "re" ]; then 42 | checklog 43 | else 44 | sleep 5 45 | fi 46 | rm -f /var/lock/subsys/${tcNo} 47 | status 48 | else 49 | echo "###### Tomcat关闭成功 ######" 50 | fi 51 | } 52 | 53 | status(){ 54 | checkrun 55 | if [ $RETVAL -eq 1 ]; then 56 | echo -n "-- Tomcat ( pid " 57 | ps ax --width=1000 |grep ${tcName}|grep "org.apache.catalina.startup.Bootstrap start" | awk '{printf $1 " "}' 58 | echo -n ") 正在运行" 59 | echo 60 | else 61 | echo "###### Tomcat未运行 ######" 62 | fi 63 | #echo "---------------------------------------------" 64 | } 65 | 66 | # 查看tomcat日志,带vl参数 67 | log(){ 68 | status 69 | checklog yes 70 | } 71 | 72 | # 如果tomcat正在运行,强行杀死tomcat进程,关闭tomcat 73 | kill(){ 74 | checkrun 75 | if [ $RETVAL -eq 1 ]; then 76 | read -p "###### 确定要杀死 ${tcName} 的进程吗?[no])" answer 77 | case $answer in 78 | Y|y|YES|yes|Yes) 79 | ps ax --width=1000 |grep ${tcName}|grep "org.apache.catalina.startup.Bootstrap start" | awk '{printf $1 " "}'|xargs kill -9 80 | status 81 | ;; 82 | *);; 83 | esac 84 | else 85 | echo "###### 退出 [$tcName] ######" 86 | fi 87 | } 88 | 89 | 90 | checkrun(){ 91 | ps ax --width=1000 |grep ${tcName}| grep "[o]rg.apache.catalina.startup.Bootstrap start" | awk '{printf $1 " "}' | wc | awk '{print $2}' >/tmp/tomcat_process_count.txt 92 | read line < /tmp/tomcat_process_count.txt 93 | if [ $line -gt 0 ]; then 94 | RETVAL=1 95 | return $RETVAL 96 | else 97 | RETVAL=0 98 | return $RETVAL 99 | fi 100 | } 101 | 102 | # 如果是直接查看日志viewlog,则不提示输入[yes],否则就是被stop和start调用,需提示是否查看日志 103 | checklog(){ 104 | answer=$1 105 | if [ "$answer" != "yes" ]; then 106 | read -p "###### 查看 日志吗 $2?[yes])" answer 107 | fi 108 | case $answer in 109 | Y|y|YES|yes|Yes|"") 110 | tail -f ${tclog} 111 | ;; 112 | *) 113 | # status 114 | # exit 0 115 | ;; 116 | esac 117 | } 118 | checkexist(){ 119 | if [ ! -d $basedir ]; then 120 | echo "###### tomcat $basedir 不存在 ######" 121 | exit 0 122 | fi 123 | } 124 | 125 | 126 | case "$2" in 127 | start) 128 | checkexist 129 | start 130 | exit 0 131 | ;; 132 | stop) 133 | checkexist 134 | stop 135 | exit 0 136 | ;; 137 | restart) 138 | checkexist 139 | stop re 140 | start 141 | exit 0 142 | ;; 143 | status) 144 | checkexist 145 | status 146 | #$basedir/bin/catalina.sh version 147 | exit 0 148 | ;; 149 | log) 150 | checkexist 151 | log 152 | exit 0 153 | ;; 154 | kill) 155 | checkexist 156 | status 157 | kill 158 | exit 0 159 | ;; 160 | *) 161 | echo "###### 使用方法: service $0 [start|stop|restart|status|log|kill]" 162 | echo "###### 举个栗子-> service tomcat xintr start" 163 | esac 164 | 165 | exit 0 166 | ``` 167 | -------------------------------------------------------------------------------- /_book/shell/uninstall_jdk_tomcat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | ############################################### 4 | #author: biezhi 5 | #email:i@biezhi.me 6 | #date: 2015-09-16 7 | ############################################### 8 | 9 | #卸载JDK 10 | uninstall_jdk(){ 11 | JAVA_DIR=/usr/local/java/jdk1.8.0_60 12 | TOMCAT_DIR=/usr/local/tomcat8 13 | 14 | if [ -d "$JAVA_DIR" ]; then 15 | rm -rf $JAVA_DIR 16 | fi 17 | 18 | if [ -d "$TOMCAT_DIR" ]; then 19 | rm -rf $TOMCAT_DIR 20 | fi 21 | 22 | #环境变量 23 | if [ -f "~/.bashrc.backup.tomcat8" ]; then 24 | mv ~/.bashrc.backup.tomcat8 ~/.bashrc 25 | fi 26 | 27 | if [ -f "~/.bashrc.backup.java" ]; then 28 | mv ~/.bashrc.backup.java ~/.bashrc 29 | fi 30 | source ~/.bashrc 31 | echo "JDK,Tomcat uninstall success!" 32 | cd 33 | } 34 | 35 | main(){ 36 | uninstall_jdk 37 | if [ $? != 0 ]; then 38 | echo "JDK,Tomcat uninstall failed" 39 | exit 1 40 | fi 41 | } 42 | main 43 | -------------------------------------------------------------------------------- /_book/simple-java/classes-and-interfaces/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /_book/simple-java/collections/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /_book/simple-java/common-methods/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /_book/simple-java/compiler-and-jvm/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /_book/simple-java/concurrency/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /_book/simple-java/exceptions/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /_book/simple-java/generics/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /_book/simple-java/io-and-database/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /_book/simple-java/reflection-tutorial.md: -------------------------------------------------------------------------------- 1 | # reflection-tutorial.md -------------------------------------------------------------------------------- /_book/simple-java/string-and-array/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 2 | 3 | 1. [String的不可变是什么?](diagram-to-show-java-strings-immutability.md) 4 | 2. [substring()如何工作?](the-substring-method-in-jdk-6-and-jdk-7.md) 5 | 3. [为什么String是不可变的?]() 6 | 4. [使用""和构造函数创建字符串]() 7 | 5. [String是引用传递吗?]() 8 | 6. [length 和 length()]() 9 | 7. [如何检查数组是否有效包含一个值?]() 10 | 8. []() -------------------------------------------------------------------------------- /_book/simple-java/string-and-array/diagram-to-show-java-strings-immutability.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellokaton/java-bible/68b3e4fa1d5eaf08003d480802f2884c4243b41e/_book/simple-java/string-and-array/diagram-to-show-java-strings-immutability.md -------------------------------------------------------------------------------- /_book/simple-java/string-and-array/the-substring-method-in-jdk-6-and-jdk-7.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellokaton/java-bible/68b3e4fa1d5eaf08003d480802f2884c4243b41e/_book/simple-java/string-and-array/the-substring-method-in-jdk-6-and-jdk-7.md -------------------------------------------------------------------------------- /articles/electrical-business-architecture.md: -------------------------------------------------------------------------------- 1 | # 大型网站架构系列:电商网站架构案例 2 | 3 | 大型网站架构是一个系列文档,欢迎大家关注。本次分享主题:电商网站架构案例。从电商网站的需求,到单机架构,逐步演变为常用的,可供参考的分布式架构的原型。除具备功能需求外,还具备一定的高性能,高可用,可伸缩,可扩展等非功能质量需求(架构目标)。 4 | 5 | 根据实际需要,进行改造,扩展,支持千万PV,是没问题的。 6 | 7 | ## 本次分享大纲 8 | 9 | 1. 电商案例的原因 10 | 1. 电商网站需求 11 | 1. 网站初级架构 12 | 1. 系统容量估算 13 | 1. 网站架构分析 14 | 1. 网站架构优化 15 | 1. 架构总结 16 | 17 | 电商网站案例,一共有三篇本篇主要说明网站的需求,网站初始架构,系统容量估算方法。 18 | 19 | ## 一、电商案例的原因 20 | 21 | 分布式大型网站,目前看主要有几类1.大型门户,比如网易,新浪等;2.SNS网站,比如校内,开心网等;3.电商网站:比如阿里巴巴,京东商城,国美在线,汽车之家等。大型门户一般是新闻类信息,可以使用CDN,静态化等方式优化,开心网等交互性比较多,可能会引入更多的NOSQL,分布式缓存,使用高性能的通信框架等。电商网站具备以上两类的特点,比如产品详情可以采用CDN,静态化,交互性高的需要采用NOSQL等技术。因此,我们采用电商网站作为案例,进行分析。 22 | 23 | ## 二、电商网站需求 24 | 25 | 客户需求: 26 | 27 | - 建立一个全品类的电子商务网站(B2C),用户可以在线购买商品,可以在线支付,也可以货到付款; 28 | - 用户购买时可以在线与客服沟通; 29 | - 用户收到商品后,可以给商品打分,评价; 30 | - 目前有成熟的进销存系统;需要与网站对接; 31 | - 希望能够支持3~5年,业务的发展; 32 | - 预计3~5年用户数达到1000万; 33 | - 定期举办双11,双12,三八男人节等活动; 34 | - 其他的功能参考京东或国美在线等网站。 35 | 36 | 客户就是客户,不会告诉你具体要什么,只会告诉你他想要什么,我们很多时候要引导,挖掘客户的需求。好在提供了明确的参考网站。因此,下一步要进行大量的分析,结合行业,以及参考网站,给客户提供方案。 37 | 38 | 其他的略~~~~~ 39 | 40 | 需求功能矩阵 41 | 42 | 需求管理传统的做法,会使用用例图或模块图(需求列表)进行需求的描述。这样做常常忽视掉一个很重要的需求(非功能需求),因此推荐大家使用需求功能矩阵,进行需求描述。 43 | 44 | 本电商网站的需求矩阵如下: 45 | 46 | 47 | | 网站需求 | 功能需求 | 非功能需求 | 48 | | --- | --------- | --------- | 49 | | 全品类的电子商务网站 | 分类管理,商品管理 | 方便进行多品类管理(灵活性)
网站访问速度要快(高性能)
图片存储的要求(海量小图片) | 50 | | 用户可以在线购买商品 | 会员管理,购物车,结算功能 | 良好购物体验(可用性,性能) | 51 | | 在线支付或货到付款 | 多种在线支付方式 | 支付过程要安全,数据加密(安全性)多种支付接口灵活切换(灵活性,扩展性) | 52 | | 可以在线与客服沟通 | 在线客服功能 | 可靠性:即时通讯 | 53 | | 商品打分评价 | 商品评论 |   | 54 | | 目前有成熟的进销存系统 | 对接进销存 | 属于约束条件对接时要考虑数据一致性,鲁棒性 | 55 | | 支持3~5年,业务的发展 |   | 属于约束条件伸缩性,可扩展性 | 56 | | 3~5年用户数达到1000万 |   | 约束条件 | 57 | | 举办双11,双12,三八男人节等活动 | 活动管理,秒杀 | 突增访问流量(可伸缩)实时性要求(高性能) | 58 | | 参考京东或国美在线 |   | 参考条件 | 59 | 60 | 以上是对电商网站需求的简单举例,目的是说明(1)需求分析的时候,要全面,大型分布式系统重点考虑非功能需求;(2)描述一个简单的电商需求场景,使大家对下一步的分析设计有个依据。 61 | 62 | ## 三、网站初级架构 63 | 64 | 一般网站,刚开始的做法,是三台服务器,一台部署应用,一台部署数据库,一台部署NFS文件系统。 65 | 66 | 这是前几年比较传统的做法,之前见到一个网站10万多会员,垂直服装设计门户,N多图片。使用了一台服务器部署了应用,数据库以及图片存储。出现了很多性能问题。 67 | 68 | 如下图: 69 | 70 | ![](http://i.imgur.com/iMvaZOc.png) 71 | 72 | 但是,目前主流的网站架构已经发生了翻天覆地的变化。一般都会采用集群的方式,进行高可用设计。至少是下面这个样子。 73 | 74 | ![](http://i.imgur.com/e30NCbS.png) 75 | 76 | (1) 使用集群对应用服务器进行冗余,实现高可用;(负载均衡设备可与应用一块部署) 77 | 78 | (2) 使用数据库主备模式,实现数据备份和高可用; 79 | 80 | ## 四、系统容量预估 81 | 82 | 预估步骤: 83 | 84 | 1. 注册用户数-日均UV量-每日的PV量-每天的并发量; 85 | 1. 峰值预估:平常量的2~3倍; 86 | 1. 根据并发量(并发,事务数),存储容量计算系统容量。 87 | 88 | 客户需求:3~5年用户数达到1000万注册用户; 89 | 90 | 每秒并发数预估: 91 | 92 | 1. 每天的UV为200万(二八原则); 93 | 1. 每日每天点击浏览30次; 94 | 1. PV量:200*30=6000万; 95 | 1. 集中访问量:24*0.2=4.8小时会有6000万*0.8=4800万(二八原则); 96 | 1. 每分并发量:4.8*60=288分钟,每分钟访问4800/288=16.7万(约等于); 97 | 1. 每秒并发量:16.7万/60=2780(约等于); 98 | 1. 假设:高峰期为平常值的三倍,则每秒的并发数可以达到8340次。 99 | 1. 1毫秒=1.3次访问; 100 | 101 | 没好好学数学后悔了吧?!(不知道以上算是否有错误,呵呵~~) 102 | 103 | 服务器预估:(以tomcat服务器举例) 104 | 105 | 1. 按一台web服务器,支持每秒300个并发计算。平常需要10台服务器(约等于);[tomcat默认配置是150] 106 | 1. 高峰期:需要30台服务器; 107 | 108 | 容量预估:70/90原则 109 | 110 | 系统CPU一般维持在70%左右的水平,高峰期达到90%的水平,是不浪费资源,并比较稳定的。内存,IO类似。 111 | 112 | 以上预估仅供参考,因为服务器配置,业务逻辑复杂度等都有影响。在此CPU,硬盘,网络等不再进行评估。 113 | 114 | ## 五、网站架构分析 115 | 116 | 根据以上预估,有几个问题: 117 | 118 | - 需要部署大量的服务器,高峰期计算,可能要部署30台Web服务器。并且这三十台服务器,只有秒杀,活动时才会用到,存在大量的浪费。 119 | - 所有的应用部署在同一台服务器,应用之间耦合严重。需要进行垂直切分和水平切分。 120 | - 大量应用存在冗余代码 121 | - 服务器SESSION同步耗费大量内存和网络带宽 122 | - 数据需要频繁访问数据库,数据库访问压力巨大。 123 | 124 | 大型网站一般需要做以下架构优化(优化是架构设计时,就要考虑的,一般从架构/代码级别解决,调优主要是简单参数的调整,比如JVM调优;如果调优涉及大量代码改造,就不是调优了,属于重构): 125 | 126 | - 业务拆分 127 | - 应用集群部署(分布式部署,集群部署和负载均衡) 128 | - 多级缓存 129 | - 单点登录(分布式Session) 130 | - 数据库集群(读写分离,分库分表) 131 | - 服务化 132 | - 消息队列 133 | - 其他技术 134 | 135 | ## 六、网站架构优化 136 | 137 | ### 6.1业务拆分 138 | 139 | 根据业务属性进行垂直切分,划分为产品子系统,购物子系统,支付子系统,评论子系统,客服子系统,接口子系统(对接如进销存,短信等外部系统)。 140 | 141 | 根据业务子系统进行等级定义,可分为核心系统和非核心系统。核心系统:产品子系统,购物子系统,支付子系统;非核心:评论子系统,客服子系统,接口子系统。 142 | 143 | 业务拆分作用:提升为子系统可由专门的团队和部门负责,专业的人做专业的事,解决模块之间耦合以及扩展性问题;每个子系统单独部署,避免集中部署导致一个应用挂了,全部应用不可用的问题。 144 | 145 | 等级定义作用:用于流量突发时,对关键应用进行保护,实现优雅降级;保护关键应用不受到影响。 146 | 147 | 拆分后的架构图: 148 | 149 | ![](http://i.imgur.com/dYH3p9Z.png) 150 | 151 | 参考部署方案2 152 | 153 | ![](http://i.imgur.com/dEZGHso.png) 154 | 155 | 1. 如上图每个应用单独部署 156 | 1. 核心系统和非核心系统组合部署 157 | 158 | ### 6.2应用集群部署(分布式,集群,负载均衡) 159 | 160 | 分布式部署:将业务拆分后的应用单独部署,应用直接通过RPC进行远程通信; 161 | 162 | 集群部署:电商网站的高可用要求,每个应用至少部署两台服务器进行集群部署; 163 | 164 | 负载均衡:是高可用系统必须的,一般应用通过负载均衡实现高可用,分布式服务通过内置的负载均衡实现高可用,关系型数据库通过主备方式实现高可用。 165 | 166 | 集群部署后架构图: 167 | 168 | ![](http://i.imgur.com/5q4qsYo.png) 169 | 170 | ### 6.3 多级缓存 171 | 172 | 缓存按照存放的位置一般可分为两类本地缓存和分布式缓存。本案例采用二级缓存的方式,进行缓存的设计。一级缓存为本地缓存,二级缓存为分布式缓存。(还有页面缓存,片段缓存等,那是更细粒度的划分) 173 | 174 | 一级缓存,缓存数据字典,和常用热点数据等基本不可变/有规则变化的信息,二级缓存缓存需要的所有缓存。当一级缓存过期或不可用时,访问二级缓存的数据。如果二级缓存也没有,则访问数据库。 175 | 176 | 缓存的比例,一般1:4,即可考虑使用缓存。(理论上是1:2即可)。 177 | 178 | ![](http://i.imgur.com/ZmmQPU7.png) 179 | 180 | 根据业务特性可使用以下缓存过期策略: 181 | 182 | 1. 缓存自动过期; 183 | 1. 缓存触发过期; 184 | 185 | ### 6.4单点登录(分布式Session) 186 | 187 | 系统分割为多个子系统,独立部署后,不可避免的会遇到会话管理的问题。一般可采用Session同步,Cookies,分布式Session方式。电商网站一般采用分布式Session实现。 188 | 189 | 再进一步可以根据分布式Session,建立完善的单点登录或账户管理系统。 190 | 191 | ![](http://i.imgur.com/Q2RK4a6.png) 192 | 193 | 流程说明 194 | 195 | 1. 用户第一次登录时,将会话信息(用户Id和用户信息),比如以用户Id为Key,写入分布式Session; 196 | 1. 用户再次登录时,获取分布式Session,是否有会话信息,如果没有则调到登录页; 197 | 1. 一般采用Cache中间件实现,建议使用Redis,因此它有持久化功能,方便分布式Session宕机后,可以从持久化存储中加载会话信息; 198 | 1. 存入会话时,可以设置会话保持的时间,比如15分钟,超过后自动超时; 199 | 200 | 结合Cache中间件,实现的分布式Session,可以很好的模拟Session会话。 201 | 202 | ### 6.5数据库集群(读写分离,分库分表) 203 | 204 | 大型网站需要存储海量的数据,为达到海量数据存储,高可用,高性能一般采用冗余的方式进行系统设计。一般有两种方式读写分离和分库分表。 205 | 206 | 读写分离:一般解决读比例远大于写比例的场景,可采用一主一备,一主多备或多主多备方式。 207 | 208 | 本案例在业务拆分的基础上,结合分库分表和读写分离。如下图: 209 | 210 | ![](http://i.imgur.com/CqrYY4X.png) 211 | 212 | 213 | 1. 业务拆分后:每个子系统需要单独的库; 214 | 1. 如果单独的库太大,可以根据业务特性,进行再次分库,比如商品分类库,产品库; 215 | 1. 分库后,如果表中有数据量很大的,则进行分表,一般可以按照Id,时间等进行分表;(高级的用法是一致性Hash) 216 | 1. 在分库,分表的基础上,进行读写分离; 217 | 218 | 相关中间件可参考Cobar(阿里,目前已不在维护),TDDL(阿里),Atlas(奇虎360),MyCat(在Cobar基础上,国内很多牛人,号称国内第一开源项目)。 219 | 220 | 分库分表后序列的问题,JOIN,事务的问题,会在分库分表主题分享中,介绍。 221 | 222 | ### 6.6服务化 223 | 224 | 将多个子系统公用的功能/模块,进行抽取,作为公用服务使用。比如本案例的会员子系统就可以抽取为公用的服务。 225 | 226 | ![](http://i.imgur.com/WsKuaCF.png) 227 | 228 | ### 6.7消息队列 229 | 230 | 消息队列可以解决子系统/模块之间的耦合,实现异步,高可用,高性能的系统。是分布式系统的标准配置。本案例中,消息队列主要应用在购物,配送环节。 231 | 232 | 1. 用户下单后,写入消息队列,后直接返回客户端; 233 | 1. 库存子系统:读取消息队列信息,完成减库存; 234 | 1. 配送子系统:读取消息队列信息,进行配送; 235 | 236 | ![](http://i.imgur.com/hymmzKI.png) 237 | 238 | 目前使用较多的MQ有Active MQ,Rabbit MQ,Zero MQ,MS MQ等,需要根据具体的业务场景进行选择。建议可以研究下Rabbit MQ。 239 | 240 | ### 6.8其他架构(技术) 241 | 242 | 除了以上介绍的业务拆分,应用集群,多级缓存,单点登录,数据库集群,服务化,消息队列外。还有CDN,反向代理,分布式文件系统,大数据处理等系统。 243 | 244 | 此处不详细介绍,大家可以问度娘/Google,有机会的话也可以分享给大家。 245 | 246 | ## 七、架构总结 247 | 248 | ![](http://i.imgur.com/UNIRdSM.png) 249 | 250 | 以上是本次分享的架构总结,其中细节可参考前面分享的内容。其中还有很多可以优化和细化的地方,因为是案例分享,主要针对重要部分做了介绍,工作中需要大家根据具体的业务场景进行架构设计。 251 | 252 | 以上是电商网站架构案例的分享一共有三篇,从电商网站的需求,到单机架构,逐步演变为常用的,可供参考的分布式架构的原型。除具备功能需求外,还具备一定的高性能,高可用,可伸缩,可扩展等非功能质量需求(架构目标)。 -------------------------------------------------------------------------------- /articles/software_architecture_patterns.md: -------------------------------------------------------------------------------- 1 | # 软件架构模式 2 | 3 | ![](http://i.imgur.com/3GFrpZ5.jpg) 4 | 5 | ## 目录 6 | 7 | ## 简介 8 | 9 | 对程序员来说很常见一种情况是在没有合理的程序架构时就开始编程,没有一个清晰的和定义好的架构的时候,大多数开发者和架构师通常会使用标准式的传统分层架构模式(也被称为多层架构)—通过将源码模块分割为几个不同的层到不同的包中。不幸的是,这种编码方式会导致一系列没有组织性的代码模块,这些模块缺乏明确的规则、职责和同其他模块之间的关联。这通常被称为架构大泥球。 10 | 11 | 应用程序缺乏合理的架构一般会导致程序过度耦合、容易被破坏、难以应对变化,同时很难有个清晰的版本或者方向性。这样的结果是,如果你没有充分理解程序系统里每个组件和模块,就很难定义这个程序的结构特征。有关于程序的部署和维护的基本问题都难以回答,比如:程序架构是什么规模?应用程序有什么性能特点?应用程序有多容易应对变化?应用程序的部署特点是什么?架构是如何反应的? 12 | 13 | 架构模式帮助你定义应用程序的基本特征和行为。例如,些架构模式会让程序自己自然⽽而然地朝着具有良 好伸缩性的方向发展,⽽而其他架构模式会让程序朝着高度灵活的方向发展。知道了这些特点,了解架构模式 的优点和缺点是非常必要的,它帮助我们选择个适合自己特定的业务需求和目标的的程序。 14 | 15 | 作为个架构师,你必须证明你的架构模式的决策是正确的,特别是当需要选择一个特定的体系结构模式或方法 的时候。这本迷你书的目的就是给你⾜足够的信息让你去做出正确的架构决策。 16 | 17 | ## 第一章 分层架构 18 | 19 | 分层架构是一种很常⻅见的架构模式,它也叫N层架构。这种架构是大多数Jave EE应用的实际标准,因此很多 的架构师,设计师,还有程序员都知道它。许多传统IT公司的组织架构和分层模式十分的相似。所以它很自 然的成为大多数应用的架构模式。 20 | 21 | ### 模式分析 22 | 23 | 分层架构模式里的组件被分成几个平行的层次,每一层都代表了应用的一个功能(展示逻辑或者业务逻辑)。 尽管分层架构没有规定自⾝身要分成几层几种,大多数的结构都分成四个层次:展示层,业务层,持久层,和数 据库层。如表1-1,有时候,业务层和持久层会合并成单独的一个业务层,尤其是持久层的逻辑绑定在业务层 的组件当中。因此,有一些小的应用可能只有3层,一些有着更复杂的业务的大应用可能有5层或者更多的分 层。 24 | 25 | 分层架构中的每一层都着特定的角色和职能。举个例子,展示层负责处理所有的界面展示以及交互逻辑,业 务层负责处理请求对应的业务。架构里的层次是具体工作的高度抽象,它们都是为了实现某种特定的业务请 求。比如说展示层并不需要关⼼心怎样得到用户数据,它只需在屏幕上以特定的格式展示信息。业务层并不关 26 | ⼼心要展示在屏幕上的用户数据格式,也不关⼼心这些用户数据从哪里来。它只需要从持久层得到数据,执行与 数据有关的相应业务逻辑,然后把这些信息传递给展示层。 27 | 28 | ![](http://i.imgur.com/SxUxjRZ.png) 29 | 30 | 分层架构的一个突出特性是组件间关注点分离 (separation of concerns)。一个层中的组件只会处理本层的逻 辑。比如说,展示层的组件只会处理展示逻辑,业务层中的组件只会去处理业务逻辑。多亏了组件分离,让 我们更容易构造有效的角色和强⼒力的模型。这样应用变的更好开发,测试,管理和维护。 31 | 32 | ### 关键概念 33 | 34 | 注意表1-2中每一层都是封闭的。这是分层架构中非常重要的特点。这意味request必须一层一层的传递。举 个例子,从展示层传递来的请求⾸首先会传递到业务层,然后传递到持久层,最后才传递到数据层。 35 | 36 | ![](http://i.imgur.com/D7sRDmv.png) 37 | 38 | 那么为什么不允许展示层直接访问数据层呢。如果只是获得以及读取数据,展示层直接访问数据层,比穿过一层一层来得到数据来的快多了。这涉及到一个概念:层隔离。 39 | 40 | 层隔离就是说架构中的某一层的改变不会影响到其他层:这些变化的影响范围限于当前层次。如果展示层能够 直接访问持久层了,假如持久层中的SQL变化了,这对业务层和展示层都有一定的影响。这只会让应用变得 紧耦合,组件之间互相依赖。这种架构会非常的难以维护。 41 | 42 | 从另外一个方面来说,分层隔离使得层与层之间都是相互独立的,架构中的每一层的互相了解都很少。为了 说明这个概念的⽜牛逼之处,想象一个超级重构,把展示层从JSP换成JSF。假设展示层和业务层的之间的联系 保持一致,业务层不会受到重构的影响,它和展示层所使用的界面架构完全独立。 43 | 44 | 然⽽而封闭的架构层次也有不便之处,有时候也应该开放某一层。如果想往包含了一些由业务层的组件调用的 普通服务组件的架构中添加个分享服务层。在这个例子里,新建一个服务层通常是个好主意,因为从架构上来说,它限制了分享服务访问业务层(也不允许访问展示层)。如果没有隔离层,就没有任何架构来限制展示层访问普通服务,难以进行权限管理。 45 | 46 | 在这个例子中,新的服务层是处于业务层之下的,展示层不能直接访问这个服务层中的组件。但是现在业务 层还要通过服务层才能访问到持久层,这一点也不合理。这是分层架构中的老问题了,解决的办法是开放某 些层。如表1-3所示,服务层现在是开放的了。请求可以绕过这一层,直接访问这层下面的层。既然服务层 是开放的,业务层可以绕过服务层,直接访问数据持久层。这样就非常合理。 47 | 48 | ![](http://i.imgur.com/NEnLu8b.png) 49 | 50 | 开放和封闭层的概念确定了架构层和请求流之间的关系,并且给设计师和开发人员提供了必要的信息理解架 构里各种层之间的访问限制。如果随意的开放或者封闭架构里的层,整个项目可能都是紧耦合,一团糟的。以后也难以测试,维护和部署。 51 | 52 | ### 示例 53 | 54 | 为了演示分层架构是如何工作的,想象一个场景,如表1-4,用户发出了个请求要获得客户的信息。黑色的箭头是从数据库中获得用户数据的请求流,红色箭头显示用户数据的返回流的方向。在这个例子中,用户信 息由客户数据和订单数组组成(客户下的订单)。 55 | 56 | 用户界面只管接受请求以及显示客户信息。它不管怎么得到数据的,或者说得到这些数据要用到哪些数据 表。如果用户界面接到了一个查询客户信息的请求,它就会转发这个请求给用户委托(Customer Delegate)模 块。这个模块能找到业务层里对应的模块处理对应数据(约束关系)。业务层里的customer object聚合了业务 请求需要的所有信息(在这个例子里获取客户信息)。这个模块调用持久层中的 customer dao 来得到客户信 息,调用order dao来得到订单信息。这些模块会执行SQL语句,然后返回相应的数据给业务层。当 customer object收到数据以后,它就会聚合这些数据然后传递给 customer delegate,然后传递这些数据到 customer screen 展示在用户面前。 57 | 58 | ![](http://i.imgur.com/kKlpuwM.png) 59 | 60 | 从技术的角度来说,有很多的方式能够实现这些模块。比如说在Java平台中,customer screen 对应的是 (JSF) Java Server Faces ,用 bean 组件来实现 customer delegate。用本地的Spring bean或者远程的EJB3 bean 来实现业务层中的customer object。上例中的数据访问可以用简单的POJP's(Plain Old Java Objects),或者可以用MyBatis,还可以用JDBC或者Hibernate查询。Microsoft平台上,customer screen能 61 | 用 .NET 库的ASP模块来访问业务层中的C#模块,用ADO来实现用户和订单数据的访问模块。 62 | 63 | 我们看一下淘宝前几年的架构的例子。 64 | 65 | ![](http://i.imgur.com/R5pzfpW.png) 66 | 67 | 这是一个标准的分层的架构。每一层中又可以详细的分成更细的层,比如服务层。 68 | 69 | ![](http://i.imgur.com/M10wcT6.png) 70 | 71 | 围着着这个主架构还有一些外围的产品。比如监控和审计。 72 | 73 | ### 注意事项 74 | 75 | 分层架构是一个很可靠的架构模式。它适合大多数的应用。如果你不确定在项目中使用什么架构,分层架构 是再好不过的了。然后,从架构的角度上来说,选择这个模式还要考虑很多的东⻄西。 76 | 77 | 第一个要注意的就是 污水池反模式(architecture sinkhole anti-pattern)。 在这个模式中,请求流只是简单的 穿过层次,不留一点云彩,或者说只留下一阵⻘青烟。比如说界面层响应了一个获得数据的请求。响应层把这 个请求传递给了业务层,业务层也只是传递了这个请求到持久层,持久层对数据库做简单的SQL查询获得用户的数据。这个数据按照原理返回,不会有任何的二次处理,返回到界面上。 78 | 79 | 每个分层架构或多或少都可能遇到这种场景。关键在于这样的请求有多少。80-20原则可以帮助你确定架构是 否处于反污水模式。大概有百分之二十的请求仅仅是做简单的穿越,百分之八十的请求会做一些业务逻辑操 作。然而,如果这个比例反过来,大部分的请求都是仅仅穿过层,不做逻辑操作。那么开放一些架构层会比较好。不过由于缺少了层次隔离,项目会变得难以控制。 80 | 81 | ### 模式分析 82 | 83 | 下⾯面的的表⾥里分析了分层架构的各个⽅方⾯面。 84 | 85 | #### 整体灵活性 86 | 87 | 评级:低 分析:总体灵活性是响应环境变化的能⼒力。尽管分层模式中的变化可以隔绝起来,想在这种架构中做⼀一些也改 变也是并且费时费⼒力的。分层模式的笨重以及经常出现的组件之间的紧耦合是导致灵活性降低的原因。 88 | 89 | #### 易于部署 90 | 91 | 评级:低 分析:这取决于你怎么发布这种模式,发布程序可能⽐比较⿇麻烦,尤其是很⼤大的项目。⼀一个组件的⼩小⼩小改动可能 会影响到整个程序的发布(或者程序的⼤大部分)。发布必须是按照计划,在⾮非⼯工作时间或者周末进⾏行发布。因此,分层模式导致应⽤用发布⼀一点也不流畅,在发布上降低了灵活性。 92 | 93 | #### 可测试性 94 | 95 | 评级:高 分析:因为组件都处于各⾃自的层次中,可以模拟其他的层,或者说直接去掉层,所以分层模式很容易测试。开发者可以单独模拟⼀一个展⽰示组件,对业务组件进⾏行隔绝测试。还可以模拟业务层来测试某个展⽰示功能。 96 | 97 | #### 性能 98 | 99 | 评级:低 分析:尽管某些分层架构的性能表现的确不错,但是这个模式的特点导致它⽆无法带来⾼高性能。因为⼀一次业务请求要穿越所有的架构层,做了很多不必要的⼯工作。 100 | 101 | #### 伸缩性 102 | 103 | 评级:低 分析:由于这种模式以紧密耦合的趋势在发展,规模也⽐比较⼤大,⽤用分层架构构建的程序都⽐比较难以扩展。你可 以把各个层分成单独的物理模块或者干脆把整个程序分成多个节点来扩展分层架构,但是总体的关系过于紧 104 | 105 | 密,这样很难扩展。 106 | 107 | #### 易开发性 108 | 109 | 评级:容易 分析:在开发难度上⾯面,分层架构得到了⽐比较⾼高的分数。因为这种架构对⼤大家来说很熟悉,不难实现。⼤大部分 公司在开发项⺫⽬目的都是通过层来区分技术的,这种模式对于⼤大多数的商业项目开发来说都很合适。公司的组织架构和他们软件架构之间的联系被戏称为"Conway's law"。你可以Google⼀一下查查这个有趣的联系。 110 | 111 | > 未完待续 -------------------------------------------------------------------------------- /cache/1.concept.md: -------------------------------------------------------------------------------- 1 | # IOC的概念 2 | 3 | ## 什么是IOC? 4 | 5 | IoC(Inversion of Control),意为控制反转,不是什么技术,而是一种设计思想。Ioc意味着**将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制**。 6 | 7 | 如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下: 8 | 9 | - **谁控制谁,控制什么**:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。 10 | 11 | - **为何是反转,哪些方面反转了**:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。 12 | 13 | 下面举个例子说明说明是IOC: 14 | 15 | 假设我们要设计一个Girl和一个Boy类,其中Girl有kiss方法,即Girl想要Kiss一个Boy。那么,我们的问题是,Girl如何能够认识这个Boy? 16 | 17 | 在我们中国,常见的MM与GG的认识方式有以下几种: 18 | 19 | 20 | 1. 青梅竹马 21 | 2. 亲友介绍 22 | 3. 父母包办 23 | 24 | 那么哪一种才是最好呢? 25 |    26 | 1. **青梅竹马:Girl从小就知道自己的Boy。** 27 | 28 | ```java 29 | public class Girl {  30 | void kiss(){ 31 |     Boy boy = new Boy(); 32 |   } 33 | } 34 | ``` 35 | 36 | 然而从开始就创建的Boy缺点就是无法在更换。并且要负责Boy的整个生命周期。如果我们的Girl想要换一个怎么办?(笔者严重不支持Girl经常更换Boy) 37 | 38 | 2. **亲友介绍:由中间人负责提供Boy来见面** 39 | 40 | ```java 41 | public class Girl { 42 |   void kiss(){ 43 |     Boy boy = BoyFactory.createBoy();    44 |   } 45 | } 46 | ``` 47 | 48 | 亲友介绍,固然是好。如果不满意,尽管另外换一个好了。但是,亲友BoyFactory经常是以Singleton的形式出现,不然就是,存在于Globals,无处不在,无处不能。实在是太繁琐了一点,不够灵活。我为什么一定要这个亲友掺和进来呢?为什么一定要付给她介绍费呢?万一最好的朋友爱上了我的男朋友呢? 49 | 50 | 3. **父母包办:一切交给父母,自己不用费吹灰之力,只需要等着Kiss就好了。** 51 | 52 | ```java 53 | public class Girl { 54 |   void kiss(Boy boy){ 55 |     // kiss boy  56 |    boy.kiss(); 57 |   } 58 | } 59 | ``` 60 | 61 | Well,这是对Girl最好的方法,只要想办法贿赂了Girl的父母,并把Boy交给他。那么我们就可以轻松的和Girl来Kiss了。看来几千年传统的父母之命还真是有用哦。至少Boy和Girl不用自己瞎忙乎了。 62 | 63 | 这就是IOC,将对象的创建和获取提取到外部。由外部容器提供需要的组件。 64 | 65 | ## IoC能做什么 66 | 67 | IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。 68 | 69 | 其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。 70 | 71 | IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。 72 | 73 | ## IoC和DI 74 | 75 | DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。 76 | 77 | 理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下: 78 | 79 | - 谁依赖于谁:当然是应用程序依赖于IoC容器; 80 | 81 | - 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源; 82 | 83 | - 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象; 84 | 85 | - 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。 86 | 87 | IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。 88 | 89 | 对于Spring Ioc这个核心概念,我相信每一个学习Spring的人都会有自己的理解。这种概念上的理解没有绝对的标准答案,仁者见仁智者见智。 90 | 理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在框架中堆积木而已,下一节来看看Spring是怎么用的 91 | 92 | ## links 93 | * [目录]() 94 | * 下一节: [Spring中怎么用](<2.spring.md>) -------------------------------------------------------------------------------- /cache/2.spring.md: -------------------------------------------------------------------------------- 1 | # Spring中怎么用 2 | 3 | 我们在Spring中是这样获取对象的: 4 | 5 | ```java 6 | public static void main(String[] args) { 7 | ApplicationContext context = new FileSystemXmlApplicationContext("applicationContext.xml"); 8 | Lol lol = (Lol) context.getBean("lol"); 9 | lol.gank(); 10 | } 11 | ``` 12 | 13 | 一起看看Spring如何让它生效呢,在 `applicationContext.xml` 配置文件中是酱紫的: 14 | 15 | ```xml 16 | 17 | 18 | 19 | ``` 20 | 21 | `Person` 类是这样的: 22 | 23 | ```java 24 | public class Lol { 25 | 26 | private String name; 27 | 28 | public Lol() { 29 | } 30 | 31 | public void gank(){ 32 | System.out.println(this.name + "在gank!!"); 33 | } 34 | 35 | public String getName() { 36 | return name; 37 | } 38 | 39 | public void setName(String name) { 40 | this.name = name; 41 | } 42 | } 43 | ``` 44 | 45 | 上面的代码运行结果自然是 `剑圣在gank!!`。 46 | 47 | Spring更高级的用法,在3.0版本之后有了基于Annotation的注入实现,为毛每次都要配置 `Xml` 看到都蛋疼。。 48 | 49 | 首先还是要在 `xml` 中配置启用注解方式 50 | 51 | ```xml 52 | 53 | ``` 54 | 55 | 这样就能使用注解驱动依赖注入了,下面是一个使用场景 56 | 57 | ```java 58 | public class Lol { 59 | 60 | @Autowired 61 | private DuangService duangService ; 62 | 63 | public void buyDuang(String name, int money) { 64 | duangService.buy(name, money); 65 | } 66 | } 67 | ``` 68 | 69 | ```java 70 | @Service("duangService") 71 | public class DuangService { 72 | 73 | public void buy(String name, int money){ 74 | if(money > 0){ 75 | System.out.println(name + "买了" + money + "毛钱的特效,装逼成功!"); 76 | } else{ 77 | System.out.println(name + "没钱还想装逼,真是匪夷所思"); 78 | } 79 | } 80 | } 81 | ``` 82 | 83 | 这只是一个简单的例子,剑圣打野的时候想要买5毛钱的三杀特效,嗯。。虽然不符合逻辑 84 | 85 | 此时 `DuangService` 已经注入到 `Lol` 对象中,运行代码的结果(这里是例子,代码不能运行的)就是: 86 | 87 | ```sh 88 | 德玛买了5毛钱的特效,装逼成功! 89 | ``` 90 | 91 | 好了,深入的不说了,我们不是学spring的,只是知道一下ioc在spring中高大上的形象,接下来步入正轨,开始设计一个IOC容器 92 | 93 | ## links 94 | * [目录]() 95 | * 上一节: [IOC的概念](<1.concept.md>) 96 | * 下一节: [设计一个IOC](<3.myioc.md>) -------------------------------------------------------------------------------- /cache/index.md: -------------------------------------------------------------------------------- 1 | # 缓存的理解与实现 2 | 3 | ![](http://i.imgur.com/HLyJOSv.png) 4 | 5 | IOC是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、微信号...(balabala),想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从 `JNDI` 中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。 6 | 下面的章节带你理解并实现一个IOC容器。 7 | 8 | IOC源码:[https://github.com/junicorn/easy-ioc](https://github.com/junicorn/easy-ioc) 9 | 10 | ## 目录 11 | 12 | * [IOC的概念](1.concept.md) 13 | * [Spring中怎么用](2.spring.md) 14 | * [设计一个IOC](3.myioc.md) 15 | -------------------------------------------------------------------------------- /designpatterns/singleton.md: -------------------------------------------------------------------------------- 1 | # 如何正确地写出单例模式 2 | 3 | 单例模式算是设计模式中最容易理解,也是最容易手写代码的模式了吧。但是其中的坑却不少,所以也常作为面试题来考。本文主要对几种单例写法的整理,并分析其优缺点。很多都是一些老生常谈的问题,但如果你不知道如何创建一个线程安全的单例,不知道什么是双检锁,那这篇文章可能会帮助到你。 4 | 5 | ### 懒汉式,线程不安全 6 | 7 | 当被问到要实现一个单例模式时,很多人的第一反应是写出如下的代码,包括教科书上也是这样教我们的。 8 | 9 | ```java 10 | public class Singleton { 11 | private static Singleton instance; 12 | private Singleton (){} 13 | 14 | public static Singleton getInstance() { 15 | if (instance == null) { 16 | instance = new Singleton(); 17 | } 18 | return instance; 19 | } 20 | } 21 | ``` 22 | 23 | 这段代码简单明了,而且使用了懒加载模式,但是却存在致命的问题。当有多个线程并行调用 getInstance() 的时候,就会创建多个实例。也就是说在多线程下不能正常工作。 24 | 25 | ### 懒汉式,线程安全 26 | 27 | 为了解决上面的问题,最简单的方法是将整个 getInstance() 方法设为同步(synchronized)。 28 | 29 | ```java 30 | public static synchronized Singleton getInstance() { 31 | if (instance == null) { 32 | instance = new Singleton(); 33 | } 34 | return instance; 35 | } 36 | ``` 37 | 38 | 虽然做到了线程安全,并且解决了多实例的问题,但是它并不高效。因为在任何时候只能有一个线程调用 getInstance() 方法。但是同步操作只需要在第一次调用时才被需要,即第一次创建单例实例对象时。这就引出了双重检验锁。 39 | 40 | ### 双重检验锁 41 | 42 | 双重检验锁模式(double checked locking pattern),是一种使用同步块加锁的方法。程序员称其为双重检查锁,因为会有两次检查 `instance == null`,一次是在同步块外,一次是在同步块内。为什么在同步块内还要再检验一次?因为可能会有多个线程一起进入同步块外的 if,如果在同步块内不进行二次检验的话就会生成多个实例了。 43 | 44 | ```java 45 | public static Singleton getSingleton() { 46 | if (instance == null) { //Single Checked 47 | synchronized (Singleton.class) { 48 | if (instance == null) { //Double Checked 49 | instance = new Singleton(); 50 | } 51 | } 52 | } 53 | return instance ; 54 | } 55 | ``` 56 | 57 | 这段代码看起来很完美,很可惜,它是有问题。主要在于instance = new Singleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情。 58 | 59 | 1. 给 instance 分配内存 60 | 2. 调用 Singleton 的构造函数来初始化成员变量 61 | 3. 将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了) 62 | 63 | 但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。 64 | 65 | 我们只需要将 instance 变量声明成 volatile 就可以了。 66 | 67 | ```java 68 | public class Singleton { 69 | private volatile static Singleton instance; //声明成 volatile 70 | private Singleton (){} 71 | 72 | public static Singleton getSingleton() { 73 | if (instance == null) { 74 | synchronized (Singleton.class) { 75 | if (instance == null) { 76 | instance = new Singleton(); 77 | } 78 | } 79 | } 80 | return instance; 81 | } 82 | 83 | } 84 | ``` 85 | 86 | 有些人认为使用 volatile 的原因是可见性,也就是可以保证线程在本地不会存有 instance 的副本,每次都是去主内存中读取。但其实是不对的。使用 volatile 的主要原因是其另一个特性:禁止指令重排序优化。也就是说,在 volatile 变量的赋值操作后面会有一个内存屏障(生成的汇编代码上),读操作不会被重排序到内存屏障之前。比如上面的例子,取操作必须在执行完 1-2-3 之后或者 1-3-2 之后,不存在执行到 1-3 然后取到值的情况。从「先行发生原则」的角度理解的话,就是对于一个 volatile 变量的写操作都先行发生于后面对这个变量的读操作(这里的“后面”是时间上的先后顺序)。 87 | 88 | 但是特别注意在 Java 5 以前的版本使用了 volatile 的双检锁还是有问题的。其原因是 Java 5 以前的 JMM (Java 内存模型)是存在缺陷的,即时将变量声明成 volatile 也不能完全避免重排序,主要是 volatile 变量前后的代码仍然存在重排序问题。这个 volatile 屏蔽重排序的问题在 Java 5 中才得以修复,所以在这之后才可以放心使用 volatile。 89 | 90 | 相信你不会喜欢这种复杂又隐含问题的方式,当然我们有更好的实现线程安全的单例模式的办法。 91 | 92 | ### 饿汉式 static final field 93 | 94 | 这种方法非常简单,因为单例的实例被声明成 static 和 final 变量了,在第一次加载类到内存中时就会初始化,所以创建实例本身是线程安全的。 95 | 96 | ```java 97 | public class Singleton{ 98 | //类加载时就初始化 99 | private static final Singleton instance = new Singleton(); 100 | 101 | private Singleton(){} 102 | 103 | public static Singleton getInstance(){ 104 | return instance; 105 | } 106 | } 107 | ``` 108 | 109 | 这种写法如果完美的话,就没必要在啰嗦那么多双检锁的问题了。缺点是它不是一种懒加载模式(lazy initialization),单例会在加载类后一开始就被初始化,即使客户端没有调用 getInstance()方法。饿汉式的创建方式在一些场景中将无法使用:譬如 Singleton 实例的创建是依赖参数或者配置文件的,在 getInstance() 之前必须调用某个方法设置参数给它,那样这种单例写法就无法使用了。 110 | 111 | ### 静态内部类 static nested class 112 | 113 | 我比较倾向于使用静态内部类的方法,这种方法也是《Effective Java》上所推荐的。 114 | 115 | ```java 116 | public class Singleton { 117 | private static class SingletonHolder { 118 | private static final Singleton INSTANCE = new Singleton(); 119 | } 120 | private Singleton (){} 121 | public static final Singleton getInstance() { 122 | return SingletonHolder.INSTANCE; 123 | } 124 | } 125 | ``` 126 | 127 | 这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。 128 | 129 | ### 枚举 Enum 130 | 131 | 用枚举写单例实在太简单了!这也是它最大的优点。下面这段代码就是声明枚举实例的通常做法。 132 | 133 | ```java 134 | public enum EasySingleton{ 135 | INSTANCE; 136 | } 137 | ``` 138 | 139 | 我们可以通过EasySingleton.INSTANCE来访问实例,这比调用getInstance()方法简单多了。创建枚举默认就是线程安全的,所以不需要担心double checked locking,而且还能防止反序列化导致重新创建新的对象。但是还是很少看到有人这样写,可能是因为不太熟悉吧。 140 | 141 | ### 总结 142 | 143 | 一般来说,单例模式有五种写法:懒汉、饿汉、双重检验锁、静态内部类、枚举。上述所说都是线程安全的实现,文章开头给出的第一种方法不算正确的写法。 144 | 145 | 就我个人而言,一般情况下直接使用饿汉式就好了,如果明确要求要懒加载(lazy initialization)会倾向于使用静态内部类,如果涉及到反序列化创建对象时会试着使用枚举的方式来实现单例。 146 | 147 | ### Read More 148 | 149 | - [Double Checked Locking on Singleton Class in Java](http://javarevisited.blogspot.sg/2014/05/double-checked-locking-on-singleton-in-java.html) 150 | - [http://javarevisited.blogspot.sg/2012/07/why-enum-singleton-are-better-in-java.html](http://javarevisited.blogspot.sg/2012/07/why-enum-singleton-are-better-in-java.html) 151 | - [How to create thread safe Singleton in Java](http://javarevisited.blogspot.com/2012/12/how-to-create-thread-safe-singleton-in-java-example.html) 152 | - [10 Singleton Pattern Interview questions in Java](http://javarevisited.blogspot.com/2011/03/10-interview-questions-on-singleton.html) 153 | 154 | 由于作者这篇文章写的非常好,我也就没有重写 [原文出处](http://wuchong.me/blog/2014/08/28/how-to-correctly-write-singleton-pattern/#from=biezhi.me) -------------------------------------------------------------------------------- /designpatterns/strategy.md: -------------------------------------------------------------------------------- 1 | # 什么是策略模式 2 | 3 | - [策略模式](#策略模式) 4 | - [一、策略模式的定义](#策略模式的定义) 5 | - [二、策略模式的实际应用](#策略模式的实际应用) 6 | - [三、策略模式中的设计原则](#策略模式中的设计原则) 7 | 8 | ## 策略模式 9 | 10 | ### 策略模式的定义 11 | 12 | **策略模式**,顾名思义就是指对象具有某个行为,但是在不同的业务场景下,这个行为应该有不同的表现形式,也就是有了不同的策略。让对象能再不同的场景下对同一行为有不同的实现,这就是策略模式。 13 | 14 | 下面是策略模式的类图: 15 | 16 | ![](http://i.imgur.com/PE99rte.jpg) 17 | 18 | 1. 首先定义一个策略接口: 19 | ```java 20 | public interface Strategy { 21 | public void algorithmStartegy() ; 22 | } 23 | ``` 24 | 2. 定义两个具体的策略类: 25 | ```java 26 | public class ConcentrateStrategy_1 implements Strategy{ 27 | @Override 28 | public void algorithmStartegy() { 29 | System.out.println("I am algorithm strategy 1"); 30 | } 31 | } 32 | public class ConcentrateStrategy_2 implements Strategy{ 33 | @Override 34 | public void algorithmStartegy() { 35 | System.out.println("I am algorithm strategy 2"); 36 | } 37 | } 38 | ``` 39 | 3. 定义一个算法使用场景 40 | ```java 41 | public class Situation { 42 | Strategy strategy; 43 | public Situation(Strategy strategy) { 44 | this.strategy = strategy; 45 | } 46 | public void handleAlgorithm() { 47 | strategy.algorithmStartegy(); 48 | } 49 | } 50 | ``` 51 | 4. 客户端调用 52 | ```java 53 | Situation situation = new Situation(new ConcentrateStrategy_1()); 54 | situation.handleAlgorithm(); 55 | ... 56 | ``` 57 | 58 | 从策略模式的描述以及类图来看真的是非常简单,总结起来就是策略模式定义了一组算法,它们有一个共同的**策略行为接口**,并且这些算法之间可以**互相替换**,使算法可以根据场景的不同而改变。 59 | 60 | ### 策略模式的实际应用 61 | 62 | 策略模式的应用有很多, 比如说JDK中`FilenameFilter`的使用过程, 比如场景`java.util.Collections.sort(Listlist, Comparatorc)`与策略`java.util.Comparator`的使用等等。 63 | 下面我以一个实际的业务场景来具体实现以下策略模式: 64 | 65 | 1. 背景: 中国银行的便民服务包括中国移动手机充值, 中国联通,中国电信。用户选择某个便民服务时, 服务器后台会向银行发送不同的业务XML报文, 已达到通信目的。 66 | 2. 实现 67 | 1. 定义策略接口 : 包含一个生成报文的方法 68 | ```java 69 | public interface IProduct { 70 | public String generateXML() ; 71 | } 72 | ``` 73 | 2. 定义一组具体策略类: 74 | ```java 75 | @XMLType("Default") 76 | class DefaultHead implements IProduct { 77 | public String generateXML() { 78 | return "Defalut XML"; 79 | } 80 | } 81 | @XMLType("ChinaMobile") 82 | class ChinaMobile implements IProduct{ 83 | public String generateXML() { 84 | return "China Mobile XML"; 85 | } 86 | } 87 | @XMLType("ChinaUnicom") 88 | class ChinaUnicom implements IProduct { 89 | public String generateXML() { 90 | return "Chinal Unicom XML"; 91 | } 92 | } 93 | ``` 94 | 3. 定义一个场景类用于生成XML报文 95 | ```java 96 | public class XMLGenerator { 97 | private IProduct product = new DefaultHead(); 98 | //根据用户缴费类型,生成不同的通信报文 99 | public String generate(String type) { 100 | //注意此处 101 | product = ProductFactory.getInstance().createProduct(type); 102 | return product.generateXML(); 103 | } 104 | } 105 | ``` 106 | 4. 创建具体的策略类, 本来是可以用一系列的`if else`判断, 然后new相应的策略类。这里为了避免`if else`我们定义一个策略工厂,来生产具体的策略类。 107 | **策略类工厂**:这个工厂创建策略类的思路就是,载入一些列策略类,根据不同策略类的**自定义注解**和用户的传入参数来生成具体的策略类。 108 | ```java 109 | public class ProductFactory { 110 | // singleton 111 | private ProductFactory() {} 112 | 113 | public static ProductFactory getInstance() { 114 | return ProductFactoryInstance.instance; 115 | } 116 | 117 | private static class ProductFactoryInstance { 118 | static final ProductFactory instance = new ProductFactory(); 119 | } 120 | 121 | //创建一个具体的策略类 122 | public IProduct createProduct(String productType) throws URISyntaxException { 123 | // load all startegys 124 | List> startegyList = loadAllStrategy("com.su.startegy"); 125 | //遍历所有策略类, 根据条件找出需要用到的 126 | for(Class clazz : startegyList) { 127 | //解析策略类的注解 128 | XMLType xmlType = praseAnnotation(clazz); 129 | if(xmlType.value().equals(productType)) 130 | try { 131 | return clazz.newInstance(); 132 | } catch (InstantiationException | IllegalAccessException e) { 133 | e.printStackTrace(); 134 | } 135 | } 136 | } 137 | //create IProduct Object failed 138 | return null; 139 | } 140 | 141 | //载入策略类方法 142 | private List> loadAllStrategy(String packageName) throws URISyntaxException { 143 | //定义一个策略类的列表 144 | List> strategyList = new ArrayList>(); 145 | URI filePath = getClass().getClassLoader().getResource(packageName.replace(".", "/")).toURI(); 146 | //获取filepath 147 | File[] files = new File(filePath).listFiles(new FilenameFilter() { 148 | @Override 149 | public boolean accept(File dir, String name) { 150 | if (name.endsWith(".class")) 151 | return true; 152 | return false; 153 | } 154 | }); 155 | // load class 156 | for (File file : files) { 157 | try { 158 | Class clazz = getClass().getClassLoader().loadClass(packageName + "." + file.getName().replace(".class", "")); 159 | if (clazz != IProduct.class && IProduct.class.isAssignableFrom(clazz)) { 160 | strategyList.add(clazz); 161 | } 162 | } catch (ClassNotFoundException e) { 163 | e.printStackTrace(); 164 | } 165 | } 166 | return strategyList; 167 | } 168 | 169 | //解析注解方法 170 | private XMLType praseAnnotation(Class clazz) { 171 | XMLType xmlType = clazz.getAnnotation(XMLType.class); 172 | if (xmlType == null) { 173 | return null; 174 | } 175 | return xmlType; 176 | } 177 | 178 | } 179 | ``` 180 | 181 | 此处我使用**注解**的原因是为了简单, 跟代码的耦合紧一点。 182 | 183 | **自定义的注解类: ** 184 | 185 | ```java 186 | @Target(ElementType.TYPE) 187 | @Retention(RetentionPolicy.RUNTIME) 188 | public @interface XMLType { 189 | public String value() default "defalut"; 190 | } 191 | ``` 192 | 193 | 以上就是此处的策略模式的设计过程,如果需要多种策略的叠加, 也就要相应的使用注解的嵌套了, 这里就不在发挥了。 194 | 195 | ### 策略模式中的设计原则 196 | 197 | 学习策略模式, 不要记住代码是怎么实现的,更重要的是记住其设计原则。根据原则写代码, 而不是生搬硬套。其实设计模式都是设计原则的体现, 如果理解了设计原则, 那么你写的代码也可以变成一种模式。 198 | 199 | 1. **开闭原则(Open-Closed Principle,缩写为OCP)** 200 | 一个软件实体应当对扩展(例如对抽象层的扩展)开放,对修改(例如对抽象层的修改)关闭。即在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展。 201 | 开闭原则的关键,在于抽象。策略模式,是开闭原则的一个极好的应用范例。 202 | 203 | 2. **里氏替换原则(Liskov Substitution Principle,缩写为LSP)** 204 | 一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且它根本不能察觉到基类对象和子类对象的区别。比如,假设有两个类,一个是Base类,一个是Derived类,并且Derived类是Base类的子类。那么一个方法如果可以接受一个基类对象b的话:method1(Base b),那么它必然可以接受一个子类对象d,也即可以有method1(d)。反之,则不一定成立 205 | 里氏替换原则讲的是基类与子类的关系。只有当这种关系存在时,里氏替换关系才存在,反之则不存在。 206 | 207 | 策略模式之所以可行的基础便是里氏替换原则:策略模式要求所有的策略对象都是可以互换的,因此它们都必须是一个抽象策略角色的子类。在客户端则仅知道抽象策略角色类型,虽然变量的真实类型可以是任何一个具体策略角色的实例 208 | 209 | ## 参考文献 210 | 211 | [策略模式设计原则](http://blog.csdn.net/caihaijiang/article/details/8764226) -------------------------------------------------------------------------------- /git/guide.md: -------------------------------------------------------------------------------- 1 | # git - 简明指南 2 | 3 | > 助你入门 git 的简明指南,木有高深内容 ;) 4 | 5 | ## 安装 6 | 7 | * [下载 git OSX 版](http://code.google.com/p/git-osx-installer/downloads/list?can=3) 8 | * [下载 git Windows 版](http://msysgit.github.io/) 9 | * [下载 git Linux 版](http://book.git-scm.com/2_installing_git.html) 10 | 11 | ## 创建新仓库 12 | 13 | 创建新文件夹,打开,然后执行 14 | 15 | ```sh 16 | git init 17 | ``` 18 | 19 | 以创建新的 git 仓库。 20 | 21 | ## 检出仓库 22 | 23 | 执行如下命令以创建一个本地仓库的克隆版本: 24 | 25 | ```sh 26 | git clone /path/to/repository 27 | ``` 28 | 29 | 如果是远端服务器上的仓库,你的命令会是这个样子: 30 | 31 | ```sh 32 | git clone username@host:/path/to/repository 33 | ``` 34 | 35 | ## 工作流 36 | 37 | 你的本地仓库由 git 维护的三棵“树”组成。第一个是你的 `工作目录`,它持有实际文件;第二个是 `暂存区(Index)`,它像个缓存区域,临时保存你的改动;最后是 `HEAD`,它指向你最后一次提交的结果。 38 | 39 | ![](http://7jpobz.com1.z0.glb.clouddn.com/trees.png) 40 | 41 | ## 添加和提交 42 | 43 | 你可以提出更改(把它们添加到暂存区),使用如下命令: 44 | 45 | ```sh 46 | git add 47 | git add * 48 | ``` 49 | 50 | 这是 git 基本工作流程的第一步;使用如下命令以实际提交改动: 51 | 52 | ```sh 53 | git commit -m "代码提交信息" 54 | ``` 55 | 56 | 现在,你的改动已经提交到了 **HEAD**,但是还没到你的远端仓库。 57 | 58 | ## 推送改动 59 | 60 | 你的改动现在已经在本地仓库的 **HEAD** 中了。执行如下命令以将这些改动提交到远端仓库: 61 | 62 | ```sh 63 | git push origin master 64 | ``` 65 | 66 | 可以把 ***master*** 换成你想要推送的任何分支。 67 | 68 | 如果你还没有克隆现有仓库,并欲将你的仓库连接到某个远程服务器,你可以使用如下命令添加: 69 | 70 | ```sh 71 | git remote add origin 72 | ``` 73 | 74 | 如此你就能够将你的改动推送到所添加的服务器上去了。 75 | 76 | ## 分支 77 | 78 | 分支是用来将特性开发绝缘开来的。在你创建仓库的时候,***master*** 是“默认的”分支。在其他分支上进行开发,完成后再将它们合并到主分支上。 79 | 80 | ![](http://7jpobz.com1.z0.glb.clouddn.com/branches.png) 81 | 82 | 创建一个叫做“feature_x”的分支,并切换过去: 83 | 84 | ```sh 85 | git checkout -b feature_x 86 | ``` 87 | 88 | 切换回主分支: 89 | 90 | ```sh 91 | git checkout master 92 | ``` 93 | 94 | 再把新建的分支删掉: 95 | 96 | ```sh 97 | git branch -d feature_x 98 | ``` 99 | 100 | 除非你将分支推送到远端仓库,不然该分支就是 *不为他人所见的*: 101 | 102 | ```sh 103 | git push origin 104 | ``` 105 | 106 | ## 更新与合并 107 | 108 | 要更新你的本地仓库至最新改动,执行: 109 | 110 | ```sh 111 | git pull 112 | ``` 113 | 114 | 以在你的工作目录中 *获取*(fetch) 并 *合并*(merge) 远端的改动。 115 | 要合并其他分支到你的当前分支(例如 master),执行: 116 | 117 | ```sh 118 | git merge 119 | ``` 120 | 121 | 在这两种情况下,git 都会尝试去自动合并改动。遗憾的是,这可能并非每次都成功,并可能出现*冲突*(conflicts)。 这时候就需要你修改这些文件来手动合并这些*冲突*(conflicts)。改完之后,你需要执行如下命令以将它们标记为合并成功: 122 | 123 | ```sh 124 | git add 125 | ``` 126 | 127 | 在合并改动之前,你可以使用如下命令预览差异: 128 | 129 | ```sh 130 | git diff 131 | ``` 132 | 133 | ## 标签 134 | 135 | 为软件发布创建标签是推荐的。这个概念早已存在,在 SVN 中也有。你可以执行如下命令创建一个叫做 *1.0.0* 的标签: 136 | 137 | ```sh 138 | git tag 1.0.0 1b2e1d63ff 139 | ``` 140 | 141 | *1b2e1d63ff* 是你想要标记的提交 ID 的前 10 位字符。可以使用下列命令获取提交 ID: 142 | 143 | ```sh 144 | git log 145 | ``` 146 | 147 | 你也可以使用少一点的提交 ID 前几位,只要它的指向具有唯一性。 148 | 149 | ## 替换本地改动 150 | 151 | 假如你操作失误(当然,这最好永远不要发生),你可以使用如下命令替换掉本地改动: 152 | 153 | ```sh 154 | git checkout -- 155 | ``` 156 | 157 | 此命令会使用 HEAD 中的最新内容替换掉你的工作目录中的文件。已添加到暂存区的改动以及新文件都不会受到影响。 158 | 159 | 假如你想丢弃你在本地的所有改动与提交,可以到服务器上获取最新的版本历史,并将你本地主分支指向它: 160 | 161 | ```sh 162 | git fetch origin 163 | git reset --hard origin/master 164 | ``` 165 | 166 | ## 实用小贴士 167 | 168 | 内建的图形化 git: 169 | 170 | ```sh 171 | gitk 172 | ``` 173 | 174 | 彩色的 git 输出: 175 | 176 | ```sh 177 | git config color.ui true 178 | ``` 179 | 180 | 显示历史记录时,每个提交的信息只显示一行: 181 | 182 | ```sh 183 | git config format.pretty oneline 184 | ``` 185 | 186 | 交互式添加文件到暂存区: 187 | 188 | ```sh 189 | git add -i 190 | ``` 191 | 192 | ## 链接与资源 193 | 194 | ### 图形化客户端 195 | 196 | - [GitX (L) (OSX, 开源软件)](http://gitx.laullon.com/) 197 | - [Tower (OSX)](http://www.git-tower.com/) 198 | - [Source Tree (OSX, 免费)](http://www.sourcetreeapp.com/) 199 | - [GitHub for Mac (OSX, 免费)](http://mac.github.com/) 200 | - [GitBox (OSX, App Store)](https://itunes.apple.com/gb/app/gitbox/id403388357?mt=12) 201 | 202 | ### 指南和手册 203 | 204 | - [Git 社区参考书](http://book.git-scm.com/) 205 | - [专业 Git](http://progit.org/book/) 206 | - [像 git 那样思考](http://think-like-a-git.net/) 207 | - [GitHub 帮助](http://help.github.com/) 208 | - [图解 Git](http://marklodato.github.io/visual-git-guide/index-zh-cn.html) 209 | -------------------------------------------------------------------------------- /hexo/hello.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 使用hexo搭建博客系列 4 | 5 | #### 1. 分分钟部署一个Hexo环境 6 | #### 2. 了解配置 7 | 8 | 9 | ## 1. 安装NodeJS 10 | 11 | 我的系统环境:Win7_x64 12 | 去Nodejs的官网下载:[https://nodejs.org/en/download/](https://nodejs.org/en/download/) 13 | 14 | 这里我下载的是 15 | 16 | ![](https://i.imgur.com/l1hRF0V.png) 17 | 18 | 然后按照提示一步一步安装即可,我装在了C盘,这个盘装了SSD会快一些。 19 | NodeJS会自动将`bin`写入环境变量,来试试是否安装成功 输入`node -v`命令查看nodejs版本。 20 | 21 | ![](https://i.imgur.com/Mvxp2C1.png) 22 | 23 | ## 2. 配置淘宝 NPM 镜像 24 | cnpmjs.org是一个非常棒的npm国内镜像。由于其使用量越来越大,加上淘宝内部也有很多项目使用 NodeJS,于是,淘宝正式基于 cnpmjs 推出了镜像服务 25 | 淘宝的 NPM 镜像是一个完整的npmjs.org镜像。你可以用此代替官方版本(只读),同步频率目前为 15分钟 一次以保证尽量与官方服务同步。 26 | 27 | ![](https://i.imgur.com/eJFvHTK.png) 28 | 29 | 当前 registry.npm.taobao.org 是从 registry.npmjs.org 进行全量同步的. 30 | 当前 npm.taobao.org 运行版本是: cnpmjs.org@0.4.1 31 | 系统运行在 Node.js@v0.11.12 上. 32 | 33 | ### 使用说明 34 | 你可以使用淘宝定制的 [cnpm](https://github.com/cnpm/cnpm) (gzip 压缩支持) 命令行工具代替默认的 `npm`: 35 | ```bash 36 | $ npm install -g cnpm --registry=https://registry.npm.taobao.org 37 | ``` 38 | 我使用的就是这个。 39 | 40 | ![](https://i.imgur.com/ZIw1Vkr.png) 41 | 42 | 或者你直接通过添加 `npm` 参数 `alias` 一个新命令: 43 | ```bash 44 | alias cnpm="npm --registry=https://registry.npm.taobao.org \ 45 | --cache=$HOME/.npm/.cache/cnpm \ 46 | --disturl=https://npm.taobao.org/dist \ 47 | --userconfig=$HOME/.cnpmrc" 48 | 49 | # Or alias it in .bashrc or .zshrc 50 | $ echo '\n#alias for cnpm\nalias cnpm="npm --registry=https://registry.npm.taobao.org \ 51 | --cache=$HOME/.npm/.cache/cnpm \ 52 | --disturl=https://npm.taobao.org/dist \ 53 | --userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc 54 | ``` 55 | 56 | ### 安装模块 57 | 58 | 从 [registry.npm.taobao.org](http://registry.npm.taobao.org/) 安装所有模块. 当安装的时候发现安装的模块还没有同步过来, 淘宝 NPM 会自动在后台进行同步, 并且会让你从官方 NPM [registry.npmjs.org](http://registry.npmjs.org/) 进行安装. 下次你再安装这个模块的时候, 就会直接从 淘宝 NPM 安装了. 59 | 60 | ```bash 61 | $ cnpm install [name] 62 | ``` 63 | 64 | ### 同步模块 65 | 直接通过 `sync` 命令马上同步一个模块, 只有 `cnpm` 命令行才有此功能: 66 | ```bash 67 | $ cnpm sync connect 68 | ``` 69 | 当然, 你可以直接通过 web 方式来同步: [/sync/connect](http://npm.taobao.org/sync/connect) 70 | ```bash 71 | $ open https://npm.taobao.org/sync/connect 72 | ``` 73 | 74 | ## 3. 安装hexo 75 | 76 | [hexo](https://hexo.io/) 官网的安装说明是 `npm install hexo-cli -g` 77 | 78 | 因为我们安装了淘宝的NPM,所以需要使用 `cnpm` 命令 79 | 80 | ![](https://i.imgur.com/yPkpF4O.png) 81 | 82 | 这样就安装成功了! 83 | 84 | ## 4. HelloWorld 85 | 86 | 按照官方教程(注意使用 `cnpm` 哦): 87 | ```bash 88 | $ hexo init blog 89 | $ cd blog 90 | $ cnpm install 91 | $ hexo server 92 | ``` 93 | 这样就可以运行一个最简单的博客程序了~ 94 | 95 | 下一节:[各种配置详解](config.md) 96 | -------------------------------------------------------------------------------- /ioc/1.concept.md: -------------------------------------------------------------------------------- 1 | # IOC的概念 2 | 3 | ## 什么是IOC? 4 | 5 | IoC(Inversion of Control),意为控制反转,不是什么技术,而是一种设计思想。Ioc意味着**将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制**。 6 | 7 | 如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下: 8 | 9 | - **谁控制谁,控制什么**:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。 10 | 11 | - **为何是反转,哪些方面反转了**:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。 12 | 13 | 下面举个例子说明说明是IOC: 14 | 15 | 假设我们要设计一个Girl和一个Boy类,其中Girl有kiss方法,即Girl想要Kiss一个Boy。那么,我们的问题是,Girl如何能够认识这个Boy? 16 | 17 | 在我们中国,常见的MM与GG的认识方式有以下几种: 18 | 19 | 20 | 1. 青梅竹马 21 | 2. 亲友介绍 22 | 3. 父母包办 23 | 24 | 那么哪一种才是最好呢? 25 |    26 | 1. **青梅竹马:Girl从小就知道自己的Boy。** 27 | 28 | ```java 29 | public class Girl {  30 | void kiss(){ 31 |     Boy boy = new Boy(); 32 |   } 33 | } 34 | ``` 35 | 36 | 然而从开始就创建的Boy缺点就是无法在更换。并且要负责Boy的整个生命周期。如果我们的Girl想要换一个怎么办?(笔者严重不支持Girl经常更换Boy) 37 | 38 | 2. **亲友介绍:由中间人负责提供Boy来见面** 39 | 40 | ```java 41 | public class Girl { 42 |   void kiss(){ 43 |     Boy boy = BoyFactory.createBoy();    44 |   } 45 | } 46 | ``` 47 | 48 | 亲友介绍,固然是好。如果不满意,尽管另外换一个好了。但是,亲友BoyFactory经常是以Singleton的形式出现,不然就是,存在于Globals,无处不在,无处不能。实在是太繁琐了一点,不够灵活。我为什么一定要这个亲友掺和进来呢?为什么一定要付给她介绍费呢?万一最好的朋友爱上了我的男朋友呢? 49 | 50 | 3. **父母包办:一切交给父母,自己不用费吹灰之力,只需要等着Kiss就好了。** 51 | 52 | ```java 53 | public class Girl { 54 |   void kiss(Boy boy){ 55 |     // kiss boy  56 |    boy.kiss(); 57 |   } 58 | } 59 | ``` 60 | 61 | Well,这是对Girl最好的方法,只要想办法贿赂了Girl的父母,并把Boy交给他。那么我们就可以轻松的和Girl来Kiss了。看来几千年传统的父母之命还真是有用哦。至少Boy和Girl不用自己瞎忙乎了。 62 | 63 | 这就是IOC,将对象的创建和获取提取到外部。由外部容器提供需要的组件。 64 | 65 | ## IoC能做什么 66 | 67 | IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。 68 | 69 | 其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。 70 | 71 | IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。 72 | 73 | ## IoC和DI 74 | 75 | DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。 76 | 77 | 理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下: 78 | 79 | - 谁依赖于谁:当然是应用程序依赖于IoC容器; 80 | 81 | - 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源; 82 | 83 | - 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象; 84 | 85 | - 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。 86 | 87 | IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。 88 | 89 | 对于Spring Ioc这个核心概念,我相信每一个学习Spring的人都会有自己的理解。这种概念上的理解没有绝对的标准答案,仁者见仁智者见智。 90 | 理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在框架中堆积木而已,下一节来看看Spring是怎么用的 91 | 92 | ## links 93 | * [目录]() 94 | * 下一节: [Spring中怎么用](<2.spring.md>) -------------------------------------------------------------------------------- /ioc/2.spring.md: -------------------------------------------------------------------------------- 1 | # Spring中怎么用 2 | 3 | 我们在Spring中是这样获取对象的: 4 | 5 | ```java 6 | public static void main(String[] args) { 7 | ApplicationContext context = new FileSystemXmlApplicationContext("applicationContext.xml"); 8 | Lol lol = (Lol) context.getBean("lol"); 9 | lol.gank(); 10 | } 11 | ``` 12 | 13 | 一起看看Spring如何让它生效呢,在 `applicationContext.xml` 配置文件中是酱紫的: 14 | 15 | ```xml 16 | 17 | 18 | 19 | ``` 20 | 21 | `Lol` 类是这样的: 22 | 23 | ```java 24 | public class Lol { 25 | 26 | private String name; 27 | 28 | public Lol() { 29 | } 30 | 31 | public void gank(){ 32 | System.out.println(this.name + "在gank!!"); 33 | } 34 | 35 | public String getName() { 36 | return name; 37 | } 38 | 39 | public void setName(String name) { 40 | this.name = name; 41 | } 42 | } 43 | ``` 44 | 45 | 上面的代码运行结果自然是 `剑圣在gank!!`。 46 | 47 | Spring更高级的用法,在3.0版本之后有了基于Annotation的注入实现,为毛每次都要配置 `Xml` 看到都蛋疼。。 48 | 49 | 首先还是要在 `xml` 中配置启用注解方式 50 | 51 | ```xml 52 | 53 | ``` 54 | 55 | 这样就能使用注解驱动依赖注入了,下面是一个使用场景 56 | 57 | ```java 58 | public class Lol { 59 | 60 | @Autowired 61 | private DuangService duangService ; 62 | 63 | public void buyDuang(String name, int money) { 64 | duangService.buy(name, money); 65 | } 66 | } 67 | ``` 68 | 69 | ```java 70 | @Service("duangService") 71 | public class DuangService { 72 | 73 | public void buy(String name, int money){ 74 | if(money > 0){ 75 | System.out.println(name + "买了" + money + "毛钱的特效,装逼成功!"); 76 | } else{ 77 | System.out.println(name + "没钱还想装逼,真是匪夷所思"); 78 | } 79 | } 80 | } 81 | ``` 82 | 83 | 这只是一个简单的例子,剑圣打野的时候想要买5毛钱的三杀特效,嗯。。虽然不符合逻辑 84 | 85 | 此时 `DuangService` 已经注入到 `Lol` 对象中,运行代码的结果(这里是例子,代码不能运行的)就是: 86 | 87 | ```sh 88 | 德玛买了5毛钱的特效,装逼成功! 89 | ``` 90 | 91 | 好了,深入的不说了,我们不是学spring的,只是知道一下ioc在spring中高大上的形象,接下来步入正轨,开始设计一个IOC容器 92 | 93 | ## links 94 | * [目录]() 95 | * 上一节: [IOC的概念](<1.concept.md>) 96 | * 下一节: [设计一个IOC](<3.myioc.md>) 97 | -------------------------------------------------------------------------------- /ioc/4.principle.md: -------------------------------------------------------------------------------- 1 | # 原理分析 2 | 3 | ## 类装载器ClassLoader 4 | 5 | ### 类装载器工作机制 6 | 7 | 类装载器就是寻找类的节码文件并构造出类在JVM内部表示对象的组件。在Java中,类装载器把一个类装入JVM中,要经过以下步骤: 8 | 9 | [1.]装载:查找和导入Class文件; 10 | [2.]链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的: 11 | [2.1]校验:检查载入Class文件数据的正确性; 12 | [2.2]准备:给类的静态变量分配存储空间; 13 | [2.3]解析:将符号引用转成直接引用; 14 | [3.]初始化:对类的静态变量、静态代码块执行初始化工作。 15 | 16 | 类装载工作由ClassLoader及其子类负责,ClassLoader是一个重要的Java运行时系统组件,它负责在运行时查找和装入Class字节码文件。JVM在运行时会产生三个ClassLoader:根装载器、ExtClassLoader(扩展类装载器)和AppClassLoader(系统类装载器)。其中,根装载器不是ClassLoader的子类,它使用C++编写,因此我们在Java中看不到它,根装载器负责装载JRE的核心类库,如JRE目标下的rt.jar、charsets.jar等。ExtClassLoader和AppClassLoader都是ClassLoader的子类。其中ExtClassLoader负责装载JRE扩展目录ext中的JAR类包;AppClassLoader负责装载Classpath路径下的类包。 17 | 18 | 这三个类装载器之间存在父子层级关系,即根装载器是ExtClassLoader的父装载器,ExtClassLoader是AppClassLoader的父装载器。默认情况下,使用AppClassLoader装载应用程序的类,我们可以做一个实验: 19 | 20 | ```java 21 | public class ClassLoaderTest { 22 | public static void main(String[] args) { 23 | ClassLoader loader = Thread.currentThread().getContextClassLoader(); 24 | System.out.println("current loader:"+loader); 25 | System.out.println("parent loader:"+loader.getParent()); 26 | System.out.println("grandparent loader:"+loader.getParent(). getParent()); 27 | } 28 | } 29 | ``` 30 | 31 | 运行以上代码,在控制台上将打出以下信息: 32 | 33 | ```sh 34 | current loader:sun.misc.Launcher$AppClassLoader@131f71a 35 | parent loader:sun.misc.Launcher$ExtClassLoader@15601ea 36 | //①根装载器在Java中访问不到,所以返回null 37 | grandparent loader:null 38 | ``` 39 | 40 | 通过以上的输出信息,我们知道当前的ClassLoader是AppClassLoader,父ClassLoader是ExtClassLoader,祖父ClassLoader是根类装载器,因为在Java中无法获得它的句柄,所以仅返回null。 41 | 42 | JVM装载类时使用“全盘负责委托机制”,“全盘负责”是指当一个ClassLoader装载一个类的时,除非显式地使用另一个ClassLoader,该类所依赖及引用的类也由这个ClassLoader载入;“委托机制”是指先委托父装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标类。这一点是从安全角度考虑的,试想如果有人编写了一个恶意的基础类(如java.lang.String)并装载到JVM中将会引起多么可怕的后果。但是由于有了“全盘负责委托机制”,java.lang.String永远是由根装载器来装载的,这样就避免了上述事件的发生。 43 | 44 | ### ClassLoader重要方法 45 | 46 | 在Java中,ClassLoader是一个抽象类,位于java.lang包中。下面对该类的一些重要接口方法进行介绍: 47 | 48 | - `Class loadClass(String name)` 49 | name参数指定类装载器需要装载类的名字,必须使用全限定类名,如com.baobaotao. beans.Car。该方法有一个重载方法loadClass(String name ,boolean resolve),resolve参数告诉类装载器是否需要解析该类。在初始化类之前,应考虑进行类解析的工作,但并不是所有的类都需要解析,如果JVM只需要知道该类是否存在或找出该类的超类,那么就不需要进行解析。 50 | - `Class defineClass(String name, byte[] b, int off, int len)` 51 | 将类文件的字节数组转换成JVM内部的java.lang.Class对象。字节数组可以从本地文件系统、远程网络获取。name为字节数组对应的全限定类名。 52 | - `Class findSystemClass(String name)` 53 | 从本地文件系统载入Class文件,如果本地文件系统不存在该Class文件,将抛出ClassNotFoundException异常。该方法是JVM默认使用的装载机制。 54 | - `Class findLoadedClass(String name)` 55 | 调用该方法来查看ClassLoader是否已装入某个类。如果已装入,那么返回java.lang.Class对象,否则返回null。如果强行装载已存在的类,将会抛出链接错误。 56 | - `ClassLoader getParent()` 57 | 获取类装载器的父装载器,除根装载器外,所有的类装载器都有且仅有一个父装载器,ExtClassLoader的父装载器是根装载器,因为根装载器非Java编写,所以无法获得,将返回null。 58 | 59 | 除JVM默认的三个ClassLoader以外,可以编写自己的第三方类装载器,以实现一些特殊的需求。类文件被装载并解析后,在JVM内将拥有一个对应的java.lang.Class类描述对象,该类的实例都拥有指向这个类描述对象的引用,而类描述对象又拥有指向关联ClassLoader的引用,如图所示。 60 | 61 | ![](http://i.imgur.com/HuLuFXD.png) 62 | 63 | 每一个类在JVM中都拥有一个对应的java.lang.Class对象,它提供了类结构信息的描述。数组、枚举、注解以及基本Java类型(如int、double等),甚至void都拥有对应的Class对象。Class没有public的构造方法。Class对象是在装载类时由JVM通过调用类装载器中的defineClass()方法自动构造的。 64 | 65 | ## Java反射机制 66 | 67 | Class反射对象描述类语义结构,可以从Class对象中获取构造函数、成员变量、方法类等类元素的反射对象,并以编程的方式通过这些反射对象对目标类对象进行操作。这些反射对象类在java.reflect包中定义,下面是最主要的三个反射类: 68 | 69 | - `Constructor`:类的构造函数反射类,通过Class#getConstructors()方法可以获得类的所有构造函数反射对象数组。在JDK5.0中,还可以通过getConstructor(Class... parameterTypes)获取拥有特定入参的构造函数反射对象。Constructor的一个主要方法是newInstance(Object[] initargs),通过该方法可以创建一个对象类的实例,相当于new关键字。在JDK5.0中该方法演化为更为灵活的形式:newInstance (Object... initargs)。 70 | - `Method`:类方法的反射类,通过Class#getDeclaredMethods()方法可以获取类的所有方法反射类对象数组Method[]。在JDK5.0中可以通过getDeclaredMethod(String name, Class... parameterTypes)获取特定签名的方法,name为方法名;Class...为方法入参类型列表。Method最主要的方法是invoke(Object obj, Object[] args),obj表示操作的目标对象;args为方法入参,代码清单3 10③处演示了这个反射类的使用方法。在JDK 5.0中,该方法的形式调整为invoke(Object obj, Object... args)。此外,Method还有很多用于获取类方法更多信息的方法: 71 | 1)Class getReturnType():获取方法的返回值类型; 72 | 2)Class[] getParameterTypes():获取方法的入参类型数组; 73 | 3)Class[] getExceptionTypes():获取方法的异常类型数组; 74 | 4)Annotation[][] getParameterAnnotations():获取方法的注解信息,JDK 5.0中的新方法; 75 | - `Field`:类的成员变量的反射类,通过Class#getDeclaredFields()方法可以获取类的成员变量反射对象数组,通过Class#getDeclaredField(String name)则可获取某个特定名称的成员变量反射对象。Field类最主要的方法是set(Object obj, Object value),obj表示操作的目标对象,通过value为目标对象的成员变量设置值。如果成员变量为基础类型,用户可以使用Field类中提供的带类型名的值设置方法,如setBoolean(Object obj, boolean value)、setInt(Object obj, int value)等。 76 | 77 | 此外,Java还为包提供了Package反射类,在JDK 5.0中还为注解提供了AnnotatedElement反射类。总之,Java的反射体系保证了可以通过程序化的方式访问目标类中所有的元素,对于private或protected的成员变量和方法,只要JVM的安全机制允许,也可以通过反射进行调用,请看下面的例子: 78 | 79 | ```java 80 | /** 81 | * LOL英雄 82 | */ 83 | public class PrivateHero { 84 | 85 | // 英雄名称 86 | private String name; 87 | // 装备名称 88 | private String outfit; 89 | 90 | public PrivateHero() { 91 | } 92 | 93 | public void say(){ 94 | System.out.println(name + "购买了" + outfit); 95 | } 96 | } 97 | ``` 98 | 99 | ```java 100 | public class Test2 { 101 | 102 | public static void main(String[] args) throws Exception { 103 | 104 | ClassLoader loader = Thread.currentThread().getContextClassLoader(); 105 | Class clazz = loader.loadClass("com.biezhi.ioc.PrivateHero"); 106 | 107 | PrivateHero hero = (PrivateHero) clazz.newInstance(); 108 | 109 | Field name = clazz.getDeclaredField("name"); 110 | // 取消Java语言访问检查以访问private变量 111 | name.setAccessible(true); 112 | name.set(hero, "德玛西亚之力"); 113 | 114 | Field outfit = clazz.getDeclaredField("outfit"); 115 | outfit.setAccessible(true); 116 | outfit.set(hero, "神盾"); 117 | 118 | // 运行方法 119 | hero.say(); 120 | } 121 | } 122 | ``` 123 | 124 | 运行该类,打印出以下信息: 125 | 126 | ```java 127 | 德玛西亚之力购买了神盾 128 | ``` 129 | 130 | 在访问private、protected成员变量和方法时必须通过setAccessible(boolean access)方法取消Java语言检查,否则将抛出IllegalAccessException。 131 | 如果JVM的安全管理器设置了相应的安全机制,调用该方法将抛出SecurityException。 132 | 133 | ## links 134 | * [目录]() 135 | * 上一节: [设计一个IOC](3.myioc.md) -------------------------------------------------------------------------------- /ioc/IOC容器实现篇: -------------------------------------------------------------------------------- 1 | # IOC容器实现篇 2 | 3 | -------------------------------------------------------------------------------- /ioc/IOC容器实现篇.md: -------------------------------------------------------------------------------- 1 | # IOC容器实现篇 2 | 3 | -------------------------------------------------------------------------------- /ioc/index.md: -------------------------------------------------------------------------------- 1 | # 理解并实现一个IOC容器 2 | 3 | ![](http://i.imgur.com/HLyJOSv.png) 4 | 5 | IOC是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、微信号...(balabala),想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从 `JNDI` 中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。 6 | 下面的章节带你理解并实现一个IOC容器。 7 | 8 | IOC源码:[https://github.com/junicorn/easy-ioc](https://github.com/junicorn/easy-ioc) 9 | 10 | ## 目录 11 | 12 | * [IOC的概念](1.concept.md) 13 | * [Spring中怎么用](2.spring.md) 14 | * [设计一个IOC](3.myioc.md) 15 | * [原理分析](4.principle.md) 16 | -------------------------------------------------------------------------------- /java8/foreach.md: -------------------------------------------------------------------------------- 1 | # java8 foreach 2 | 3 | 在这篇文章中我将向你演示如何使用Java8中的`foreach`操作`List`和`Map` 4 | 5 | ### 1. Foreach操作Map 6 | 1.1 正常方式遍历Map 7 | ```java 8 | Map items = new HashMap<>(); 9 | items.put("A", 10); 10 | items.put("B", 20); 11 | items.put("C", 30); 12 | items.put("D", 40); 13 | items.put("E", 50); 14 | items.put("F", 60); 15 | 16 | for (Map.Entry entry : items.entrySet()) { 17 | System.out.println("Item : " + entry.getKey() + " Count : " + entry.getValue()); 18 | } 19 | ``` 20 | 21 | 22 | 23 | 1.2 使用Java8的`foreach`+`lambda`表达式遍历Map 24 | ```java 25 | Map items = new HashMap<>(); 26 | items.put("A", 10); 27 | items.put("B", 20); 28 | items.put("C", 30); 29 | items.put("D", 40); 30 | items.put("E", 50); 31 | items.put("F", 60); 32 | 33 | items.forEach((k,v)->System.out.println("Item : " + k + " Count : " + v)); 34 | 35 | items.forEach((k,v)->{ 36 | System.out.println("Item : " + k + " Count : " + v); 37 | if("E".equals(k)){ 38 | System.out.println("Hello E"); 39 | } 40 | }); 41 | ``` 42 | 43 | ###2. Foreach操作List 44 | 2.1 普通方式循环List 45 | ```java 46 | List items = new ArrayList<>(); 47 | items.add("A"); 48 | items.add("B"); 49 | items.add("C"); 50 | items.add("D"); 51 | items.add("E"); 52 | 53 | for(String item : items){ 54 | System.out.println(item); 55 | } 56 | ``` 57 | 2.2 在Java8中使用`foreach`+`lambda`表达式遍历List 58 | ```java 59 | List items = new ArrayList<>(); 60 | items.add("A"); 61 | items.add("B"); 62 | items.add("C"); 63 | items.add("D"); 64 | items.add("E"); 65 | 66 | //lambda 67 | //Output : A,B,C,D,E 68 | items.forEach(item->System.out.println(item)); 69 | 70 | //Output : C 71 | items.forEach(item->{ 72 | if("C".equals(item)){ 73 | System.out.println(item); 74 | } 75 | }); 76 | 77 | //method reference 78 | //Output : A,B,C,D,E 79 | items.forEach(System.out::println); 80 | 81 | //Steam and filter 82 | //Output : B 83 | items.stream() 84 | .filter(s->s.contains("B")) 85 | .forEach(System.out::println); 86 | ``` 87 | 88 | 参考资料: 89 | 1. [Java 8 Iterable forEach JavaDoc](https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html#forEach-java.util.function.Consumer-) 90 | 2. [Java 8 forEach JavaDoc](https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#forEach-java.util.function.BiConsumer-) 91 | 92 | 欢迎star开源web框架Blade:[http://github.com/biezhi/blade](http://github.com/biezhi/blade) -------------------------------------------------------------------------------- /learn_server/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 写给java开发的运维笔记 3 | 4 | 对于Java开发人员,掌握基本的运维技能是必须的,如果你还不熟悉Linux环境,可以看[这个](https://github.com/judasn/Linux-Tutorial)教程。如果你觉得这个笔记还不错记得给这个项目一个 [star](https://github.com/biezhi/java-bible/stargazers) 😊 5 | 6 | ## 初级篇 7 | 8 | 1. [在虚拟机里安装centos6](virtual-machine-install-centos6.md) 9 | 2. [初始化操作系统](init-os.md) 10 | 3. [安装jdk环境](install-jdk.md) 11 | 4. [安装tomcat](install-tomcat.md) 12 | 5. [安装mysql及配置](install-mysql.md) 13 | 6. [安装nginx](install-nginx.md) 14 | 7. [安装redis3](install-redis.md) 15 | 8. [安装svn服务](install-svn.md) 16 | 17 | ## 中级篇 18 | 19 | 1. [配置tomcat为服务](config-tomcat-service.md) 20 | 2. [配置tomcat+nginx反向代理](config-nginx-proxy.md) 21 | 3. [使用jemeter测试tomcat性能](use-jemeter-test-tomcat.md) 22 | 4. [优化tomcat8](optimization-tomcat8.md) 23 | 5. [配置多个tomcat](config-more-tomcat.md) 24 | 6. [优化nginx配置](optimization-nginx.md) 25 | 7. [优化mysql配置](optimization-mysql.md) 26 | 8. [nginx+startssl配置https](nginx-startssl-https.md) 27 | -------------------------------------------------------------------------------- /learn_server/config-nginx-proxy.md: -------------------------------------------------------------------------------- 1 | # 配置tomcat+nginx反向代理 2 | 3 | 一般我们服务器对外只暴力22, 443, 80端口,其他的尽量都在内网访问,那么tomcat的8080端口是不应该对外访问的, 4 | nginx作为一个性能卓越的web服务器提供了反向代理的功能,可以做到转发。 5 | 6 | 假设我们现在有一个域名绑定在服务器的80端口上,使用tomcat搭建的程序,但是我又不想修改tomcat端口,该怎么办呢? 7 | 8 | nginx默认监听了80端口,配置文件在 `/usr/local/nginx/conf`文件夹下的 `nginx.conf`。 9 | 10 | ## 取消默认站点 11 | 12 | ```bash 13 | [root@localhost]# cd /usr/local/nginx/conf 14 | [root@localhost conf]# vim nginx.conf 15 | ``` 16 | 17 | 将 `server` 块注释即可。然后我们在 `conf` 文件夹下创建一个 `vhost` 目录存储虚拟主机配置文件。 18 | 19 | ```bash 20 | [root@localhost conf]# mkdir vhost 21 | ``` 22 | 23 | 创建一个tomcat的虚拟主机配置文件。 24 | 25 | ```bash 26 | [root@localhost conf]# vim vhost/tomcat8.conf 27 | ``` 28 | 29 | 加入以下配置 30 | 31 | ```bash 32 | server { 33 | listen 80; 34 | server_name localhost; 35 | 36 | location / { 37 | proxy_pass http://127.0.0.1:8080; 38 | } 39 | } 40 | ``` 41 | 42 | 在 `nginx.conf` 中将 `vhost` 文件夹下的配置文件引入,只需在 `http` 块中加入一行 `include vhost/*.conf` 保存即可。 43 | 44 | 重启nginx 45 | 46 | ```bash 47 | [root@localhost conf]# service nginx restart 48 | Stopping Nginx: [ OK ] 49 | Starting Nginx: [ OK ] 50 | ``` 51 | 52 | 查看tomcat是否已经启动,如果关闭将它开启,然后访问 [http://192.168.100.128/](http://192.168.100.128/) 53 | 54 | ![](https://ooo.0o0.ooo/2016/09/09/57d260a9a1004.png) 55 | 56 | 这样tomcat的8080端口就被nginx转发了,我们此时用域名直接绑定到80端口即可! 57 | 58 | ## links 59 | * [目录]() 60 | * 上一节: [配置tomcat为服务]() 61 | * 下一节: [使用jemeter测试tomcat性能]() -------------------------------------------------------------------------------- /learn_server/init-os.md: -------------------------------------------------------------------------------- 1 | # 初始化操作系统 2 | 3 | Ok,我们安装好了CentOS系统,可以使用SSH工具连接上去进行尝试了,我推荐使用 [XShell] 这款工具,支持中文,还有一些主题使用。 4 | 5 | 现在我们还不知道CentOS的IP是无法连接的,所以先在虚拟机中启动CentOS。 6 | 7 | ![](https://ooo.0o0.ooo/2016/09/09/57d225f429a16.png) 8 | 9 | 这里账户输入 `root` 密码是你在安装的时候设置的。 10 | 11 | ## 网络配置 12 | 13 | 这时候我们键入 `ifconfig` 查看ip 14 | 15 | ```bash 16 | [root@localhost ~]# ifconfig 17 | 18 | lo Link encap:Local Loopback 19 | inet addr:127.0.0.1 Mask:255.0.0.0 20 | inet6 addr: ::1/128 Scope:Host 21 | UP LOOPBACK RUNNING MTU:65536 Metric:1 22 | RX packets:0 errors:0 dropped:0 overruns:0 frame:0 23 | TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 24 | collisions:0 txqueuelen:0 25 | RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) 26 | ``` 27 | 28 | 发现还没有,我们需要设置一下网卡配置。 29 | 30 | ```bash 31 | [root@localhost ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0 32 | ``` 33 | 34 | 使用 `vi` 命令编辑第一块网卡的配置 35 | 36 | ```bash 37 | DEVICE=eth0 38 | HWADDR=00:0C:29:50:58:BE 39 | TYPE=Ethernet 40 | UUID=58f93b51-314d-49bb-9db2-036bf91161fb 41 | ONBOOT=no 42 | NM_CONTROLLED=yes 43 | BOOTPROTO=dhcp 44 | ``` 45 | 46 | 只需要将 `ONBOOT` 修改为 `yes` ,然后保存。 47 | 48 | ```bash 49 | [root@localhost ~]# service network restart 50 | Shutting down interface eth0: [ OK ] 51 | Shutting down loopback interface: [ OK ] 52 | Bringing up loopback interface: [ OK ] 53 | Bringing up interface eth0: 54 | Determining IP information for eth0... done. 55 | [ OK ] 56 | ``` 57 | 58 | 这时候我们再查看一下ip 59 | 60 | ```bash 61 | [root@localhost ~]# ifconfig 62 | eth0 Link encap:Ethernet HWaddr 00:0C:29:50:58:BE 63 | inet addr:192.168.100.128 Bcast:192.168.100.255 Mask:255.255.255.0 64 | inet6 addr: fe80::20c:29ff:fe50:58be/64 Scope:Link 65 | UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 66 | RX packets:302 errors:0 dropped:0 overruns:0 frame:0 67 | TX packets:189 errors:0 dropped:0 overruns:0 carrier:0 68 | collisions:0 txqueuelen:1000 69 | RX bytes:33591 (32.8 KiB) TX bytes:29591 (28.8 KiB) 70 | 71 | lo Link encap:Local Loopback 72 | inet addr:127.0.0.1 Mask:255.0.0.0 73 | inet6 addr: ::1/128 Scope:Host 74 | UP LOOPBACK RUNNING MTU:65536 Metric:1 75 | RX packets:0 errors:0 dropped:0 overruns:0 frame:0 76 | TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 77 | collisions:0 txqueuelen:0 78 | RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) 79 | ``` 80 | 81 | 网卡的配置已经被应用到了,我们CentOS的IP是 `192.168.100.128` 你的可能和我不一样,那这个192.168.100开头的网段是在哪里设置的呢? 82 | 83 | ![](https://ooo.0o0.ooo/2016/09/09/57d2293d28fe5.png) 84 | 85 | 点击虚拟网络编辑器 86 | 87 | ![](https://ooo.0o0.ooo/2016/09/09/57d229a2311f4.png) 88 | 89 | 如果你在执行 `service network restart` 的时候失败可以在这里修改一个网段试试。 90 | 91 | 此时你已经可以使用SSH工具连接到你的CenOS主机了。 92 | 93 | 关于更详细的网络设置大家可以参考这2篇文章: 94 | 95 | - [虚拟机下CentOS 6.5配置IP地址的三种方法](http://www.centoscn.com/CentOS/config/2014/1112/4112.html) 96 | - [Vmware安装Centos NAT方式设置静态IP](http://www.centoscn.com/CentosBug/osbug/2015/1224/6568.html) 97 | 98 | 99 | ## yum源设置 100 | 101 | 先安装 `wget` 工具,我们安装的操作系统mini版的,默认没有wget命令,执行以下命令: 102 | 103 | ```bash 104 | yum install -y wget 105 | ``` 106 | 107 | 然后设置yum源,我选择的是网易的源,你也可以设置阿里的或者其他。 108 | 109 | ```bash 110 | cd /etc/yum.repos.d 111 | mv CentOS-Base.repo bak-CentOS-Base.repo 112 | wget http://mirrors.163.com/.help/CentOS6-Base-163.repo 113 | yum clean all 114 | yum makecache 115 | ``` 116 | 117 | 安全性的配置在这里先不讲解,我们先用 `root`账户来操作。 118 | 119 | ## links 120 | * [目录]() 121 | * 上一节: [在虚拟机里安装centos6]() 122 | * 下一节: [安装jdk环境]() -------------------------------------------------------------------------------- /learn_server/install-jdk.md: -------------------------------------------------------------------------------- 1 | # 安装jdk环境 2 | 3 | 服务器上如果不需要编码实际应该不安装JDK只安装JRE,我们考虑到以后可能安装其他软件就直接装JDK了。 4 | 5 | ## 下载JDK 6 | 7 | [下载jdk](http://stackoverflow.com/questions/10268583/downloading-java-jdk-on-linux-via-wget-is-shown-license-page-instead) 8 | 9 | 上面的连接是stackoverflow有开发者写的不使用cookie下载jdk和jre的命令。 10 | 11 | ```bash 12 | [root@localhost ~]# wget -c --header "Cookie: oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u171-b11/512cd62ec5174c3487ac17c61aaa89e8/jdk-8u171-linux-x64.tar.gz 13 | ``` 14 | 15 | ## 解压 16 | 17 | ```bash 18 | [root@localhost ~]# tar -zxvf jdk-8u171-linux-x64.tar.gz 19 | [root@localhost ~]# mkdir /usr/local/java 20 | [root@localhost ~]# mv jdk1.8.0_171/ /usr/local/java/ 21 | ``` 22 | 23 | ## 配置环境变量 24 | 25 | ```bash 26 | [root@localhost ~]# vim /etc/profile 27 | ``` 28 | 29 | 在最后一行添加 30 | 31 | ```bash 32 | # java 33 | export JAVA_HOME=/usr/local/java/jdk1.8.0_171 34 | export JRE_HOME=/usr/local/java/jdk1.8.0_171/jre 35 | export CLASSPATH=.:$JRE_HOME/lib/dt.jar:$JRE_HOME/lib/tools.jar 36 | export PATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH 37 | ``` 38 | 39 | ## 生效 40 | 41 | ```bash 42 | [root@localhost ~]# source /etc/profile 43 | [root@localhost ~]# java -version 44 | java version "1.8.0_171" 45 | Java(TM) SE Runtime Environment (build 1.8.0_171-b11) 46 | Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode) 47 | ``` 48 | 这里我安装的是最新版的JDK。 49 | 50 | ## links 51 | * [目录]() 52 | * 上一节: [初始化操作系统]() 53 | * 下一节: [安装tomcat]() 54 | -------------------------------------------------------------------------------- /learn_server/install-mysql.md: -------------------------------------------------------------------------------- 1 | # 安装mysql及配置 2 | 3 | ## 卸载旧版本 4 | 5 | 查找本机是否已经安装mysql 6 | 7 | ```bash 8 | [root@localhost ~]# rpm -qa | grep mysql 9 | mysql-libs-5.1.73-7.el6.x86_64 10 | ``` 11 | 12 | 卸载 13 | 14 | ```bash 15 | [root@localhost ~]# rpm -e --nodeps mysql-libs-5.1.73-7.el6.x86_64 16 | ``` 17 | 18 | ## 安装MySQL 19 | 20 | 这里我们使用yum方式进行安装,编译安装比较慢也很繁琐,查看系统里面有没有mysql的repo 21 | 22 | ```bash 23 | [root@localhost ~]# yum repolist all | grep mysql 24 | Repository base is listed more than once in the configuration 25 | Repository updates is listed more than once in the configuration 26 | Repository extras is listed more than once in the configuration 27 | Repository centosplus is listed more than once in the configuration 28 | Repository contrib is listed more than once in the configuration 29 | ``` 30 | 31 | 先执行如下语句,安装相关依赖 32 | 33 | ```bash 34 | [root@localhost ~]# yum install gcc-c++ jemalloc-devel openssl-devel openssl -y 35 | ``` 36 | 37 | 安装mysql的yum源 38 | 39 | ```bash 40 | [root@localhost ~]# wget http://dev.mysql.com/get/mysql57-community-release-el6-8.noarch.rpm 41 | ``` 42 | 43 | 然后更新 44 | 45 | ```bash 46 | [root@localhost ~]# sudo rpm -Uvh mysql57-community-release-el6-8.noarch.rpm 47 | warning: mysql57-community-release-el6-8.noarch.rpm: Header V3 DSA/SHA1 Signature, key ID 5072e1f5: NOKEY 48 | Preparing... ########################################### [100%] 49 | 1:mysql57-community-relea########################################### [100%] 50 | ``` 51 | 52 | 更新源,将mysql56的 `enable` 置为1,其余置为0 53 | 54 | ```bash 55 | [root@localhost ~]# vim /etc/yum.repos.d/mysql-community.repo 56 | ``` 57 | 58 | 修改后是这样 59 | 60 | ```bash 61 | [root@localhost ~]# cat /etc/yum.repos.d/mysql-community.repo 62 | [mysql-connectors-community] 63 | name=MySQL Connectors Community 64 | baseurl=http://repo.mysql.com/yum/mysql-connectors-community/el/6/$basearch/ 65 | enabled=0 66 | gpgcheck=1 67 | gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql 68 | 69 | [mysql-tools-community] 70 | name=MySQL Tools Community 71 | baseurl=http://repo.mysql.com/yum/mysql-tools-community/el/6/$basearch/ 72 | enabled=0 73 | gpgcheck=1 74 | gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql 75 | 76 | # Enable to use MySQL 5.5 77 | [mysql55-community] 78 | name=MySQL 5.5 Community Server 79 | baseurl=http://repo.mysql.com/yum/mysql-5.5-community/el/6/$basearch/ 80 | enabled=0 81 | gpgcheck=1 82 | gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql 83 | 84 | # Enable to use MySQL 5.6 85 | [mysql56-community] 86 | name=MySQL 5.6 Community Server 87 | baseurl=http://repo.mysql.com/yum/mysql-5.6-community/el/6/$basearch/ 88 | enabled=1 89 | gpgcheck=1 90 | gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql 91 | 92 | [mysql57-community] 93 | name=MySQL 5.7 Community Server 94 | baseurl=http://repo.mysql.com/yum/mysql-5.7-community/el/6/$basearch/ 95 | enabled=0 96 | gpgcheck=1 97 | gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql 98 | 99 | [mysql-tools-preview] 100 | name=MySQL Tools Preview 101 | baseurl=http://repo.mysql.com/yum/mysql-tools-preview/el/6/$basearch/ 102 | enabled=0 103 | gpgcheck=1 104 | gpgkey=file:/etc/pki/rpm-gpg/RPM-GPG-KEY-mysql 105 | ``` 106 | 107 | 执行安装 108 | 109 | ```bash 110 | [root@localhost ~]# yum install -y mysql-community-server 111 | ``` 112 | 113 | ## 启动Mysql 114 | 115 | ok,安装完成了,我们启动mysql 116 | 117 | ```bash 118 | [root@localhost ~]# service mysqld start 119 | ``` 120 | 121 | ## 配置MySQL 122 | 123 | yum安装的时候会把mysql的配置文件存放在 `/etc/my.cnf` 这个位置,在第一次启动的时候可以看到。 124 | 125 | ### 设置mysql root密码 126 | 127 | 有两种方式可以设置mysql的root密码 128 | 129 | ```bash 130 | [root@localhost ~]# /usr/bin/mysqladmin -u root password 'new-password' 131 | ``` 132 | 133 | 或者通过该命令给root账号设置密码 134 | 135 | ```bash 136 | [root@localhost ~]# mysqladmin -u root password 'new-password' 137 | ``` 138 | 139 | 此时我们就可以使用刚才设置的密码进行登录了 140 | 141 | ```bash 142 | [root@localhost ~]# mysql -uroot -p 143 | Enter password: 144 | Welcome to the MySQL monitor. Commands end with ; or \g. 145 | Your MySQL connection id is 4 146 | Server version: 5.6.33 MySQL Community Server (GPL) 147 | 148 | Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. 149 | 150 | Oracle is a registered trademark of Oracle Corporation and/or its 151 | affiliates. Other names may be trademarks of their respective 152 | owners. 153 | 154 | Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. 155 | 156 | mysql> 157 | ``` 158 | 159 | ### 设置开机启动 160 | 161 | 我们可以 通过 `chkconfig mysqld on` 命令来设置mysql开机启动 162 | 163 | ```bash 164 | [root@localhost ~]# chkconfig mysqld on 165 | ``` 166 | 167 | 看一下 168 | 169 | ```bash 170 | [root@localhost ~]# chkconfig --list | grep mysqld 171 | mysqld 0:off 1:off 2:on 3:on 4:on 5:on 6:off 172 | ``` 173 | 174 | 在这一步Mysql的安装就已经完成了,我们先步入下一个软件的安装,在之后的章节中还会继续讲解Mysql的配置。 175 | 176 | ## links 177 | * [目录]() 178 | * 上一节: [安装tomcat]() 179 | * 下一节: [安装nginx]() -------------------------------------------------------------------------------- /learn_server/install-nginx.md: -------------------------------------------------------------------------------- 1 | # 安装nginx 2 | 3 | 在安装nginx前,需要确保系统安装了g++、gcc、openssl-devel、pcre-devel和zlib-devel软件。 4 | 5 | ```bash 6 | [root@localhost ~]# yum -y install gcc-c++ zlib zlib-devel openssl openssl-devel pcre pcre-devel 7 | ``` 8 | 9 | ## 下载nginx 10 | 11 | ```bash 12 | [root@localhost ~]# wget http://nginx.org/download/nginx-1.10.1.tar.gz 13 | ``` 14 | 15 | ```bash 16 | [root@localhost ~]# tar -zxvf nginx-1.10.1.tar.gz 17 | [root@localhost ~]# cd nginx-1.10.1 18 | [root@localhost nginx-1.10.1]# ./configure --prefix=/usr/local/nginx --with-http_ssl_module 19 | ``` 20 | 21 | 上面 `--prefix` 配置nginx所在目录,`--with-http_ssl_module`配置nginx支持ssl,配置https会用到。 22 | 23 | ## 编译安装 24 | 25 | ```bash 26 | [root@localhost nginx-1.10.1]# make && make install 27 | ``` 28 | 29 | 来看看 30 | 31 | ```bash 32 | [root@localhost nginx-1.10.1]# ll /usr/local/nginx/ 33 | total 16 34 | drwxr-xr-x. 2 root root 4096 Sep 9 22:07 conf 35 | drwxr-xr-x. 2 root root 4096 Sep 9 22:07 html 36 | drwxr-xr-x. 2 root root 4096 Sep 9 22:07 logs 37 | drwxr-xr-x. 2 root root 4096 Sep 9 22:07 sbin 38 | ``` 39 | 40 | ## 启动nginx 41 | 42 | ```bash 43 | [root@localhost nginx-1.10.1]# cd /usr/local/nginx/sbin/ 44 | [root@localhost sbin]# ./nginx 45 | ``` 46 | 47 | 这样就启动nginx,nginx默认监听在80端口,但是我们不要忘了把80端口对外开放。 48 | 49 | 在 `/etc/sysconfig/iptables` 中添加80端口 50 | 51 | ```bash 52 | -A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT 53 | ``` 54 | 55 | 保存后重启一下防火墙 56 | 57 | ```bash 58 | [root@localhost sbin]# service iptables restart 59 | iptables: Setting chains to policy ACCEPT: filter [ OK ] 60 | iptables: Flushing firewall rules: [ OK ] 61 | iptables: Unloading modules: [ OK ] 62 | iptables: Applying firewall rules: [ OK ] 63 | ``` 64 | 65 | 访问 [http://192.168.100.128/](http://192.168.100.128/) 你将看到 66 | 67 | ![](https://ooo.0o0.ooo/2016/09/09/57d253381cff7.png) 68 | 69 | ## 关闭nginx 70 | 71 | ```bash 72 | #查询nginx主进程号 73 | [root@localhost sbin]# ps -ef | grep nginx 74 | #停止进程 75 | [root@localhost sbin]# kill -QUIT 主进程号 76 | #快速停止 77 | [root@localhost sbin]# kill -TERM 主进程号 78 | #强制停止 79 | [root@localhost sbin]# pkill -9 nginx 80 | ``` 81 | 82 | ## 重启nginx 83 | 84 | ```bash 85 | [root@localhost ~]# /usr/local/nginx/sbin/nginx -s reload 86 | ``` 87 | 88 | ## 配置nginx为服务 89 | 90 | ```bash 91 | [root@localhost ~]# vim /etc/init.d/nginx 92 | ``` 93 | 94 | 将服务脚本粘贴进去 95 | 96 | _服务脚本_ 97 | 98 | ```bash 99 | #!/bin/sh 100 | # chkconfig: 2345 85 15 101 | # description:Nginx Server 102 | 103 | NGINX_HOME=/usr/local/nginx 104 | NGINX_SBIN=$NGINX_HOME/sbin/nginx 105 | NGINX_CONF=$NGINX_HOME/conf/nginx.conf 106 | NGINX_PID=$NGINX_HOME/logs/nginx.pid 107 | 108 | NGINX_NAME="Nginx" 109 | 110 | . /etc/rc.d/init.d/functions 111 | 112 | if [ ! -f $NGINX_SBIN ] 113 | then 114 | echo "$NGINX_NAME startup: $NGINX_SBIN not exists! " 115 | exit 116 | fi 117 | 118 | start() { 119 | $NGINX_SBIN -c $NGINX_CONF 120 | ret=$? 121 | if [ $ret -eq 0 ]; then 122 | action $"Starting $NGINX_NAME: " /bin/true 123 | else 124 | action $"Starting $NGINX_NAME: " /bin/false 125 | fi 126 | } 127 | 128 | stop() { 129 | kill `cat $NGINX_PID` 130 | ret=$? 131 | if [ $ret -eq 0 ]; then 132 | action $"Stopping $NGINX_NAME: " /bin/true 133 | else 134 | action $"Stopping $NGINX_NAME: " /bin/false 135 | fi 136 | } 137 | 138 | restart() { 139 | stop 140 | start 141 | } 142 | 143 | check() { 144 | $NGINX_SBIN -c $NGINX_CONF -t 145 | } 146 | 147 | 148 | reload() { 149 | kill -HUP `cat $NGINX_PID` && echo "reload success!" 150 | } 151 | 152 | relog() { 153 | kill -USR1 `cat $NGINX_PID` && echo "relog success!" 154 | } 155 | 156 | case "$1" in 157 | start) 158 | start 159 | ;; 160 | stop) 161 | stop 162 | ;; 163 | restart) 164 | restart 165 | ;; 166 | check|chk) 167 | check 168 | ;; 169 | status) 170 | status -p $NGINX_PID 171 | ;; 172 | reload) 173 | reload 174 | ;; 175 | relog) 176 | relog 177 | ;; 178 | *) 179 | echo $"Usage: $0 {start|stop|restart|reload|status|check|relog}" 180 | exit 1 181 | esac 182 | ``` 183 | 184 | 给脚本可执行权限 185 | 186 | ```bash 187 | [root@localhost ~]# chmod +x /etc/init.d/nginx 188 | ``` 189 | 190 | 然后你就可以使用 `service nginx start` 的方式启动nginx了 191 | 192 | ```bash 193 | [root@localhost ~]# service nginx 194 | Usage: /etc/init.d/nginx {start|stop|restart|reload|status|check|relog} 195 | ``` 196 | 197 | ## 添加到开机项 198 | 199 | ```bash 200 | [root@localhost ~]# chkconfig --add nginx 201 | [root@localhost ~]# chkconfig 202 | auditd 0:off 1:off 2:on 3:on 4:on 5:on 6:off 203 | blk-availability 0:off 1:on 2:on 3:on 4:on 5:on 6:off 204 | crond 0:off 1:off 2:on 3:on 4:on 5:on 6:off 205 | ip6tables 0:off 1:off 2:on 3:on 4:on 5:on 6:off 206 | iptables 0:off 1:off 2:on 3:on 4:on 5:on 6:off 207 | iscsi 0:off 1:off 2:off 3:on 4:on 5:on 6:off 208 | iscsid 0:off 1:off 2:off 3:on 4:on 5:on 6:off 209 | lvm2-monitor 0:off 1:on 2:on 3:on 4:on 5:on 6:off 210 | mdmonitor 0:off 1:off 2:on 3:on 4:on 5:on 6:off 211 | multipathd 0:off 1:off 2:off 3:off 4:off 5:off 6:off 212 | mysqld 0:off 1:off 2:on 3:on 4:on 5:on 6:off 213 | netconsole 0:off 1:off 2:off 3:off 4:off 5:off 6:off 214 | netfs 0:off 1:off 2:off 3:on 4:on 5:on 6:off 215 | network 0:off 1:off 2:on 3:on 4:on 5:on 6:off 216 | nginx 0:off 1:off 2:on 3:on 4:on 5:on 6:off 217 | postfix 0:off 1:off 2:on 3:on 4:on 5:on 6:off 218 | rdisc 0:off 1:off 2:off 3:off 4:off 5:off 6:off 219 | restorecond 0:off 1:off 2:off 3:off 4:off 5:off 6:off 220 | rsyslog 0:off 1:off 2:on 3:on 4:on 5:on 6:off 221 | saslauthd 0:off 1:off 2:off 3:off 4:off 5:off 6:off 222 | sshd 0:off 1:off 2:on 3:on 4:on 5:on 6:off 223 | udev-post 0:off 1:on 2:on 3:on 4:on 5:on 6:off 224 | ``` 225 | 我们可以看到nginx已经被添加到开机启动了。 226 | 227 | ## links 228 | * [目录]() 229 | * 上一节: [安装mysql及配置]() 230 | * 下一节: [安装redis3]() -------------------------------------------------------------------------------- /learn_server/install-redis.md: -------------------------------------------------------------------------------- 1 | # 安装redis3 2 | 3 | ## 安装依赖软件 4 | 5 | ```bash 6 | yum install -y gcc* 7 | yum install -y tcl 8 | ``` 9 | 10 | ## 安装redis 11 | 12 | ```bash 13 | [root@localhost ~]# wget http://download.redis.io/releases/redis-3.2.3.tar.gz 14 | [root@localhost ~]# tar -zxvf redis-3.2.3.tar.gz 15 | [root@localhost ~]# cd redis-3.2.3 16 | [root@localhost redis-3.2.3]# make 17 | [root@localhost redis-3.2.3]# make test 18 | [root@localhost redis-3.2.3]# make install 19 | [root@localhost redis-3.2.3]# cd utils 20 | [root@localhost redis-3.2.3]# chmod +x install_server.sh 21 | [root@localhost redis-3.2.3]# ./install_server.sh 22 | ``` 23 | 24 | 在install的时候提示选项,全部选择默认即可,你看到如下画面表示安装成功 25 | 26 | ```bash 27 | Please select the redis port for this instance: [6379] 28 | Selecting default: 6379 29 | Please select the redis config file name [/etc/redis/6379.conf] 30 | Selected default - /etc/redis/6379.conf 31 | Please select the redis log file name [/var/log/redis_6379.log] 32 | Selected default - /var/log/redis_6379.log 33 | Please select the data directory for this instance [/var/lib/redis/6379] 34 | Selected default - /var/lib/redis/6379 35 | Please select the redis executable path [/usr/local/bin/redis-server] 36 | Selected config: 37 | Port : 6379 38 | Config file : /etc/redis/6379.conf 39 | Log file : /var/log/redis_6379.log 40 | Data dir : /var/lib/redis/6379 41 | Executable : /usr/local/bin/redis-server 42 | Cli Executable : /usr/local/bin/redis-cli 43 | Is this ok? Then press ENTER to go on or Ctrl-C to abort. 44 | Copied /tmp/6379.conf => /etc/init.d/redis_6379 45 | Installing service... 46 | Successfully added to chkconfig! 47 | Successfully added to runlevels 345! 48 | Starting Redis server... 49 | Installation successful! 50 | ``` 51 | 52 | ## 测试一下 53 | 54 | ```bash 55 | [root@localhost ~]# redis-cli 56 | 127.0.0.1:6379> set name jack 57 | OK 58 | 127.0.0.1:6379> get name 59 | "jack" 60 | ``` 61 | 62 | ## 查看redis状态 63 | 64 | ```bash 65 | [root@localhost ~]# service redis_6379 status 66 | Redis is running (14927) 67 | ``` 68 | 69 | ## 启动/关闭redis 70 | 71 | ```bash 72 | [root@localhost ~]# service redis_6379 stop 73 | Stopping ... 74 | Waiting for Redis to shutdown ... 75 | Redis stopped 76 | [root@localhost ~]# service redis_6379 start 77 | Starting Redis server... 78 | ``` 79 | 80 | ## 设置redis认证密码 81 | 82 | ```bash 83 | [root@localhost ~]# vim /etc/redis/6379.conf 84 | ``` 85 | 86 | 找到 `# requirepass foobared` 将 `#` 去掉,设置一个密码。 87 | 88 | 然后重启redis 89 | 90 | ```bash 91 | [root@localhost ~]# service redis_6379 restart 92 | Stopping ... 93 | Redis stopped 94 | Starting Redis server... 95 | ``` 96 | 97 | wow,你已经完成初级篇的所有任务了,接下里我们会玩点有趣的 :) 98 | 99 | ## links 100 | * [目录]() 101 | * 上一节: [安装nginx]() 102 | * 下一节: [配置tomcat为服务]() -------------------------------------------------------------------------------- /learn_server/install-svn.md: -------------------------------------------------------------------------------- 1 | # 安装svn服务 2 | 3 | Subversion是一个自由,开源的版本控制系统。Subversion将文件存放在中心版本库里。这个版本库很像一个普通的文件服务器,不同的是,它可以记录每一次文件和目录的修改情况。这样就可以籍此将数据恢复到以前的版本,并可以查看数据的更改细节。Subversion是Apache基金会下的一个项目,官网 [https://subversion.apache.org](https://subversion.apache.org) 4 | 5 | ## 安装依赖 6 | 7 | ```bash 8 | [root@localhost ~]# yum install sqlite sqlite-devel apr-util apr-util-devel -y 9 | ``` 10 | 11 | ## 安装subversion 12 | 13 | ```bash 14 | [root@localhost ~]# wget http://mirrors.cnnic.cn/apache/subversion/subversion-1.8.16.tar.gz 15 | [root@localhost ~]# tar -zxvf subversion-1.8.16.tar.gz 16 | [root@localhost ~]# cd subversion-1.8.16 17 | [root@localhost subversion-1.8.16]# ./configure --prefix=/usr/local/subversion 18 | [root@localhost subversion-1.8.16]# make && make install 19 | ``` 20 | 21 | ## 配置环境 22 | 23 | ```bash 24 | [root@localhost ~]# vim /etc/profile 25 | ``` 26 | 27 | 加入 `PATH=$PATH:/usr/local/subversion/bin` 28 | 29 | ## 查看版本 30 | 31 | ```bash 32 | [root@localhost ~]# svn --version 33 | svn, version 1.8.16 (r1740329) 34 | compiled Sep 26 2016, 06:42:53 on x86_64-unknown-linux-gnu 35 | 36 | Copyright (C) 2016 The Apache Software Foundation. 37 | This software consists of contributions made by many people; 38 | see the NOTICE file for more information. 39 | Subversion is open source software, see http://subversion.apache.org/ 40 | 41 | The following repository access (RA) modules are available: 42 | 43 | * ra_svn : Module for accessing a repository using the svn network protocol. 44 | - with Cyrus SASL authentication 45 | - handles 'svn' scheme 46 | * ra_local : Module for accessing a repository on local disk. 47 | - handles 'file' scheme 48 | ``` 49 | 50 | ## 开始配置 51 | 52 | ### 建立仓库目录 53 | 54 | ```bash 55 | [root@localhost ~]# mkdir -p /data/svn/repos 56 | ``` 57 | 58 | ### 创建版本 59 | 60 | ```bash 61 | [root@localhost ~]# svnadmin create /data/svn/repos/ 62 | ``` 63 | 64 | ### 修改配置 65 | 66 | ```bash 67 | [root@localhost ~]# vim /data/svn/repos/conf/svnserve.conf 68 | ``` 69 | 70 | ```bash 71 | [general] 72 | anon-access = none 73 | auth-access = write 74 | password-db = passwd #用户密码文件 75 | authz-db = authz #授权登录文件 76 | realm = repos 77 | ``` 78 | 79 | 修改`/data/svn/repos/conf/passwd`文件,添加用户及密码: 80 | 81 | ```bash 82 | [root@localhost ~]# vim /data/svn/repos/conf/passwd 83 | 84 | [users] 85 | username=password #用户名=密码   一行一个 86 | ``` 87 | 88 | 修改`/data/svn/repos/conf/authz`文件,控制用户权限 89 | 90 | ```bash 91 | [root@localhost ~]# vim /data/svn/repos/conf/authz 92 | ``` 93 | 94 | > 注意: 95 | 96 | * 权限配置文件中出现的用户名必须已在用户配置文件中定义。 97 | * 对权限配置文件的修改立即生效,不必重启svn。 98 | 99 | 用户组格式: 100 | 101 | ```bash 102 | [groups] 103 | = , 104 | ``` 105 | 106 | 其中,1个用户组可以包含1个或多个用户,用户间以逗号分隔。 107 | 版本库目录格式: 108 | 109 | ```bash 110 | [<版本库>:/项目/目录] 111 | @<用户组名> = <权限> 112 | <用户名> = <权限> 113 | ``` 114 | 115 | 其中,方框号内部分可以有多种写法: 116 | 117 | - [/],表示根目录及以下,根目录是svnserve启动时指定的,我们指定为/home/svndata,[/]就是表示对全部版本库设置权限。 118 | - [repos:/] 表示对版本库repos设置权限; 119 | - [repos:/abc] 表示对版本库repos中的abc项目设置权限; 120 | - [repos:/abc/aaa] 表示对版本库repos中的abc项目的aaa目录设置权限; 121 | - 122 | 权限主体可以是`用户组`、`用户`或`*`,用户组在前面加`@`,`*`表示全部用户。 123 | 权限可以是`w`、`r`、`wr`和空,空表示没有任何权限。 124 | 125 | ## 启动SVN 126 | 127 | ```bash 128 | [root@localhost ~]# svnserve -d --listen-port 10901 -r /data/svn 129 | ``` 130 | 131 | - -d :表示以daemon方式(后台运行)运行; 132 | - --listen-port 10901 :表示使用10901端口,可以换成你需要的端口。但注意,使用1024以下的端口需要root权限; 133 | - -r /data/svn :指定根目录是/data/svn。 134 | 135 | ## 将svn作为服务 136 | 137 | ```bash 138 | #!/bin/bash 139 | # build this file in /etc/init.d/svn 140 | # chmod 755 /etc/init.d/svn 141 | # centos下可以用如下命令管理svn: service svn start(restart/stop) 142 | SVN_HOME=/home/svn 143 | if [ ! -f "/usr/local/subversion/bin/svnserve" ] 144 | then 145 | echo "svnserver startup: cannot start" 146 | exit 147 | fi 148 | case "$1" in 149 | start) 150 | echo "Starting svnserve..." 151 | /usr/local/subversion/bin/svnserve -d --listen-port 10901 -r $SVN_HOME 152 | echo "Finished!" 153 | ;; 154 | stop) 155 | echo "Stoping svnserve..." 156 | killall svnserve 157 | echo "Finished!" 158 | ;; 159 | restart) 160 | $0 stop 161 | $0 start 162 | ;; 163 | *) 164 | echo "Usage: svn { start | stop | restart } " 165 | exit 1 166 | esac 167 | ``` 168 | 169 | wow,你已经完成初级篇的所有任务了,接下里我们会玩点有趣的 :) 170 | 171 | ## links 172 | * [目录]() 173 | * 上一节: [安装nginx]() 174 | * 下一节: [配置tomcat为服务]() 175 | -------------------------------------------------------------------------------- /learn_server/install-tomcat.md: -------------------------------------------------------------------------------- 1 | # 安装tomcat 2 | 3 | 上一章节我们安装了JDK的环境,Tomcat运行的前提是要有JDK环境。 4 | 5 | ## 下载Tomcat 6 | 7 | ```bash 8 | [root@localhost ~]# wget http://mirror.bit.edu.cn/apache/tomcat/tomcat-8/v8.5.5/bin/apache-tomcat-8.5.5.tar.gz 9 | [root@localhost ~]# tar -zxvf apache-tomcat-8.5.5.tar.gz 10 | [root@localhost ~]# mv apache-tomcat-8.5.5 /usr/local/tomcat8 11 | ``` 12 | 13 | ## 启动tomcat 14 | 15 | ```bash 16 | [root@localhost ~]# cd /usr/local/tomcat8/bin/ 17 | [root@localhost bin]# ./startup.sh 18 | Using CATALINA_BASE: /usr/local/tomcat8 19 | Using CATALINA_HOME: /usr/local/tomcat8 20 | Using CATALINA_TMPDIR: /usr/local/tomcat8/temp 21 | Using JRE_HOME: /usr/local/java/jdk1.8.0_102/jre 22 | Using CLASSPATH: /usr/local/tomcat8/bin/bootstrap.jar:/usr/local/tomcat8/bin/tomcat-juli.jar 23 | Tomcat started. 24 | ``` 25 | 26 | 现在打开 [http://192.168.100.128:8080](http://192.168.100.128:8080) 应该就可以看到Tomcat的汤姆猫页面。 27 | 而事实是你看到这个: 28 | 29 | ![](https://ooo.0o0.ooo/2016/09/09/57d23ea8c353e.png) 30 | 31 | 哦草。。。为什么,机智的同学已经想到了,防火墙啊。对我们没有对防火墙进行任何配置,实际上8080端口是不对外开放的, 32 | 那么如何解决呢? 33 | 34 | - 关闭防火墙 35 | - 开放8080端口 36 | 37 | ## 配置防火墙 38 | 39 | 在CentOS上关闭防火墙是非常简单的 40 | 41 | ```bash 42 | [root@localhost bin]# service iptables stop 43 | iptables: Setting chains to policy ACCEPT: filter [ OK ] 44 | iptables: Flushing firewall rules: [ OK ] 45 | iptables: Unloading modules: [ OK ] 46 | ``` 47 | 48 | 这时候你再访问 [http://192.168.100.128:8080](http://192.168.100.128:8080) 就可以看到 49 | 50 | ![](https://ooo.0o0.ooo/2016/09/09/57d23f752bfce.png) 51 | 52 | 当然这种方式是简单粗暴的,我们在真实服务器上不可能这么做,怎么做呢? 53 | 54 | ```bash 55 | [root@localhost bin]# vim /etc/sysconfig/iptables 56 | ``` 57 | 58 | 我们看到 `iptables` 的默认配置是这样的: 59 | 60 | ```bash 61 | # Firewall configuration written by system-config-firewall 62 | # Manual customization of this file is not recommended. 63 | *filter 64 | :INPUT ACCEPT [0:0] 65 | :FORWARD ACCEPT [0:0] 66 | :OUTPUT ACCEPT [0:0] 67 | -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 68 | -A INPUT -p icmp -j ACCEPT 69 | -A INPUT -i lo -j ACCEPT 70 | -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT 71 | -A INPUT -j REJECT --reject-with icmp-host-prohibited 72 | -A FORWARD -j REJECT --reject-with icmp-host-prohibited 73 | COMMIT 74 | ``` 75 | 76 | 只需要添加一行和 `22` 端口一样的配置: 77 | 78 | ```bash 79 | -A INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -j ACCEPT 80 | ``` 81 | 82 | 这样既把8080的TCP端口对外开放了,然后重启防火墙 83 | 84 | ```bash 85 | [root@localhost bin]# service iptables restart 86 | iptables: Setting chains to policy ACCEPT: filter [ OK ] 87 | iptables: Flushing firewall rules: [ OK ] 88 | iptables: Unloading modules: [ OK ] 89 | iptables: Applying firewall rules: [ OK ] 90 | ``` 91 | 92 | 可以达到同样的效果。 93 | 94 | ## links 95 | * [目录]() 96 | * 上一节: [安装jdk环境]() 97 | * 下一节: [安装mysql及配置]() -------------------------------------------------------------------------------- /learn_server/optimization-nginx.md: -------------------------------------------------------------------------------- 1 | # 优化nginx配置 -------------------------------------------------------------------------------- /learn_server/optimization-tomcat8.md: -------------------------------------------------------------------------------- 1 | # 优化tomcat8 2 | 3 | 我们优化tomcat的目的是提高并发性,即在多线程环境下能够快速响应,提高吞吐量。 4 | 5 | 首先在tomcat的bin目录下新建一个名为 `setenv.sh` 的文件,tomcat启动时会自动加载该文件。 6 | 7 | ```bash 8 | [root@localhost bin]# vim setenv.sh 9 | ``` 10 | 11 | 加入tomcat基础配置 12 | 13 | ```bash 14 | #!/usr/bin 15 | 16 | export CATALINA_HOME=/usr/local/tomcat8 17 | export CATALINA_BASE=/usr/local/tomcat8 18 | ``` 19 | 20 | ## JAVA_OPTS 21 | 22 | 加入如下配置,我们服务器的内存是1G。这里我设置最大占用768 23 | 24 | ```bash 25 | export JAVA_OPTS="$JAVA_OPTS\ 26 | -server\ 27 | -Xms768m\ 28 | -Xmx768m\ 29 | -Xss512k\ 30 | -Djava.awt.headless=true\ 31 | -Dfile.encoding=utf-8\ 32 | -Djava.net.preferIPv4Stack=true\ 33 | -Djava.security.egd=file:/dev/./urandom" 34 | ``` 35 | 36 | - -server:表示这是应用于服务器的配置,JVM 内部会有特殊处理的 37 | - -Xms768m:设置JVM最大可用内存为768MB 38 | - -Xmx768m:设置JVM最小内存为768MB。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。 39 | - -Dfile.encoding:默认文件编码 40 | - -Djava.net.preferIPv4Stack:使用IPV4 41 | - -Djava.security.egd:[详细解释](http://fengbin2005.iteye.com/blog/2313845) 42 | 43 | ## 优化`server.xml` 44 | 45 | ```xml 46 | 56 | ``` 57 | 58 | **maxThreads 连接数限制** 59 | 60 | maxThreads 是 Tomcat 所能接受最大连接数。一般设置不要超过8000以上,如果你的网站访问量非常大可能使用运行多个Tomcat实例的方法。 61 | 62 | ## 安装apr 63 | 64 | 安装依赖 65 | 66 | ```bash 67 | [root@localhost ~]# yum install -y openssl-devel 68 | ``` 69 | 70 | 下载apr相关包 71 | 72 | ```bash 73 | [root@localhost ~]# wget http://mirrors.tuna.tsinghua.edu.cn/apache//apr/apr-1.5.2.tar.gz 74 | [root@localhost ~]# wget http://mirrors.tuna.tsinghua.edu.cn/apache//apr/apr-util-1.5.4.tar.gz 75 | [root@localhost ~]# wget http://mirrors.tuna.tsinghua.edu.cn/apache//apr/apr-iconv-1.2.1.tar.gz 76 | 77 | # 安装apr 78 | [root@localhost ~]# tar -zxvf apr-1.5.2.tar.gz 79 | 80 | [root@localhost ~]# cd apr-1.5.2 81 | [root@localhost apr-1.5.2]# ./configure && make && make install 82 | 83 | # 安装apr-util 84 | [root@localhost ~]# tar -zxvf apr-util-1.5.4.tar.gz 85 | [root@localhost ~]# cd apr-util-1.5.4 86 | [root@localhost apr-util-1.5.4]# ./configure --with-apr=/usr/local/apr && make && make install 87 | 88 | # 安装apr-iconv 89 | [root@localhost ~]# cd apr-iconv-1.2.1 90 | [root@localhost apr-iconv-1.2.1]# ./configure --with-apr=/usr/local/apr && make && make install 91 | ``` 92 | 93 | 配置tomcat 94 | 95 | ```bash 96 | [root@localhost apr-iconv-1.2.1]# cd /usr/local/tomcat8/bin/ 97 | [root@localhost bin]# tar -zxf tomcat-native.tar.gz 98 | [root@localhost bin]# cd tomcat-native-1.2.8-src/native/ 99 | [root@localhost native]# ./configure --with-apr=/usr/local/apr && make && make install 100 | ``` 101 | 102 | 这是提示我 103 | 104 | ```bash 105 | configure: error: Your version of OpenSSL is not compatible with this version of tcnative 106 | ``` 107 | 108 | 由于centos 当前的yum 库只有1.0.1 的OpenSSL,所以我们需要手工安装1.0.2 109 | 110 | ```bash 111 | [root@localhost ~]# wget https://www.openssl.org/source/openssl-1.0.2-latest.tar.gz 112 | [root@localhost ~]# tar -zxf openssl-1.0.2-latest.tar.gz 113 | [root@localhost ~]# cd openssl-1.0.2h 114 | [root@localhost openssl-1.0.2h]# ./config --prefix=/usr/local/openssl -fPIC 115 | ``` 116 | 117 | > 注意这里需要加入 -fPIC参数,否则后面在安装tomcat native 组件会出错 118 | > 注意:不要按照提示去运行 make depend 119 | 120 | ```bash 121 | [root@localhost openssl-1.0.2h]# make 122 | [root@localhost openssl-1.0.2h]# make install 123 | [root@localhost openssl-1.0.2h]# mv /usr/bin/openssl ~ 124 | [root@localhost openssl-1.0.2h]# ln -s /usr/local/openssl/bin/openssl /usr/bin/openssl 125 | [root@localhost openssl-1.0.2h]# openssl version 126 | OpenSSL 1.0.2h 3 May 2016 127 | ``` 128 | 129 | 重新安装 tomcat-native组件 130 | 131 | ```bash 132 | [root@localhost openssl-1.0.2h]# cd /usr/local/tomcat8/bin/tomcat-native-1.2.8-src/native/ 133 | [root@localhost native]# ./configure --with-apr=/usr/local/apr --with-ssl=/usr/local/openssl 134 | [root@localhost native]# make && make install 135 | ``` 136 | 137 | 在 `setenv.sh` 文件中添加 138 | 139 | ```bash 140 | LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/apr/lib 141 | export LD_LIBRARY_PATH 142 | ``` 143 | 144 | 在 `server.xml` 中加 145 | 146 | ```xml 147 | 150 | ``` 151 | 152 | 启动tomcat,打开控制台日志可以看到如图所示的日志 153 | 154 | ![](https://ooo.0o0.ooo/2016/09/09/57d281093d907.png) 155 | 156 | ## 性能测试 -------------------------------------------------------------------------------- /learn_server/use-jemeter-test-tomcat.md: -------------------------------------------------------------------------------- 1 | # 使用jemeter测试tomcat性能 2 | 3 | JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域。 4 | 5 | 下载地址:[http://jmeter.apache.org/download_jmeter.cgi](http://jmeter.apache.org/download_jmeter.cgi) 6 | 7 | 安装启动即可。 8 | 9 | ![](https://ooo.0o0.ooo/2016/09/09/57d26181a3bbc.png) 10 | 11 | ## 服务器环境 12 | 13 | - CPU:Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz 14 | - 内存:1G 15 | - 操作系统:CentOS6.8_x64 16 | - JDK:1.8.0_102 17 | - Tomcat:8.5.5 18 | 19 | 下面所有测试都是基于1000个请求做的,且都是访问Tomcat默认的ROOT首页 20 | 21 | ## 创建测试计划 22 | 23 | ![](https://ooo.0o0.ooo/2016/09/09/57d262594f2ec.png) 24 | ![](https://ooo.0o0.ooo/2016/09/09/57d262640c284.png) 25 | ![](https://ooo.0o0.ooo/2016/09/09/57d2626eddf57.png) 26 | 27 | 配置参数,这里我们进行多次测试. 28 | 29 | ![](https://ooo.0o0.ooo/2016/09/09/57d262af81eaf.png) 30 | ![](https://ooo.0o0.ooo/2016/09/09/57d262b9a7fe3.png) 31 | 32 | | 并发用户数 | 吞吐量/每秒 | 请求等待时间/毫秒 | 错误请求数/百分比 | 33 | | :----: | :----: | :----: | :----: | 34 | | 10 | 1999 | 8 | 0.00 | 35 | | 20 | 2667 | 11 | 0.00 | 36 | | 30 | 2746 | 13 | 0.00 | 37 | | 40 | 2730 | 16 | 0.00 | 38 | | 50 | 2682 | 20 | 0.00 | 39 | | 60 | 2756 | 23 | 0.00 | 40 | | 70 | 2764 | 27 | 0.00 | 41 | | 80 | 2714 | 32 | 0.00 | 42 | | 90 | 2131 | 35 | 0.00 | 43 | | 100 | 2739 | 38 | 0.00 | 44 | | 200 | 1404 | 43 | 0.34% | 45 | | 300 | 1066 | 50 | 0.77% | 46 | | 400 | 995 | 52 | 1.23% | 47 | | 500 | 1086 | 46 | 1.42% | 48 | | 1000 | 1163 | 59 | 2.83% | 49 | 50 | ![](https://ooo.0o0.ooo/2016/09/09/57d276759cccc.png) 51 | 52 | ![](https://ooo.0o0.ooo/2016/09/09/57d2768276105.png) 53 | 54 | 从上面的测试结果来看,在90-100个并发的时候出现不稳定,其他都比较平缓,请求时间一直在上涨。CPU负载均在60%左右。 55 | 56 | 在聚合报告中,会显示一行数据,共有10个字段,含义分别如下。 57 | 58 | - Label:每个 JMeter 的 element(例如 HTTP Request)都有一个 Name 属性,这里显示的就是 Name 属性的值 59 | - #Samples:表示你这次测试中一共发出了多少个请求,如果模拟10个用户,每个用户迭代10次,那么这里显示100 60 | - Average:平均响应时间——默认情况下是单个 Request 的平均响应时间,当使用了 Transaction Controller 时,也可以以Transaction 为单位显示平均响应时间 61 | - Median:中位数,也就是 50% 用户的响应时间 62 | - 90% Line:90% 用户的响应时间 63 | - Min:最小响应时间 64 | - Max:最大响应时间 65 | - Error%:本次测试中出现错误的请求的数量/请求的总数 66 | - Throughput:吞吐量——默认情况下表示每秒完成的请求数(Request per Second) 67 | - KB/Sec:每秒从服务器端接收到的数据量,相当于LoadRunner中的Throughput/Sec 68 | 69 | 在下一章节我们介绍对tomcat8的优化。 70 | 71 | ## links 72 | * [目录]() 73 | * 上一节: [配置tomcat+nginx反向代理]() 74 | * 下一节: [优化tomcat8]() -------------------------------------------------------------------------------- /learn_server/virtual-machine-install-centos6.md: -------------------------------------------------------------------------------- 1 | # 在虚拟机里安装centos6 2 | 3 | ## 基础环境 4 | 5 | - 操作系统:Win7操作系统 6 | - 虚拟机:VMware® Workstation 12 Pro 7 | - Linux系统:CentOS 64位 8 | 9 | 接下里我们创建一个Linux虚拟机。 10 | 11 | ![](https://ooo.0o0.ooo/2016/09/09/57d21ba70219e.png) 12 | 13 | 这里选择自定义配置 14 | 15 | ![](https://ooo.0o0.ooo/2016/09/09/57d21bcabe68c.png) 16 | 17 | 我们选择了Workstation 8x, 为了兼容低版本的vmvware 18 | 19 | ![](https://ooo.0o0.ooo/2016/09/09/57d21bd500c46.png) 20 | 21 | 稍后安装操作系统 22 | 23 | ![](https://ooo.0o0.ooo/2016/09/09/57d21bdd67f7f.png) 24 | 25 | 选择Linux -> centos64位 26 | 27 | ![](https://ooo.0o0.ooo/2016/09/09/57d21be618ba3.png) 28 | 29 | 保存虚拟机到本地文件夹 30 | 31 | ![](https://ooo.0o0.ooo/2016/09/09/57d21bf02cb1f.png) 32 | 33 | 选择处理器数量和核心数,这里我选择默认的,根据你的机器情况可适当调整。 34 | 35 | ![](https://ooo.0o0.ooo/2016/09/09/57d21bf903216.png) 36 | 37 | 设置Centos内存,我设置1G 38 | 39 | ![](https://ooo.0o0.ooo/2016/09/09/57d21c048caba.png) 40 | 41 | 如果你在局域网环境并且希望其他人可以访问到你的centos,可以选择桥接模式, 42 | 这里我只有宿主机访问虚拟机,就设置了NAT模式,桥接的时候会和宿主机处于同一IP段。 43 | 44 | ![](https://ooo.0o0.ooo/2016/09/09/57d21c5b16bc8.png) 45 | ![](https://ooo.0o0.ooo/2016/09/09/57d21c63c49fa.png) 46 | 47 | 这里选择默认即可。 48 | 49 | ![](https://ooo.0o0.ooo/2016/09/09/57d21c70a4f1e.png) 50 | ![](https://ooo.0o0.ooo/2016/09/09/57d21c7a64405.png) 51 | 52 | 磁盘大小设置20G,用到更多可以累加上去,然后将虚拟磁盘存储为单文件,防止磁盘碎片。 53 | 54 | ![](https://ooo.0o0.ooo/2016/09/09/57d21ccc2db15.png) 55 | 56 | 点击完成。 57 | 58 | ![](https://ooo.0o0.ooo/2016/09/09/57d21cd385b5a.png) 59 | 60 | 选择你的IOS镜像文件,如果没有可以在 [这里](http://isoredirect.centos.org/centos/6/isos/x86_64/) 下载 61 | 62 | ![](https://ooo.0o0.ooo/2016/09/09/57d21cd94a75e.png) 63 | 64 | ![](https://ooo.0o0.ooo/2016/09/09/57d21ce210877.png) 65 | 66 | 安装操作系统 67 | 68 | ![](https://ooo.0o0.ooo/2016/09/09/57d21cf139606.png) 69 | 70 | 这里要检查硬件,可以直接跳过。 71 | 72 | ![](https://ooo.0o0.ooo/2016/09/09/57d21cf8bbfb7.png) 73 | 74 | 选择语言环境,我选择英文,避免在以后的操作中遇到未知的错误。 75 | 76 | ![](https://ooo.0o0.ooo/2016/09/09/57d21de293453.png) 77 | ![](https://ooo.0o0.ooo/2016/09/09/57d21de9279bf.png) 78 | 79 | 确定将配置写入到磁盘 80 | 81 | ![](https://ooo.0o0.ooo/2016/09/09/57d21def353cf.png) 82 | 83 | 这里就默认把,暂时用不到 84 | 85 | ![](https://ooo.0o0.ooo/2016/09/09/57d21dfa1624b.png) 86 | 87 | 选择时区,我们选择Asia/shanghai 88 | 89 | ![](https://ooo.0o0.ooo/2016/09/09/57d21dffbd0ac.png) 90 | 91 | 设置你的ROOT用户密码,请牢记以后会经常用到。 92 | 93 | ![](https://ooo.0o0.ooo/2016/09/09/57d21e06b65c4.png) 94 | 95 | 使用全部空间,就不分区了。 96 | 97 | ![](https://ooo.0o0.ooo/2016/09/09/57d21e0c94266.png) 98 | 99 | 将修改写入到磁盘。 100 | 101 | ![](https://ooo.0o0.ooo/2016/09/09/57d21e21e909f.png) 102 | 103 | 等待CentOS为你安装基础软件环境。 104 | 105 | ![](https://ooo.0o0.ooo/2016/09/09/57d21e687d0de.png) 106 | 107 | 看到这个界面你的CentOS就安装完成了,点击REBOOT即重启机器。 108 | 可以进行下一关了,上车! 109 | 110 | ## links 111 | * [目录]() 112 | * 下一节: [初始化操作系统]() -------------------------------------------------------------------------------- /mvc/1.plan.md: -------------------------------------------------------------------------------- 1 | # 项目规划 2 | 3 | 做任何事情都需要做好规划,那么我们在开发博客系统之前,同样需要做好项目的规划,如何设置目录结构,如何理解整个项目的流程图,当我们理解了应用的执行过程,那么接下来的设计编码就会变得相对容易了 4 | 5 | # 创建一个maven项目 6 | 7 | ## 约定一下框架基础信息 8 | 9 | * 假设我们的web框架名称是 `mario` 10 | * 包名是 `com.junicorn.mario` 11 | 12 | ### 命令行创建 13 | 14 | ```sh 15 | mvn archetype:create -DgroupId=com.junicorn -DartifactId=mario -DpackageName=com.junicorn.mario 16 | ``` 17 | 18 | ### Eclipse创建 19 | 20 | ![](http://i.imgur.com/2Spe2n6.png) 21 | 22 | ![](http://i.imgur.com/ElMZvuG.png) 23 | 24 | 创建好的基本结构是这样的 25 | 26 | ![](http://i.imgur.com/DxHVt9m.png) 27 | 28 | 初始化一下 `pom.xml` 29 | 30 | ```xml 31 | 33 | 4.0.0 34 | 35 | com.junicorn 36 | mario 37 | 0.0.1-SNAPSHOT 38 | jar 39 | 40 | mario 41 | https://github.com/junicorn/mario 42 | 43 | 44 | 1.6 45 | 1.6 46 | UTF-8 47 | 3.0.1 48 | 49 | 50 | 51 | 52 | javax.servlet 53 | javax.servlet-api 54 | 3.1.0 55 | provided 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.apache.maven.plugins 63 | maven-compiler-plugin 64 | 3.1 65 | 66 | 1.6 67 | 1.6 68 | UTF-8 69 | 70 | 71 | 72 | 73 | 74 | ``` 75 | 76 | OK,项目创建好了,这个将是我们的框架。 77 | 78 | # 框架流程 79 | 80 | web程序是基于 `M(模型)V(视图)C(控制器)` 设计的。MVC是一种将应用程序的逻辑层和表现层进行分离的结构方式。在实践中,由于表现层从 Java 中分离了出来,所以它允许你的网页中只包含很少的脚本。 81 | 82 | * 模型 (Model) 代表数据结构。通常来说,模型类将包含取出、插入、更新数据库资料等这些功能。 83 | * 视图 (View) 是展示给用户的信息的结构及样式。一个视图通常是一个网页,但是在Java中,一个视图也可以是一个页面片段,如页头、页尾。它还可以是一个 RSS 页面,或其它类型的“页面”,Jsp已经很好的实现了View层中的部分功能。 84 | * 控制器 (Controller) 是模型、视图以及其他任何处理HTTP请求所必须的资源之间的中介,并生成网页。 85 | 86 | # 设计思路 87 | 88 | mario 是基于servlet实现的mvc,用一个全局的Filter来做核心控制器,使用sql2o框架作为数据库基础访问。 89 | 使用一个接口 `Bootstrap` 作为初始化启动,实现它并遵循Filter参数约定即可。 90 | 91 | 建立路由、数据库、视图相关的包和类,下面是结构: 92 | 93 | ![](http://i.imgur.com/LEHfXYT.png) 94 | 95 | 96 | ## links 97 | * [目录]() 98 | * 下一节: [路由设计](<2.route.md>) 99 | -------------------------------------------------------------------------------- /mvc/2.route.md: -------------------------------------------------------------------------------- 1 | # 路由设计 2 | 3 | 现代 Web 应用的 URL 十分优雅,易于人们辨识记忆。 路由的表现形式如下: 4 | 5 | ``` 6 | /resources/:resource/actions/:action 7 | http://bladejava.com 8 | http://bladejava.com/docs/modules/route 9 | ``` 10 | 11 | 那么我们在java语言中将他定义一个 `Route` 类, 用于封装一个请求的最小单元, 12 | 在Mario中我们设计一个路由的对象如下: 13 | 14 | ```java 15 | /** 16 | * 路由 17 | * @author biezhi 18 | */ 19 | public class Route { 20 | 21 | /** 22 | * 路由path 23 | */ 24 | private String path; 25 | 26 | /** 27 | * 执行路由的方法 28 | */ 29 | private Method action; 30 | 31 | /** 32 | * 路由所在的控制器 33 | */ 34 | private Object controller; 35 | 36 | public Route() { 37 | } 38 | 39 | public String getPath() { 40 | return path; 41 | } 42 | 43 | public void setPath(String path) { 44 | this.path = path; 45 | } 46 | 47 | public Method getAction() { 48 | return action; 49 | } 50 | 51 | public void setAction(Method action) { 52 | this.action = action; 53 | } 54 | 55 | public Object getController() { 56 | return controller; 57 | } 58 | 59 | public void setController(Object controller) { 60 | this.controller = controller; 61 | } 62 | 63 | } 64 | ``` 65 | 66 | 所有的请求在程序中是一个路由,匹配在 `path` 上,执行靠 `action`,处于 `controller` 中。 67 | 68 | Mario使用一个Filter接收所有请求,因为从Filter过来的请求有无数,如何知道哪一个请求对应哪一个路由呢? 69 | 这时候需要设计一个路由匹配器去查找路由处理我们配置的请求, 70 | 有了路由匹配器还不够,这么多的路由我们如何管理呢?再来一个路由管理器吧,下面就创建路由匹配器和管理器2个类: 71 | 72 | ```java 73 | /** 74 | * 路由管理器,存放所有路由的 75 | * @author biezhi 76 | */ 77 | public class Routers { 78 | 79 | private static final Logger LOGGER = Logger.getLogger(Routers.class.getName()); 80 | 81 | private List routes = new ArrayList(); 82 | 83 | public Routers() { 84 | } 85 | 86 | public void addRoute(List routes){ 87 | routes.addAll(routes); 88 | } 89 | 90 | public void addRoute(Route route){ 91 | routes.add(route); 92 | } 93 | 94 | public void removeRoute(Route route){ 95 | routes.remove(route); 96 | } 97 | 98 | public void addRoute(String path, Method action, Object controller){ 99 | Route route = new Route(); 100 | route.setPath(path); 101 | route.setAction(action); 102 | route.setController(controller); 103 | 104 | routes.add(route); 105 | LOGGER.info("Add Route:[" + path + "]"); 106 | } 107 | 108 | public List getRoutes() { 109 | return routes; 110 | } 111 | 112 | public void setRoutes(List routes) { 113 | this.routes = routes; 114 | } 115 | 116 | } 117 | ``` 118 | 119 | 这里的代码很简单,这个管理器里用List存储所有路由,公有的 `addRoute` 方法是给外部调用的。 120 | 121 | ```java 122 | /** 123 | * 路由匹配器,用于匹配路由 124 | * @author biezhi 125 | */ 126 | public class RouteMatcher { 127 | 128 | private List routes; 129 | 130 | public RouteMatcher(List routes) { 131 | this.routes = routes; 132 | } 133 | 134 | public void setRoutes(List routes) { 135 | this.routes = routes; 136 | } 137 | 138 | /** 139 | * 根据path查找路由 140 | * @param path 请求地址 141 | * @return 返回查询到的路由 142 | */ 143 | public Route findRoute(String path) { 144 | String cleanPath = parsePath(path); 145 | List matchRoutes = new ArrayList(); 146 | for (Route route : this.routes) { 147 | if (matchesPath(route.getPath(), cleanPath)) { 148 | matchRoutes.add(route); 149 | } 150 | } 151 | // 优先匹配原则 152 | giveMatch(path, matchRoutes); 153 | 154 | return matchRoutes.size() > 0 ? matchRoutes.get(0) : null; 155 | } 156 | 157 | private void giveMatch(final String uri, List routes) { 158 | Collections.sort(routes, new Comparator() { 159 | @Override 160 | public int compare(Route o1, Route o2) { 161 | if (o2.getPath().equals(uri)) { 162 | return o2.getPath().indexOf(uri); 163 | } 164 | return -1; 165 | } 166 | }); 167 | } 168 | 169 | private boolean matchesPath(String routePath, String pathToMatch) { 170 | routePath = routePath.replaceAll(PathUtil.VAR_REGEXP, PathUtil.VAR_REPLACE); 171 | return pathToMatch.matches("(?i)" + routePath); 172 | } 173 | 174 | private String parsePath(String path) { 175 | path = PathUtil.fixPath(path); 176 | try { 177 | URI uri = new URI(path); 178 | return uri.getPath(); 179 | } catch (URISyntaxException e) { 180 | return null; 181 | } 182 | } 183 | 184 | } 185 | ``` 186 | 187 | 路由匹配器使用了正则去遍历路由列表,匹配合适的路由。当然我不认为这是最好的方法, 188 | 因为路由的量很大之后遍历的效率会降低,但这样是可以实现的,如果你有更好的方法可以告诉我 :) 189 | 190 | 在下一章节我们需要对请求处理做设计了~ 191 | 192 | ## links 193 | * [目录]() 194 | * 上一节: [项目规划](<1.plan.md>) 195 | * 下一节: [控制器设计](<3.controller.md>) 196 | -------------------------------------------------------------------------------- /mvc/3.controller.md: -------------------------------------------------------------------------------- 1 | # 控制器设计 2 | 3 | 一个MVC框架里 `C` 是核心的一块,也就是控制器,每个请求的接收,都是由控制器去处理的。 4 | 在Mario中我们把控制器放在路由对象的controller字段上,实际上一个请求过来之后最终是落在某个方法去处理的。 5 | 6 | 简单的方法我们可以使用反射实现动态调用方法执行,当然这对性能并不友好,你可以用缓存Method或者更高明的技术去做。 7 | 在这里我们不提及太麻烦的东西,因为初步目标是实现MVC框架,所以给大家提醒一下有些了解即可。 8 | 9 | 控制器的处理部分放在了核心Filter中,代码如下: 10 | 11 | ```java 12 | /** 13 | * Mario MVC核心处理器 14 | * @author biezhi 15 | * 16 | */ 17 | public class MarioFilter implements Filter { 18 | 19 | private static final Logger LOGGER = Logger.getLogger(MarioFilter.class.getName()); 20 | 21 | private RouteMatcher routeMatcher = new RouteMatcher(new ArrayList()); 22 | 23 | private ServletContext servletContext; 24 | 25 | @Override 26 | public void init(FilterConfig filterConfig) throws ServletException { 27 | Mario mario = Mario.me(); 28 | if(!mario.isInit()){ 29 | 30 | String className = filterConfig.getInitParameter("bootstrap"); 31 | Bootstrap bootstrap = this.getBootstrap(className); 32 | bootstrap.init(mario); 33 | 34 | Routers routers = mario.getRouters(); 35 | if(null != routers){ 36 | routeMatcher.setRoutes(routers.getRoutes()); 37 | } 38 | servletContext = filterConfig.getServletContext(); 39 | 40 | mario.setInit(true); 41 | } 42 | } 43 | 44 | private Bootstrap getBootstrap(String className) { 45 | if(null != className){ 46 | try { 47 | Class clazz = Class.forName(className); 48 | Bootstrap bootstrap = (Bootstrap) clazz.newInstance(); 49 | return bootstrap; 50 | } catch (ClassNotFoundException e) { 51 | throw new RuntimeException(e); 52 | } catch (InstantiationException e) { 53 | e.printStackTrace(); 54 | } catch (IllegalAccessException e) { 55 | e.printStackTrace(); 56 | } 57 | } 58 | throw new RuntimeException("init bootstrap class error!"); 59 | } 60 | 61 | @Override 62 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { 63 | HttpServletRequest request = (HttpServletRequest) servletRequest; 64 | HttpServletResponse response = (HttpServletResponse) servletResponse; 65 | 66 | // 请求的uri 67 | String uri = PathUtil.getRelativePath(request); 68 | 69 | LOGGER.info("Request URI:" + uri); 70 | 71 | Route route = routeMatcher.findRoute(uri); 72 | 73 | // 如果找到 74 | if (route != null) { 75 | // 实际执行方法 76 | handle(request, response, route); 77 | } else{ 78 | chain.doFilter(request, response); 79 | } 80 | } 81 | 82 | private void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Route route){ 83 | 84 | // 初始化上下文 85 | Request request = new Request(httpServletRequest); 86 | Response response = new Response(httpServletResponse); 87 | MarioContext.initContext(servletContext, request, response); 88 | 89 | Object controller = route.getController(); 90 | // 要执行的路由方法 91 | Method actionMethod = route.getAction(); 92 | // 执行route方法 93 | executeMethod(controller, actionMethod, request, response); 94 | } 95 | 96 | /** 97 | * 获取方法内的参数 98 | */ 99 | private Object[] getArgs(Request request, Response response, Class[] params){ 100 | 101 | int len = params.length; 102 | Object[] args = new Object[len]; 103 | 104 | for(int i=0; i paramTypeClazz = params[i]; 106 | if(paramTypeClazz.getName().equals(Request.class.getName())){ 107 | args[i] = request; 108 | } 109 | if(paramTypeClazz.getName().equals(Response.class.getName())){ 110 | args[i] = response; 111 | } 112 | } 113 | 114 | return args; 115 | } 116 | 117 | /** 118 | * 执行路由方法 119 | */ 120 | private Object executeMethod(Object object, Method method, Request request, Response response){ 121 | int len = method.getParameterTypes().length; 122 | method.setAccessible(true); 123 | if(len > 0){ 124 | Object[] args = getArgs(request, response, method.getParameterTypes()); 125 | return ReflectUtil.invokeMehod(object, method, args); 126 | } else { 127 | return ReflectUtil.invokeMehod(object, method); 128 | } 129 | } 130 | 131 | } 132 | ``` 133 | 134 | 这里执行的流程是酱紫的: 135 | 136 | 1. 接收用户请求 137 | 2. 查找路由 138 | 3. 找到即执行配置的方法 139 | 4. 找不到你看到的应该是404 140 | 141 | 看到这里也许很多同学会有点疑问,我们在说路由、控制器、匹配器,可是我怎么让它运行起来呢? 142 | 您可说到点儿上了,几乎在任何框架中都必须有配置这项,所谓的零配置都是扯淡。不管硬编码还是配置文件方式, 143 | 没有配置,框架的易用性和快速开发靠什么完成,又一行一行编写代码吗? 如果你说机器学习,至少现在好像没人用吧。 144 | 145 | 扯淡完毕,下一节来进入全局配置设计 -> 146 | 147 | ## links 148 | * [目录]() 149 | * 上一节: [路由设计](<2.route.md>) 150 | * 下一节: [配置设计](<4.config.md>) 151 | -------------------------------------------------------------------------------- /mvc/4.config.md: -------------------------------------------------------------------------------- 1 | # 配置设计 2 | 3 | Mario中所有的配置都可以在 `Mario` 全局唯一对象完成,将它设计为单例。 4 | 5 | 要运行起来整个框架,Mario对象是核心,看看里面都需要什么吧! 6 | 7 | - 添加路由 8 | - 读取资源文件 9 | - 读取配置 10 | - 等等 11 | 12 | 由此我们简单的设计一个Mario全局对象: 13 | 14 | ```java 15 | /** 16 | * Mario 17 | * @author biezhi 18 | * 19 | */ 20 | public final class Mario { 21 | 22 | /** 23 | * 存放所有路由 24 | */ 25 | private Routers routers; 26 | 27 | /** 28 | * 配置加载器 29 | */ 30 | private ConfigLoader configLoader; 31 | 32 | /** 33 | * 框架是否已经初始化 34 | */ 35 | private boolean init = false; 36 | 37 | private Mario() { 38 | routers = new Routers(); 39 | configLoader = new ConfigLoader(); 40 | } 41 | 42 | public boolean isInit() { 43 | return init; 44 | } 45 | 46 | public void setInit(boolean init) { 47 | this.init = init; 48 | } 49 | 50 | private static class MarioHolder { 51 | private static Mario ME = new Mario(); 52 | } 53 | 54 | public static Mario me(){ 55 | return MarioHolder.ME; 56 | } 57 | 58 | public Mario addConf(String conf){ 59 | configLoader.load(conf); 60 | return this; 61 | } 62 | 63 | public String getConf(String name){ 64 | return configLoader.getConf(name); 65 | } 66 | 67 | public Mario addRoutes(Routers routers){ 68 | this.routers.addRoute(routers.getRoutes()); 69 | return this; 70 | } 71 | 72 | public Routers getRouters() { 73 | return routers; 74 | } 75 | 76 | /** 77 | * 添加路由 78 | * @param path 映射的PATH 79 | * @param methodName 方法名称 80 | * @param controller 控制器对象 81 | * @return 返回Mario 82 | */ 83 | public Mario addRoute(String path, String methodName, Object controller){ 84 | try { 85 | Method method = controller.getClass().getMethod(methodName, Request.class, Response.class); 86 | this.routers.addRoute(path, method, controller); 87 | } catch (NoSuchMethodException e) { 88 | e.printStackTrace(); 89 | } catch (SecurityException e) { 90 | e.printStackTrace(); 91 | } 92 | return this; 93 | } 94 | 95 | } 96 | ``` 97 | 98 | 这样在系统中永远保持一个Mario实例,我们用它来操作所有配置即可。 99 | 100 | ### 在`Boostrap`的`init`方法中使用 101 | 102 | ```java 103 | @Override 104 | public void init(Mario mario) { 105 | Index index = new Index(); 106 | mario.addRoute("/", "index", index); 107 | mario.addRoute("/html", "html", index); 108 | } 109 | ``` 110 | 111 | 这样,一个简单的MVC后端已经形成了!接下来我们要将结果展现在JSP文件中,要做视图的渲染设计 LET'S GO! 112 | 113 | ## links 114 | * [目录]() 115 | * 上一节: [控制器设计](<3.controller.md>) 116 | * 下一节: [视图设计](<5.view.md>) 117 | -------------------------------------------------------------------------------- /mvc/5.view.md: -------------------------------------------------------------------------------- 1 | # 视图设计 2 | 3 | 我们已经完成了MVC中的C层,还有M和V没有做呢。这一小节来对视图进行设计,从后台到前台的渲染是这样的 4 | 后台给定一个视图位置,输出到前端JSP或者其他模板引擎上,做一个非常简单的接口: 5 | 6 | ```java 7 | /** 8 | * 视图渲染接口 9 | * @author biezhi 10 | * 11 | */ 12 | public interface Render { 13 | 14 | /** 15 | * 渲染到视图 16 | * @param view 视图名称 17 | * @param writer 写入对象 18 | */ 19 | public void render(String view, Writer writer); 20 | 21 | } 22 | ``` 23 | 24 | 具体的实现我们先写一个JSP的,当你在使用Servlet进行开发的时候已经习惯了这句语法: 25 | 26 | ```java 27 | servletRequest.getRequestDispatcher(viewPath).forward(servletRequest, servletResponse); 28 | ``` 29 | 30 | 那么一个JSP的渲染实现就很简单了 31 | 32 | ```java 33 | /** 34 | * JSP渲染实现 35 | * @author biezhi 36 | * 37 | */ 38 | public class JspRender implements Render { 39 | 40 | @Override 41 | public void render(String view, Writer writer) { 42 | 43 | String viewPath = this.getViewPath(view); 44 | 45 | HttpServletRequest servletRequest = MarioContext.me().getRequest().getRaw(); 46 | HttpServletResponse servletResponse = MarioContext.me().getResponse().getRaw(); 47 | try { 48 | servletRequest.getRequestDispatcher(viewPath).forward(servletRequest, servletResponse); 49 | } catch (ServletException e) { 50 | e.printStackTrace(); 51 | } catch (IOException e) { 52 | e.printStackTrace(); 53 | } 54 | 55 | } 56 | 57 | private String getViewPath(String view){ 58 | Mario mario = Mario.me(); 59 | String viewPrfix = mario.getConf(Const.VIEW_PREFIX_FIELD); 60 | String viewSuffix = mario.getConf(Const.VIEW_SUFFIX_FIELD); 61 | 62 | if (null == viewSuffix || viewSuffix.equals("")) { 63 | viewSuffix = Const.VIEW_SUFFIX; 64 | } 65 | if (null == viewPrfix || viewPrfix.equals("")) { 66 | viewPrfix = Const.VIEW_PREFIX; 67 | } 68 | String viewPath = viewPrfix + "/" + view; 69 | if (!view.endsWith(viewSuffix)) { 70 | viewPath += viewSuffix; 71 | } 72 | return viewPath.replaceAll("[/]+", "/"); 73 | } 74 | 75 | } 76 | ``` 77 | 78 | 配置 JSP 视图的位置和后缀可以在配置文件或者硬编码中进行,当然这看你的习惯, 79 | 默认设置了 JSP 在 `/WEB-INF/` 下,后缀是 `.jsp` 你懂的! 80 | 81 | 怎么用可以参考 `mario-sample` 这个项目,因为真的很简单 相信你自己。 82 | 83 | 在下一节中我们就要和数据库打交道了,尝试新的旅程吧 :) 84 | 85 | ## links 86 | * [目录]() 87 | * 上一节: [配置设计](<4.config.md>) 88 | * 下一节: [数据库操作](<6.dbutil.md>) 89 | 90 | -------------------------------------------------------------------------------- /mvc/6.dbutil.md: -------------------------------------------------------------------------------- 1 | # 数据库操作 2 | 3 | 这一小节是对数据库操作做一个简单的封装,不涉及复杂的事务操作等。 4 | 5 | 我选用了Sql2o作为底层数据库框架作为支持,它的简洁易用性让我刮目相看,后面我们也会写如何实现一个ORM框架。 6 | 7 | ```java 8 | /** 9 | * 数据库支持 10 | * @author biezhi 11 | * 12 | */ 13 | public final class MarioDb { 14 | 15 | private static Sql2o sql2o = null; 16 | 17 | private MarioDb() { 18 | } 19 | 20 | /** 21 | * 初始化数据库配置 22 | * @param url 23 | * @param user 24 | * @param pass 25 | */ 26 | public static void init(String url, String user, String pass){ 27 | sql2o = new Sql2o(url, user, pass); 28 | } 29 | 30 | /** 31 | * 初始化数据库配置 32 | * @param dataSource 33 | */ 34 | public static void init(DataSource dataSource){ 35 | sql2o = new Sql2o(dataSource); 36 | } 37 | 38 | /** 39 | * 查询一个对象 40 | * @param sql 41 | * @param clazz 42 | * @return 43 | */ 44 | public static T get(String sql, Class clazz){ 45 | return get(sql, clazz, null); 46 | } 47 | 48 | /** 49 | * 查询一个列表 50 | * @param sql 51 | * @param clazz 52 | * @return 53 | */ 54 | public static List getList(String sql, Class clazz){ 55 | return getList(sql, clazz, null); 56 | } 57 | 58 | /** 59 | * 查询一个对象返回为map类型 60 | * @param sql 61 | * @return 62 | */ 63 | public static Map getMap(String sql){ 64 | return getMap(sql, null); 65 | } 66 | 67 | /** 68 | * 查询一个列表并返回为list类型 69 | * @param sql 70 | * @return 71 | */ 72 | public static List> getMapList(String sql){ 73 | return getMapList(sql, null); 74 | } 75 | 76 | /** 77 | * 插入一条记录 78 | * @param sql 79 | * @param params 80 | * @return 81 | */ 82 | public static int insert(String sql, Object ... params){ 83 | StringBuffer sqlBuf = new StringBuffer(sql); 84 | sqlBuf.append(" values ("); 85 | 86 | int start = sql.indexOf("(") + 1; 87 | int end = sql.indexOf(")"); 88 | String a = sql.substring(start, end); 89 | String[] fields = a.split(","); 90 | 91 | Map map = new HashMap(); 92 | 93 | int i=0; 94 | for(String name : fields){ 95 | sqlBuf.append(":" + name.trim() + " ,"); 96 | map.put(name.trim(), params[i]); 97 | i++; 98 | } 99 | 100 | String newSql = sqlBuf.substring(0, sqlBuf.length() - 1) + ")"; 101 | 102 | Connection con = sql2o.open(); 103 | Query query = con.createQuery(newSql); 104 | 105 | executeQuery(query, map); 106 | 107 | int res = query.executeUpdate().getResult(); 108 | 109 | con.close(); 110 | 111 | return res; 112 | } 113 | /** 114 | * 更新 115 | * @param sql 116 | * @return 117 | */ 118 | public static int update(String sql){ 119 | return update(sql, null); 120 | } 121 | 122 | /** 123 | * 带参数更新 124 | * @param sql 125 | * @param params 126 | * @return 127 | */ 128 | public static int update(String sql, Map params){ 129 | Connection con = sql2o.open(); 130 | Query query = con.createQuery(sql); 131 | executeQuery(query, params); 132 | int res = query.executeUpdate().getResult(); 133 | con.close(); 134 | return res; 135 | } 136 | 137 | public static T get(String sql, Class clazz, Map params){ 138 | Connection con = sql2o.open(); 139 | Query query = con.createQuery(sql); 140 | executeQuery(query, params); 141 | T t = query.executeAndFetchFirst(clazz); 142 | con.close(); 143 | return t; 144 | } 145 | 146 | @SuppressWarnings("unchecked") 147 | public static Map getMap(String sql, Map params){ 148 | Connection con = sql2o.open(); 149 | Query query = con.createQuery(sql); 150 | executeQuery(query, params); 151 | Map t = (Map) query.executeScalar(); 152 | con.close(); 153 | return t; 154 | } 155 | 156 | public static List> getMapList(String sql, Map params){ 157 | Connection con = sql2o.open(); 158 | Query query = con.createQuery(sql); 159 | executeQuery(query, params); 160 | List> t = query.executeAndFetchTable().asList(); 161 | con.close(); 162 | return t; 163 | } 164 | 165 | public static List getList(String sql, Class clazz, Map params){ 166 | Connection con = sql2o.open(); 167 | Query query = con.createQuery(sql); 168 | executeQuery(query, params); 169 | List list = query.executeAndFetch(clazz); 170 | con.close(); 171 | return list; 172 | } 173 | 174 | private static void executeQuery(Query query, Map params){ 175 | if (null != params && params.size() > 0) { 176 | Set keys = params.keySet(); 177 | for(String key : keys){ 178 | query.addParameter(key, params.get(key)); 179 | } 180 | } 181 | } 182 | } 183 | ``` 184 | 185 | 设计MVC框架部分已经完成,下一节是一个增删改查的例子 186 | 187 | ## links 188 | * [目录]() 189 | * 上一节: [视图设计](<5.view.md>) 190 | * 下一节: [增删改查](<7.crud.md>) 191 | -------------------------------------------------------------------------------- /mvc/7.crud.md: -------------------------------------------------------------------------------- 1 | # 增删改查 2 | 3 | ```java 4 | /** 5 | * 用户控制器 6 | */ 7 | public class UserController { 8 | 9 | /** 10 | * 用户列表 11 | * @param request 12 | * @param response 13 | */ 14 | public void users(Request request, Response response){ 15 | List users = MarioDb.getList("select * from t_user", User.class); 16 | request.attr("users", users); 17 | response.render("users"); 18 | } 19 | 20 | /** 21 | * 添加用户界面 22 | * @param request 23 | * @param response 24 | */ 25 | public void show_add(Request request, Response response){ 26 | response.render("user_add"); 27 | } 28 | 29 | /** 30 | * 保存方法 31 | * @param request 32 | * @param response 33 | * @throws ParseException 34 | */ 35 | public void save(Request request, Response response) throws ParseException{ 36 | String name = request.query("name"); 37 | Integer age = request.queryAsInt("age"); 38 | String date = request.query("birthday"); 39 | 40 | if(null == name || null == age || null == date){ 41 | request.attr("res", "error"); 42 | response.render("user_add"); 43 | return; 44 | } 45 | 46 | Date bir = new SimpleDateFormat("yyyy-MM-dd").parse(date); 47 | 48 | int res = MarioDb.insert("insert into t_user(name, age, birthday)", name, age, bir); 49 | if(res > 0){ 50 | String ctx = MarioContext.me().getContext().getContextPath(); 51 | String location = ctx + "/users"; 52 | response.redirect(location.replaceAll("[/]+", "/")); 53 | } else { 54 | request.attr("res", "error"); 55 | response.render("user_add"); 56 | } 57 | } 58 | 59 | /** 60 | * 编辑页面 61 | * @param request 62 | * @param response 63 | */ 64 | public void edit(Request request, Response response){ 65 | Integer id = request.queryAsInt("id"); 66 | if(null != id){ 67 | Map map = new HashMap(); 68 | map.put("id", id); 69 | User user = MarioDb.get("select * from t_user where id = :id", User.class, map); 70 | request.attr("user", user); 71 | response.render("user_edit"); 72 | } 73 | } 74 | 75 | /** 76 | * 修改信息 77 | * @param request 78 | * @param response 79 | */ 80 | public void update(Request request, Response response){ 81 | Integer id = request.queryAsInt("id"); 82 | String name = request.query("name"); 83 | Integer age = request.queryAsInt("age"); 84 | 85 | if(null == id || null == name || null == age ){ 86 | request.attr("res", "error"); 87 | response.render("user_edit"); 88 | return; 89 | } 90 | 91 | Map map = new HashMap(); 92 | map.put("id", id); 93 | map.put("name", name); 94 | map.put("age", age); 95 | 96 | int res = MarioDb.update("update t_user set name = :name, age = :age where id = :id", map); 97 | if(res > 0){ 98 | String ctx = MarioContext.me().getContext().getContextPath(); 99 | String location = ctx + "/users"; 100 | response.redirect(location.replaceAll("[/]+", "/")); 101 | } else { 102 | request.attr("res", "error"); 103 | response.render("user_edit"); 104 | } 105 | } 106 | 107 | /** 108 | * 删除 109 | * @param request 110 | * @param response 111 | */ 112 | public void delete(Request request, Response response){ 113 | Integer id = request.queryAsInt("id"); 114 | if(null != id){ 115 | Map map = new HashMap(); 116 | map.put("id", id); 117 | MarioDb.update("delete from t_user where id = :id", map); 118 | } 119 | 120 | String ctx = MarioContext.me().getContext().getContextPath(); 121 | String location = ctx + "/users"; 122 | response.redirect(location.replaceAll("[/]+", "/")); 123 | } 124 | } 125 | ``` 126 | 127 | ![](http://i5.tietuku.com/b77fdcfe2cecf753.png) 128 | 129 | + [演示程序代码](https://github.com/junicorn/mario-sample) 130 | + [Blade框架](https://github.com/biezhi/blade) 131 | 132 | ## links 133 | * [目录]() 134 | * 上一节: [数据库操作](<6.dbutil.md>) 135 | -------------------------------------------------------------------------------- /mvc/MVC框架实现篇.md: -------------------------------------------------------------------------------- 1 | # MVC框架实现篇 2 | 3 | -------------------------------------------------------------------------------- /mvc/index.md: -------------------------------------------------------------------------------- 1 | # 如何设计一个JavaWeb MVC框架 2 | 3 | 通过使用Java语言实现一个完整的框架设计,这个框架中主要内容有第一小节介绍的Web框架的结构规划,例如采用MVC模式来进行开发,程序的执行流程设计等内容;第二小节介绍框架的第一个功能:路由,如何让访问的URL映射到相应的处理逻辑;第三小节介绍处理逻辑,如何设计一个公共的 `调度器`,对象继承之后处理函数中如何处理response和request;第四小节至第六小节介绍如何框架的一些辅助功能,例如配置信息,数据库操作等;最后介绍如何基于Web框架实现一个简单的增删改查,包括User的添加、修改、删除、显示列表等操作。 4 | 5 | 通过这么一个完整的项目例子,我期望能够让读者了解如何开发Web应用,如何搭建自己的目录结构,如何实现路由,如何实现MVC模式等各方面的开发内容。在框架盛行的今天,MVC也不再是神话。经常听到很多程序员讨论哪个框架好,哪个框架不好, 其实框架只是工具,没有好与不好,只有适合与不适合,适合自己的就是最好的,所以教会大家自己动手写框架,那么不同的需求都可以用自己的思路去实现。 6 | 7 | ![](http://i.imgur.com/QH8SRfB.png) 8 | 9 | - 项目源码:[https://github.com/junicorn/mario](https://github.com/junicorn/mario) 10 | - 示例代码:[https://github.com/junicorn/mario-sample](https://github.com/junicorn/mario-sample) 11 | 12 | 欢迎Star我写的一个简洁优雅的MVC框架 [Blade](https://github.com/biezhi/blade) :wink: 13 | 14 | # 目录 15 | 16 | * [项目规划](1.plan.md) 17 | * [路由设计](2.route.md) 18 | * [控制器设计](3.controller.md) 19 | * [配置设计](4.config.md) 20 | * [视图设计](5.view.md) 21 | * [数据库操作](6.dbutil.md) 22 | * [增删改查](7.crud.md) 23 | 24 | 接下来开始我们的 [框架之旅](1.plan.md) 吧~ 25 | -------------------------------------------------------------------------------- /shell/download-jdk.md: -------------------------------------------------------------------------------- 1 | ## Download Oracle Java JRE & JDK using a script 2 | 3 | Oracle has recently disallowed direct downloads of java from their servers (without going through the browser and agreeing to their terms, which you can look at here: Oracle terms). So, if you try: 4 | 5 | ```sh 6 | wget "http://download.oracle.com/otn-pub/java/jdk/7u4-b20/jdk-7u4-linux-x64.tar.gz" 7 | ``` 8 | 9 | you will receive a page with "In order to download products from Oracle Technology Network you must agree to the OTN license terms" error message. 10 | 11 | This can be rather troublesome for setting up servers with automated scripts. 12 | 13 | Luckily, it seems that a single cookie is all that is needed to bypass this (you still have to agree to the terms to install): 14 | 15 | ```sh 16 | Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie 17 | ``` 18 | 19 | So, if you want to download jdk7u4 for 64-bit Linux (e.g., Ubuntu) using wget, you can use: 20 | 21 | ```sh 22 | wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/7u4-b20/jdk-7u4-linux-x64.tar.gz" 23 | ``` 24 | 25 | Just for reference, here are the links to the current (at the time of posting) downloads of JDK and JRE 26 | 27 | **JDK 8u66** 28 | 29 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-linux-i586.rpm 30 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-linux-i586.tar.gz 31 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-linux-x64.rpm 32 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-linux-x64.tar.gz 33 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-macosx-x64.dmg 34 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-solaris-sparcv9.tar.Z 35 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-solaris-sparcv9.tar.gz 36 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-solaris-x64.tar.Z 37 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-solaris-x64.tar.gz 38 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-windows-i586.exe 39 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-windows-x64.exe 40 | 41 | **JRE 8u66** 42 | 43 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-linux-i586.rpm 44 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-linux-i586.tar.gz 45 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-linux-x64.rpm 46 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-linux-x64.tar.gz 47 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-macosx-x64.dmg 48 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-macosx-x64.tar.gz 49 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-solaris-sparcv9.tar.gz 50 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-solaris-x64.tar.gz 51 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-windows-i586-iftw.exe 52 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-windows-i586.exe 53 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-windows-i586.tar.gz 54 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-windows-x64.exe 55 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jre-8u66-windows-x64.tar.gz 56 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/server-jre-8u66-linux-x64.tar.gz 57 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/server-jre-8u66-solaris-sparcv9.tar.gz 58 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/server-jre-8u66-solaris-x64.tar.gz 59 | - http://download.oracle.com/otn-pub/java/jdk/8u66-b17/server-jre-8u66-windows-x64.tar.gz 60 | -------------------------------------------------------------------------------- /shell/install_jdk_tomcat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | ############################################### 4 | #author: biezhi 5 | #email:i@biezhi.me 6 | #date: 2015-09-16 7 | ############################################### 8 | 9 | base_dir=$(cd "$(dirname "$0")";pwd) 10 | 11 | JDK_FILE=$(ls | grep jdk-*-linux-*.tar.gz) 12 | TOMCAT_FILE=$(ls | grep apache-tomcat-*.tar.gz) 13 | #下载JDK 14 | download(){ 15 | os_version=`uname -a` 16 | echo $os_version 17 | architecture="64" 18 | echo "$os_version" | grep -q "$architecture" 19 | 20 | if [ $? -eq 0 ] 21 | then 22 | # 不存在即去外网下载jdk文件 23 | if [ ! -f "$JDK_FILE" ]; then 24 | echo "您正在使用64位操作系统,为您选择64位JDK" 25 | wget http://7xls9k.dl1.z0.glb.clouddn.com/jdk-8u60-linux-x64.tar.gz 26 | fi 27 | else 28 | # 不存在即去外网下载jdk文件 29 | if [ ! -f "$JDK_FILE" ]; then 30 | echo "您正在使用32位操作系统,为您选择32位JDK" 31 | wget http://7xls9k.dl1.z0.glb.clouddn.com/jdk-8u60-linux-i586.tar.gz 32 | fi 33 | fi 34 | #下载tomcat 35 | if [ ! -f "$TOMCAT_FILE" ]; then 36 | wget http://7xls9k.dl1.z0.glb.clouddn.com/apache-tomcat-8.0.26.tar.gz 37 | fi 38 | JDK_FILE=$(ls | grep jdk-*-linux-*.tar.gz) 39 | TOMCAT_FILE=$(ls | grep apache-tomcat-*.tar.gz) 40 | } 41 | 42 | #安装JDK 43 | install_jdk(){ 44 | JAVA_DIR=/usr/local/java 45 | JDK_DIR="jdk1.8.0_60" 46 | JDK_PATH="$JAVA_DIR"/"$JDK_DIR" 47 | 48 | tar xzf $JDK_FILE 49 | 50 | mkdir -p $JAVA_DIR 51 | mv $JDK_DIR $JAVA_DIR 52 | #配置环境变量 53 | cp ~/.bashrc ~/.bashrc.backup.java 54 | if [ ! -n "$JAVA_HOME" ]; then 55 | echo "export JAVA_HOME=\"$JDK_PATH\"" >> ~/.bashrc 56 | fi 57 | if [ ! -n "$JRE_HOME" ]; then 58 | echo "export JRE_HOME=\"\$JAVA_HOME/jre\"" >> ~/.bashrc 59 | fi 60 | if [ ! -n "$CLASSPATH" ]; then 61 | echo "export CLASSPATH=.:\$JDK_PATH/lib/dt.jar:\$JDK_PATH/lib/tools.jar" >> ~/.bashrc 62 | fi 63 | echo "export PATH=\$JAVA_HOME/bin:\$JRE_HOME/bin:\$PATH" >> ~/.bashrc 64 | source ~/.bashrc 65 | echo "JDK install success!" 66 | } 67 | #安装tomcat 68 | install_tomcat(){ 69 | TOMCAT_DIR=/usr/local/tomcat8 70 | 71 | mkdir -p $TOMCAT_DIR 72 | 73 | tar xzf $TOMCAT_FILE 74 | mv apache-tomcat-8.0.26 tomcat8 75 | mv tomcat8 /usr/local/ 76 | 77 | cp ~/.bashrc ~/.bashrc.backup.tomcat8 78 | if [ ! -n "$TOMCAT_HOME" ]; then 79 | echo "export TOMCAT_HOME=$TOMCAT_DIR" >> ~/.bashrc 80 | fi 81 | if [ ! -n "$CATALINA_HOME" ]; then 82 | echo "export CATALINA_HOME=$TOMCAT_DIR" >> ~/.bashrc 83 | fi 84 | source ~/.bashrc 85 | echo "Tomact install success!" 86 | } 87 | 88 | main(){ 89 | download 90 | if [ $? != 0 ]; then 91 | echo "tomcat & JDK download failed" 92 | exit 1 93 | fi 94 | install_jdk 95 | if [ $? != 0 ]; then 96 | echo "JDK install failed" 97 | exit 1 98 | fi 99 | install_tomcat 100 | if [ $? != 0 ]; then 101 | echo "Tomcat install failed" 102 | exit 1 103 | fi 104 | } 105 | main 106 | -------------------------------------------------------------------------------- /shell/tomcat.md: -------------------------------------------------------------------------------- 1 | ```sh 2 | #!/bin/bash 3 | # author: Sean Chow (seanlook7@gmail.com) 4 | # 5 | # 6 | # chkconfig: 345 80 15 7 | # description: use service tomcat xintr 8 | 9 | # Source function library. 10 | . /etc/rc.d/init.d/functions 11 | 12 | export JAVA_HOME= 13 | export JRE_HOME= 14 | 15 | # tomcat名字 16 | tcName=tomcat-$1 17 | basedir=/data/program/tomcat/$tcName 18 | tclog=${basedir}/logs/catalina.out 19 | 20 | RETVAL=0 21 | 22 | start(){ 23 | checkrun 24 | if [ $RETVAL -eq 0 ]; then 25 | echo "###### Tomcat正在启动 ######" 26 | $basedir/bin/startup.sh 27 | touch /var/lock/subsys/${tcNo} 28 | checklog 29 | status 30 | else 31 | echo "###### Tomcat启动成功 ######" 32 | fi 33 | } 34 | 35 | # 停止某一台tomcat,如果是重启则带re参数,表示不查看日志,等待启动时再提示查看 36 | stop(){ 37 | checkrun 38 | if [ $RETVAL -eq 1 ]; then 39 | echo "###### Tomcat正在关闭 ######" 40 | $basedir/bin/shutdown.sh 41 | if [ "$1" != "re" ]; then 42 | checklog 43 | else 44 | sleep 5 45 | fi 46 | rm -f /var/lock/subsys/${tcNo} 47 | status 48 | else 49 | echo "###### Tomcat关闭成功 ######" 50 | fi 51 | } 52 | 53 | status(){ 54 | checkrun 55 | if [ $RETVAL -eq 1 ]; then 56 | echo -n "-- Tomcat ( pid " 57 | ps ax --width=1000 |grep ${tcName}|grep "org.apache.catalina.startup.Bootstrap start" | awk '{printf $1 " "}' 58 | echo -n ") 正在运行" 59 | echo 60 | else 61 | echo "###### Tomcat未运行 ######" 62 | fi 63 | #echo "---------------------------------------------" 64 | } 65 | 66 | # 查看tomcat日志,带vl参数 67 | log(){ 68 | status 69 | checklog yes 70 | } 71 | 72 | # 如果tomcat正在运行,强行杀死tomcat进程,关闭tomcat 73 | kill(){ 74 | checkrun 75 | if [ $RETVAL -eq 1 ]; then 76 | read -p "###### 确定要杀死 ${tcName} 的进程吗?[no])" answer 77 | case $answer in 78 | Y|y|YES|yes|Yes) 79 | ps ax --width=1000 |grep ${tcName}|grep "org.apache.catalina.startup.Bootstrap start" | awk '{printf $1 " "}'|xargs kill -9 80 | status 81 | ;; 82 | *);; 83 | esac 84 | else 85 | echo "###### 退出 [$tcName] ######" 86 | fi 87 | } 88 | 89 | 90 | checkrun(){ 91 | ps ax --width=1000 |grep ${tcName}| grep "[o]rg.apache.catalina.startup.Bootstrap start" | awk '{printf $1 " "}' | wc | awk '{print $2}' >/tmp/tomcat_process_count.txt 92 | read line < /tmp/tomcat_process_count.txt 93 | if [ $line -gt 0 ]; then 94 | RETVAL=1 95 | return $RETVAL 96 | else 97 | RETVAL=0 98 | return $RETVAL 99 | fi 100 | } 101 | 102 | # 如果是直接查看日志viewlog,则不提示输入[yes],否则就是被stop和start调用,需提示是否查看日志 103 | checklog(){ 104 | answer=$1 105 | if [ "$answer" != "yes" ]; then 106 | read -p "###### 查看 日志吗 $2?[yes])" answer 107 | fi 108 | case $answer in 109 | Y|y|YES|yes|Yes|"") 110 | tail -f ${tclog} 111 | ;; 112 | *) 113 | # status 114 | # exit 0 115 | ;; 116 | esac 117 | } 118 | checkexist(){ 119 | if [ ! -d $basedir ]; then 120 | echo "###### tomcat $basedir 不存在 ######" 121 | exit 0 122 | fi 123 | } 124 | 125 | 126 | case "$2" in 127 | start) 128 | checkexist 129 | start 130 | exit 0 131 | ;; 132 | stop) 133 | checkexist 134 | stop 135 | exit 0 136 | ;; 137 | restart) 138 | checkexist 139 | stop re 140 | start 141 | exit 0 142 | ;; 143 | status) 144 | checkexist 145 | status 146 | #$basedir/bin/catalina.sh version 147 | exit 0 148 | ;; 149 | log) 150 | checkexist 151 | log 152 | exit 0 153 | ;; 154 | kill) 155 | checkexist 156 | status 157 | kill 158 | exit 0 159 | ;; 160 | *) 161 | echo "###### 使用方法: service $0 [start|stop|restart|status|log|kill]" 162 | echo "###### 举个栗子-> service tomcat xintr start" 163 | esac 164 | 165 | exit 0 166 | ``` 167 | -------------------------------------------------------------------------------- /shell/uninstall_jdk_tomcat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | ############################################### 4 | #author: biezhi 5 | #email:i@biezhi.me 6 | #date: 2015-09-16 7 | ############################################### 8 | 9 | #卸载JDK 10 | uninstall_jdk(){ 11 | JAVA_DIR=/usr/local/java/jdk1.8.0_60 12 | TOMCAT_DIR=/usr/local/tomcat8 13 | 14 | if [ -d "$JAVA_DIR" ]; then 15 | rm -rf $JAVA_DIR 16 | fi 17 | 18 | if [ -d "$TOMCAT_DIR" ]; then 19 | rm -rf $TOMCAT_DIR 20 | fi 21 | 22 | #环境变量 23 | if [ -f "~/.bashrc.backup.tomcat8" ]; then 24 | mv ~/.bashrc.backup.tomcat8 ~/.bashrc 25 | fi 26 | 27 | if [ -f "~/.bashrc.backup.java" ]; then 28 | mv ~/.bashrc.backup.java ~/.bashrc 29 | fi 30 | source ~/.bashrc 31 | echo "JDK,Tomcat uninstall success!" 32 | cd 33 | } 34 | 35 | main(){ 36 | uninstall_jdk 37 | if [ $? != 0 ]; then 38 | echo "JDK,Tomcat uninstall failed" 39 | exit 1 40 | fi 41 | } 42 | main 43 | -------------------------------------------------------------------------------- /simple-java/classes-and-interfaces/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /simple-java/collections/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /simple-java/common-methods/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /simple-java/compiler-and-jvm/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /simple-java/concurrency/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /simple-java/exceptions/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /simple-java/generics/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /simple-java/io-and-database/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 -------------------------------------------------------------------------------- /simple-java/reflection-tutorial.md: -------------------------------------------------------------------------------- 1 | # reflection-tutorial.md -------------------------------------------------------------------------------- /simple-java/string-and-array/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和数组 2 | 3 | 1. [String的不可变是什么?](diagram-to-show-java-strings-immutability.md) 4 | 2. [substring()如何工作?](the-substring-method-in-jdk-6-and-jdk-7.md) 5 | 3. [为什么String是不可变的?]() 6 | 4. [使用""和构造函数创建字符串]() 7 | 5. [String是引用传递吗?]() 8 | 6. [length 和 length()]() 9 | 7. [如何检查数组是否有效包含一个值?]() 10 | 8. []() -------------------------------------------------------------------------------- /simple-java/string-and-array/diagram-to-show-java-strings-immutability.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellokaton/java-bible/68b3e4fa1d5eaf08003d480802f2884c4243b41e/simple-java/string-and-array/diagram-to-show-java-strings-immutability.md -------------------------------------------------------------------------------- /simple-java/string-and-array/the-substring-method-in-jdk-6-and-jdk-7.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellokaton/java-bible/68b3e4fa1d5eaf08003d480802f2884c4243b41e/simple-java/string-and-array/the-substring-method-in-jdk-6-and-jdk-7.md -------------------------------------------------------------------------------- /web/test_tool.md: -------------------------------------------------------------------------------- 1 | # Web服务器性能压力测试工具 2 | 3 | ## 目录 4 | 5 | * [http_load](#http_load) 6 | * [webbench](#webbench) 7 | * [ab](#ab) 8 | * [siege](#siege) 9 | 10 | ### http_load 11 | 12 | 程序非常小,解压后也不到100K 13 | http_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载。 14 | 但是它不同于大多数压力测试工具,它可以以一个单一的进程运行,一般不会把客户机搞死。 15 | 还可以测试HTTPS类的网站请求。 16 | 下载地址:[http_load-12mar2006.tar.gz](http://acme.com/software/http_load/http_load-12mar2006.tar.gz) 17 | 18 | > 安装很简单 19 | 20 | ```sh 21 | tar zxvf http_load-12mar2006.tar.gz 22 | cd http_load-12mar2006 23 | make && make install 24 | ``` 25 | 26 | > 基本用法: 27 | 28 | ```sh 29 | http_load -p 并发访问进程数 -s 访问时间 需要访问的URL文件 30 | ``` 31 | 32 | 参数其实可以自由组合,参数之间的选择并没有什么限制。 33 | 比如你写成 `http_load -parallel 5 -seconds 300 urllist.txt` 也是可以的。 34 | 我们把参数给大家简单说明一下。 35 | `-parallel` 简写-p :含义是并发的用户进程数。 36 | `-fetches` 简写-f :含义是总计的访问次数 37 | `-rate` 简写-p :含义是每秒的访问频率 38 | `-seconds` 简写-s :含义是总计的访问时间 39 | 40 | 准备URL文件:`urllist.txt`,文件格式是每行一个URL,URL最好超过50-100个测试效果比较好。 41 | 42 | > 文件格式如下: 43 | 44 | ```sh 45 | http://www.domain.com/ 46 | http://www.domain.com/blog/ 47 | http://www.domain.com/signin/ 48 | http://www.domain.com/signup/ 49 | http://www.domain.com/article/1.html 50 | http://www.domain.com/article/2.html 51 | http://www.domain.com/article/3.html 52 | http://www.domain.com/article/4.html 53 | http://www.domain.com/article/5.html 54 | http://www.domain.com/article/6.html 55 | http://www.domain.com/article/7.html 56 | ``` 57 | 58 | > 例如: 59 | 60 | ```sh 61 | http_load -p 30 -s 60 urllist.txt 62 | ``` 63 | 64 | 参数了解了,我们来看运行一条命令来看看它的返回结果如下: 65 | 66 | ![](http://i.imgur.com/ZT37WJg.png) 67 | 68 | > 结果分析: 69 | 70 | 1. 294 fetches, 30 max parallel, 3.83835e+06 bytes, in 60.0026 seconds 71 | 说明在上面的测试中运行了294个请求,最大的并发进程数是30,总计传输的数据是3.83835e+06bytes,运行的时间是60.0026秒 72 | 2. 13055.6 mean bytes/connection 73 | 说明每一连接平均传输的数据量3.83835e+06/294=13055.6 74 | 3. 4.89979 fetches/sec, 63969.7 bytes/sec 75 | 说明每秒的响应请求为4.89979,每秒传递的数据为63969.7 bytes/sec 76 | 4. msecs/connect: 312.009 mean, 1319.57 max, 209.994 min 77 | 说明每连接的平均响应时间是312.009 msecs,最大的响应时间1319.57 msecs,最小的响应时间209.994 msecs 78 | 5. msecs/first-response: 1191.01 mean, 10212.4 max, 220.78 min 79 | 6. HTTP response codes: 80 | code 200 -- 127 81 | code 502 -- 166 82 | 说明打开响应页面的类型 83 | 如果403的类型过多,那可能要注意是否系统遇到了瓶颈。 84 | 85 | **特殊说明:** 86 | 87 | 测试结果中主要的指标是 `fetches/sec`、`msecs/connect` 这个选项,即服务器每秒能够响应的查询次数。 88 | 用这个指标来衡量性能。似乎比apache的ab准确率要高一些,也更有说服力一些。 89 | `Qpt`-每秒响应用户数和response time,每连接响应用户时间。 90 | 测试的结果主要也是看这两个值。 91 | 当然仅有这两个指标并不能完成对性能的分析,我们还需要对服务器的`cpu`、`men`进行分析,才能得出结论。 92 | 93 | ### webbench 94 | 95 | webbench是Linux下的一个网站压力测试工具,最多可以模拟3万个并发连接去测试网站的负载能力。 96 | 下载地址可以到google搜,我这里给出一个 97 | 下载地址:[http://soft.vpser.net/test/webbench/webbench-1.5.tar.gz](http://soft.vpser.net/test/webbench/webbench-1.5.tar.gz) 98 | 这个程序更小,解压后不到50K,呵呵 99 | 安装非常简单 100 | 101 | ```sh 102 | tar zxvf webbench-1.5.tar.gz 103 | cd webbench-1.5 104 | make && make install 105 | ``` 106 | 107 | 会在当前目录生成webbench可执行文件,直接可以使用了 108 | 用法:webbench -c 并发数 -t 运行测试时间 URL 109 | 110 | > 例如: 111 | 112 | ```sh 113 | webbench -c 1000 -t 130 http://www.baidu.com 114 | ``` 115 | 116 | ### ab 117 | 118 | ab是apache自带的一款功能强大的测试工具。 119 | 安装了apache一般就自带了。 120 | 用法可以查看它的说明 121 | 122 | ```sh 123 | ./ab 124 | ``` 125 | 126 | ![](http://i.imgur.com/Zgtcmug.png) 127 | 128 | 参数众多,一般我们用到的是 `-n` 和 `-c` 129 | 130 | 例如: 131 | 132 | ```sh 133 | webbench -c 1000 -t 130 http://www.baidu.com/index.php 134 | ``` 135 | 136 | 这个表示同时处理1000个请求并运行130次index.php文件。 137 | 138 | ### siege 139 | 140 | 一款开源的压力测试工具,可以根据配置对一个WEB站点进行多用户的并发访问,记录每个用户所有请求过程的相应时间,并在一定数量的并发访问下重复进行。 141 | Siege官方:[http://www.joedog.org/](http://www.joedog.org/) 142 | Siege下载:[http://www.joedog.org/pub/siege/siege-latest.tar.gz](http://www.joedog.org/pub/siege/siege-latest.tar.gz) 143 | Siege解压并安装: 144 | 145 | ```sh 146 | tar -zxvf siege-latest.tar.gz 147 | cd siege-latest/ 148 | ./configure 149 | make 150 | make install 151 | ``` 152 | 153 | > Siege使用: 154 | 155 | ```sh 156 | siege -c 100 -r 10 -f site.url 157 | ``` 158 | 159 | -c是并发量,-r是重复次数。 160 | url文件就是一个文本,每行都是一个url,它会从里面随机访问的。 161 | site.url内容: 162 | 163 | ```sh 164 | http://www.qixing318.com/ 165 | http://www.zendsns.com/ 166 | http://www.qixing.info/ 167 | ``` 168 | 169 | 测试结果: 170 | 171 | ![](http://i.imgur.com/eVMBVRe.png) 172 | 173 | 结果说明: 174 | 175 | ```sh 176 | Transactions: 550 hits //完成550次处理 177 | Availability: 55.00 % //55.00 % 成功率 178 | Elapsed time: 31.32 secs //总共用时 179 | Data transferred: 1.15 MB //共数据传输1.15 MB 180 | Response time: 3.04 secs //显示网络连接的速度 181 | Transaction rate: 17.56 trans/sec //均每秒完成 17.56 次处理:表示服务器后 182 | Throughput: 0.04 MB/sec //平均每秒传送数据 183 | Concurrency: 53.44 //实际最高并发数 184 | Successful transactions: 433 //成功处理次数 185 | Failed transactions: 450 //失败处理次数 186 | Longest transaction: 15.50 //每次传输所花最长时间 187 | Shortest transaction: 0.42 //每次传输所花最短时间 188 | ``` -------------------------------------------------------------------------------- /实用工具.md: -------------------------------------------------------------------------------- 1 | # 实用工具/API 2 | 3 | -------------------------------------------------------------------------------- /开发者指南.md: -------------------------------------------------------------------------------- 1 | # 开发者指南 2 | 3 | -------------------------------------------------------------------------------- /开源组件实现.md: -------------------------------------------------------------------------------- 1 | # 开源组件实现 2 | 3 | -------------------------------------------------------------------------------- /服务器相关.md: -------------------------------------------------------------------------------- 1 | # 服务器/域名/SSL证书 2 | 3 | -------------------------------------------------------------------------------- /经典文章.md: -------------------------------------------------------------------------------- 1 | # 经典文章 2 | 3 | -------------------------------------------------------------------------------- /设计模式系列.md: -------------------------------------------------------------------------------- 1 | # 设计模式系列 2 | 3 | -------------------------------------------------------------------------------- /运维相关.md: -------------------------------------------------------------------------------- 1 | # 运维相关 2 | 3 | --------------------------------------------------------------------------------