Jekyll2018-02-08T22:37:55+08:00https://cpp255.github.io/tarda的个人博客"cpp255消息推送方案之谈2018-02-09T04:32:10+08:002018-02-09T04:32:10+08:00https://cpp255.github.io/2018/02/08/messages-post<h2 id="消息推送方案之谈">消息推送方案之谈</h2>
<p> 由于是对接其它平台,接收第三方消息推送细节比较多,如果遇上做活动或者某个时刻并发上来,即使 tomcat 把所有的这些消息请求都放进来,框架内部也可能遇到持久层并发的限制。消息推送虽然也有重发确认机制,但代码总是人写的,一次完整的把所有流程没有问题的运行,还是有概率出现错误。这些问题集中起来,做到一个完整的健壮的系统,还是得想清楚,有一套完整的机制,从设计上减少错误的出现。</p>
<p> 收到第三方推送的消息后,通过一步消息推送到另一个地方,统一处理,增加高并发抗峰值的能力,同时,也可以使代码解耦,消息接收、处理是分开的。</p>
<p> <strong>推送方案图:</strong></p>
<p><img src="/images/posts/message_post.jpg" alt="" /></p>
<p> 消息处理的过程中,关键的点尽量把日志打出来,打印关键或者所有的变量都可以,推荐尽量重写 Object 的 toString() 方法,不要怕麻烦,到时候上线运行了,出错,日志能让你尽量知道错误现场那一刻的变量,找出bug,而不用猜测各种可能性。</p>
<p> 在异步消息的处理过程中,可以的话,考虑设计好错误处理机制,错误了该怎么处理:直接抛出异常、重试机制、通知第三方消息处理失败,多考虑一点,出现问题的概率小一点。</p>
<p> 总结了自己想到的和真实项目中遇到的,希望有用。</p>cpp255消息推送方案之谈 由于是对接其它平台,接收第三方消息推送细节比较多,如果遇上做活动或者某个时刻并发上来,即使 tomcat 把所有的这些消息请求都放进来,框架内部也可能遇到持久层并发的限制。消息推送虽然也有重发确认机制,但代码总是人写的,一次完整的把所有流程没有问题的运行,还是有概率出现错误。这些问题集中起来,做到一个完整的健壮的系统,还是得想清楚,有一套完整的机制,从设计上减少错误的出现。Spring Servlet 源码实现原理(1)2018-02-06T07:45:10+08:002018-02-06T07:45:10+08:00https://cpp255.github.io/2018/02/05/Spring-Servlet<h1 id="spring-servlet-源码实现原理1">Spring Servlet 源码实现原理(1)</h1>
<hr />
<p>Servlet: <br />
<img src="/images/posts/servlert.jpg" alt="" /></p>
<p><strong>DispatcherServlet</strong></p>
<p> 它们的 view 拆分策略通过实现 ViewResolver 进行指定,分解象征性的 view 的名字到 View 对象中。</p>
<p> 一个 web 应用可以定义不同的 DispatcherServlets,每一个 servlet 会被不同的属性命名空间操作,用 mappings、handlers等加载各自的 application context。一旦有任意一个 root application context 被 ContextLoaderListener 加载,都会被分享。</p>
<p> 在 Spring 3.1 中,DispatcherServlet 可能会被一个 web application context 注入,而不是在各自内部创建。servlet 实例支持程序加载,在 Servlet 3.0 的环境中将会非常有用。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">DispatcherServlet</span> <span class="kd">extends</span> <span class="n">FrameworkServlet</span> <span class="o">{</span>
<span class="cm">/**
* 处理实际的发送给 handler。handler 将从申请 servlet 的 HandlerMappings 中获得。HandlerAdapter将
* 通过查询 servlet 安装的 HandlerAdapters 去查找第一个支持这个 handler 的类,并从中获得。
* 所有的 HTTP 方法都会被这个方法处理。它将会提交给 HandlerAdapters 或者 handlers 去决定哪些方法
* 被接受。
*/</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">doDispatch</span><span class="o">(</span><span class="n">HttpServletRequest</span> <span class="n">request</span><span class="o">,</span> <span class="n">HttpServletResponse</span> <span class="n">response</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
<span class="n">HttpServletRequest</span> <span class="n">processedRequest</span> <span class="o">=</span> <span class="n">request</span><span class="o">;</span>
<span class="n">HandlerExecutionChain</span> <span class="n">mappedHandler</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="kt">boolean</span> <span class="n">multipartRequestParsed</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
<span class="n">WebAsyncManager</span> <span class="n">asyncManager</span> <span class="o">=</span> <span class="n">WebAsyncUtils</span><span class="o">.</span><span class="na">getAsyncManager</span><span class="o">(</span><span class="n">request</span><span class="o">);</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">ModelAndView</span> <span class="n">mv</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="n">Exception</span> <span class="n">dispatchException</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">processedRequest</span> <span class="o">=</span> <span class="n">checkMultipart</span><span class="o">(</span><span class="n">request</span><span class="o">);</span>
<span class="n">multipartRequestParsed</span> <span class="o">=</span> <span class="o">(</span><span class="n">processedRequest</span> <span class="o">!=</span> <span class="n">request</span><span class="o">);</span>
<span class="c1">// Determine handler for the current request.</span>
<span class="n">mappedHandler</span> <span class="o">=</span> <span class="n">getHandler</span><span class="o">(</span><span class="n">processedRequest</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">mappedHandler</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">mappedHandler</span><span class="o">.</span><span class="na">getHandler</span><span class="o">()</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">noHandlerFound</span><span class="o">(</span><span class="n">processedRequest</span><span class="o">,</span> <span class="n">response</span><span class="o">);</span>
<span class="k">return</span><span class="o">;</span>
<span class="o">}</span>
<span class="c1">// Determine handler adapter for the current request.</span>
<span class="n">HandlerAdapter</span> <span class="n">ha</span> <span class="o">=</span> <span class="n">getHandlerAdapter</span><span class="o">(</span><span class="n">mappedHandler</span><span class="o">.</span><span class="na">getHandler</span><span class="o">());</span>
<span class="c1">// Process last-modified header, if supported by the handler.</span>
<span class="n">String</span> <span class="n">method</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="na">getMethod</span><span class="o">();</span>
<span class="kt">boolean</span> <span class="n">isGet</span> <span class="o">=</span> <span class="s">"GET"</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">method</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">isGet</span> <span class="o">||</span> <span class="s">"HEAD"</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">method</span><span class="o">))</span> <span class="o">{</span>
<span class="kt">long</span> <span class="n">lastModified</span> <span class="o">=</span> <span class="n">ha</span><span class="o">.</span><span class="na">getLastModified</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">mappedHandler</span><span class="o">.</span><span class="na">getHandler</span><span class="o">());</span>
<span class="k">if</span> <span class="o">(</span><span class="n">logger</span><span class="o">.</span><span class="na">isDebugEnabled</span><span class="o">())</span> <span class="o">{</span>
<span class="n">logger</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">"Last-Modified value for ["</span> <span class="o">+</span> <span class="n">getRequestUri</span><span class="o">(</span><span class="n">request</span><span class="o">)</span> <span class="o">+</span> <span class="s">"] is: "</span> <span class="o">+</span> <span class="n">lastModified</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="k">new</span> <span class="n">ServletWebRequest</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">response</span><span class="o">).</span><span class="na">checkNotModified</span><span class="o">(</span><span class="n">lastModified</span><span class="o">)</span> <span class="o">&&</span> <span class="n">isGet</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">mappedHandler</span><span class="o">.</span><span class="na">applyPreHandle</span><span class="o">(</span><span class="n">processedRequest</span><span class="o">,</span> <span class="n">response</span><span class="o">))</span> <span class="o">{</span>
<span class="k">return</span><span class="o">;</span>
<span class="o">}</span>
<span class="c1">// 调用实际的 handler </span>
<span class="c1">// Actually invoke the handler.</span>
<span class="n">mv</span> <span class="o">=</span> <span class="n">ha</span><span class="o">.</span><span class="na">handle</span><span class="o">(</span><span class="n">processedRequest</span><span class="o">,</span> <span class="n">response</span><span class="o">,</span> <span class="n">mappedHandler</span><span class="o">.</span><span class="na">getHandler</span><span class="o">());</span>
<span class="k">if</span> <span class="o">(</span><span class="n">asyncManager</span><span class="o">.</span><span class="na">isConcurrentHandlingStarted</span><span class="o">())</span> <span class="o">{</span>
<span class="k">return</span><span class="o">;</span>
<span class="o">}</span>
<span class="n">applyDefaultViewName</span><span class="o">(</span><span class="n">processedRequest</span><span class="o">,</span> <span class="n">mv</span><span class="o">);</span>
<span class="n">mappedHandler</span><span class="o">.</span><span class="na">applyPostHandle</span><span class="o">(</span><span class="n">processedRequest</span><span class="o">,</span> <span class="n">response</span><span class="o">,</span> <span class="n">mv</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
<span class="n">dispatchException</span> <span class="o">=</span> <span class="n">ex</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">catch</span> <span class="o">(</span><span class="n">Throwable</span> <span class="n">err</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// As of 4.3, we're processing Errors thrown from handler methods as well,</span>
<span class="c1">// making them available for @ExceptionHandler methods and other scenarios.</span>
<span class="n">dispatchException</span> <span class="o">=</span> <span class="k">new</span> <span class="n">NestedServletException</span><span class="o">(</span><span class="s">"Handler dispatch failed"</span><span class="o">,</span> <span class="n">err</span><span class="o">);</span>
<span class="o">}</span>
<span class="n">processDispatchResult</span><span class="o">(</span><span class="n">processedRequest</span><span class="o">,</span> <span class="n">response</span><span class="o">,</span> <span class="n">mappedHandler</span><span class="o">,</span> <span class="n">mv</span><span class="o">,</span> <span class="n">dispatchException</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
<span class="n">triggerAfterCompletion</span><span class="o">(</span><span class="n">processedRequest</span><span class="o">,</span> <span class="n">response</span><span class="o">,</span> <span class="n">mappedHandler</span><span class="o">,</span> <span class="n">ex</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">catch</span> <span class="o">(</span><span class="n">Throwable</span> <span class="n">err</span><span class="o">)</span> <span class="o">{</span>
<span class="n">triggerAfterCompletion</span><span class="o">(</span><span class="n">processedRequest</span><span class="o">,</span> <span class="n">response</span><span class="o">,</span> <span class="n">mappedHandler</span><span class="o">,</span>
<span class="k">new</span> <span class="nf">NestedServletException</span><span class="o">(</span><span class="s">"Handler processing failed"</span><span class="o">,</span> <span class="n">err</span><span class="o">));</span>
<span class="o">}</span>
<span class="k">finally</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">asyncManager</span><span class="o">.</span><span class="na">isConcurrentHandlingStarted</span><span class="o">())</span> <span class="o">{</span>
<span class="c1">// Instead of postHandle and afterCompletion</span>
<span class="k">if</span> <span class="o">(</span><span class="n">mappedHandler</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">mappedHandler</span><span class="o">.</span><span class="na">applyAfterConcurrentHandlingStarted</span><span class="o">(</span><span class="n">processedRequest</span><span class="o">,</span> <span class="n">response</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">else</span> <span class="o">{</span>
<span class="c1">// Clean up any resources used by a multipart request.</span>
<span class="k">if</span> <span class="o">(</span><span class="n">multipartRequestParsed</span><span class="o">)</span> <span class="o">{</span>
<span class="n">cleanupMultipart</span><span class="o">(</span><span class="n">processedRequest</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="cm">/**
* 暴露了 DispatcherServlet 指定的请求属性,代理 doDispatch 给实际的分发处理。FrameworkServlet 未
* 实现的方法
*/</span>
<span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">doService</span><span class="o">(</span><span class="n">HttpServletRequest</span> <span class="n">request</span><span class="o">,</span> <span class="n">HttpServletResponse</span> <span class="n">response</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">logger</span><span class="o">.</span><span class="na">isDebugEnabled</span><span class="o">())</span> <span class="o">{</span>
<span class="n">String</span> <span class="n">resumed</span> <span class="o">=</span> <span class="n">WebAsyncUtils</span><span class="o">.</span><span class="na">getAsyncManager</span><span class="o">(</span><span class="n">request</span><span class="o">).</span><span class="na">hasConcurrentResult</span><span class="o">()</span> <span class="o">?</span> <span class="s">" resumed"</span> <span class="o">:</span> <span class="s">""</span><span class="o">;</span>
<span class="n">logger</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">"DispatcherServlet with name '"</span> <span class="o">+</span> <span class="n">getServletName</span><span class="o">()</span> <span class="o">+</span> <span class="s">"'"</span> <span class="o">+</span> <span class="n">resumed</span> <span class="o">+</span>
<span class="s">" processing "</span> <span class="o">+</span> <span class="n">request</span><span class="o">.</span><span class="na">getMethod</span><span class="o">()</span> <span class="o">+</span> <span class="s">" request for ["</span> <span class="o">+</span> <span class="n">getRequestUri</span><span class="o">(</span><span class="n">request</span><span class="o">)</span> <span class="o">+</span> <span class="s">"]"</span><span class="o">);</span>
<span class="o">}</span>
<span class="c1">// Keep a snapshot of the request attributes in case of an include,</span>
<span class="c1">// to be able to restore the original attributes after the include.</span>
<span class="n">Map</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">Object</span><span class="o">></span> <span class="n">attributesSnapshot</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="n">WebUtils</span><span class="o">.</span><span class="na">isIncludeRequest</span><span class="o">(</span><span class="n">request</span><span class="o">))</span> <span class="o">{</span>
<span class="n">attributesSnapshot</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HashMap</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">Object</span><span class="o">>();</span>
<span class="n">Enumeration</span><span class="o"><?></span> <span class="n">attrNames</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="na">getAttributeNames</span><span class="o">();</span>
<span class="k">while</span> <span class="o">(</span><span class="n">attrNames</span><span class="o">.</span><span class="na">hasMoreElements</span><span class="o">())</span> <span class="o">{</span>
<span class="n">String</span> <span class="n">attrName</span> <span class="o">=</span> <span class="o">(</span><span class="n">String</span><span class="o">)</span> <span class="n">attrNames</span><span class="o">.</span><span class="na">nextElement</span><span class="o">();</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">cleanupAfterInclude</span> <span class="o">||</span> <span class="n">attrName</span><span class="o">.</span><span class="na">startsWith</span><span class="o">(</span><span class="n">DEFAULT_STRATEGIES_PREFIX</span><span class="o">))</span> <span class="o">{</span>
<span class="n">attributesSnapshot</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">attrName</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="n">attrName</span><span class="o">));</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="c1">// 请求参数的封装初始化</span>
<span class="c1">// Make framework objects available to handlers and view objects.</span>
<span class="n">request</span><span class="o">.</span><span class="na">setAttribute</span><span class="o">(</span><span class="n">WEB_APPLICATION_CONTEXT_ATTRIBUTE</span><span class="o">,</span> <span class="n">getWebApplicationContext</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="n">LOCALE_RESOLVER_ATTRIBUTE</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">localeResolver</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="n">THEME_RESOLVER_ATTRIBUTE</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">themeResolver</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="n">THEME_SOURCE_ATTRIBUTE</span><span class="o">,</span> <span class="n">getThemeSource</span><span class="o">());</span>
<span class="n">FlashMap</span> <span class="n">inputFlashMap</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">flashMapManager</span><span class="o">.</span><span class="na">retrieveAndUpdate</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">response</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">inputFlashMap</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</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="n">INPUT_FLASH_MAP_ATTRIBUTE</span><span class="o">,</span> <span class="n">Collections</span><span class="o">.</span><span class="na">unmodifiableMap</span><span class="o">(</span><span class="n">inputFlashMap</span><span class="o">));</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="n">OUTPUT_FLASH_MAP_ATTRIBUTE</span><span class="o">,</span> <span class="k">new</span> <span class="n">FlashMap</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="n">FLASH_MAP_MANAGER_ATTRIBUTE</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">flashMapManager</span><span class="o">);</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">doDispatch</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">response</span><span class="o">);</span> <span class="c1">// 具体调用执行</span>
<span class="o">}</span>
<span class="k">finally</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">WebAsyncUtils</span><span class="o">.</span><span class="na">getAsyncManager</span><span class="o">(</span><span class="n">request</span><span class="o">).</span><span class="na">isConcurrentHandlingStarted</span><span class="o">())</span> <span class="o">{</span>
<span class="c1">// Restore the original attribute snapshot, in case of an include.</span>
<span class="k">if</span> <span class="o">(</span><span class="n">attributesSnapshot</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">restoreAttributesAfterInclude</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">attributesSnapshot</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="cm">/**
* 渲染给定的 ModelAndView,这个是请求中处理的最后一个策略。它可能会需要通过名字拆分.
**/</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">render</span><span class="o">(</span><span class="n">ModelAndView</span> <span class="n">mv</span><span class="o">,</span> <span class="n">HttpServletRequest</span> <span class="n">request</span><span class="o">,</span> <span class="n">HttpServletResponse</span> <span class="n">response</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
<span class="c1">// Determine locale for request and apply it to the response.</span>
<span class="n">Locale</span> <span class="n">locale</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">localeResolver</span><span class="o">.</span><span class="na">resolveLocale</span><span class="o">(</span><span class="n">request</span><span class="o">);</span>
<span class="n">response</span><span class="o">.</span><span class="na">setLocale</span><span class="o">(</span><span class="n">locale</span><span class="o">);</span>
<span class="n">View</span> <span class="n">view</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="n">mv</span><span class="o">.</span><span class="na">isReference</span><span class="o">())</span> <span class="o">{</span>
<span class="c1">// We need to resolve the view name.</span>
<span class="n">view</span> <span class="o">=</span> <span class="n">resolveViewName</span><span class="o">(</span><span class="n">mv</span><span class="o">.</span><span class="na">getViewName</span><span class="o">(),</span> <span class="n">mv</span><span class="o">.</span><span class="na">getModelInternal</span><span class="o">(),</span> <span class="n">locale</span><span class="o">,</span> <span class="n">request</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">view</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">ServletException</span><span class="o">(</span><span class="s">"Could not resolve view with name '"</span> <span class="o">+</span> <span class="n">mv</span><span class="o">.</span><span class="na">getViewName</span><span class="o">()</span> <span class="o">+</span>
<span class="s">"' in servlet with name '"</span> <span class="o">+</span> <span class="n">getServletName</span><span class="o">()</span> <span class="o">+</span> <span class="s">"'"</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">else</span> <span class="o">{</span>
<span class="c1">// No need to lookup: the ModelAndView object contains the actual View object.</span>
<span class="n">view</span> <span class="o">=</span> <span class="n">mv</span><span class="o">.</span><span class="na">getView</span><span class="o">();</span>
<span class="k">if</span> <span class="o">(</span><span class="n">view</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">ServletException</span><span class="o">(</span><span class="s">"ModelAndView ["</span> <span class="o">+</span> <span class="n">mv</span> <span class="o">+</span> <span class="s">"] neither contains a view name nor a "</span> <span class="o">+</span>
<span class="s">"View object in servlet with name '"</span> <span class="o">+</span> <span class="n">getServletName</span><span class="o">()</span> <span class="o">+</span> <span class="s">"'"</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="c1">// Delegate to the View object for rendering.</span>
<span class="k">if</span> <span class="o">(</span><span class="n">logger</span><span class="o">.</span><span class="na">isDebugEnabled</span><span class="o">())</span> <span class="o">{</span>
<span class="n">logger</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">"Rendering view ["</span> <span class="o">+</span> <span class="n">view</span> <span class="o">+</span> <span class="s">"] in DispatcherServlet with name '"</span> <span class="o">+</span> <span class="n">getServletName</span><span class="o">()</span> <span class="o">+</span> <span class="s">"'"</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">try</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">mv</span><span class="o">.</span><span class="na">getStatus</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</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="n">mv</span><span class="o">.</span><span class="na">getStatus</span><span class="o">().</span><span class="na">value</span><span class="o">());</span>
<span class="o">}</span>
<span class="n">view</span><span class="o">.</span><span class="na">render</span><span class="o">(</span><span class="n">mv</span><span class="o">.</span><span class="na">getModelInternal</span><span class="o">(),</span> <span class="n">request</span><span class="o">,</span> <span class="n">response</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">logger</span><span class="o">.</span><span class="na">isDebugEnabled</span><span class="o">())</span> <span class="o">{</span>
<span class="n">logger</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">"Error rendering view ["</span> <span class="o">+</span> <span class="n">view</span> <span class="o">+</span> <span class="s">"] in DispatcherServlet with name '"</span> <span class="o">+</span>
<span class="n">getServletName</span><span class="o">()</span> <span class="o">+</span> <span class="s">"'"</span><span class="o">,</span> <span class="n">ex</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">throw</span> <span class="n">ex</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>HttpServlet</strong></p>
<p> 给子类提供了一个抽象的类,为一个 Web 网站去生成 HTTP servlet 套件。一个抽象的 HttpServlet 必需重写最少一个方法,一般是以下这些。不用重写 service 方法。</p>
<p> Servlets 通常运行在多线程的servers。可以知道的是,一个 servlet 一定要处理 并且请求和注意访问共享资源的同步。共享资源包括内存中的数据,比如实例或者类变量和扩展对象,比如文件、数据库连接和网络连接。</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> * <ul>
* <li> <code>doGet</code>, if the servlet supports HTTP GET requests
* <li> <code>doPost</code>, for HTTP POST requests
* <li> <code>doPut</code>, for HTTP PUT requests
* <li> <code>doDelete</code>, for HTTP DELETE requests
* <li> <code>init</code> and <code>destroy</code>,
</code></pre></div></div>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">HttpServlet</span> <span class="kd">extends</span> <span class="n">GenericServlet</span> <span class="o">{</span>
<span class="c1">// 请求方法都只有这两个参数 HttpServletRequest, HttpServletResponse</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">doPost</span><span class="o">(</span><span class="n">HttpServletRequest</span> <span class="n">req</span><span class="o">,</span> <span class="n">HttpServletResponse</span> <span class="n">resp</span><span class="o">)</span>
<span class="kd">throws</span> <span class="n">ServletException</span><span class="o">,</span> <span class="n">IOException</span>
<span class="o">{</span>
<span class="n">String</span> <span class="n">protocol</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="na">getProtocol</span><span class="o">();</span>
<span class="n">String</span> <span class="n">msg</span> <span class="o">=</span> <span class="n">lStrings</span><span class="o">.</span><span class="na">getString</span><span class="o">(</span><span class="s">"http.method_post_not_supported"</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">protocol</span><span class="o">.</span><span class="na">endsWith</span><span class="o">(</span><span class="s">"1.1"</span><span class="o">))</span> <span class="o">{</span>
<span class="n">resp</span><span class="o">.</span><span class="na">sendError</span><span class="o">(</span><span class="n">HttpServletResponse</span><span class="o">.</span><span class="na">SC_METHOD_NOT_ALLOWED</span><span class="o">,</span> <span class="n">msg</span><span class="o">);</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="n">resp</span><span class="o">.</span><span class="na">sendError</span><span class="o">(</span><span class="n">HttpServletResponse</span><span class="o">.</span><span class="na">SC_BAD_REQUEST</span><span class="o">,</span> <span class="n">msg</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="cm">/**
* 通过公共的 service 方法接收标准的 HTTP 请求,分发给在这个类中定义的 doXX 的方法。这个方法是一个 service方法的 HTTP指定版本,不需要重写该方法。
**/</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">service</span><span class="o">(</span><span class="n">HttpServletRequest</span> <span class="n">req</span><span class="o">,</span> <span class="n">HttpServletResponse</span> <span class="n">resp</span><span class="o">)</span>
<span class="kd">throws</span> <span class="n">ServletException</span><span class="o">,</span> <span class="n">IOException</span>
<span class="o">{</span>
<span class="n">String</span> <span class="n">method</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="na">getMethod</span><span class="o">();</span>
<span class="k">if</span> <span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">METHOD_GET</span><span class="o">))</span> <span class="o">{</span>
<span class="kt">long</span> <span class="n">lastModified</span> <span class="o">=</span> <span class="n">getLastModified</span><span class="o">(</span><span class="n">req</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">lastModified</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// servlet doesn't support if-modified-since, no reason</span>
<span class="c1">// to go through further expensive logic</span>
<span class="n">doGet</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">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="kt">long</span> <span class="n">ifModifiedSince</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="na">getDateHeader</span><span class="o">(</span><span class="n">HEADER_IFMODSINCE</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">ifModifiedSince</span> <span class="o"><</span> <span class="n">lastModified</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// If the servlet mod time is later, call doGet()</span>
<span class="c1">// Round down to the nearest second for a proper compare</span>
<span class="c1">// A ifModifiedSince of -1 will always be less</span>
<span class="n">maybeSetLastModified</span><span class="o">(</span><span class="n">resp</span><span class="o">,</span> <span class="n">lastModified</span><span class="o">);</span>
<span class="n">doGet</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">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="n">resp</span><span class="o">.</span><span class="na">setStatus</span><span class="o">(</span><span class="n">HttpServletResponse</span><span class="o">.</span><span class="na">SC_NOT_MODIFIED</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">METHOD_HEAD</span><span class="o">))</span> <span class="o">{</span>
<span class="kt">long</span> <span class="n">lastModified</span> <span class="o">=</span> <span class="n">getLastModified</span><span class="o">(</span><span class="n">req</span><span class="o">);</span>
<span class="n">maybeSetLastModified</span><span class="o">(</span><span class="n">resp</span><span class="o">,</span> <span class="n">lastModified</span><span class="o">);</span>
<span class="n">doHead</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">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">METHOD_POST</span><span class="o">))</span> <span class="o">{</span>
<span class="n">doPost</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">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">METHOD_PUT</span><span class="o">))</span> <span class="o">{</span>
<span class="n">doPut</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">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">METHOD_DELETE</span><span class="o">))</span> <span class="o">{</span>
<span class="n">doDelete</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">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">METHOD_OPTIONS</span><span class="o">))</span> <span class="o">{</span>
<span class="n">doOptions</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">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">METHOD_TRACE</span><span class="o">))</span> <span class="o">{</span>
<span class="n">doTrace</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">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="c1">//</span>
<span class="c1">// Note that this means NO servlet supports whatever</span>
<span class="c1">// method was requested, anywhere on this server.</span>
<span class="c1">//</span>
<span class="n">String</span> <span class="n">errMsg</span> <span class="o">=</span> <span class="n">lStrings</span><span class="o">.</span><span class="na">getString</span><span class="o">(</span><span class="s">"http.method_not_implemented"</span><span class="o">);</span>
<span class="n">Object</span><span class="o">[]</span> <span class="n">errArgs</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Object</span><span class="o">[</span><span class="mi">1</span><span class="o">];</span>
<span class="n">errArgs</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">=</span> <span class="n">method</span><span class="o">;</span>
<span class="n">errMsg</span> <span class="o">=</span> <span class="n">MessageFormat</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="n">errMsg</span><span class="o">,</span> <span class="n">errArgs</span><span class="o">);</span>
<span class="n">resp</span><span class="o">.</span><span class="na">sendError</span><span class="o">(</span><span class="n">HttpServletResponse</span><span class="o">.</span><span class="na">SC_NOT_IMPLEMENTED</span><span class="o">,</span> <span class="n">errMsg</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="cm">/*
* Sets the Last-Modified entity header field, if it has not
* already been set and if the value is meaningful. Called before
* doGet, to ensure that headers are set before response data is
* written. A subclass might have set this header already, so we
* check.
*/</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">maybeSetLastModified</span><span class="o">(</span><span class="n">HttpServletResponse</span> <span class="n">resp</span><span class="o">,</span>
<span class="kt">long</span> <span class="n">lastModified</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">resp</span><span class="o">.</span><span class="na">containsHeader</span><span class="o">(</span><span class="n">HEADER_LASTMOD</span><span class="o">))</span>
<span class="k">return</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="n">lastModified</span> <span class="o">>=</span> <span class="mi">0</span><span class="o">)</span>
<span class="n">resp</span><span class="o">.</span><span class="na">setDateHeader</span><span class="o">(</span><span class="n">HEADER_LASTMOD</span><span class="o">,</span> <span class="n">lastModified</span><span class="o">);</span>
<span class="o">}</span>
<span class="cm">/**
* Dispatches client requests to the protected
* <code>service</code> method. There's no need to
* override this method.
*
* @param req the {@link HttpServletRequest} object that
* contains the request the client made of
* the servlet
*
* @param res the {@link HttpServletResponse} object that
* contains the response the servlet returns
* to the client
*
* @exception IOException if an input or output error occurs
* while the servlet is handling the
* HTTP request
*
* @exception ServletException if the HTTP request cannot
* be handled
*
* @see javax.servlet.Servlet#service
*/</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">service</span><span class="o">(</span><span class="n">ServletRequest</span> <span class="n">req</span><span class="o">,</span> <span class="n">ServletResponse</span> <span class="n">res</span><span class="o">)</span>
<span class="kd">throws</span> <span class="n">ServletException</span><span class="o">,</span> <span class="n">IOException</span>
<span class="o">{</span>
<span class="n">HttpServletRequest</span> <span class="n">request</span><span class="o">;</span>
<span class="n">HttpServletResponse</span> <span class="n">response</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(!(</span><span class="n">req</span> <span class="k">instanceof</span> <span class="n">HttpServletRequest</span> <span class="o">&&</span>
<span class="n">res</span> <span class="k">instanceof</span> <span class="n">HttpServletResponse</span><span class="o">))</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">ServletException</span><span class="o">(</span><span class="s">"non-HTTP request or response"</span><span class="o">);</span>
<span class="o">}</span>
<span class="n">request</span> <span class="o">=</span> <span class="o">(</span><span class="n">HttpServletRequest</span><span class="o">)</span> <span class="n">req</span><span class="o">;</span>
<span class="n">response</span> <span class="o">=</span> <span class="o">(</span><span class="n">HttpServletResponse</span><span class="o">)</span> <span class="n">res</span><span class="o">;</span>
<span class="n">service</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">response</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>HttpServletBean</strong></p>
<p> 简单的扩展了 HttpServlet,在web.xml 中,它的配置参数和 servlet 标签被当做bean 属性的init-param 元素。</p>
<p> 这个通用的 servlet 的基本类并没有依赖 Spring ApplicationContext 的概念。</p>
<p> FrameworkServlet 类是一个更特定的基础 servlet 类,它加载了自己的 application context。FrameworkServlet 被当作 Spring 的完全合格的 DispatcherServlet 直接类。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">HttpServletBean</span> <span class="kd">extends</span> <span class="n">HttpServlet</span> <span class="kd">implements</span> <span class="n">EnvironmentCapable</span><span class="o">,</span> <span class="n">EnvironmentAware</span> <span class="o">{</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>FrameworkServlet</strong></p>
<p> Spring web 框架的基础 servlet。在 JavaBean 基础的总体解决方案中,提供了一个 Spring application context 的集成。</p>
<p> 这个类提供了相应的功能:</p>
<p> 1.管理每一个 servlet 的 WebApplicationContext 实例,由 beans 所在的 servlet 命名空间管理这个 servlet 的配置。</p>
<p> 2.咋就请求过程中发布事件,无论该请求是否被成功的处理。</p>
<p> 为了处理请求,子类必需实现 doService。因为这个继承 HttpServletBean 而不是直接继承 HttpServlet, beans 的属性会被自动的映射进来。子类可以重写 initFrameworkServlet() 实现自定义初始化。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">FrameworkServlet</span> <span class="kd">extends</span> <span class="n">HttpServletBean</span> <span class="kd">implements</span> <span class="n">ApplicationContextAware</span> <span class="o">{</span>
<span class="cm">/**
* 为了保证去拦截批量的请求,重写了父类的实现。
*/</span>
<span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">service</span><span class="o">(</span><span class="n">HttpServletRequest</span> <span class="n">request</span><span class="o">,</span> <span class="n">HttpServletResponse</span> <span class="n">response</span><span class="o">)</span>
<span class="kd">throws</span> <span class="n">ServletException</span><span class="o">,</span> <span class="n">IOException</span> <span class="o">{</span>
<span class="n">HttpMethod</span> <span class="n">httpMethod</span> <span class="o">=</span> <span class="n">HttpMethod</span><span class="o">.</span><span class="na">resolve</span><span class="o">(</span><span class="n">request</span><span class="o">.</span><span class="na">getMethod</span><span class="o">());</span>
<span class="k">if</span> <span class="o">(</span><span class="n">HttpMethod</span><span class="o">.</span><span class="na">PATCH</span> <span class="o">==</span> <span class="n">httpMethod</span> <span class="o">||</span> <span class="n">httpMethod</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">processRequest</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">response</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">else</span> <span class="o">{</span>
<span class="kd">super</span><span class="o">.</span><span class="na">service</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">response</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="cm">/**
* 处理请求,不管出去就发布事件。实际执行的事件处理是通过抽象的 doService 模版方法。HTTP 的请求方法都是调
* 用这个方法进行处理
**/</span>
<span class="kd">protected</span> <span class="kd">final</span> <span class="kt">void</span> <span class="nf">processRequest</span><span class="o">(</span><span class="n">HttpServletRequest</span> <span class="n">request</span><span class="o">,</span> <span class="n">HttpServletResponse</span> <span class="n">response</span><span class="o">)</span>
<span class="kd">throws</span> <span class="n">ServletException</span><span class="o">,</span> <span class="n">IOException</span> <span class="o">{</span>
<span class="kt">long</span> <span class="n">startTime</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="n">Throwable</span> <span class="n">failureCause</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="n">LocaleContext</span> <span class="n">previousLocaleContext</span> <span class="o">=</span> <span class="n">LocaleContextHolder</span><span class="o">.</span><span class="na">getLocaleContext</span><span class="o">();</span>
<span class="n">LocaleContext</span> <span class="n">localeContext</span> <span class="o">=</span> <span class="n">buildLocaleContext</span><span class="o">(</span><span class="n">request</span><span class="o">);</span>
<span class="n">RequestAttributes</span> <span class="n">previousAttributes</span> <span class="o">=</span> <span class="n">RequestContextHolder</span><span class="o">.</span><span class="na">getRequestAttributes</span><span class="o">();</span>
<span class="n">ServletRequestAttributes</span> <span class="n">requestAttributes</span> <span class="o">=</span> <span class="n">buildRequestAttributes</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">response</span><span class="o">,</span> <span class="n">previousAttributes</span><span class="o">);</span>
<span class="n">WebAsyncManager</span> <span class="n">asyncManager</span> <span class="o">=</span> <span class="n">WebAsyncUtils</span><span class="o">.</span><span class="na">getAsyncManager</span><span class="o">(</span><span class="n">request</span><span class="o">);</span>
<span class="n">asyncManager</span><span class="o">.</span><span class="na">registerCallableInterceptor</span><span class="o">(</span><span class="n">FrameworkServlet</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getName</span><span class="o">(),</span> <span class="k">new</span> <span class="n">RequestBindingInterceptor</span><span class="o">());</span>
<span class="n">initContextHolders</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">localeContext</span><span class="o">,</span> <span class="n">requestAttributes</span><span class="o">);</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">doService</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">response</span><span class="o">);</span> <span class="c1">// 调用 doService</span>
<span class="o">}</span>
<span class="k">catch</span> <span class="o">(</span><span class="n">ServletException</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
<span class="n">failureCause</span> <span class="o">=</span> <span class="n">ex</span><span class="o">;</span>
<span class="k">throw</span> <span class="n">ex</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
<span class="n">failureCause</span> <span class="o">=</span> <span class="n">ex</span><span class="o">;</span>
<span class="k">throw</span> <span class="n">ex</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">catch</span> <span class="o">(</span><span class="n">Throwable</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
<span class="n">failureCause</span> <span class="o">=</span> <span class="n">ex</span><span class="o">;</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">NestedServletException</span><span class="o">(</span><span class="s">"Request processing failed"</span><span class="o">,</span> <span class="n">ex</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">finally</span> <span class="o">{</span>
<span class="n">resetContextHolders</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">previousLocaleContext</span><span class="o">,</span> <span class="n">previousAttributes</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">requestAttributes</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">requestAttributes</span><span class="o">.</span><span class="na">requestCompleted</span><span class="o">();</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">logger</span><span class="o">.</span><span class="na">isDebugEnabled</span><span class="o">())</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">failureCause</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">logger</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">"Could not complete request"</span><span class="o">,</span> <span class="n">failureCause</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">else</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">asyncManager</span><span class="o">.</span><span class="na">isConcurrentHandlingStarted</span><span class="o">())</span> <span class="o">{</span>
<span class="n">logger</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">"Leaving response open for concurrent processing"</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">else</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">logger</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">"Successfully completed request"</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="n">publishRequestHandledEvent</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">response</span><span class="o">,</span> <span class="n">startTime</span><span class="o">,</span> <span class="n">failureCause</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="cm">/**
* 等待子类实现.子类一定得实现这个方法去执行请求处理的工作,接收 GET, POST, PUT AND DELETE 的集中回调
*
**/</span>
<span class="kd">protected</span> <span class="kd">abstract</span> <span class="kt">void</span> <span class="nf">doService</span><span class="o">(</span><span class="n">HttpServletRequest</span> <span class="n">request</span><span class="o">,</span> <span class="n">HttpServletResponse</span> <span class="n">response</span><span class="o">)</span>
<span class="kd">throws</span> <span class="n">Exception</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p>servlet 调用堆栈:
<img src="/images/posts/servlet_request.jpg" alt="" /></p>cpp255Spring Servlet 源码实现原理(1) Servlet:Spring 定时器实现原理(2)2018-01-23T03:47:10+08:002018-01-23T03:47:10+08:00https://cpp255.github.io/2018/01/22/Spring-Quartz-sources-2<h1 id="spring-定时器实现原理2">Spring 定时器实现原理(2)</h1>
<hr />
<p><strong>QuartzScheduler</strong></p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>类说明里面是这么说的: Quartz 的心脏,间接实现了 org.quartz.Scheduler 的接口,包含了 schedule 调度 Job 的方法,JobListener 实例的注册等。 监听事件会在 scheduler 执行了不同的方法后,调用这些监听事件。RemotableQuartzScheduler 接口是继承的 java.rmi.Remote,声明了任务调度的方法。
</code></pre></div></div>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">QuartzScheduler</span> <span class="kd">implements</span> <span class="n">RemotableQuartzScheduler</span> <span class="o">{</span>
<span class="c1">// 定时器的监听管理和注册监听容器</span>
<span class="kd">private</span> <span class="n">ListenerManager</span> <span class="n">listenerManager</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ListenerManagerImpl</span><span class="o">();</span>
<span class="c1">// 这里不是很明白为什么要定为常量10,HashMap 中的数组一定是2的指数,10 肯定会变成大小为 16 </span>
<span class="kd">private</span> <span class="n">HashMap</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">JobListener</span><span class="o">></span> <span class="n">internalJobListeners</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HashMap</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">JobListener</span><span class="o">>(</span><span class="mi">10</span><span class="o">);</span>
<span class="kd">private</span> <span class="n">HashMap</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">TriggerListener</span><span class="o">></span> <span class="n">internalTriggerListeners</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HashMap</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">TriggerListener</span><span class="o">>(</span><span class="mi">10</span><span class="o">);</span>
<span class="kd">private</span> <span class="n">ArrayList</span><span class="o"><</span><span class="n">SchedulerListener</span><span class="o">></span> <span class="n">internalSchedulerListeners</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o"><</span><span class="n">SchedulerListener</span><span class="o">>(</span><span class="mi">10</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>QuartzSchedulerThread</strong></p>
<p> QuartzSchedulerThread 继承了 java 的 Thread 类,负责执行在 QuartzScheduler 中注册了 Trigger 的触发点的定时调用。这个线程只是执行触发器的定点计算和管理,并不会在这里面进行 Job 的运行,当然,这里会把符合到点的任务提交到 scheduler 线程池中运行。线程里面主要的类是 run() 方法,只要没有设置中断或者停机状态,里面的 while 循环是不停的运行,并默认30s,根据当前 scheduler 可用线程等状态,获取到点的可运行的 triggers 列表,再创建相应的任务,把跟 trigger 相关的 job 提交运行。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">QuartzSchedulerThread</span> <span class="kd">extends</span> <span class="n">Thread</span> <span class="o">{</span>
<span class="c1">// scheduler 携带的资源信息</span>
<span class="kd">private</span> <span class="n">QuartzSchedulerResources</span> <span class="n">qsRsrcs</span><span class="o">;</span>
<span class="cm">/**
* 该线程会一直不停的循环执行(线程是正常运行状态,没有被停止)
**/</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">acquiresFailed</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="k">while</span> <span class="o">(!</span><span class="n">halted</span><span class="o">.</span><span class="na">get</span><span class="o">())</span> <span class="o">{</span>
<span class="k">try</span> <span class="o">{</span>
<span class="c1">// check if we're supposed to pause...</span>
<span class="kd">synchronized</span> <span class="o">(</span><span class="n">sigLock</span><span class="o">)</span> <span class="o">{</span>
<span class="k">while</span> <span class="o">(</span><span class="n">paused</span> <span class="o">&&</span> <span class="o">!</span><span class="n">halted</span><span class="o">.</span><span class="na">get</span><span class="o">())</span> <span class="o">{</span>
<span class="k">try</span> <span class="o">{</span>
<span class="c1">// wait until togglePause(false) is called...</span>
<span class="n">sigLock</span><span class="o">.</span><span class="na">wait</span><span class="o">(</span><span class="mi">1000L</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">InterruptedException</span> <span class="n">ignore</span><span class="o">)</span> <span class="o">{</span>
<span class="o">}</span>
<span class="c1">// reset failure counter when paused, so that we don't</span>
<span class="c1">// wait again after unpausing</span>
<span class="n">acquiresFailed</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">halted</span><span class="o">.</span><span class="na">get</span><span class="o">())</span> <span class="o">{</span>
<span class="k">break</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="c1">// wait a bit, if reading from job store is consistently</span>
<span class="c1">// failing (e.g. DB is down or restarting)..</span>
<span class="k">if</span> <span class="o">(</span><span class="n">acquiresFailed</span> <span class="o">></span> <span class="mi">1</span><span class="o">)</span> <span class="o">{</span>
<span class="k">try</span> <span class="o">{</span>
<span class="kt">long</span> <span class="n">delay</span> <span class="o">=</span> <span class="n">computeDelayForRepeatedErrors</span><span class="o">(</span><span class="n">qRsrcs</span><span class="o">.</span><span class="na">getJobStore</span><span class="o">(),</span> <span class="n">acquiresFailed</span><span class="o">);</span>
<span class="n">Thread</span><span class="o">.</span><span class="na">sleep</span><span class="o">(</span><span class="n">delay</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">ignore</span><span class="o">)</span> <span class="o">{</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="kt">int</span> <span class="n">availThreadCount</span> <span class="o">=</span> <span class="n">qsRsrcs</span><span class="o">.</span><span class="na">getThreadPool</span><span class="o">().</span><span class="na">blockForAvailableThreads</span><span class="o">();</span>
<span class="k">if</span><span class="o">(</span><span class="n">availThreadCount</span> <span class="o">></span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// will always be true, due to semantics of blockForAvailableThreads...</span>
<span class="n">List</span><span class="o"><</span><span class="n">OperableTrigger</span><span class="o">></span> <span class="n">triggers</span><span class="o">;</span>
<span class="kt">long</span> <span class="n">now</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="n">clearSignaledSchedulingChange</span><span class="o">();</span>
<span class="k">try</span> <span class="o">{</span>
<span class="c1">// 获取从现在起,一定时间(默认30s)内,根据可用线程数等条件获取即将到来的触发器列表</span>
<span class="n">triggers</span> <span class="o">=</span> <span class="n">qsRsrcs</span><span class="o">.</span><span class="na">getJobStore</span><span class="o">().</span><span class="na">acquireNextTriggers</span><span class="o">(</span>
<span class="n">now</span> <span class="o">+</span> <span class="n">idleWaitTime</span><span class="o">,</span> <span class="n">Math</span><span class="o">.</span><span class="na">min</span><span class="o">(</span><span class="n">availThreadCount</span><span class="o">,</span> <span class="n">qsRsrcs</span><span class="o">.</span><span class="na">getMaxBatchSize</span><span class="o">()),</span> <span class="n">qsRsrcs</span><span class="o">.</span><span class="na">getBatchTimeWindow</span><span class="o">());</span>
<span class="n">acquiresFailed</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="n">log</span><span class="o">.</span><span class="na">isDebugEnabled</span><span class="o">())</span>
<span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">"batch acquisition of "</span> <span class="o">+</span> <span class="o">(</span><span class="n">triggers</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">?</span> <span class="mi">0</span> <span class="o">:</span> <span class="n">triggers</span><span class="o">.</span><span class="na">size</span><span class="o">())</span> <span class="o">+</span> <span class="s">" triggers"</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">JobPersistenceException</span> <span class="n">jpe</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">acquiresFailed</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="n">qs</span><span class="o">.</span><span class="na">notifySchedulerListenersError</span><span class="o">(</span>
<span class="s">"An error occurred while scanning for the next triggers to fire."</span><span class="o">,</span>
<span class="n">jpe</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">acquiresFailed</span> <span class="o"><</span> <span class="n">Integer</span><span class="o">.</span><span class="na">MAX_VALUE</span><span class="o">)</span>
<span class="n">acquiresFailed</span><span class="o">++;</span>
<span class="k">continue</span><span class="o">;</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">RuntimeException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">acquiresFailed</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="n">getLog</span><span class="o">().</span><span class="na">error</span><span class="o">(</span><span class="s">"quartzSchedulerThreadLoop: RuntimeException "</span>
<span class="o">+</span><span class="n">e</span><span class="o">.</span><span class="na">getMessage</span><span class="o">(),</span> <span class="n">e</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">acquiresFailed</span> <span class="o"><</span> <span class="n">Integer</span><span class="o">.</span><span class="na">MAX_VALUE</span><span class="o">)</span>
<span class="n">acquiresFailed</span><span class="o">++;</span>
<span class="k">continue</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">triggers</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&&</span> <span class="o">!</span><span class="n">triggers</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span>
<span class="n">now</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="kt">long</span> <span class="n">triggerTime</span> <span class="o">=</span> <span class="n">triggers</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">).</span><span class="na">getNextFireTime</span><span class="o">().</span><span class="na">getTime</span><span class="o">();</span>
<span class="kt">long</span> <span class="n">timeUntilTrigger</span> <span class="o">=</span> <span class="n">triggerTime</span> <span class="o">-</span> <span class="n">now</span><span class="o">;</span>
<span class="k">while</span><span class="o">(</span><span class="n">timeUntilTrigger</span> <span class="o">></span> <span class="mi">2</span><span class="o">)</span> <span class="o">{</span>
<span class="kd">synchronized</span> <span class="o">(</span><span class="n">sigLock</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">halted</span><span class="o">.</span><span class="na">get</span><span class="o">())</span> <span class="o">{</span>
<span class="k">break</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">isCandidateNewTimeEarlierWithinReason</span><span class="o">(</span><span class="n">triggerTime</span><span class="o">,</span> <span class="kc">false</span><span class="o">))</span> <span class="o">{</span>
<span class="k">try</span> <span class="o">{</span>
<span class="c1">// we could have blocked a long while</span>
<span class="c1">// on 'synchronize', so we must recompute</span>
<span class="n">now</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="n">timeUntilTrigger</span> <span class="o">=</span> <span class="n">triggerTime</span> <span class="o">-</span> <span class="n">now</span><span class="o">;</span>
<span class="k">if</span><span class="o">(</span><span class="n">timeUntilTrigger</span> <span class="o">>=</span> <span class="mi">1</span><span class="o">)</span>
<span class="n">sigLock</span><span class="o">.</span><span class="na">wait</span><span class="o">(</span><span class="n">timeUntilTrigger</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">InterruptedException</span> <span class="n">ignore</span><span class="o">)</span> <span class="o">{</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">if</span><span class="o">(</span><span class="n">releaseIfScheduleChangedSignificantly</span><span class="o">(</span><span class="n">triggers</span><span class="o">,</span> <span class="n">triggerTime</span><span class="o">))</span> <span class="o">{</span>
<span class="k">break</span><span class="o">;</span>
<span class="o">}</span>
<span class="n">now</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="n">timeUntilTrigger</span> <span class="o">=</span> <span class="n">triggerTime</span> <span class="o">-</span> <span class="n">now</span><span class="o">;</span>
<span class="o">}</span>
<span class="c1">// this happens if releaseIfScheduleChangedSignificantly decided to release triggers</span>
<span class="k">if</span><span class="o">(</span><span class="n">triggers</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span>
<span class="k">continue</span><span class="o">;</span>
<span class="c1">// set triggers to 'executing'</span>
<span class="n">List</span><span class="o"><</span><span class="n">TriggerFiredResult</span><span class="o">></span> <span class="n">bndles</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o"><</span><span class="n">TriggerFiredResult</span><span class="o">>();</span>
<span class="kt">boolean</span> <span class="n">goAhead</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
<span class="kd">synchronized</span><span class="o">(</span><span class="n">sigLock</span><span class="o">)</span> <span class="o">{</span>
<span class="n">goAhead</span> <span class="o">=</span> <span class="o">!</span><span class="n">halted</span><span class="o">.</span><span class="na">get</span><span class="o">();</span>
<span class="o">}</span>
<span class="k">if</span><span class="o">(</span><span class="n">goAhead</span><span class="o">)</span> <span class="o">{</span>
<span class="k">try</span> <span class="o">{</span>
<span class="c1">// 通知这批触发器执行时间已到,调用数据库获取和修改相应的数据</span>
<span class="n">List</span><span class="o"><</span><span class="n">TriggerFiredResult</span><span class="o">></span> <span class="n">res</span> <span class="o">=</span> <span class="n">qsRsrcs</span><span class="o">.</span><span class="na">getJobStore</span><span class="o">().</span><span class="na">triggersFired</span><span class="o">(</span><span class="n">triggers</span><span class="o">);</span>
<span class="k">if</span><span class="o">(</span><span class="n">res</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
<span class="n">bndles</span> <span class="o">=</span> <span class="n">res</span><span class="o">;</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">SchedulerException</span> <span class="n">se</span><span class="o">)</span> <span class="o">{</span>
<span class="n">qs</span><span class="o">.</span><span class="na">notifySchedulerListenersError</span><span class="o">(</span>
<span class="s">"An error occurred while firing triggers '"</span>
<span class="o">+</span> <span class="n">triggers</span> <span class="o">+</span> <span class="s">"'"</span><span class="o">,</span> <span class="n">se</span><span class="o">);</span>
<span class="c1">//QTZ-179 : a problem occurred interacting with the triggers from the db</span>
<span class="c1">//we release them and loop again</span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">triggers</span><span class="o">.</span><span class="na">size</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="n">qsRsrcs</span><span class="o">.</span><span class="na">getJobStore</span><span class="o">().</span><span class="na">releaseAcquiredTrigger</span><span class="o">(</span><span class="n">triggers</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">));</span>
<span class="o">}</span>
<span class="k">continue</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">bndles</span><span class="o">.</span><span class="na">size</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="n">TriggerFiredResult</span> <span class="n">result</span> <span class="o">=</span> <span class="n">bndles</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
<span class="n">TriggerFiredBundle</span> <span class="n">bndle</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="na">getTriggerFiredBundle</span><span class="o">();</span>
<span class="n">Exception</span> <span class="n">exception</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="na">getException</span><span class="o">();</span>
<span class="k">if</span> <span class="o">(</span><span class="n">exception</span> <span class="k">instanceof</span> <span class="n">RuntimeException</span><span class="o">)</span> <span class="o">{</span>
<span class="n">getLog</span><span class="o">().</span><span class="na">error</span><span class="o">(</span><span class="s">"RuntimeException while firing trigger "</span> <span class="o">+</span> <span class="n">triggers</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">),</span> <span class="n">exception</span><span class="o">);</span>
<span class="n">qsRsrcs</span><span class="o">.</span><span class="na">getJobStore</span><span class="o">().</span><span class="na">releaseAcquiredTrigger</span><span class="o">(</span><span class="n">triggers</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">));</span>
<span class="k">continue</span><span class="o">;</span>
<span class="o">}</span>
<span class="c1">// it's possible to get 'null' if the triggers was paused,</span>
<span class="c1">// blocked, or other similar occurrences that prevent it being</span>
<span class="c1">// fired at this time... or if the scheduler was shutdown (halted)</span>
<span class="k">if</span> <span class="o">(</span><span class="n">bndle</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">qsRsrcs</span><span class="o">.</span><span class="na">getJobStore</span><span class="o">().</span><span class="na">releaseAcquiredTrigger</span><span class="o">(</span><span class="n">triggers</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">));</span>
<span class="k">continue</span><span class="o">;</span>
<span class="o">}</span>
<span class="n">JobRunShell</span> <span class="n">shell</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="k">try</span> <span class="o">{</span>
<span class="c1">// 创建 Job 执行的实例</span>
<span class="n">shell</span> <span class="o">=</span> <span class="n">qsRsrcs</span><span class="o">.</span><span class="na">getJobRunShellFactory</span><span class="o">().</span><span class="na">createJobRunShell</span><span class="o">(</span><span class="n">bndle</span><span class="o">);</span>
<span class="n">shell</span><span class="o">.</span><span class="na">initialize</span><span class="o">(</span><span class="n">qs</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">SchedulerException</span> <span class="n">se</span><span class="o">)</span> <span class="o">{</span>
<span class="n">qsRsrcs</span><span class="o">.</span><span class="na">getJobStore</span><span class="o">().</span><span class="na">triggeredJobComplete</span><span class="o">(</span><span class="n">triggers</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">),</span> <span class="n">bndle</span><span class="o">.</span><span class="na">getJobDetail</span><span class="o">(),</span> <span class="n">CompletedExecutionInstruction</span><span class="o">.</span><span class="na">SET_ALL_JOB_TRIGGERS_ERROR</span><span class="o">);</span>
<span class="k">continue</span><span class="o">;</span>
<span class="o">}</span>
<span class="c1">// Job 的任务实例提交到 scheduler 中执行</span>
<span class="k">if</span> <span class="o">(</span><span class="n">qsRsrcs</span><span class="o">.</span><span class="na">getThreadPool</span><span class="o">().</span><span class="na">runInThread</span><span class="o">(</span><span class="n">shell</span><span class="o">)</span> <span class="o">==</span> <span class="kc">false</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// this case should never happen, as it is indicative of the</span>
<span class="c1">// scheduler being shutdown or a bug in the thread pool or</span>
<span class="c1">// a thread pool being used concurrently - which the docs</span>
<span class="c1">// say not to do...</span>
<span class="n">getLog</span><span class="o">().</span><span class="na">error</span><span class="o">(</span><span class="s">"ThreadPool.runInThread() return false!"</span><span class="o">);</span>
<span class="n">qsRsrcs</span><span class="o">.</span><span class="na">getJobStore</span><span class="o">().</span><span class="na">triggeredJobComplete</span><span class="o">(</span><span class="n">triggers</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">),</span> <span class="n">bndle</span><span class="o">.</span><span class="na">getJobDetail</span><span class="o">(),</span> <span class="n">CompletedExecutionInstruction</span><span class="o">.</span><span class="na">SET_ALL_JOB_TRIGGERS_ERROR</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">continue</span><span class="o">;</span> <span class="c1">// while (!halted)</span>
<span class="o">}</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span> <span class="c1">// if(availThreadCount > 0)</span>
<span class="c1">// should never happen, if threadPool.blockForAvailableThreads() follows contract</span>
<span class="k">continue</span><span class="o">;</span> <span class="c1">// while (!halted)</span>
<span class="o">}</span>
<span class="kt">long</span> <span class="n">now</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="kt">long</span> <span class="n">waitTime</span> <span class="o">=</span> <span class="n">now</span> <span class="o">+</span> <span class="n">getRandomizedIdleWaitTime</span><span class="o">();</span>
<span class="kt">long</span> <span class="n">timeUntilContinue</span> <span class="o">=</span> <span class="n">waitTime</span> <span class="o">-</span> <span class="n">now</span><span class="o">;</span>
<span class="kd">synchronized</span><span class="o">(</span><span class="n">sigLock</span><span class="o">)</span> <span class="o">{</span>
<span class="k">try</span> <span class="o">{</span>
<span class="k">if</span><span class="o">(!</span><span class="n">halted</span><span class="o">.</span><span class="na">get</span><span class="o">())</span> <span class="o">{</span>
<span class="c1">// QTZ-336 A job might have been completed in the mean time and we might have</span>
<span class="c1">// missed the scheduled changed signal by not waiting for the notify() yet</span>
<span class="c1">// Check that before waiting for too long in case this very job needs to be</span>
<span class="c1">// scheduled very soon</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">isScheduleChanged</span><span class="o">())</span> <span class="o">{</span>
<span class="n">sigLock</span><span class="o">.</span><span class="na">wait</span><span class="o">(</span><span class="n">timeUntilContinue</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">InterruptedException</span> <span class="n">ignore</span><span class="o">)</span> <span class="o">{</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span> <span class="k">catch</span><span class="o">(</span><span class="n">RuntimeException</span> <span class="n">re</span><span class="o">)</span> <span class="o">{</span>
<span class="n">getLog</span><span class="o">().</span><span class="na">error</span><span class="o">(</span><span class="s">"Runtime error occurred in main trigger firing loop."</span><span class="o">,</span> <span class="n">re</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span> <span class="c1">// while (!halted)</span>
<span class="c1">// drop references to scheduler stuff to aid garbage collection...</span>
<span class="n">qs</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="n">qsRsrcs</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>QuartzSchedulerResources</strong></p>
<p> 包含了创建 QuartzScheduler 实例必需的所有资源: JobStore、ThreadPool。</p>
<p><strong>JobRunShell</strong></p>
<p> JobRunShell 的实例负责提供安全的环境为 Job 运行,并且运行所有的 Job 的工作任务,捕抓人和抛出的异常,更新 Job 涉及的 Trigger。</p>
<p> JobRunShell 是通过工厂类 JobRunShellFactory 生成的实例,当 scheduler 判定一个 Job 已经被触发了,QuartzSchedulerThread 将会从配置的 ThreadPool 中的一个线程运行这个 shell。</p>
<p> JobRunShell 实现了 Runnable,因此 Job 的任务调用是在 run 的方法中运行。当然,这里也会涉及到注册事件的监听处理。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">JobRunShell</span> <span class="kd">extends</span> <span class="n">SchedulerListenerSupport</span> <span class="kd">implements</span> <span class="n">Runnable</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
<span class="n">qs</span><span class="o">.</span><span class="na">addInternalSchedulerListener</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">OperableTrigger</span> <span class="n">trigger</span> <span class="o">=</span> <span class="o">(</span><span class="n">OperableTrigger</span><span class="o">)</span> <span class="n">jec</span><span class="o">.</span><span class="na">getTrigger</span><span class="o">();</span>
<span class="n">JobDetail</span> <span class="n">jobDetail</span> <span class="o">=</span> <span class="n">jec</span><span class="o">.</span><span class="na">getJobDetail</span><span class="o">();</span>
<span class="k">do</span> <span class="o">{</span>
<span class="n">JobExecutionException</span> <span class="n">jobExEx</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="n">Job</span> <span class="n">job</span> <span class="o">=</span> <span class="n">jec</span><span class="o">.</span><span class="na">getJobInstance</span><span class="o">();</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">begin</span><span class="o">();</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">SchedulerException</span> <span class="n">se</span><span class="o">)</span> <span class="o">{</span>
<span class="n">qs</span><span class="o">.</span><span class="na">notifySchedulerListenersError</span><span class="o">(</span><span class="s">"Error executing Job ("</span>
<span class="o">+</span> <span class="n">jec</span><span class="o">.</span><span class="na">getJobDetail</span><span class="o">().</span><span class="na">getKey</span><span class="o">()</span>
<span class="o">+</span> <span class="s">": couldn't begin execution."</span><span class="o">,</span> <span class="n">se</span><span class="o">);</span>
<span class="k">break</span><span class="o">;</span>
<span class="o">}</span>
<span class="c1">// notify job & trigger listeners...</span>
<span class="k">try</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">notifyListenersBeginning</span><span class="o">(</span><span class="n">jec</span><span class="o">))</span> <span class="o">{</span>
<span class="k">break</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span> <span class="k">catch</span><span class="o">(</span><span class="n">VetoedException</span> <span class="n">ve</span><span class="o">)</span> <span class="o">{</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">CompletedExecutionInstruction</span> <span class="n">instCode</span> <span class="o">=</span> <span class="n">trigger</span><span class="o">.</span><span class="na">executionComplete</span><span class="o">(</span><span class="n">jec</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
<span class="n">qs</span><span class="o">.</span><span class="na">notifyJobStoreJobVetoed</span><span class="o">(</span><span class="n">trigger</span><span class="o">,</span> <span class="n">jobDetail</span><span class="o">,</span> <span class="n">instCode</span><span class="o">);</span>
<span class="c1">// QTZ-205</span>
<span class="c1">// Even if trigger got vetoed, we still needs to check to see if it's the trigger's finalized run or not.</span>
<span class="k">if</span> <span class="o">(</span><span class="n">jec</span><span class="o">.</span><span class="na">getTrigger</span><span class="o">().</span><span class="na">getNextFireTime</span><span class="o">()</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">qs</span><span class="o">.</span><span class="na">notifySchedulerListenersFinalized</span><span class="o">(</span><span class="n">jec</span><span class="o">.</span><span class="na">getTrigger</span><span class="o">());</span>
<span class="o">}</span>
<span class="n">complete</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">SchedulerException</span> <span class="n">se</span><span class="o">)</span> <span class="o">{</span>
<span class="n">qs</span><span class="o">.</span><span class="na">notifySchedulerListenersError</span><span class="o">(</span><span class="s">"Error during veto of Job ("</span>
<span class="o">+</span> <span class="n">jec</span><span class="o">.</span><span class="na">getJobDetail</span><span class="o">().</span><span class="na">getKey</span><span class="o">()</span>
<span class="o">+</span> <span class="s">": couldn't finalize execution."</span><span class="o">,</span> <span class="n">se</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">break</span><span class="o">;</span>
<span class="o">}</span>
<span class="kt">long</span> <span class="n">startTime</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="kt">long</span> <span class="n">endTime</span> <span class="o">=</span> <span class="n">startTime</span><span class="o">;</span>
<span class="c1">// execute the job 执行具体的任务</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">"Calling execute on job "</span> <span class="o">+</span> <span class="n">jobDetail</span><span class="o">.</span><span class="na">getKey</span><span class="o">());</span>
<span class="n">job</span><span class="o">.</span><span class="na">execute</span><span class="o">(</span><span class="n">jec</span><span class="o">);</span>
<span class="n">endTime</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="k">catch</span> <span class="o">(</span><span class="n">JobExecutionException</span> <span class="n">jee</span><span class="o">)</span> <span class="o">{</span>
<span class="n">endTime</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="n">jobExEx</span> <span class="o">=</span> <span class="n">jee</span><span class="o">;</span>
<span class="n">getLog</span><span class="o">().</span><span class="na">info</span><span class="o">(</span><span class="s">"Job "</span> <span class="o">+</span> <span class="n">jobDetail</span><span class="o">.</span><span class="na">getKey</span><span class="o">()</span> <span class="o">+</span>
<span class="s">" threw a JobExecutionException: "</span><span class="o">,</span> <span class="n">jobExEx</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">Throwable</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="n">endTime</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="n">getLog</span><span class="o">().</span><span class="na">error</span><span class="o">(</span><span class="s">"Job "</span> <span class="o">+</span> <span class="n">jobDetail</span><span class="o">.</span><span class="na">getKey</span><span class="o">()</span> <span class="o">+</span>
<span class="s">" threw an unhandled Exception: "</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span>
<span class="n">SchedulerException</span> <span class="n">se</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SchedulerException</span><span class="o">(</span>
<span class="s">"Job threw an unhandled exception."</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span>
<span class="n">qs</span><span class="o">.</span><span class="na">notifySchedulerListenersError</span><span class="o">(</span><span class="s">"Job ("</span>
<span class="o">+</span> <span class="n">jec</span><span class="o">.</span><span class="na">getJobDetail</span><span class="o">().</span><span class="na">getKey</span><span class="o">()</span>
<span class="o">+</span> <span class="s">" threw an exception."</span><span class="o">,</span> <span class="n">se</span><span class="o">);</span>
<span class="n">jobExEx</span> <span class="o">=</span> <span class="k">new</span> <span class="n">JobExecutionException</span><span class="o">(</span><span class="n">se</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span>
<span class="o">}</span>
<span class="n">jec</span><span class="o">.</span><span class="na">setJobRunTime</span><span class="o">(</span><span class="n">endTime</span> <span class="o">-</span> <span class="n">startTime</span><span class="o">);</span>
<span class="c1">// notify all job listeners</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">notifyJobListenersComplete</span><span class="o">(</span><span class="n">jec</span><span class="o">,</span> <span class="n">jobExEx</span><span class="o">))</span> <span class="o">{</span>
<span class="k">break</span><span class="o">;</span>
<span class="o">}</span>
<span class="n">CompletedExecutionInstruction</span> <span class="n">instCode</span> <span class="o">=</span> <span class="n">CompletedExecutionInstruction</span><span class="o">.</span><span class="na">NOOP</span><span class="o">;</span>
<span class="c1">// update the trigger</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">instCode</span> <span class="o">=</span> <span class="n">trigger</span><span class="o">.</span><span class="na">executionComplete</span><span class="o">(</span><span class="n">jec</span><span class="o">,</span> <span class="n">jobExEx</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// If this happens, there's a bug in the trigger...</span>
<span class="n">SchedulerException</span> <span class="n">se</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SchedulerException</span><span class="o">(</span>
<span class="s">"Trigger threw an unhandled exception."</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span>
<span class="n">qs</span><span class="o">.</span><span class="na">notifySchedulerListenersError</span><span class="o">(</span>
<span class="s">"Please report this error to the Quartz developers."</span><span class="o">,</span>
<span class="n">se</span><span class="o">);</span>
<span class="o">}</span>
<span class="c1">// notify all trigger listeners</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">notifyTriggerListenersComplete</span><span class="o">(</span><span class="n">jec</span><span class="o">,</span> <span class="n">instCode</span><span class="o">))</span> <span class="o">{</span>
<span class="k">break</span><span class="o">;</span>
<span class="o">}</span>
<span class="c1">// update job/trigger or re-execute job</span>
<span class="k">if</span> <span class="o">(</span><span class="n">instCode</span> <span class="o">==</span> <span class="n">CompletedExecutionInstruction</span><span class="o">.</span><span class="na">RE_EXECUTE_JOB</span><span class="o">)</span> <span class="o">{</span>
<span class="n">jec</span><span class="o">.</span><span class="na">incrementRefireCount</span><span class="o">();</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">complete</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">SchedulerException</span> <span class="n">se</span><span class="o">)</span> <span class="o">{</span>
<span class="n">qs</span><span class="o">.</span><span class="na">notifySchedulerListenersError</span><span class="o">(</span><span class="s">"Error executing Job ("</span>
<span class="o">+</span> <span class="n">jec</span><span class="o">.</span><span class="na">getJobDetail</span><span class="o">().</span><span class="na">getKey</span><span class="o">()</span>
<span class="o">+</span> <span class="s">": couldn't finalize execution."</span><span class="o">,</span> <span class="n">se</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">continue</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">complete</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">SchedulerException</span> <span class="n">se</span><span class="o">)</span> <span class="o">{</span>
<span class="n">qs</span><span class="o">.</span><span class="na">notifySchedulerListenersError</span><span class="o">(</span><span class="s">"Error executing Job ("</span>
<span class="o">+</span> <span class="n">jec</span><span class="o">.</span><span class="na">getJobDetail</span><span class="o">().</span><span class="na">getKey</span><span class="o">()</span>
<span class="o">+</span> <span class="s">": couldn't finalize execution."</span><span class="o">,</span> <span class="n">se</span><span class="o">);</span>
<span class="k">continue</span><span class="o">;</span>
<span class="o">}</span>
<span class="n">qs</span><span class="o">.</span><span class="na">notifyJobStoreJobComplete</span><span class="o">(</span><span class="n">trigger</span><span class="o">,</span> <span class="n">jobDetail</span><span class="o">,</span> <span class="n">instCode</span><span class="o">);</span>
<span class="k">break</span><span class="o">;</span>
<span class="o">}</span> <span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">);</span>
<span class="o">}</span> <span class="k">finally</span> <span class="o">{</span>
<span class="n">qs</span><span class="o">.</span><span class="na">removeInternalSchedulerListener</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>cpp255Spring 定时器实现原理(2)Spring 定时器实现原理(1)2018-01-20T20:03:10+08:002018-01-20T20:03:10+08:00https://cpp255.github.io/2018/01/20/Spring-Quartz-sources-1<h1 id="spring-定时器实现原理1">Spring 定时器实现原理(1)</h1>
<hr />
<p> 定时器是常用到的功能之一,底层也是使用了多线程的任务调度,然后封装了更多的功能。</p>
<p><strong>Trigger</strong></p>
<p> 触发器—-任务被调度执行的触发规则。描述了决定下一次关联的任务执行时间的最基本触发器通用接口。CronTrigger 基于它实现,CronTrigger 允许定义到天相关、周相关、月相关、年相关的时间,3.x 的版本前还还有 CronTriggerBean 继续继承 CronTrigger,使得任务的使用更简单,4.3.x 之后已经删除该类,使用 CronTriggerFactoryBean 进行扩展,可定义的参数比之前的更丰富。类似的,该类也实现了 FactoryBean 的接口。类似简单的用法还有 SimpleTrigger。</p>
<p><strong>JobDetail</strong></p>
<p> 作业详情——定义了被调度任务。 JobDetail 实现 了 org.quartz.Job jobDetail 接口,JobDetail 实例后注册到 Scheduler 中。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CronTriggerFactoryBean</span> <span class="kd">implements</span> <span class="n">FactoryBean</span><span class="o"><</span><span class="n">CronTrigger</span><span class="o">>,</span> <span class="n">BeanNameAware</span><span class="o">,</span> <span class="n">InitializingBean</span> <span class="o">{</span>
<span class="kd">private</span> <span class="n">CronTrigger</span> <span class="n">cronTrigger</span><span class="o">;</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">afterPropertiesSet</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">ParseException</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">name</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">name</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">beanName</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">group</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">group</span> <span class="o">=</span> <span class="n">Scheduler</span><span class="o">.</span><span class="na">DEFAULT_GROUP</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">jobDetail</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">jobDataMap</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"jobDetail"</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">jobDetail</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">startDelay</span> <span class="o">></span> <span class="mi">0</span> <span class="o">||</span> <span class="k">this</span><span class="o">.</span><span class="na">startTime</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">startTime</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Date</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="k">this</span><span class="o">.</span><span class="na">startDelay</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">timeZone</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">timeZone</span> <span class="o">=</span> <span class="n">TimeZone</span><span class="o">.</span><span class="na">getDefault</span><span class="o">();</span>
<span class="o">}</span>
<span class="c1">// cronTrigger 实例使用了 CronTriggerImpl 生成</span>
<span class="n">CronTriggerImpl</span> <span class="n">cti</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CronTriggerImpl</span><span class="o">();</span>
<span class="n">cti</span><span class="o">.</span><span class="na">setName</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">name</span><span class="o">);</span>
<span class="n">cti</span><span class="o">.</span><span class="na">setGroup</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">group</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">jobDetail</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">cti</span><span class="o">.</span><span class="na">setJobKey</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">jobDetail</span><span class="o">.</span><span class="na">getKey</span><span class="o">());</span>
<span class="o">}</span>
<span class="n">cti</span><span class="o">.</span><span class="na">setJobDataMap</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">jobDataMap</span><span class="o">);</span>
<span class="n">cti</span><span class="o">.</span><span class="na">setStartTime</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">startTime</span><span class="o">);</span>
<span class="n">cti</span><span class="o">.</span><span class="na">setCronExpression</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">cronExpression</span><span class="o">);</span>
<span class="n">cti</span><span class="o">.</span><span class="na">setTimeZone</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">timeZone</span><span class="o">);</span>
<span class="n">cti</span><span class="o">.</span><span class="na">setCalendarName</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">calendarName</span><span class="o">);</span>
<span class="n">cti</span><span class="o">.</span><span class="na">setPriority</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">priority</span><span class="o">);</span>
<span class="n">cti</span><span class="o">.</span><span class="na">setMisfireInstruction</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">misfireInstruction</span><span class="o">);</span>
<span class="n">cti</span><span class="o">.</span><span class="na">setDescription</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">description</span><span class="o">);</span>
<span class="k">this</span><span class="o">.</span><span class="na">cronTrigger</span> <span class="o">=</span> <span class="n">cti</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p> CronTrigger 实现了 Trigger 的接口,</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CronTrigger</span> <span class="kd">implements</span> <span class="n">Trigger</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">CronSequenceGenerator</span> <span class="n">sequenceGenerator</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p> CronSequenceGenerator 里对定时器的运行规则做了定义,expression 模式用六个空格分割的域,从左到右分别是:秒 分 时 日 月 周。月和周可以使用英文的前三个字母表示。比如 “0 0 8-10 * * *” 代表每天 8、9、10点。expression 会被拆解到具体的时间上,这里用了 BitSet 数据结构存储时间,类的构造器里面,通过传进来的 expression 参数,进行分解,然后根据定义的规则进行定义,就可以获取到具体的时间。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CronSequenceGenerator</span> <span class="o">{</span>
<span class="c1">// 定时器重复的模式</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">expression</span><span class="o">;</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">TimeZone</span> <span class="n">timeZone</span><span class="o">;</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">BitSet</span> <span class="n">months</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BitSet</span><span class="o">(</span><span class="mi">12</span><span class="o">);</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">BitSet</span> <span class="n">daysOfMonth</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BitSet</span><span class="o">(</span><span class="mi">31</span><span class="o">);</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">BitSet</span> <span class="n">daysOfWeek</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BitSet</span><span class="o">(</span><span class="mi">7</span><span class="o">);</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">BitSet</span> <span class="n">hours</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BitSet</span><span class="o">(</span><span class="mi">24</span><span class="o">);</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">BitSet</span> <span class="n">minutes</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BitSet</span><span class="o">(</span><span class="mi">60</span><span class="o">);</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">BitSet</span> <span class="n">seconds</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BitSet</span><span class="o">(</span><span class="mi">60</span><span class="o">);</span>
<span class="c1">// 被构造器调用的解析方法</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">parse</span><span class="o">(</span><span class="n">String</span> <span class="n">expression</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IllegalArgumentException</span> <span class="o">{</span>
<span class="c1">// 空格拆解为6个时间域</span>
<span class="n">String</span><span class="o">[]</span> <span class="n">fields</span> <span class="o">=</span> <span class="n">StringUtils</span><span class="o">.</span><span class="na">tokenizeToStringArray</span><span class="o">(</span><span class="n">expression</span><span class="o">,</span> <span class="s">" "</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">areValidCronFields</span><span class="o">(</span><span class="n">fields</span><span class="o">))</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="n">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span>
<span class="s">"Cron expression must consist of 6 fields (found %d in \"%s\")"</span><span class="o">,</span> <span class="n">fields</span><span class="o">.</span><span class="na">length</span><span class="o">,</span> <span class="n">expression</span><span class="o">));</span>
<span class="o">}</span>
<span class="n">doParse</span><span class="o">(</span><span class="n">fields</span><span class="o">);</span>
<span class="o">}</span>
<span class="c1">// 分别对6个域拆解分析</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">doParse</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">fields</span><span class="o">)</span> <span class="o">{</span>
<span class="n">setNumberHits</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">seconds</span><span class="o">,</span> <span class="n">fields</span><span class="o">[</span><span class="mi">0</span><span class="o">],</span> <span class="mi">0</span><span class="o">,</span> <span class="mi">60</span><span class="o">);</span>
<span class="n">setNumberHits</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">minutes</span><span class="o">,</span> <span class="n">fields</span><span class="o">[</span><span class="mi">1</span><span class="o">],</span> <span class="mi">0</span><span class="o">,</span> <span class="mi">60</span><span class="o">);</span>
<span class="n">setNumberHits</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">hours</span><span class="o">,</span> <span class="n">fields</span><span class="o">[</span><span class="mi">2</span><span class="o">],</span> <span class="mi">0</span><span class="o">,</span> <span class="mi">24</span><span class="o">);</span>
<span class="n">setDaysOfMonth</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">daysOfMonth</span><span class="o">,</span> <span class="n">fields</span><span class="o">[</span><span class="mi">3</span><span class="o">]);</span>
<span class="c1">// 月和周因为是用英文的前三个字母描述,所以得把字母替换成数字再处理</span>
<span class="n">setMonths</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">months</span><span class="o">,</span> <span class="n">fields</span><span class="o">[</span><span class="mi">4</span><span class="o">]);</span>
<span class="n">setDays</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">daysOfWeek</span><span class="o">,</span> <span class="n">replaceOrdinals</span><span class="o">(</span><span class="n">fields</span><span class="o">[</span><span class="mi">5</span><span class="o">],</span> <span class="s">"SUN,MON,TUE,WED,THU,FRI,SAT"</span><span class="o">),</span> <span class="mi">8</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">daysOfWeek</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">7</span><span class="o">))</span> <span class="o">{</span>
<span class="c1">// Sunday can be represented as 0 or 7</span>
<span class="k">this</span><span class="o">.</span><span class="na">daysOfWeek</span><span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
<span class="k">this</span><span class="o">.</span><span class="na">daysOfWeek</span><span class="o">.</span><span class="na">clear</span><span class="o">(</span><span class="mi">7</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="c1">// 对单独的一个时间区域进行规则匹配分解</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">setNumberHits</span><span class="o">(</span><span class="n">BitSet</span> <span class="n">bits</span><span class="o">,</span> <span class="n">String</span> <span class="n">value</span><span class="o">,</span> <span class="kt">int</span> <span class="n">min</span><span class="o">,</span> <span class="kt">int</span> <span class="n">max</span><span class="o">)</span> <span class="o">{</span>
<span class="n">String</span><span class="o">[]</span> <span class="n">fields</span> <span class="o">=</span> <span class="n">StringUtils</span><span class="o">.</span><span class="na">delimitedListToStringArray</span><span class="o">(</span><span class="n">value</span><span class="o">,</span> <span class="s">","</span><span class="o">);</span>
<span class="k">for</span> <span class="o">(</span><span class="n">String</span> <span class="n">field</span> <span class="o">:</span> <span class="n">fields</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">field</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="s">"/"</span><span class="o">))</span> <span class="o">{</span> <span class="c1">// 单独一个值</span>
<span class="c1">// Not an incrementer so it must be a range (possibly empty)</span>
<span class="kt">int</span><span class="o">[]</span> <span class="n">range</span> <span class="o">=</span> <span class="n">getRange</span><span class="o">(</span><span class="n">field</span><span class="o">,</span> <span class="n">min</span><span class="o">,</span> <span class="n">max</span><span class="o">);</span>
<span class="n">bits</span><span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="n">range</span><span class="o">[</span><span class="mi">0</span><span class="o">],</span> <span class="n">range</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">+</span> <span class="mi">1</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">else</span> <span class="o">{</span> <span class="c1">// 类似 "0/30"</span>
<span class="n">String</span><span class="o">[]</span> <span class="n">split</span> <span class="o">=</span> <span class="n">StringUtils</span><span class="o">.</span><span class="na">delimitedListToStringArray</span><span class="o">(</span><span class="n">field</span><span class="o">,</span> <span class="s">"/"</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">split</span><span class="o">.</span><span class="na">length</span> <span class="o">></span> <span class="mi">2</span><span class="o">)</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Incrementer has more than two fields: '"</span> <span class="o">+</span>
<span class="n">field</span> <span class="o">+</span> <span class="s">"' in expression \""</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">expression</span> <span class="o">+</span> <span class="s">"\""</span><span class="o">);</span>
<span class="o">}</span>
<span class="kt">int</span><span class="o">[]</span> <span class="n">range</span> <span class="o">=</span> <span class="n">getRange</span><span class="o">(</span><span class="n">split</span><span class="o">[</span><span class="mi">0</span><span class="o">],</span> <span class="n">min</span><span class="o">,</span> <span class="n">max</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">split</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="na">contains</span><span class="o">(</span><span class="s">"-"</span><span class="o">))</span> <span class="o">{</span>
<span class="n">range</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">=</span> <span class="n">max</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="kt">int</span> <span class="n">delta</span> <span class="o">=</span> <span class="n">Integer</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">split</span><span class="o">[</span><span class="mi">1</span><span class="o">]);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">delta</span> <span class="o"><=</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Incrementer delta must be 1 or higher: '"</span> <span class="o">+</span>
<span class="n">field</span> <span class="o">+</span> <span class="s">"' in expression \""</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">expression</span> <span class="o">+</span> <span class="s">"\""</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">range</span><span class="o">[</span><span class="mi">0</span><span class="o">];</span> <span class="n">i</span> <span class="o"><=</span> <span class="n">range</span><span class="o">[</span><span class="mi">1</span><span class="o">];</span> <span class="n">i</span> <span class="o">+=</span> <span class="n">delta</span><span class="o">)</span> <span class="o">{</span>
<span class="n">bits</span><span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="c1">// 对一个域里面拆出来的一个值继续分解</span>
<span class="kd">private</span> <span class="kt">int</span><span class="o">[]</span> <span class="nf">getRange</span><span class="o">(</span><span class="n">String</span> <span class="n">field</span><span class="o">,</span> <span class="kt">int</span> <span class="n">min</span><span class="o">,</span> <span class="kt">int</span> <span class="n">max</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span><span class="o">[]</span> <span class="n">result</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">int</span><span class="o">[</span><span class="mi">2</span><span class="o">];</span>
<span class="k">if</span> <span class="o">(</span><span class="n">field</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="s">"*"</span><span class="o">))</span> <span class="o">{</span> <span class="c1">// 没有设置,则是这个区间所有的值都被包括</span>
<span class="n">result</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">=</span> <span class="n">min</span><span class="o">;</span>
<span class="n">result</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">=</span> <span class="n">max</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span>
<span class="k">return</span> <span class="n">result</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">field</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="s">"-"</span><span class="o">))</span> <span class="o">{</span> <span class="c1">// 单值数字的时候,区间值都是同一个数</span>
<span class="n">result</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">=</span> <span class="n">result</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">=</span> <span class="n">Integer</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">field</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">else</span> <span class="o">{</span> <span class="c1">// 类似 "8-10"</span>
<span class="n">String</span><span class="o">[]</span> <span class="n">split</span> <span class="o">=</span> <span class="n">StringUtils</span><span class="o">.</span><span class="na">delimitedListToStringArray</span><span class="o">(</span><span class="n">field</span><span class="o">,</span> <span class="s">"-"</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">split</span><span class="o">.</span><span class="na">length</span> <span class="o">></span> <span class="mi">2</span><span class="o">)</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Range has more than two fields: '"</span> <span class="o">+</span>
<span class="n">field</span> <span class="o">+</span> <span class="s">"' in expression \""</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">expression</span> <span class="o">+</span> <span class="s">"\""</span><span class="o">);</span>
<span class="o">}</span>
<span class="n">result</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">=</span> <span class="n">Integer</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">split</span><span class="o">[</span><span class="mi">0</span><span class="o">]);</span>
<span class="n">result</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">=</span> <span class="n">Integer</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">split</span><span class="o">[</span><span class="mi">1</span><span class="o">]);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">result</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">>=</span> <span class="n">max</span> <span class="o">||</span> <span class="n">result</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">>=</span> <span class="n">max</span><span class="o">)</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Range exceeds maximum ("</span> <span class="o">+</span> <span class="n">max</span> <span class="o">+</span> <span class="s">"): '"</span> <span class="o">+</span>
<span class="n">field</span> <span class="o">+</span> <span class="s">"' in expression \""</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">expression</span> <span class="o">+</span> <span class="s">"\""</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">result</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o"><</span> <span class="n">min</span> <span class="o">||</span> <span class="n">result</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o"><</span> <span class="n">min</span><span class="o">)</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Range less than minimum ("</span> <span class="o">+</span> <span class="n">min</span> <span class="o">+</span> <span class="s">"): '"</span> <span class="o">+</span>
<span class="n">field</span> <span class="o">+</span> <span class="s">"' in expression \""</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">expression</span> <span class="o">+</span> <span class="s">"\""</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">result</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">></span> <span class="n">result</span><span class="o">[</span><span class="mi">1</span><span class="o">])</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Invalid inverted range: '"</span> <span class="o">+</span> <span class="n">field</span> <span class="o">+</span>
<span class="s">"' in expression \""</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">expression</span> <span class="o">+</span> <span class="s">"\""</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">result</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>MethodInvokingJobDetailFactoryBean</strong></p>
<p> 具体的任务执行 bean 由 之前 3.2.x 版本的 BeanInvokingJobDetailFactoryBean 替换成了 MethodInvokingJobDetailFactoryBean,可配置项也更加丰富。可以指定具体的被调度的类,和被调度类的实例的方法。该类也同样的实现了 FactoryBean 接口。如下是定义了串行的运行 ID 为 testSkuExecutor 实例的 execute 方法.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><property</span> <span class="na">name=</span><span class="s">"concurrent"</span> <span class="na">value=</span><span class="s">"false"</span><span class="nt">/></span>
<span class="nt"><property</span> <span class="na">name=</span><span class="s">"targetBean"</span> <span class="na">value=</span><span class="s">"testSkuExecutor"</span><span class="nt">/></span>
<span class="nt"><property</span> <span class="na">name=</span><span class="s">"targetMethod"</span> <span class="na">value=</span><span class="s">"execute"</span><span class="nt">/></span>
</code></pre></div></div>
<p><strong>SchedulerFactoryBean</strong></p>
<p> 在调度任务中执行持久化操作时,强烈推荐使用 Spring 管理或者 JTA 的事务方式。否则,数据库锁将不能正常的工作和可能被破坏。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">SchedulerFactoryBean</span> <span class="kd">extends</span> <span class="n">SchedulerAccessor</span> <span class="kd">implements</span> <span class="n">FactoryBean</span><span class="o"><</span><span class="n">Scheduler</span><span class="o">>,</span>
<span class="n">BeanNameAware</span><span class="o">,</span> <span class="n">ApplicationContextAware</span><span class="o">,</span> <span class="n">InitializingBean</span><span class="o">,</span> <span class="n">DisposableBean</span><span class="o">,</span> <span class="n">SmartLifecycle</span> <span class="o">{</span>
<span class="c1">// 父类 SchedulerAccessor 中定义的数据结构,用来保存触发器列表的id值</span>
<span class="kd">private</span> <span class="n">List</span><span class="o"><</span><span class="n">Trigger</span><span class="o">></span> <span class="n">triggers</span><span class="o">;</span>
<span class="kd">private</span> <span class="n">DataSource</span> <span class="n">dataSource</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>Scheduler</strong></p>
<p> org.quartz 包中的 Scheduler 维护了 JobDetail 和 Triger 的注册。注册后,一旦关联的 Trigger 到触发点, Scheduler 会负责相应的 Job 执行。SchedulerFactory 负责生成 Scheduler 的实例。Scheduler 创建后必须执行 start() 方法才可以运行 Job。scheduleJob(JobDetail, Trigger) 或者 addJob(JobDetail, boolean) 方法用来注册 JobDetail 的实例。</p>
<p>Job 和 Trigger 会被命名并且加入到 group,group 用来创建逻辑的组或者有关 Job 和 Triggers 的类目,默认可以使用名字为常量的 DEFAULT_GROUP group。</p>
<p> 程序提供了监听 JobListener 接口实现监听相关的事件。TriggerListener 提供了 Trigger 触发点的运行,SchedulerListener 提供了 Scheduler 运行的事件和错误。使用 ListenerManager 添加监听事件。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">Scheduler</span> <span class="o">{</span>
<span class="n">Date</span> <span class="nf">scheduleJob</span><span class="o">(</span><span class="n">JobDetail</span> <span class="n">jobDetail</span><span class="o">,</span> <span class="n">Trigger</span> <span class="n">trigger</span><span class="o">)</span>
<span class="kd">throws</span> <span class="n">SchedulerException</span><span class="o">;</span>
<span class="c1">// 添加 JobDetail 且没有 Trigger 关联,Job 将会被搁置,直至用一个 Trigger 进行调度,或者被 cheduler.triggerJob() 调用</span>
<span class="kt">void</span> <span class="nf">addJob</span><span class="o">(</span><span class="n">JobDetail</span> <span class="n">jobDetail</span><span class="o">,</span> <span class="kt">boolean</span> <span class="n">replace</span><span class="o">)</span>
<span class="kd">throws</span> <span class="n">SchedulerException</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p> Spring 3.2.x 下的定时任务使用:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><bean</span> <span class="na">id=</span><span class="s">"scheduler"</span> <span class="na">class=</span><span class="s">"org.springframework.scheduling.quartz.SchedulerFactoryBean"</span><span class="nt">></span>
<span class="nt"><property</span> <span class="na">name=</span><span class="s">"configLocation"</span> <span class="na">value=</span><span class="s">"classpath:quartz.properties"</span> <span class="nt">/></span>
<span class="nt"><property</span> <span class="na">name=</span><span class="s">"dataSource"</span> <span class="na">ref=</span><span class="s">"dataSource"</span> <span class="nt">/></span>
<span class="nt"><property</span> <span class="na">name=</span><span class="s">"overwriteExistingJobs"</span> <span class="na">value=</span><span class="s">"true"</span> <span class="nt">/></span>
<span class="c"><!-- 被调度的 Trigger 列表 --></span>
<span class="nt"><property</span> <span class="na">name=</span><span class="s">"triggers"</span><span class="nt">></span>
<span class="nt"><list></span>
<span class="nt"><ref</span> <span class="na">bean=</span><span class="s">"testExecutorCronTrigger"</span> <span class="nt">/></span>
<span class="nt"></list></span>
<span class="nt"></property></span>
<span class="nt"></bean></span>
<span class="c"><!-- 被调度的 JobDetail --></span>
<span class="nt"><bean</span> <span class="na">id=</span><span class="s">"testExecutorJobDetail"</span> <span class="na">class=</span><span class="s">"frameworkx.springframework.scheduling.quartz.BeanInvokingJobDetailFactoryBean"</span><span class="nt">></span>
<span class="c"><!-- 具体调度任务执行的类id --></span>
<span class="nt"><property</span> <span class="na">name=</span><span class="s">"targetBean"</span> <span class="na">value=</span><span class="s">"testExecutor"</span> <span class="nt">/></span>
<span class="c"><!-- 具体调度任务执行的类的方法 --></span>
<span class="nt"><property</span> <span class="na">name=</span><span class="s">"targetMethod"</span> <span class="na">value=</span><span class="s">"execute"</span> <span class="nt">/></span>
<span class="c"><!-- 是否允许并行执行 --></span>
<span class="nt"><property</span> <span class="na">name=</span><span class="s">"concurrent"</span> <span class="na">value=</span><span class="s">"false"</span> <span class="nt">/></span>
<span class="nt"></bean></span>
<span class="c"><!-- 被调度的 Trigger --></span>
<span class="nt"><bean</span> <span class="na">id=</span><span class="s">"testExecutorCronTrigger"</span> <span class="na">class=</span><span class="s">"org.springframework.scheduling.quartz.CronTriggerBean"</span><span class="nt">></span>
<span class="c"><!-- 相关的 JobDetail --></span>
<span class="nt"><property</span> <span class="na">name=</span><span class="s">"jobDetail"</span> <span class="na">ref=</span><span class="s">"testExecutorJobDetail"</span> <span class="nt">/></span>
<span class="c"><!-- 触发器的时间定义:频率为每2分钟一次 --></span>
<span class="nt"><property</span> <span class="na">name=</span><span class="s">"cronExpression"</span> <span class="na">value=</span><span class="s">"0 */2 * * * ?"</span> <span class="nt">/></span>
<span class="nt"></bean></span>
</code></pre></div></div>cpp255Spring 定时器实现原理(1)Spring 多线程2018-01-18T04:05:35+08:002018-01-18T04:05:35+08:00https://cpp255.github.io/2018/01/17/Spring-Thread<h1 id="spring-多线程">Spring 多线程</h1>
<hr />
<p> Spring 的多线程开发的时候也会涉及到的,定时器、请求、异步任务等,不过很多都是Spring封装好的,拿来就用,最近项目里面用到了,就在 Spring 4.3.x 的版本上整理了相关的类。Spring 涉及到的类在好几个模块下:</p>
<p>1.基础类和接口定义在 spring-core 模块中,主要有:TaskExecutor、SyncTaskExecutor、AsyncTaskExecutor、SimpleAsyncTaskExecutor、TaskExecutorAdapter等。可以拿来即用,但是比较简单,一般无法满足我们的需求。其他模块相关的,都是继承和实现这个模块中的类。</p>
<p>2.定时器相关的类在 spring-context-support 中,有 scheduling.quartz, scheduling.commonj 2个包,另写一份文档。</p>
<p>3.spring-context 模块中的 scheduling 包下,有基础类的更完善的实现,比如:ThreadPoolExecutorFactoryBean、ThreadPoolTaskExecutor、ScheduledExecutorService等,这些类几乎可以满足我们的需求了。</p>
<p><strong>基础的TaskExecutor</strong></p>
<p>TaskExecutor 是最基础的任务执行接口,实现可以使用不同的执行策略:同步、异步、线程池等。跟jdk1.5的java.util.concurrent.Executor是相等的,Spring 3.0后是继承的 Executor。</p>
<p><strong>同步的SyncTaskExecutor</strong></p>
<p>主要用在测试场景,在许多场景中,推荐使用异步的 asynchronous</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">SyncTaskExecutor</span> <span class="kd">implements</span> <span class="n">TaskExecutor</span><span class="o">,</span> <span class="n">Serializable</span> <span class="o">{</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>异步的AsyncTaskExecutor</strong></p>
<p>实现 这个接口意味着 execute(Runnable) 方法,不会在调用线程中执行 Runnable,而是异步的在其他线程执行</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">AsyncTaskExecutor</span> <span class="kd">extends</span> <span class="n">TaskExecutor</span> <span class="o">{</span>
<span class="cm">/** Constant that indicates immediate execution */</span>
<span class="c1">// 立即执行</span>
<span class="kt">long</span> <span class="n">TIMEOUT_IMMEDIATE</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="cm">/** Constant that indicates no time limit */</span>
<span class="c1">// 执行时间不受限制 </span>
<span class="kt">long</span> <span class="n">TIMEOUT_INDEFINITE</span> <span class="o">=</span> <span class="n">Long</span><span class="o">.</span><span class="na">MAX_VALUE</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>SimpleAsyncTaskExecutor</strong></p>
<p>实现了 TaskExecutor ,为每一个 task 生成一个新的线程,异步执行。支持通过bean的配置 “concurrencyLimit” 实现线程数的配置,默认线程数是无限的。使用于大量的短任务。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@SuppressWarnings</span><span class="o">(</span><span class="s">"serial"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">SimpleAsyncTaskExecutor</span> <span class="kd">extends</span> <span class="n">CustomizableThreadCreator</span>
<span class="kd">implements</span> <span class="n">AsyncListenableTaskExecutor</span><span class="o">,</span> <span class="n">Serializable</span> <span class="o">{</span>
<span class="cm">/**
* execute 方法最终会调用该方法,默认如果没有实现 *threadFactory,则创建一个新的线程,并开始运行
**/</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">doExecute</span><span class="o">(</span><span class="n">Runnable</span> <span class="n">task</span><span class="o">)</span> <span class="o">{</span>
<span class="n">Thread</span> <span class="n">thread</span> <span class="o">=</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">threadFactory</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">?</span> <span class="k">this</span><span class="o">.</span><span class="na">threadFactory</span><span class="o">.</span><span class="na">newThread</span><span class="o">(</span><span class="n">task</span><span class="o">)</span> <span class="o">:</span> <span class="n">createThread</span><span class="o">(</span><span class="n">task</span><span class="o">));</span>
<span class="n">thread</span><span class="o">.</span><span class="na">start</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>**TaskExecutorAdapter **</p>
<p>适配器使用jdk的 Executor 暴露了 Spring 的 TaskExecutor,以此实现一个 TaskExecutor 运行.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">TaskExecutorAdapter</span> <span class="kd">implements</span> <span class="n">AsyncListenableTaskExecutor</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">Executor</span> <span class="n">concurrentExecutor</span><span class="o">;</span>
<span class="kd">private</span> <span class="n">TaskDecorator</span> <span class="n">taskDecorator</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>ThreadPoolExecutorFactoryBean</strong></p>
<p>实现了 FactoryBean 接口的类,通过 getObject() 方法可以获取到 ExecutorService 的实例,ExecutorService 的实现也是用的ThreadPoolExecutor。当然,这里面的 corePoolSize、maxPoolSize默认是1、 Integer.MAX_VALUE,不适合开发具体的场景的话,可以再修改参数。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ThreadPoolExecutorFactoryBean</span> <span class="kd">extends</span> <span class="n">ExecutorConfigurationSupport</span>
<span class="kd">implements</span> <span class="n">FactoryBean</span><span class="o"><</span><span class="n">ExecutorService</span><span class="o">>,</span> <span class="n">InitializingBean</span><span class="o">,</span> <span class="n">DisposableBean</span> <span class="o">{</span>
<span class="kd">private</span> <span class="n">ExecutorService</span> <span class="n">exposedExecutor</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>ThreadPoolTaskExecutor</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ThreadPoolTaskExecutor</span> <span class="kd">extends</span> <span class="n">ExecutorConfigurationSupport</span>
<span class="kd">implements</span> <span class="n">AsyncListenableTaskExecutor</span><span class="o">,</span> <span class="n">SchedulingTaskExecutor</span> <span class="o">{</span>
<span class="kd">private</span> <span class="n">ThreadPoolExecutor</span> <span class="n">threadPoolExecutor</span><span class="o">;</span>
<span class="c1">// 具体的ThreadPoolExecutor初始化</span>
<span class="nd">@Override</span>
<span class="kd">protected</span> <span class="n">ExecutorService</span> <span class="nf">initializeExecutor</span><span class="o">(</span>
<span class="n">ThreadFactory</span> <span class="n">threadFactory</span><span class="o">,</span> <span class="n">RejectedExecutionHandler</span> <span class="n">rejectedExecutionHandler</span><span class="o">)</span> <span class="o">{</span>
<span class="n">BlockingQueue</span><span class="o"><</span><span class="n">Runnable</span><span class="o">></span> <span class="n">queue</span> <span class="o">=</span> <span class="n">createQueue</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">queueCapacity</span><span class="o">);</span>
<span class="n">ThreadPoolExecutor</span> <span class="n">executor</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">taskDecorator</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">executor</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ThreadPoolExecutor</span><span class="o">(</span>
<span class="k">this</span><span class="o">.</span><span class="na">corePoolSize</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">maxPoolSize</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">keepAliveSeconds</span><span class="o">,</span> <span class="n">TimeUnit</span><span class="o">.</span><span class="na">SECONDS</span><span class="o">,</span>
<span class="n">queue</span><span class="o">,</span> <span class="n">threadFactory</span><span class="o">,</span> <span class="n">rejectedExecutionHandler</span><span class="o">)</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">execute</span><span class="o">(</span><span class="n">Runnable</span> <span class="n">command</span><span class="o">)</span> <span class="o">{</span>
<span class="kd">super</span><span class="o">.</span><span class="na">execute</span><span class="o">(</span><span class="n">taskDecorator</span><span class="o">.</span><span class="na">decorate</span><span class="o">(</span><span class="n">command</span><span class="o">));</span>
<span class="o">}</span>
<span class="o">};</span>
<span class="o">}</span>
<span class="k">else</span> <span class="o">{</span>
<span class="n">executor</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ThreadPoolExecutor</span><span class="o">(</span>
<span class="k">this</span><span class="o">.</span><span class="na">corePoolSize</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">maxPoolSize</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">keepAliveSeconds</span><span class="o">,</span> <span class="n">TimeUnit</span><span class="o">.</span><span class="na">SECONDS</span><span class="o">,</span>
<span class="n">queue</span><span class="o">,</span> <span class="n">threadFactory</span><span class="o">,</span> <span class="n">rejectedExecutionHandler</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">allowCoreThreadTimeOut</span><span class="o">)</span> <span class="o">{</span>
<span class="n">executor</span><span class="o">.</span><span class="na">allowCoreThreadTimeOut</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">this</span><span class="o">.</span><span class="na">threadPoolExecutor</span> <span class="o">=</span> <span class="n">executor</span><span class="o">;</span>
<span class="k">return</span> <span class="n">executor</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>ThreadPoolTaskScheduler</strong></p>
<p>带调度功能的线程池配置,线程池实现是由 ScheduledExecutorService 的实现类 ScheduledThreadPoolExecutor,也有一些调度的方法。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ThreadPoolTaskScheduler</span> <span class="kd">extends</span> <span class="n">ExecutorConfigurationSupport</span>
<span class="kd">implements</span> <span class="n">AsyncListenableTaskExecutor</span><span class="o">,</span> <span class="n">SchedulingTaskExecutor</span><span class="o">,</span> <span class="n">TaskScheduler</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">volatile</span> <span class="n">ScheduledExecutorService</span> <span class="n">scheduledExecutor</span><span class="o">;</span>
<span class="c1">// 具体的ScheduledThreadPoolExecutor实例生成</span>
<span class="nd">@Override</span>
<span class="kd">protected</span> <span class="n">ExecutorService</span> <span class="nf">initializeExecutor</span><span class="o">(</span>
<span class="n">ThreadFactory</span> <span class="n">threadFactory</span><span class="o">,</span> <span class="n">RejectedExecutionHandler</span> <span class="n">rejectedExecutionHandler</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">scheduledExecutor</span> <span class="o">=</span> <span class="n">createExecutor</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">poolSize</span><span class="o">,</span> <span class="n">threadFactory</span><span class="o">,</span> <span class="n">rejectedExecutionHandler</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">removeOnCancelPolicy</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">setRemoveOnCancelPolicyAvailable</span> <span class="o">&&</span> <span class="k">this</span><span class="o">.</span><span class="na">scheduledExecutor</span> <span class="k">instanceof</span> <span class="n">ScheduledThreadPoolExecutor</span><span class="o">)</span> <span class="o">{</span>
<span class="o">((</span><span class="n">ScheduledThreadPoolExecutor</span><span class="o">)</span> <span class="k">this</span><span class="o">.</span><span class="na">scheduledExecutor</span><span class="o">).</span><span class="na">setRemoveOnCancelPolicy</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">else</span> <span class="o">{</span>
<span class="n">logger</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"Could not apply remove-on-cancel policy - not a Java 7+ ScheduledThreadPoolExecutor"</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">return</span> <span class="k">this</span><span class="o">.</span><span class="na">scheduledExecutor</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">protected</span> <span class="n">ScheduledExecutorService</span> <span class="nf">createExecutor</span><span class="o">(</span>
<span class="kt">int</span> <span class="n">poolSize</span><span class="o">,</span> <span class="n">ThreadFactory</span> <span class="n">threadFactory</span><span class="o">,</span> <span class="n">RejectedExecutionHandler</span> <span class="n">rejectedExecutionHandler</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">ScheduledThreadPoolExecutor</span><span class="o">(</span><span class="n">poolSize</span><span class="o">,</span> <span class="n">threadFactory</span><span class="o">,</span> <span class="n">rejectedExecutionHandler</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>cpp255Spring 多线程Thread2017-12-28T02:46:55+08:002017-12-28T02:46:55+08:00https://cpp255.github.io/2017/12/27/Thread-sources<h2 id="thread-实现">#Thread 实现</h2>
<p>需要等待前置线程处理完后,才允许当前线程执行或者结束,可以有不同的实现方式,join 方法是其中之一。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Waits for this thread to die (等待发起调用方法的这个线程死亡)</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kt">void</span> <span class="nf">join</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">InterruptedException</span> <span class="o">{</span><span class="err">
</span>
<span class="n">join</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span> <span class="c1">// 0 表示一直等待
</span>
<span class="o">}</span>
</code></pre></div></div>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait *forever.
* 等待最多 millis 时间该线程结束,否则超时则继续运行。0 表示一直运行等待线程结束。
**/</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kd">synchronized</span> <span class="kt">void</span> <span class="nf">join</span><span class="o">(</span><span class="kt">long</span> <span class="n">millis</span><span class="o">)</span><span class="err">
</span><span class="kd">throws</span> <span class="n">InterruptedException</span> <span class="o">{</span><span class="err">
</span>
<span class="kt">long</span> <span class="n">base</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="err">
</span>
<span class="kt">long</span> <span class="n">now</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span><span class="err">
</span>
<span class="k">if</span> <span class="o">(</span><span class="n">millis</span> <span class="o"><</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span><span class="err">
</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"timeout value is negative"</span><span class="o">);</span><span class="err">
</span>
<span class="o">}</span><span class="err">
</span>
<span class="k">if</span> <span class="o">(</span><span class="n">millis</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span><span class="err">
</span>
<span class="k">while</span> <span class="o">(</span><span class="n">isAlive</span><span class="o">())</span> <span class="o">{</span> <span class="c1">// 0 表示只要当线程满足 isAlive 则循环等待
</span>
<span class="n">wait</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span><span class="err">
</span>
<span class="o">}</span><span class="err">
</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span><span class="err">
</span>
<span class="k">while</span> <span class="o">(</span><span class="n">isAlive</span><span class="o">())</span> <span class="o">{</span><span class="err">
</span>
<span class="kt">long</span> <span class="n">delay</span> <span class="o">=</span> <span class="n">millis</span> <span class="o">-</span> <span class="n">now</span><span class="o">;</span><span class="err">
</span>
<span class="k">if</span> <span class="o">(</span><span class="n">delay</span> <span class="o"><=</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span><span class="err">
</span> <span class="c1">// 时间到后,结束等待,终止循环</span>
<span class="k">break</span><span class="o">;</span><span class="err">
</span>
<span class="o">}</span><span class="err">
</span>
<span class="n">wait</span><span class="o">(</span><span class="n">delay</span><span class="o">);</span><span class="err">
</span>
<span class="n">now</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">base</span><span class="o">;</span><span class="err">
</span>
<span class="o">}</span><span class="err">
</span>
<span class="o">}</span>
<span class="err">
</span><span class="o">}</span>
</code></pre></div></div>
<p>还有另一个方法 join(long millis, int nanos) 多了一个参数,表示join() 的时间为: millis + nanos; 最后还是根据输入值换算成 join(long millis)</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">final</span> <span class="kd">synchronized</span> <span class="kt">void</span> <span class="nf">join</span><span class="o">(</span><span class="kt">long</span> <span class="n">millis</span><span class="o">,</span> <span class="kt">int</span> <span class="n">nanos</span><span class="o">)</span><span class="err">
</span><span class="kd">throws</span> <span class="n">InterruptedException</span> <span class="o">{</span><span class="err">
</span>
<span class="k">if</span> <span class="o">(</span><span class="n">millis</span> <span class="o"><</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span><span class="err">
</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"timeout value is negative"</span><span class="o">);</span><span class="err">
</span>
<span class="o">}</span><span class="err">
</span>
<span class="k">if</span> <span class="o">(</span><span class="n">nanos</span> <span class="o"><</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">nanos</span> <span class="o">></span> <span class="mi">999999</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// 超过了 1 ms 或者 负值不满足条件
</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="err">
</span><span class="s">"nanosecond timeout value out of range"</span><span class="o">);</span><span class="err">
</span>
<span class="o">}</span><span class="err">
</span>
<span class="k">if</span> <span class="o">(</span><span class="n">nanos</span> <span class="o">>=</span> <span class="mi">500000</span> <span class="o">||</span> <span class="o">(</span><span class="n">nanos</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">&&</span> <span class="n">millis</span> <span class="o">==</span> <span class="mi">0</span><span class="o">))</span> <span class="o">{</span> <span class="c1">// 四舍五入或者最小单位为 1 ms
</span>
<span class="n">millis</span><span class="o">++;</span><span class="err">
</span>
<span class="o">}</span><span class="err">
</span>
<span class="n">join</span><span class="o">(</span><span class="n">millis</span><span class="o">);</span><span class="err">
</span>
<span class="o">}</span>
</code></pre></div></div>
<p>执行 sleep 会使当前线程停止执行给定的时间,但是并不会放弃锁。锁的控制应该是由具体的锁操作,而不能通过线程操作。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">sleep</span><span class="o">(</span><span class="kt">long</span> <span class="n">millis</span><span class="o">,</span> <span class="kt">int</span> <span class="n">nanos</span><span class="o">)</span>
<span class="kd">throws</span> <span class="n">InterruptedException</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">millis</span> <span class="o"><</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"timeout value is negative"</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">nanos</span> <span class="o"><</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">nanos</span> <span class="o">></span> <span class="mi">999999</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// 纳秒数量不对</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span>
<span class="s">"nanosecond timeout value out of range"</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">nanos</span> <span class="o">>=</span> <span class="mi">500000</span> <span class="o">||</span> <span class="o">(</span><span class="n">nanos</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">&&</span> <span class="n">millis</span> <span class="o">==</span> <span class="mi">0</span><span class="o">))</span> <span class="o">{</span> <span class="c1">// 最少是一毫秒,或者四舍五入加一毫秒</span>
<span class="n">millis</span><span class="o">++;</span>
<span class="o">}</span>
<span class="n">sleep</span><span class="o">(</span><span class="n">millis</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>cpp255#Thread 实现 需要等待前置线程处理完后,才允许当前线程执行或者结束,可以有不同的实现方式,join 方法是其中之一。rabbitmq 延迟队列实现2017-12-28T02:46:55+08:002017-12-28T02:46:55+08:00https://cpp255.github.io/2017/12/27/spring-rabbitmq-delay<h2 id="rabbitmq-延迟队列实现">#rabbitmq 延迟队列实现</h2>
<p>message 先发送到 delayExchange,delayExchange 接收后,根据 binding 的 delayQueue 的 ttl 时间,到期后,再通过 x-dead-letter-exchange 设置的 exchange,把消息发送到 businessExchange,businessExchange 的 businessQueue 接收到消息后就可以实现具体的业务处理。</p>
<p><strong>延迟队列定义:</strong></p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"><!-- 延时队列 --></span>
<span class="nt"><rabbit:queue</span> <span class="na">name=</span><span class="s">"delayQueue"</span> <span class="nt">></span>
<span class="nt"><rabbit:queue-arguments></span>
<span class="c"><!--<entry key="x-message-ttl">--></span>
<span class="c"><!--<value type="java.lang.Long">10000</value>--></span>
<span class="c"><!--</entry>--></span>
<span class="nt"><entry</span> <span class="na">key=</span><span class="s">"x-max-length"</span><span class="nt">></span>
<span class="nt"><value</span> <span class="na">type=</span><span class="s">"java.lang.Long"</span><span class="nt">></span>10000000<span class="nt"></value></span>
<span class="nt"></entry></span>
<span class="nt"><entry</span> <span class="na">key=</span><span class="s">"x-dead-letter-exchange"</span> <span class="na">value=</span><span class="s">"businessExchange"</span><span class="nt">/></span>
<span class="nt"></rabbit:queue-arguments></span>
<span class="nt"></rabbit:queue></span>
<span class="nt"><rabbit:topic-exchange</span> <span class="na">name=</span><span class="s">"delayExchange"</span> <span class="nt">></span>
<span class="nt"><rabbit:bindings></span>
<span class="nt"><rabbit:binding</span> <span class="na">queue=</span><span class="s">"delayQueue"</span> <span class="na">pattern=</span><span class="s">"#"</span> <span class="nt">/></span>
<span class="nt"></rabbit:bindings></span>
<span class="nt"></rabbit:topic-exchange></span>
<span class="c"><!-- 具体逻辑处理队列 --></span>
<span class="nt"><rabbit:queue</span> <span class="na">name=</span><span class="s">"businessQueue"</span><span class="nt">/></span>
<span class="nt"><rabbit:topic-exchange</span> <span class="na">name=</span><span class="s">"businessExchange"</span> <span class="nt">></span>
<span class="nt"><rabbit:bindings></span>
<span class="nt"><rabbit:binding</span> <span class="na">queue=</span><span class="s">"businessQueue"</span> <span class="na">pattern=</span><span class="s">"#"</span> <span class="nt">/></span>
<span class="nt"></rabbit:bindings></span>
<span class="nt"></rabbit:topic-exchange></span>
<span class="nt"><rabbit:listener-container</span><span class="err">
</span> <span class="na">connection-factory=</span><span class="s">"connectionFactory"</span> <span class="na">concurrency=</span><span class="s">"2"</span> <span class="na">prefetch=</span><span class="s">"2"</span><span class="nt">></span>
<span class="nt"><rabbit:listener</span> <span class="na">ref=</span><span class="s">"businessQueueListener"</span><span class="err">
</span> <span class="na">method=</span><span class="s">"listen"</span> <span class="na">queue-names=</span><span class="s">"businessQueue"</span> <span class="nt">/></span>
<span class="nt"></rabbit:listener-container></span>
</code></pre></div></div>
<p>“x-message-ttl” 为队列默认的过期时间,如果消息也设置了ttl,默认使用用两个之中最小的时间。</p>
<p>java 发送自定义过期时间,delayTime 默认为毫秒;由于消息队列的特性是先进先出,先进队列的消息如果没有到期或者被消费,后面的消息即使到期也是需要等待,因此该自定义时间有可能出现同时等待处理完毕队列头部长时间后,才被处理:</p>
<p><strong>自定义延时消息发送定义</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">rabbitTemplate</span><span class="o">.</span><span class="na">convertAndSend</span><span class="o">(</span><span class="n">exchange</span><span class="o">,</span> <span class="n">routingKey</span><span class="o">,</span> <span class="n">messageObject</span><span class="o">,</span> <span class="k">new</span> <span class="n">MessagePostProcessor</span><span class="o">()</span> <span class="o">{</span><span class="err">
</span>
<span class="nd">@Override</span><span class="err">
</span>
<span class="kd">public</span> <span class="n">Message</span> <span class="nf">postProcessMessage</span><span class="o">(</span><span class="n">Message</span> <span class="n">message</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">AmqpException</span> <span class="o">{</span><span class="err">
</span>
<span class="n">message</span><span class="o">.</span><span class="na">getMessageProperties</span><span class="o">().</span><span class="na">setExpiration</span><span class="o">(</span><span class="n">String</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">delayTime</span><span class="o">));</span><span class="err">
</span>
<span class="k">return</span> <span class="n">message</span><span class="o">;</span><span class="err">
</span>
<span class="o">}</span><span class="err">
</span>
<span class="o">});</span>
</code></pre></div></div>cpp255#rabbitmq 延迟队列实现 message 先发送到 delayExchange,delayExchange 接收后,根据 binding 的 delayQueue 的 ttl 时间,到期后,再通过 x-dead-letter-exchange 设置的 exchange,把消息发送到 businessExchange,businessExchange 的 businessQueue 接收到消息后就可以实现具体的业务处理。concurrent atomic包 和 Unsafe 类2017-12-17T22:10:20+08:002017-12-17T22:10:20+08:00https://cpp255.github.io/2017/12/17/concurrent-atomic-and-Unsafe<h2 id="concurrent-atomic包-和-unsafe-类">#concurrent atomic包 和 Unsafe 类</h2>
<h3 id="atomic-包">atomic 包</h3>
<p>AtomicBoolean、AtomicInteger、AtomicIntegerArray(数组)、AtomicLong、AtomicLongArray、AtomicReference<V>、AtomicReferenceArray<E>类。
涉及到更新操作主要使用了 Unsafe 对 value 进行更新,value 是 volatile 修饰的;</E></V></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">AtomicInteger</span> <span class="kd">extends</span> <span class="n">Number</span> <span class="kd">implements</span> <span class="n">java</span><span class="o">.</span><span class="na">io</span><span class="o">.</span><span class="na">Serializable</span> <span class="o">{</span>
<span class="c1">// setup to use Unsafe.compareAndSwapInt for updates</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">Unsafe</span> <span class="n">unsafe</span> <span class="o">=</span> <span class="n">Unsafe</span><span class="o">.</span><span class="na">getUnsafe</span><span class="o">();</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">long</span> <span class="n">valueOffset</span><span class="o">;</span>
<span class="kd">static</span> <span class="o">{</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">valueOffset</span> <span class="o">=</span> <span class="n">unsafe</span><span class="o">.</span><span class="na">objectFieldOffset</span>
<span class="o">(</span><span class="n">AtomicInteger</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getDeclaredField</span><span class="o">(</span><span class="s">"value"</span><span class="o">));</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="n">Error</span><span class="o">(</span><span class="n">ex</span><span class="o">);</span> <span class="o">}</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kd">volatile</span> <span class="kt">int</span> <span class="n">value</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<h3 id="unsafe-类">Unsafe 类</h3>
<p>并发编程下面有一个类是不能避免的,Unsafe,通过使用该类提供的一系列方法,可以执行更低层次不安全的操作;虽然这个类和它的所有方法都是 puplic 的,但是被限制使用再只有可信任的代码才能获得实例;注释大体是这个意思,应用层代码不提供实例化的方法,得通过以下方法调用</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">private</span> <span class="kd">static</span> <span class="n">Unsafe</span> <span class="nf">getUnsafeInstance</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">SecurityException</span><span class="o">,</span><span class="err">
</span> <span class="n">NoSuchFieldException</span><span class="o">,</span> <span class="n">IllegalArgumentException</span><span class="o">,</span> <span class="n">IllegalAccessException</span> <span class="o">{</span><span class="err">
</span>
<span class="n">Field</span> <span class="n">theUnsafeInstance</span> <span class="o">=</span> <span class="n">Unsafe</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getDeclaredField</span><span class="o">(</span><span class="s">"theUnsafe"</span><span class="o">);</span><span class="err">
</span>
<span class="n">theUnsafeInstance</span><span class="o">.</span><span class="na">setAccessible</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span><span class="err">
</span>
<span class="k">return</span> <span class="o">(</span><span class="n">Unsafe</span><span class="o">)</span> <span class="n">theUnsafeInstance</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">Unsafe</span><span class="o">.</span><span class="na">class</span><span class="o">);</span><span class="err">
</span>
<span class="o">}</span>
<span class="c1">// offset 就是具体类的字段内存偏移量</span>
<span class="kd">static</span> <span class="o">{</span><span class="err">
</span>
<span class="k">try</span> <span class="o">{</span><span class="err">
</span>
<span class="n">unsafe</span> <span class="o">=</span> <span class="n">getUnsafeInstance</span><span class="o">();</span><span class="err">
</span>
<span class="n">offset</span> <span class="o">=</span> <span class="n">unsafe</span><span class="o">.</span><span class="na">objectFieldOffset</span><span class="o">(</span><span class="n">UnsafeTest</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getDeclaredField</span><span class="o">(</span><span class="s">"flag"</span><span class="o">));</span><span class="err">
</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">NoSuchFieldException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span><span class="err">
</span>
<span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span><span class="err">
</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IllegalAccessException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span><span class="err">
</span>
<span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span><span class="err">
</span>
<span class="o">}</span><span class="err">
</span>
<span class="o">}</span>
<span class="c1">// 类似调用 Unsafe 的方法就可以了</span>
<span class="kd">private</span> <span class="kt">boolean</span> <span class="nf">doSwap</span><span class="o">(</span><span class="kt">long</span> <span class="n">offset</span><span class="o">,</span> <span class="kt">int</span> <span class="n">expect</span><span class="o">,</span> <span class="kt">int</span> <span class="n">update</span><span class="o">)</span> <span class="o">{</span><span class="err">
</span>
<span class="k">return</span> <span class="n">unsafe</span><span class="o">.</span><span class="na">compareAndSwapInt</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">offset</span><span class="o">,</span> <span class="n">expect</span><span class="o">,</span> <span class="n">update</span><span class="o">);</span><span class="err">
</span>
<span class="o">}</span>
</code></pre></div></div>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
* A collection of methods for performing low-level, unsafe operations.
* Although the class and all methods are public, use of this class is
* limited because only trusted code can obtain instances of it.
*
* @author John R. Rose
* @see #getUnsafe
*/</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="nc">Unsafe</span> <span class="o">{</span>
<span class="c1">// 内部实例的名字,</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">Unsafe</span> <span class="n">theUnsafe</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Unsafe</span><span class="o">();</span>
<span class="c1">// 唯一的get获取实例方法,但是被限制了,根据类加载机制,一般的应用代码肯定会抛出异常,只能通过上面的方法获取</span>
<span class="nd">@CallerSensitive</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="n">Unsafe</span> <span class="nf">getUnsafe</span><span class="o">()</span> <span class="o">{</span>
<span class="n">Class</span> <span class="n">cc</span> <span class="o">=</span> <span class="n">Reflection</span><span class="o">.</span><span class="na">getCallerClass</span><span class="o">();</span>
<span class="k">if</span> <span class="o">(</span><span class="n">cc</span><span class="o">.</span><span class="na">getClassLoader</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">SecurityException</span><span class="o">(</span><span class="s">"Unsafe"</span><span class="o">);</span>
<span class="k">return</span> <span class="n">theUnsafe</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>cpp255#concurrent atomic包 和 Unsafe 类 atomic 包 AtomicBoolean、AtomicInteger、AtomicIntegerArray(数组)、AtomicLong、AtomicLongArray、AtomicReference、AtomicReferenceArray类。 涉及到更新操作主要使用了 Unsafe 对 value 进行更新,value 是 volatile 修饰的;Jedis 订阅事件阻塞2017-12-15T20:20:20+08:002017-12-15T20:20:20+08:00https://cpp255.github.io/2017/12/15/Jedis-Keyevent<h1 id="jedis-订阅事件阻塞">Jedis 订阅事件阻塞</h1>
<p>Jedis 开启订阅事件的时候是这样的:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">psubscribe</span><span class="o">(</span><span class="kd">final</span> <span class="n">JedisPubSub</span> <span class="n">jedisPubSub</span><span class="o">,</span><span class="err">
</span> <span class="kd">final</span> <span class="n">String</span><span class="o">...</span> <span class="n">patterns</span><span class="o">)</span> <span class="o">{</span><span class="err">
</span>
<span class="n">checkIsInMulti</span><span class="o">();</span><span class="err">
</span>
<span class="n">connect</span><span class="o">();</span> <span class="c1">// 连接server,底层是通过socket进行连接
</span>
<span class="n">client</span><span class="o">.</span><span class="na">setTimeoutInfinite</span><span class="o">();</span><span class="err">
</span>
<span class="n">jedisPubSub</span><span class="o">.</span><span class="na">proceedWithPatterns</span><span class="o">(</span><span class="n">client</span><span class="o">,</span> <span class="n">patterns</span><span class="o">);</span> <span class="c1">// 主要是这里
</span>
<span class="n">client</span><span class="o">.</span><span class="na">rollbackTimeout</span><span class="o">();</span><span class="err">
</span>
<span class="o">}</span>
</code></pre></div></div>
<p>调用JedisPubSub的代码,process() 方法里面是一个 do while 循环,所以这里肯定会阻塞,只能通过开辟另一个线程进行订阅,否则整个程序就会被阻塞在这里进行循环:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">proceedWithPatterns</span><span class="o">(</span><span class="n">Client</span> <span class="n">client</span><span class="o">,</span> <span class="n">String</span><span class="o">...</span> <span class="n">patterns</span><span class="o">)</span> <span class="o">{</span><span class="err">
</span>
<span class="k">this</span><span class="o">.</span><span class="na">client</span> <span class="o">=</span> <span class="n">client</span><span class="o">;</span>
<span class="c1">// 发送订阅指令,类似 psubscribe '__keyevent*__:*'
</span>
<span class="n">client</span><span class="o">.</span><span class="na">psubscribe</span><span class="o">(</span><span class="n">patterns</span><span class="o">);</span> <span class="err">
</span>
<span class="n">client</span><span class="o">.</span><span class="na">flush</span><span class="o">();</span><span class="err">
</span>
<span class="n">process</span><span class="o">(</span><span class="n">client</span><span class="o">);</span> <span class="c1">// 实时处理返回的指令信息
</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">process</span><span class="o">(</span><span class="n">Client</span> <span class="n">client</span><span class="o">)</span> <span class="o">{</span>
<span class="k">do</span> <span class="o">{</span>
<span class="c1">// 取得相应的数据</span>
<span class="n">List</span><span class="o"><</span><span class="n">Object</span><span class="o">></span> <span class="n">reply</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="na">getRawObjectMultiBulkReply</span><span class="o">();</span>
<span class="kd">final</span> <span class="n">Object</span> <span class="n">firstObj</span> <span class="o">=</span> <span class="n">reply</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(!(</span><span class="n">firstObj</span> <span class="k">instanceof</span> <span class="kt">byte</span><span class="o">[]))</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">JedisException</span><span class="o">(</span><span class="s">"Unknown message type: "</span> <span class="o">+</span> <span class="n">firstObj</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">resp</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">[])</span> <span class="n">firstObj</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="n">Arrays</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">SUBSCRIBE</span><span class="o">.</span><span class="na">raw</span><span class="o">,</span> <span class="n">resp</span><span class="o">))</span> <span class="o">{</span>
<span class="n">subscribedChannels</span> <span class="o">=</span> <span class="o">((</span><span class="n">Long</span><span class="o">)</span> <span class="n">reply</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">2</span><span class="o">)).</span><span class="na">intValue</span><span class="o">();</span>
<span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">bchannel</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">[])</span> <span class="n">reply</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="kd">final</span> <span class="n">String</span> <span class="n">strchannel</span> <span class="o">=</span> <span class="o">(</span><span class="n">bchannel</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span>
<span class="o">:</span> <span class="n">SafeEncoder</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="n">bchannel</span><span class="o">);</span>
<span class="n">onSubscribe</span><span class="o">(</span><span class="n">strchannel</span><span class="o">,</span> <span class="n">subscribedChannels</span><span class="o">);</span>
<span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">Arrays</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">UNSUBSCRIBE</span><span class="o">.</span><span class="na">raw</span><span class="o">,</span> <span class="n">resp</span><span class="o">))</span> <span class="o">{</span>
<span class="n">subscribedChannels</span> <span class="o">=</span> <span class="o">((</span><span class="n">Long</span><span class="o">)</span> <span class="n">reply</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">2</span><span class="o">)).</span><span class="na">intValue</span><span class="o">();</span>
<span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">bchannel</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">[])</span> <span class="n">reply</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="kd">final</span> <span class="n">String</span> <span class="n">strchannel</span> <span class="o">=</span> <span class="o">(</span><span class="n">bchannel</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span>
<span class="o">:</span> <span class="n">SafeEncoder</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="n">bchannel</span><span class="o">);</span>
<span class="n">onUnsubscribe</span><span class="o">(</span><span class="n">strchannel</span><span class="o">,</span> <span class="n">subscribedChannels</span><span class="o">);</span>
<span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">Arrays</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">MESSAGE</span><span class="o">.</span><span class="na">raw</span><span class="o">,</span> <span class="n">resp</span><span class="o">))</span> <span class="o">{</span>
<span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">bchannel</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">[])</span> <span class="n">reply</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">bmesg</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">[])</span> <span class="n">reply</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">2</span><span class="o">);</span>
<span class="kd">final</span> <span class="n">String</span> <span class="n">strchannel</span> <span class="o">=</span> <span class="o">(</span><span class="n">bchannel</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span>
<span class="o">:</span> <span class="n">SafeEncoder</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="n">bchannel</span><span class="o">);</span>
<span class="kd">final</span> <span class="n">String</span> <span class="n">strmesg</span> <span class="o">=</span> <span class="o">(</span><span class="n">bmesg</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="n">SafeEncoder</span>
<span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="n">bmesg</span><span class="o">);</span>
<span class="n">onMessage</span><span class="o">(</span><span class="n">strchannel</span><span class="o">,</span> <span class="n">strmesg</span><span class="o">);</span>
<span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">Arrays</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">PMESSAGE</span><span class="o">.</span><span class="na">raw</span><span class="o">,</span> <span class="n">resp</span><span class="o">))</span> <span class="o">{</span>
<span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">bpattern</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">[])</span> <span class="n">reply</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">bchannel</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">[])</span> <span class="n">reply</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">2</span><span class="o">);</span>
<span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">bmesg</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">[])</span> <span class="n">reply</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">3</span><span class="o">);</span>
<span class="kd">final</span> <span class="n">String</span> <span class="n">strpattern</span> <span class="o">=</span> <span class="o">(</span><span class="n">bpattern</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span>
<span class="o">:</span> <span class="n">SafeEncoder</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="n">bpattern</span><span class="o">);</span>
<span class="kd">final</span> <span class="n">String</span> <span class="n">strchannel</span> <span class="o">=</span> <span class="o">(</span><span class="n">bchannel</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span>
<span class="o">:</span> <span class="n">SafeEncoder</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="n">bchannel</span><span class="o">);</span>
<span class="kd">final</span> <span class="n">String</span> <span class="n">strmesg</span> <span class="o">=</span> <span class="o">(</span><span class="n">bmesg</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="n">SafeEncoder</span>
<span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="n">bmesg</span><span class="o">);</span>
<span class="n">onPMessage</span><span class="o">(</span><span class="n">strpattern</span><span class="o">,</span> <span class="n">strchannel</span><span class="o">,</span> <span class="n">strmesg</span><span class="o">);</span>
<span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">Arrays</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">PSUBSCRIBE</span><span class="o">.</span><span class="na">raw</span><span class="o">,</span> <span class="n">resp</span><span class="o">))</span> <span class="o">{</span>
<span class="n">subscribedChannels</span> <span class="o">=</span> <span class="o">((</span><span class="n">Long</span><span class="o">)</span> <span class="n">reply</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">2</span><span class="o">)).</span><span class="na">intValue</span><span class="o">();</span>
<span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">bpattern</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">[])</span> <span class="n">reply</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="kd">final</span> <span class="n">String</span> <span class="n">strpattern</span> <span class="o">=</span> <span class="o">(</span><span class="n">bpattern</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span>
<span class="o">:</span> <span class="n">SafeEncoder</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="n">bpattern</span><span class="o">);</span>
<span class="n">onPSubscribe</span><span class="o">(</span><span class="n">strpattern</span><span class="o">,</span> <span class="n">subscribedChannels</span><span class="o">);</span>
<span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">Arrays</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">PUNSUBSCRIBE</span><span class="o">.</span><span class="na">raw</span><span class="o">,</span> <span class="n">resp</span><span class="o">))</span> <span class="o">{</span>
<span class="n">subscribedChannels</span> <span class="o">=</span> <span class="o">((</span><span class="n">Long</span><span class="o">)</span> <span class="n">reply</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">2</span><span class="o">)).</span><span class="na">intValue</span><span class="o">();</span>
<span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">bpattern</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">[])</span> <span class="n">reply</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="kd">final</span> <span class="n">String</span> <span class="n">strpattern</span> <span class="o">=</span> <span class="o">(</span><span class="n">bpattern</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span>
<span class="o">:</span> <span class="n">SafeEncoder</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="n">bpattern</span><span class="o">);</span>
<span class="n">onPUnsubscribe</span><span class="o">(</span><span class="n">strpattern</span><span class="o">,</span> <span class="n">subscribedChannels</span><span class="o">);</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">JedisException</span><span class="o">(</span><span class="s">"Unknown message type: "</span> <span class="o">+</span> <span class="n">firstObj</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span> <span class="k">while</span> <span class="o">(</span><span class="n">isSubscribed</span><span class="o">());</span>
<span class="cm">/* Invalidate instance since this thread is no longer listening */</span>
<span class="k">this</span><span class="o">.</span><span class="na">client</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="cm">/*
* Reset pipeline count because subscribe() calls would have increased
* it but nothing decremented it.
*/</span>
<span class="n">client</span><span class="o">.</span><span class="na">resetPipelinedCount</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>设置一个线程单独运行该订阅事件:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">Runnable</span> <span class="n">subRunnable</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Runnable</span><span class="o">()</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
<span class="n">salesRedisClient</span><span class="o">.</span><span class="na">psubscribe</span><span class="o">(</span><span class="n">salesOnLineExpiredListener</span><span class="o">,</span> <span class="n">ExpiredPattern</span><span class="o">,</span> <span class="n">DelPattern</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">};</span>
<span class="n">executor</span><span class="o">.</span><span class="na">execute</span><span class="o">(</span><span class="n">subRunnable</span><span class="o">);</span> <span class="c1">// 订阅事件阻塞,必须开启另一个线程</span>
</code></pre></div></div>cpp255Jedis 订阅事件阻塞 Jedis 开启订阅事件的时候是这样的: public void psubscribe(final JedisPubSub jedisPubSub,
final String... patterns) {
checkIsInMulti();
connect(); // 连接server,底层是通过socket进行连接
client.setTimeoutInfinite();
jedisPubSub.proceedWithPatterns(client, patterns); // 主要是这里
client.rollbackTimeout();
}Redis 事件监听2017-12-14T20:20:20+08:002017-12-14T20:20:20+08:00https://cpp255.github.io/2017/12/14/Redis-Keyevent<h1 id="redis-事件监听">Redis 事件监听</h1>
<hr />
<p>2.8 以后的版本开始支持事件监听,由于开启会消耗CPU资源,默认关闭。<br />
开启事件支持有两种方式:服务端配置和客户端配置;服务端的配置是永久有效的,客户端的配置重启后会失效。服务端开启事件支持后,客户端就可以开始订阅服务端推送的事件监听。</p>
<p><strong>事件类型</strong></p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>K Keyspace events, published with __keyspace@<db>__ prefix.
E Keyevent events, published with __keyevent@<db>__ prefix.
g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...
$ String commands
l List commands
s Set commands
h Hash commands
z Sorted set commands
x Expired events (events generated every time a key expires)
e Evicted events (events generated when a key is evicted for maxmemory)
A Alias for g$lshzxe, so that the "AKE" string means all the events.
</code></pre></div></div>
<p>1.服务端在 redis.conf 中打开事件监听配置,再重启redis服务,这里使用的是接收所有类型,就是”A”,具体使用的话可以随意替换类型;注意启动的时候是否使用当前目录修改的 redis.conf</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>notify-keyspace-events "EA"
</code></pre></div></div>
<p>重启 redis:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./src/redis-server redis.conf
</code></pre></div></div>
<ol>
<li>客户端</li>
</ol>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> redis-cli config set notify-keyspace-events KEA
</code></pre></div></div>
<p>如果只监听删除、过期和因为内存过大被删除的,则模式可以选择为: Egxe</p>
<p><strong>客户端接收订阅事件</strong></p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>psubscribe '__keyevent*__:*' // 订阅所有事件
psubscribe __keyevent@0__:del // 订阅数据库0的del事件
psubscribe __keyevent@0__:expired // 订阅数据库0的expired事件
</code></pre></div></div>
<p>jedis 接收订阅事件,继承 JedisPubSub 类,在实现的方法里面接收处理的逻辑:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 接收订阅事件</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">onPMessage</span><span class="o">(</span><span class="n">String</span> <span class="n">pattern</span><span class="o">,</span> <span class="n">String</span> <span class="n">channel</span><span class="o">,</span> <span class="n">String</span> <span class="n">message</span><span class="o">)</span> <span class="o">{</span>
<span class="o">}</span>
</code></pre></div></div>
<p>注册订阅事件,pattern 跟客户端订阅事件一样,可以添加不同的订阅事件,也可以添加多个事件,例如:<strong>keyevent@0</strong>:del;注册订阅事件是阻塞操作,建议单独生成另一个线程。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Jedis</span> <span class="n">jedis</span><span class="o">;</span>
<span class="n">jedis</span><span class="o">.</span><span class="na">psubscribe</span><span class="o">(</span><span class="n">jedisPubSub</span><span class="o">,</span> <span class="n">pattern</span><span class="o">);</span>
</code></pre></div></div>
<p>参考:
1 http://redisdoc.com/topic/notification.html <br />
2 https://redis.io/topics/notifications</p>cpp255Redis 事件监听