下面是我直播的文字版,直播地址: https://segmentfault.com/l/15...
代碼: https://github.com/zhoumengka...
整個項目咱們我又細分了6個版原本演進,但願更加便於你們對比學習。
我在收集你們是否願意學習 java 的時候,獲得了以下反饋:php
java == 太複雜 java == 各類亂七八糟的配置 java == 面向 ide 編程
其實咱們在學習的時候 java 的時候徹底不用接觸那些高大上的工具,也能夠儘可能減小各類配置文件,好比下面咱們只有個pom.xml
配置文件。
還有的同窗說還學 java 幹嗎,不都應該去學 go 嗎?其實語言真的不重要,咱們須要掌握的是快速學習利用一門語言的學習方法,這也是本課的真正目的。html
就像盲人摸象同樣,他要想弄清楚大象的真實面貌可能要摸好久,就比如咱們拿着放大鏡在學習 java 同樣,java 通過這麼多年的發展,能夠說很是龐大。若是咱們要知道大象長什麼樣,就應該放下手中的放大鏡,向後退遠點,反而可以很是清晰的看到它的全貌。學習一門新的語言也同樣,有不少不少網上的教程,很是的大而全,通常得系統的學習30~60小時以後才能正式的接觸項目開發。基礎很重要,可是學習了太多的基礎會讓你們失去學習的樂趣和自信心。不少知識點其實能夠項目以後再補。按需去學,反而是自我驅動着去學習的最佳方式。
前端
好比 hashmap 的哈希分佈、哈希碰撞、動態擴容,這些都是咱們後期深刻提升須要理解的內容,初期,咱們只須要知道能拿 hashmap 作什麼就行。java
好比咱們作 Web 後端 api 開發,首先是經常使用的循環/迭代、條件判斷、增刪改爲。那麼能不能快速用 java 實現一遍這些咱們用 php 作起來很是順手的事呢?
這樣有助於咱們快速提高自信心。mysql
PHP 裏如何實現,從新用 java 實現一遍就好了。nginx
當本身實現了一些小 demo 再去參考別人的項目。若是一開始就直接看別人的項目,可能徹底不知作別人在幹嗎。好比別人用了ConcurrentHashMap
,就再去思考爲何我用HashMap
他卻用ConcurrentHashMap
,帶着問題,帶着思考去看開源代碼。git
完成了一些簡單的項目了以後就能夠再回過頭來系統的學習了。這時候就會有不同的收穫。github
最後就是當項目須要調優,性能提高的時候,再各個擊破,深刻學習,更有針對性,更有目標性。web
咱們用 netty 來提供高性能的 web 服務服務。使用簡單方便(netty 並不簡單),不依賴其餘軟件。而後思考完成一個簡單的 web api 服務器須要哪些必不可少的組成部分。(其實在思考這的時候,你必需要要對作簡單的架構必須熟記於心)。redis
我簡單歸納了下:
HashSet 是一個沒有重複元素的集合。它是由HashMap實現的,不保證元素的順序,也就是說所說元素插入的順序與輸出的順序不一致。這實際上是個人老朋友了,redis 裏常常用,好比我們能夠它來實現一個黑名單,這樣查找的速度就很是快,也不用去遠程查詢 redis 了,直接在當前內存中查詢。
ArrayList 基於數組來實現集合的功能,其內部維護了一個可變長的對象數組,集合內全部對象存儲於這個數組中,並實現該數組長度的動態伸縮。
這不就是咱們的 PHP 裏面經常使用的索引數組麼?
HashMap 以哈希表數據結構實現,查找對象時經過哈希函數計算其位置,它是爲快速查詢而設計的。特色就是快,非線程安全。
這不就是咱們的 PHP 裏面經常使用的關聯數組麼?
http://www.cnblogs.com/ITtang...
http://www.jianshu.com/p/b54f...
http://www.cnblogs.com/xiaoxi...
Maven的基本原理很簡單,採用遠程倉庫和本地倉庫以及一個核心的配置文件pom.xml,pom.xml中定義的jar文件從遠程倉庫下載到本地倉庫,各個項目使用同一個本地倉庫的jar,同一個版本的jar只需下載一次,並且避免每一個應用都去拷貝jar。
這和 php 的包管理工具 composer 很像,或者是 composer 是參考着 maven 而設計的。maven 的功能更強大,composer 須要每一個項目都要導入一遍,maven 卻像 git 同樣,有一個本地倉庫,第三方包也不會直接引用到項目中,而是在編譯的時候纔會引入(是否是很方便)。另外一方面,maven 不只僅是包管理工具,並且是一個項目管理工具,集成了編譯、打包、單元測試等功能。
下面是最簡單的一個演示,依賴了 netty 、junit 兩個包。而後使用maven-compiler-plugin
指定了編譯時候的版本規則。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>mengkang.net</groupId> <artifactId>demo</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>5.0.0.Alpha2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.7</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
我複製了 netty 官方的 demo 地址以下:
https://github.com/zhoumengkang/netty-http-demo/tree/v1.0
當咱們運行api.mengkang.net.netty.HttpServer.main
方法,服務器就跑起來了,當在瀏覽器裏訪問 http://localhost:9009/ 就會返回Hello World
。
方法 | 用途 |
---|---|
api.mengkang.net.netty.HttpServer#main | 服務器啓動的入口 |
api.mengkang.net.netty.HttpServerInitializer#initChannel | 初始化 Channel |
api.mengkang.net.netty.HttpServerHandler#channelRead | 進行網絡 I/O |
這是第一步,netty 這裏就充當了一個 web server 的角色。而咱們就能夠直接在 netty 提供的接口的基礎上作編程,而不須要想 nginx + php-fpm 還須要一次反向代理,性能高了許多。(swoole 的方式就很像 netty 了)。
具體需求:提供一個 api 能夠用戶指定用戶的信息
定義接口:
http://localhost:10000/users/{id} http://localhost:10000/?method=user.get&id={id}
可能如今你們早已習慣了前者 restful 的 api 接口。
由於這裏須要一次路由的映射和 http method 的匹配,考慮到學習的成本呢,我沒有選擇這種方式。
咱們今天的目標是以最簡單有效的方式實現咱們的功能。
咱們首先從最簡單的方式來實現(其實沒有路由的 api 反而是最快的,畢竟須要作的判斷少嘛)。
後面你們有興趣能夠參考我寫的一個 restful api 的 demo netty-restful-server
具體代碼
https://github.com/zhoumengkang/netty-http-demo/tree/v2.0
這一版本中作一個過渡版本,暫時控制器還不解析過多的參數。只完成一個$_GET['method']
參數的解析。
主要的任務是經過獲取的$_GET['method']
去執行UserController
裏面的get
方法。
方法 | 用途 |
---|---|
api.mengkang.net.RequestHandler#response | 從 HttpServerHandler 處接管網絡請求 |
api.mengkang.net.RequestHandler#invoke | 執行反射調用 |
api.mengkang.net.api.UserController#get | 模擬輸出一個用戶的信息 |
Class<?> classname; Method methodName; Object result = null; classname = Class.forName("api.mengkang.net.api." + clazz + "Controller"); Object inst = classname.newInstance(); methodName = classname.getMethod(function); result = methodName.invoke(inst);
具體代碼
https://github.com/zhoumengkang/netty-http-demo/tree/v3.0
方法 | 用途 |
---|---|
api.mengkang.net.Request | 封裝一個通用 api 請求對象,包含客戶端請求的$_GET,$_POST,ip 等 |
api.mengkang.net.RequestHandler#requestFetch | 把請求解析成 api.mengkang.net.Request 對象 |
api.mengkang.net.RequestHandler#invoke | 把 api.mengkang.net.Request 傳遞給 Controller |
反射實例化對象使用了構造函數 ,這樣就把請求的對象Request
實例傳到 Controller 中去了。Controller 中的方法就能取到$_GET
,$_POST
,以及相似 php://input
的數據了。
Class<?> classname; Object classObject; Constructor constructor; Method methodName; Object result = null; classname = Class.forName("api.mengkang.net.api." + clazz + "Controller"); constructor = classname.getConstructor(Request.class); classObject = constructor.newInstance(request); methodName = classname.getMethod(function); result = methodName.invoke(classObject);
具體代碼
https://github.com/zhoumengkang/netty-http-demo/tree/v3.1
類 | 用途 |
---|---|
api.mengkang.net.Response | 封裝一個通用 api 響應對象 |
api.mengkang.net.ErrorCode | 錯誤代碼統一規範起來 |
api.mengkang.net.netty.HttpServerHandler | http 頭信息 改成 json |
這樣就更像一個正規的 api 服務了。
增長 User 對象, 增長 UserModel 來處理 User 對象的返回, 完善了錯誤返回機制.
類 | 用途 |
---|---|
api.mengkang.net.entity.User | 描述用戶對象,用於user.get 接口的數據返回 |
api.mengkang.net.model.UserModel | 供UserController 調用,簡單分層 |
api.mengkang.net.ErrorCode | 完善了錯誤類型 |
api.mengkang.net.api.UserController | 完善了錯誤類型的判斷,返回給前端錯誤更友好 |
類 | 用途 |
---|---|
api/mengkang/net/utils/mysql | 新增本身封裝的簡單的數據鏈接池的操做工具 |
api.mengkang.net.dao.UserDao | 作數據庫鏈接的查詢,返回給UserModel |
中間引入三個包,來作數據庫的查詢和數據庫的鏈接池
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.18</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> <version>1.6</version> </dependency>
最後整個項目結構以下
├── main │ ├── java │ │ └── api │ │ └── mengkang │ │ └── net │ │ ├── Config.java │ │ ├── ErrorCode.java │ │ ├── Request.java │ │ ├── RequestHandler.java │ │ ├── Response.java │ │ ├── api │ │ │ ├── BaseController.java │ │ │ └── UserController.java │ │ ├── dao │ │ │ └── UserDao.java │ │ ├── entity │ │ │ └── User.java │ │ ├── model │ │ │ └── UserModel.java │ │ ├── netty │ │ │ ├── HttpServer.java │ │ │ ├── HttpServerHandler.java │ │ │ └── HttpServerInitializer.java │ │ └── utils │ │ └── mysql │ │ ├── DMLTypes.java │ │ ├── DbFiled.java │ │ ├── JdbcPool.java │ │ ├── MySelect.java │ │ └── Mysql.java │ └── resources │ ├── api.properties │ ├── read.db.properties │ └── write.db.properties