This commit is contained in:
wentch
2016-01-20 14:51:16 +08:00
parent 089a57421f
commit 3e2f531753

111
net.html
View File

@@ -25,7 +25,7 @@
<section class="main-content">
<h3><a id="net_intro" class="anchor" href="#" aria-hidden="true"><span class="octicon octicon-link"></span></a>Net 组件介绍</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Net 组件是基于AIO(NIO.2)的一套TCP/UDP的网络框架且只提供异步接口。org.redkale.net 是所有网络协议服务的基础包。RedKale内置HTTP和远程模式Service依赖的SNCP(Service Node Communicate Protocol)协议的实现包。RedKale启动的<b>&lt;server&gt;</b>节点服务都是基于Net组件实现的协议服务</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Net 组件是基于AIO(NIO.2)的一套TCP/UDP的网络框架且只提供异步接口。org.redkale.net 是所有网络协议服务的基础包。RedKale内置HTTP和远程模式Service依赖的SNCP(Service Node Communicate Protocol)协议的实现包。RedKale启动的<b>&lt;server&gt;</b>节点服务都是基于Net组件实现的协议。下面详细介绍 <a href="#net_http">HTTP服务</a><a href="#net_sncp">SNCP协议</a></p>
<h3><a id="net_http" class="anchor" href="#" aria-hidden="true"><span class="octicon octicon-link"></span></a>HTTP 服务</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RedKale自实现的HTTP服务接口并不遵循Java EE规范JSR 340(Servlet 3.1)RedKale提倡的是HTTP+JSON的服务接口方式因此没有实现JSP规范HTTP+JSON服务接口几乎适合所有类型的客户端(PC应用程序、PC Web、微信H5、移动APP、移动Web)开发。其与JSR 340(Servlet 3.1)的主要区别如下:<br/>
@@ -38,7 +38,109 @@
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7、内置WebSocket的集群与组功能且提供伪WebSocket连接功能。<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8、HttpResponse只能异步输出。<br/>
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpRequest 方法如下: </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编写RedKale的HttpServlet与 JSR 340中的javax.servlet.http.HttpServlet 基本相同,只需继承 org.redkale.net.http.HttpServlet, Redkale也提供了更友好的基类 org.redkale.net.http.BasedHttpServlet, 比较好的习惯是一个项目先定义一个项目级的BaseServlet类这样方便以后加入类似javax.servlet.Filter的功能。 <br/> <br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一个典型的BaseSerlvet实现:
</p>
<div class="highlight"><pre><span class="kd">public</span> <span class="kd">class</span> <span class="nc">BaseSerlvet</span> <span class="kd">extends</span> <span class="n">org</span><span class="o">.</span><span class="na">redkale</span><span class="o">.</span><span class="na">net</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">BasedHttpServlet</span> <span class="o">{</span>
<span class="kd">protected</span> <span class="kd">final</span> <span class="n">Logger</span> <span class="n">logger</span> <span class="o">=</span> <span class="n">Logger</span><span class="o">.</span><span class="na">getLogger</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">getClass</span><span class="o">().</span><span class="na">getSimpleName</span><span class="o">());</span>
<span class="kd">protected</span> <span class="kd">final</span> <span class="kt">boolean</span> <span class="n">fine</span> <span class="o">=</span> <span class="n">logger</span><span class="o">.</span><span class="na">isLoggable</span><span class="o">(</span><span class="n">Level</span><span class="o">.</span><span class="na">FINE</span><span class="o">);</span>
<span class="nd">@Resource</span> <span class="c1">//[RedKale内置资源] 进程的启动时间</span>
<span class="kd">protected</span> <span class="kt">long</span> <span class="n">serverCreateTime</span><span class="o">;</span>
<span class="nd">@Resource</span> <span class="c1">//[RedKale内置资源]</span>
<span class="kd">protected</span> <span class="n">JsonConvert</span> <span class="n">jsonConvert</span><span class="o">;</span>
<span class="nd">@Resource</span> <span class="c1">//[RedKale内置资源]</span>
<span class="kd">protected</span> <span class="n">JsonFactory</span> <span class="n">jsonFactory</span><span class="o">;</span>
<span class="c1">//[RedKale内置资源], 当前进程的根目录,字段类型可以是 String、java.io.File、java.nio.file.Path</span>
<span class="nd">@Resource</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">&quot;APP_HOME&quot;</span><span class="o">)</span>
<span class="kd">protected</span> <span class="n">File</span> <span class="n">home</span><span class="o">;</span>
<span class="c1">//[RedKale内置资源], 当前Http Server的web页面的根目录字段类型可以是 String、java.io.File、java.nio.file.Path</span>
<span class="nd">@Resource</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">&quot;SERVER_ROOT&quot;</span><span class="o">)</span>
<span class="kd">protected</span> <span class="n">File</span> <span class="n">webroot</span><span class="o">;</span>
<span class="nd">@Resource</span>
<span class="kd">private</span> <span class="n">UserService</span> <span class="n">service</span><span class="o">;</span>
<span class="c1">//在调用authenticate之前调用 返回false表示请求不合法</span>
<span class="c1">//该方法可以用于判断请求源是否合法或加入一些全局的拦截操作</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">preExecute</span><span class="o">(</span><span class="kd">final</span> <span class="n">HttpRequest</span> <span class="n">request</span><span class="o">,</span> <span class="kd">final</span> <span class="n">HttpResponse</span> <span class="n">response</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">request</span><span class="o">.</span><span class="na">getHeader</span><span class="o">(</span><span class="s">&quot;User-Agent&quot;</span><span class="o">,</span> <span class="s">&quot;&quot;</span><span class="o">).</span><span class="na">contains</span><span class="o">(</span><span class="s">&quot;RedKale-Agent&quot;</span><span class="o">))</span> <span class="o">{</span> <span class="c1">//只用移动APP的接口可以判断User-Agent是否正确</span>
<span class="n">response</span><span class="o">.</span><span class="na">addHeader</span><span class="o">(</span><span class="s">&quot;retcode&quot;</span><span class="o">,</span> <span class="s">&quot;10001&quot;</span><span class="o">);</span>
<span class="n">response</span><span class="o">.</span><span class="na">addHeader</span><span class="o">(</span><span class="s">&quot;retmessage&quot;</span><span class="o">,</span> <span class="s">&quot;User-Agent error&quot;</span><span class="o">);</span>
<span class="n">response</span><span class="o">.</span><span class="na">setStatus</span><span class="o">(</span><span class="mi">201</span><span class="o">);</span>
<span class="n">response</span><span class="o">.</span><span class="na">finish</span><span class="o">(</span><span class="s">&quot;{&#39;success&#39;:false, &#39;message&#39;:&#39;User-Agent error, must be RedKale-Agent&#39;}&quot;</span><span class="o">);</span>
<span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
<span class="o">}</span>
<span class="c1">//可以加上一些统计操作</span>
<span class="k">if</span> <span class="o">(</span><span class="n">fine</span><span class="o">)</span> <span class="n">response</span><span class="o">.</span><span class="na">setRecycleListener</span><span class="o">((</span><span class="n">req</span><span class="o">,</span> <span class="n">resp</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="c1">//记录处理时间太长的请求操作</span>
<span class="kt">long</span> <span class="n">e</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">()</span> <span class="o">-</span> <span class="n">request</span><span class="o">.</span><span class="na">getCreatetime</span><span class="o">();</span>
<span class="k">if</span> <span class="o">(</span><span class="n">e</span> <span class="o">&gt;</span> <span class="mi">500</span><span class="o">)</span> <span class="n">logger</span><span class="o">.</span><span class="na">fine</span><span class="o">(</span><span class="s">&quot;耗时居然用了 &quot;</span> <span class="o">+</span> <span class="n">e</span> <span class="o">+</span> <span class="s">&quot; 毫秒. 请求为: &quot;</span> <span class="o">+</span> <span class="n">request</span><span class="o">);</span>
<span class="o">});</span>
<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
<span class="c1">//方法标记为@AuthIgnore 的将不会调用authenticate方法</span>
<span class="c1">//一般用于判断用户的登录态, 返回false表示鉴权失败</span>
<span class="c1">//moduleid值来自 @WebServlet.moduleid() 用于定义模块ID; actionid值自来@WebAction.actionid() 用于定义操作ID; 需要系统化的鉴权需要定义这两个值</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">authenticate</span><span class="o">(</span><span class="kt">int</span> <span class="n">moduleid</span><span class="o">,</span> <span class="kt">int</span> <span class="n">actionid</span><span class="o">,</span> <span class="n">HttpRequest</span> <span class="n">request</span><span class="o">,</span> <span class="n">HttpResponse</span> <span class="n">response</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
<span class="n">UserInfo</span> <span class="n">user</span> <span class="o">=</span> <span class="o">(</span><span class="n">UserInfo</span><span class="o">)</span> <span class="n">request</span><span class="o">.</span><span class="na">getAttribute</span><span class="o">(</span><span class="s">&quot;_current_userinfo&quot;</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">user</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="k">return</span> <span class="kc">true</span><span class="o">;</span> <span class="c1">//已经判断过了</span>
<span class="n">String</span> <span class="n">sessionid</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="na">getSessionid</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">sessionid</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="k">return</span> <span class="kc">false</span><span class="o">;</span> <span class="c1">//没有sessionid表示没有登录</span>
<span class="n">user</span> <span class="o">=</span> <span class="n">service</span><span class="o">.</span><span class="na">current</span><span class="o">(</span><span class="n">sessionid</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">user</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="n">request</span><span class="o">.</span><span class="na">setAttribute</span><span class="o">(</span><span class="s">&quot;_current_userinfo&quot;</span><span class="o">,</span> <span class="n">user</span><span class="o">);</span>
<span class="k">return</span> <span class="n">user</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">;</span> <span class="c1">//存在用户表示登录态正常</span>
<span class="o">}</span>
<span class="o">}</span></pre></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;继承BasedHttpServlet的子类可以使用其自带的鉴权、请求分支、缓存等功能 一个典型的操作用户的HttpServlet:
</p>
<div class="highlight"><pre><span class="nd">@WebServlet</span><span class="o">({</span><span class="s">&quot;/user/*&quot;</span><span class="o">})</span> <span class="c1">//拦截所有 /user/ 开头的请求</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">UserServlet</span> <span class="kd">extends</span> <span class="n">BaseSerlvet</span> <span class="o">{</span>
<span class="nd">@Resource</span>
<span class="kd">private</span> <span class="n">UserService</span> <span class="n">service</span><span class="o">;</span>
<span class="c1">//登录操作 </span>
<span class="nd">@AuthIgnore</span> <span class="c1">//登录操作不要判断登录态,所以需要标记为@AuthIgnore不会调用 BaseSerlvet.authenticate 方法</span>
<span class="c1">//因为WebAction的判断规则用的是String.startsWith所以WebAction.url不能用正则表达式只能是getRequestURI的前缀</span>
<span class="c1">//且同一个HttpServlet类内的所有WebAction不能存在包含关系, 如 /user/myinfo 和 /user/myinforecord 不能存在同一HttpServlet中。</span>
<span class="nd">@WebAction</span><span class="o">(</span><span class="n">url</span> <span class="o">=</span> <span class="s">&quot;/user/login&quot;</span><span class="o">)</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">login</span><span class="o">(</span><span class="n">HttpRequest</span> <span class="n">req</span><span class="o">,</span> <span class="n">HttpResponse</span> <span class="n">resp</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
<span class="n">LoginBean</span> <span class="n">bean</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="na">getJsonParameter</span><span class="o">(</span><span class="n">LoginBean</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="s">&quot;bean&quot;</span><span class="o">);</span> <span class="c1">//获取参数</span>
<span class="n">RetResult</span><span class="o">&lt;</span><span class="n">UserInfo</span><span class="o">&gt;</span> <span class="n">result</span> <span class="o">=</span> <span class="n">service</span><span class="o">.</span><span class="na">login</span><span class="o">(</span><span class="n">bean</span><span class="o">);</span> <span class="c1">//登录操作, service内部判断bean的合法性</span>
<span class="n">resp</span><span class="o">.</span><span class="na">finishJson</span><span class="o">(</span><span class="n">result</span><span class="o">);</span> <span class="c1">//输出结果</span>
<span class="o">}</span>
<span class="c1">//获取当前用户信息</span>
<span class="c1">//未登录的请求会被BaseSerlvet.authenticate方法拦截因此能进入该方法说明用户态存在</span>
<span class="nd">@WebAction</span><span class="o">(</span><span class="n">url</span> <span class="o">=</span> <span class="s">&quot;/user/myinfo&quot;</span><span class="o">)</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">myinfo</span><span class="o">(</span><span class="n">HttpRequest</span> <span class="n">req</span><span class="o">,</span> <span class="n">HttpResponse</span> <span class="n">resp</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
<span class="n">UserInfo</span> <span class="n">user</span> <span class="o">=</span> <span class="n">service</span><span class="o">.</span><span class="na">current</span><span class="o">(</span><span class="n">req</span><span class="o">.</span><span class="na">getSessionid</span><span class="o">(</span><span class="kc">false</span><span class="o">));</span>
<span class="c1">//或者使用 UserInfo user = req.getAttribute(&quot;_current_userinfo&quot;); 因为BaseSerlvet.authenticate方法已经将UserInfo注入到_current_userinfo属性中</span>
<span class="n">resp</span><span class="o">.</span><span class="na">finishJson</span><span class="o">(</span><span class="n">user</span><span class="o">);</span> <span class="c1">//输出用户信息</span>
<span class="o">}</span>
<span class="c1">//获取指定用户ID的用户信息, 请求如: /user/username/43565443</span>
<span class="nd">@AuthIgnore</span>
<span class="c1">// 默认缓存时间是15秒BasedHttpServlet会将每个进入该方法的请求的响应结果缓存15秒缓存命中时不会再进入该方法过期会清空。</span>
<span class="c1">// @HttpCacheable 必须配合 @AuthIgnore 使用, 因为跟当前用户有关的请求一般不适合所有用户请求。</span>
<span class="nd">@HttpCacheable</span><span class="o">(</span><span class="n">timeout</span> <span class="o">=</span> <span class="mi">30</span><span class="o">)</span> <span class="c1">//有效期30秒</span>
<span class="nd">@WebAction</span><span class="o">(</span><span class="n">url</span> <span class="o">=</span> <span class="s">&quot;/user/userinfo/&quot;</span><span class="o">)</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">userinfo</span><span class="o">(</span><span class="n">HttpRequest</span> <span class="n">req</span><span class="o">,</span> <span class="n">HttpResponse</span> <span class="n">resp</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
<span class="n">UserInfo</span> <span class="n">user</span> <span class="o">=</span> <span class="n">service</span><span class="o">.</span><span class="na">findUserInfo</span><span class="o">(</span><span class="n">Integer</span><span class="o">.</span><span class="na">parseInt</span><span class="o">(</span><span class="n">req</span><span class="o">.</span><span class="na">getRequstURILastPath</span><span class="o">()));</span>
<span class="n">resp</span><span class="o">.</span><span class="na">finishJson</span><span class="o">(</span><span class="n">user</span><span class="o">);</span> <span class="c1">//输出用户信息</span>
<span class="o">}</span>
<span class="o">}</span></pre></div>
<p id="net_httprequest">&nbsp;&nbsp;<b>. HttpRequest 对象</b> </p>
<div class="highlight"><pre><span class="kd">public</span> <span class="kd">class</span> <span class="nc">HttpRequest</span> <span class="o">{</span>
<span class="c1">//获取请求方法 GET、POST等</span>
@@ -212,7 +314,7 @@
<span class="o">}</span></pre></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpResponse 方法如下: </p>
<p id="net_httpresponse">&nbsp;&nbsp;<b>. HttpResponse 对象</b> </p>
<div class="highlight"><pre><span class="kd">public</span> <span class="kd">class</span> <span class="nc">HttpResponse</span> <span class="o">{</span>
<span class="c1">//设置状态码</span>
@@ -300,7 +402,8 @@
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">finish</span><span class="o">(</span><span class="n">File</span> <span class="n">file</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span><span class="o">;</span>
<span class="o">}</span></pre></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WebSocket 方法如下: </p>
<p id="net_websocket">&nbsp;&nbsp;<b>. WebSocket 对象</b> </p>
<div class="highlight"><pre><span class="kd">public</span> <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">WebSocket</span> <span class="o">{</span>
<span class="c1">//发送消息体, 包含二进制/文本 返回结果0表示成功非0表示错误码</span>