<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>个人博客</title>
  
  <subtitle>身体和灵魂，总有一个在路上</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://yoursite.com/"/>
  <updated>2018-11-21T16:54:18.895Z</updated>
  <id>http://yoursite.com/</id>
  
  <author>
    <name>kumu</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Spring MVC体系简介</title>
    <link href="http://yoursite.com/2018/11/21/spring/spring-mvc/"/>
    <id>http://yoursite.com/2018/11/21/spring/spring-mvc/</id>
    <published>2018-11-21T15:24:11.000Z</published>
    <updated>2018-11-21T16:54:18.895Z</updated>
    
    <content type="html"><![CDATA[<h4 id="Spring-MVC体系概述"><a href="#Spring-MVC体系概述" class="headerlink" title="Spring MVC体系概述"></a>Spring MVC体系概述</h4><h5 id="体系结构"><a href="#体系结构" class="headerlink" title="体系结构"></a>体系结构</h5><p><img src="/images/spring/springmvc_01.png" alt=""></p><a id="more"></a><ol><li>客户端发出一个Http请求，web应用服务器接收到这个请求。如果匹配DispatcherServlet的请求映射路径（在web.xml中指定），则web容器将该请求转交给<strong>DispatcherServlet</strong>处理。</li><li>DispatcherSerovlet接收到这个请求后，将根据请求的信息（包括URL，HTTP方法，请求报文头，请求参数，Cokie等）及<strong>HandlerMapping</strong>的配置找到处理请求的处理器（<strong>Handler</strong>）。可将HandlerMapping看作路由控制器，将Handler看做目标主机。值得注意的是，在SpringMVC中并没有定义一个Handler接口，实际上，任何一个Object都可以作为请求处理器。</li><li>当DispatcherServlet根据HandlerMapping得到对应当前请求的Handler后，通过<strong>HandlerAdapter</strong>对handler进行封装，再以统一的适配器接口调用Handler。HandlerAdapter是SpringMVC的框架级接口，顾名思义，HandlerAdapter是一个适配器，它用统一的接口对各种Handler方法进行调用</li><li>处理器完成业务逻辑的处理之后将返回一个<strong>ModelAndView</strong>给DispatcherServlet，ModelAndView包含了视图逻辑名和模型数据信息。</li><li>ModelAndView包含的是“视图逻辑名”而非真正的视图对象，DispatcherServlet借由ViewResolver完成逻辑视图名到真实视图对象的解析工作。</li><li>得到真实的视图对象View后，DispatcherServlet就使用这个View对象对ModelAndView中的模型数据进行视图渲染。</li><li>最终客户端得到的响应消息可能是一个普通的HTML页面，也可能是一个XML或JSON串，甚至是一张图片或一个PDF文档等不同媒体形式。</li></ol><h5 id="DispatcherServlet内部逻辑"><a href="#DispatcherServlet内部逻辑" class="headerlink" title="DispatcherServlet内部逻辑"></a>DispatcherServlet内部逻辑</h5><p><img src="/images/spring/springmvc_02.png" alt=""><br><br>&nbsp;&nbsp;&nbsp;&nbsp;initStrategies()方法将在WebApplicationContext初始化后自动执行，此时Spring上下文中的Bean已经初始化完毕。该方法的工作原理是：通过反射机制查找并装配Spring容器中用户显示自定义的组件Bean，如果找不到，则装配默认的组件实例。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;Spring MVC定义了一套默认的组件实现类，也就是说，即使在Spring容器中没有显示定义组件bean，DispatcherServlet也会装配好一套可用的默认组件。在Spring-webmvc-4.x.jar包的org/springframework/web/servlet类路径下拥有一个DispatcherServlet.propertiesp配置文件，该文件指定了DispatcherServlet所使用的默认组件。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;如果希望采用非默认的组件，则只需要在Spring配置文件中配置自定义的组件Bean即可。Spring MVC一旦发现上下文中有用户自定义的组件，就不会使用默认的组件。<br><br><img src="/images/spring/springmvc_03.png" alt=""><br><br><img src="/images/spring/springmvc_04.png" alt=""><br><br>&nbsp;&nbsp;&nbsp;&nbsp;有些组件最多允许存在一个实例，如MutlipartResolver，LocaleResolver等；而另一些组件允许存在多个实例，如HandlerMapping，HandlerAdapter等。同一类型的组件如果存在多个，可通过order属性确定优先级迅速，值越小优先级越高。</p><blockquote><p>注: 标注了Spring MVC通过@Controller注解的类使其成为一个可处理Http请求的控制器（使用<a href="context:component-scan/" target="_blank" rel="noopener">context:component-scan/</a>扫描相应的类包)，DispatcherServlet使用DefaultAnnotationHandlerMapping查找负责处理对应请求的处理器。</p></blockquote><h4 id="注解驱动的控制器"><a href="#注解驱动的控制器" class="headerlink" title="注解驱动的控制器"></a>注解驱动的控制器</h4><h5 id="使用-RequestMapping映射请求"><a href="#使用-RequestMapping映射请求" class="headerlink" title="使用@RequestMapping映射请求"></a>使用@RequestMapping映射请求</h5><p>&nbsp;&nbsp;&nbsp;&nbsp;@RequestMapping使用value指定请求URL，在类定义处指定的URL相对于Web应用的部署路径，而在方法定义处指定的URL则相对于类定义处指定的URL。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;@RequestMapping不但支持标准的URL，还支持Ant风格（？、*和**字符）和带{XXX}占位符的URL。例如:<br></p><ul><li>/user/*/create，匹配/user/aa/create，/user/bb/create</li><li>/user/**/create，匹配/user/create，/user/aa/create，/user/aa/bb/create</li><li>/user/create?，匹配/user/createaa，/user/createbb</li><li>/user/{userId}，匹配/user/123，/user/456</li><li>/company/{companyId}/user/{userId}/detail，匹配/company/123/user/456/detail</li></ul><p>&nbsp;&nbsp;&nbsp;&nbsp;通过@PathVariable可以将URL中的占位符参数绑定到控制器方法入参中。如@PathVariable(“userId”)。</p><h5 id="通过请求参数，请求方法或请求头进行映射"><a href="#通过请求参数，请求方法或请求头进行映射" class="headerlink" title="通过请求参数，请求方法或请求头进行映射"></a>通过请求参数，请求方法或请求头进行映射</h5><p>&nbsp;&nbsp;&nbsp;&nbsp;@RequestMapping的value，method，params及headersf分别表示请求URL，请求方法，请求参数及报文头的映射条件，它们之间是与的关系，联合使用多个条件项可让请求映射更加精细化。<br><br><img src="/images/spring/springmvc_05.png" alt=""></p><ul><li>“params1”: 表示请求必须包含名为params1的请求参数</li><li>“!params1”: 表示请求不能包含名为params1的请求参数</li><li>“params1!=value1”: 表示请求必须包含名为params1的请求参数，但其值不能为value1</li><li>{“params1=value1”,”param2”}: 表示请求必须包含名为params1和params2的两个请求参数，且params1必须为value1</li></ul><h5 id="请求处理方法签名"><a href="#请求处理方法签名" class="headerlink" title="请求处理方法签名"></a>请求处理方法签名</h5><h6 id="方法入参使用-RequestParam注解指定对应的请求参数，包含3个参数"><a href="#方法入参使用-RequestParam注解指定对应的请求参数，包含3个参数" class="headerlink" title="方法入参使用@RequestParam注解指定对应的请求参数，包含3个参数"></a>方法入参使用@RequestParam注解指定对应的请求参数，包含3个参数</h6><ul><li>value: 参数名</li><li>required: 是否必需，默认为true，不存在将抛出异常</li><li>defaultValue: 默认参数名，设置该参数时，自动将required设为false</li></ul><h6 id="使用-CookieValue可让处理方法入参绑定某个cookie的值，它和-RequestParam一样有3个参数"><a href="#使用-CookieValue可让处理方法入参绑定某个cookie的值，它和-RequestParam一样有3个参数" class="headerlink" title="使用@CookieValue可让处理方法入参绑定某个cookie的值，它和@RequestParam一样有3个参数"></a>使用@CookieValue可让处理方法入参绑定某个cookie的值，它和@RequestParam一样有3个参数</h6><p><img src="/images/spring/springmvc_06.png" alt=""></p><h6 id="使用-RequestHeader绑定请求报文头的属性值"><a href="#使用-RequestHeader绑定请求报文头的属性值" class="headerlink" title="使用@RequestHeader绑定请求报文头的属性值"></a>使用@RequestHeader绑定请求报文头的属性值</h6><p><img src="/images/spring/springmvc_07.png" alt=""></p><h6 id="使用命令-表单对象绑定请求参数值"><a href="#使用命令-表单对象绑定请求参数值" class="headerlink" title="使用命令/表单对象绑定请求参数值"></a>使用命令/表单对象绑定请求参数值</h6><p><img src="/images/spring/springmvc_08.png" alt=""></p><h6 id="使用Servlet-API对象作为入参"><a href="#使用Servlet-API对象作为入参" class="headerlink" title="使用Servlet API对象作为入参"></a>使用Servlet API对象作为入参</h6><p><img src="/images/spring/springmvc_09.png" alt=""></p><h6 id="使用I-O对象作为入参"><a href="#使用I-O对象作为入参" class="headerlink" title="使用I/O对象作为入参"></a>使用I/O对象作为入参</h6><p>&nbsp;&nbsp;&nbsp;&nbsp;Servlet的ServletRequest拥有getInputStream()和getReader()方法，可以通过它们读取请求的信息。相应的，ServletResponse拥有getOutputStream()和getReader()方法，可以通过它们输出响应信息。</p><h5 id="使用HttpMessageConverter"><a href="#使用HttpMessageConverter" class="headerlink" title="使用HttpMessageConverter"></a>使用HttpMessageConverter<t></t></h5><p>&nbsp;&nbsp;&nbsp;&nbsp;HttpMessageConverter<t>是Spring的一个重要的接口，它负责将请求信息转换为一个对象（类型为T），将对象（类型为T）输出为响应信息。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;DispatcherServlet默认已经安装了RequestMappingHandleAdapter作为HandleAdapter的组件实现类，HttpMessageConverter即由RequestMappingHandleAdapter使用，将请求信息转换为对象，或将对象转换为响应信息。</t></p><h6 id="HttpMessageConverter的实现类"><a href="#HttpMessageConverter的实现类" class="headerlink" title="HttpMessageConverter的实现类"></a>HttpMessageConverter<t>的实现类</t></h6><p><img src="/images/spring/springmvc_10.png" alt=""><br><br><img src="/images/spring/springmvc_11.png" alt=""><br><br><img src="/images/spring/springmvc_12.png" alt=""><br><br>&nbsp;&nbsp;&nbsp;&nbsp;RequestMappingHandleAdapter默认已经转配了以下HttpMessageConverter:<br></p><ol><li>StringHttpMessageConverter</li><li>ByteArrayHttpMessageConverter</li><li>SourceHttpMessageConverter</li><li>AllEncompassingFormHttpMessageConverter</li></ol><h6 id="使用HttpMessageConverter-1"><a href="#使用HttpMessageConverter-1" class="headerlink" title="使用HttpMessageConverter"></a>使用HttpMessageConverter<t></t></h6><p>&nbsp;&nbsp;&nbsp;&nbsp;SpringMVC提供了两种途径使用HttpMessageConverter<t>将请求信息转换并绑定到处理方法的入参中:<br></t></p><ul><li>使用@RequestBody/@ResponseBody对处理方法进行标注</li><li>使用HttpEntity<t>/ResponseEntity<t>作为处理方法的入参或返回值</t></t></li></ul><h6 id="1-使用-RequestBody-ResponseBody"><a href="#1-使用-RequestBody-ResponseBody" class="headerlink" title="1. 使用@RequestBody/@ResponseBody"></a>1. 使用@RequestBody/@ResponseBody</h6><p><img src="/images/spring/springmvc_13.png" alt=""><br><br>&nbsp;&nbsp;&nbsp;&nbsp;在1处，SpringMVC将根据requestBody的类型查找匹配的HttpMessageConverter，由于StringHttpMessageConverter的泛型类型对应String，所以StringHttpMessageConverter将被SpringMVC选中，用它将请求体信息进行转换并将结果绑定到requestBody入参上。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;在2处，由于方法返回值类型为byte[]，所以SpringMVC根据类型匹配的查找规则将使用ByteArrayHttpMessageConverter对返回值进行处理，即将图片数据流输出到客户端。</p><h6 id="2-使用HttpEntity-ResponseEntity"><a href="#2-使用HttpEntity-ResponseEntity" class="headerlink" title="2. 使用HttpEntity/ResponseEntity"></a>2. 使用HttpEntity<t>/ResponseEntity<t></t></t></h6><p>&nbsp;&nbsp;&nbsp;&nbsp;和@RequestBody/@ResponseBody类似，HttpEntity<t>不但可以访问请求和响应报文体的数据，还可以访问请求和响应报文头的数据。SpringMVC根据HttpEntity的泛型类型查找对应的HttpMessageConverter。<br><br><img src="/images/spring/springmvc_14.png" alt=""><br><br><img src="/images/spring/springmvc_15.png" alt=""><br><br>&nbsp;&nbsp;&nbsp;&nbsp;在1处使用HttpEntity<string>指定入参的类型，SpringMVC分析出泛型类型为String，使用StringHttpMessageConverter将请求体类型绑定到httpEntity中，返回String类型的值座位逻辑视图名。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;在2处的处理方法返回值类型为ResponseEntity&lt;byte[]&gt;，SpringMVC分析出泛型类型为byte[]，使用ByteArrayHttpMessageConverter输出图片数据流。</string></t></p><h5 id="处理XML和JSON"><a href="#处理XML和JSON" class="headerlink" title="处理XML和JSON"></a>处理XML和JSON</h5><p>&nbsp;&nbsp;&nbsp;&nbsp;SpringMVC提供了几个处理XML和JSON格式的请求/响应消息的HttpMessageConverter。</p><ul><li>MarshallingHttpMessageConverter：处理XML格式的请求或响应消息</li><li>Jaxb2RootElementHttpMessageConverter：同上，底层使用Jaxb</li><li>MappingJackson2HttpMessageConverter：处理JSON格式的请求或响应消息</li></ul><p>&nbsp;&nbsp;&nbsp;&nbsp;因此，只要在SpringMVC容器中为RequestMappingHandlerAdapter装配好相应的处理XML和JSON格式的请求/响应消息的HttpMessageConverter，并在交互中通过请求的Accept指定MIME类型，SpringMVC就可使服务端的处理方法和客户端透明的通过XML和JSON格式的消息进行通信。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;首先为RequestMappingHandleAdapter装配可处理XML和JSON格式的请求/响应消息的HttpMessageConverter<br><br><img src="/images/spring/springmvc_16.png" alt=""><br><br>&nbsp;&nbsp;&nbsp;&nbsp;控制器相应的方法如下:<br><br><img src="/images/spring/springmvc_17.png" alt=""><br><br><img src="/images/spring/springmvc_18.png" alt=""><br><br>&nbsp;&nbsp;&nbsp;&nbsp;通过以上HTTP请求/响应报文，客户端的User对象被流化为一段对象的XML报文，同时通过报文头属性Accept和Content-Type指定接收的MIME类型和本请求的报文内容均为application/xml。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;将请求报文头属性Accept和Content-Type更改为application/json，User对象的数据以JSON格式进行传递。<br><br><img src="/images/spring/springmvc_19.png" alt=""></p>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;Spring-MVC体系概述&quot;&gt;&lt;a href=&quot;#Spring-MVC体系概述&quot; class=&quot;headerlink&quot; title=&quot;Spring MVC体系概述&quot;&gt;&lt;/a&gt;Spring MVC体系概述&lt;/h4&gt;&lt;h5 id=&quot;体系结构&quot;&gt;&lt;a href=&quot;#体系结构&quot; class=&quot;headerlink&quot; title=&quot;体系结构&quot;&gt;&lt;/a&gt;体系结构&lt;/h5&gt;&lt;p&gt;&lt;img src=&quot;/images/spring/springmvc_01.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="spring" scheme="http://yoursite.com/categories/spring/"/>
    
    
      <category term="spring" scheme="http://yoursite.com/tags/spring/"/>
    
      <category term="spring mvc" scheme="http://yoursite.com/tags/spring-mvc/"/>
    
  </entry>
  
  <entry>
    <title>JVM进程常用参数</title>
    <link href="http://yoursite.com/2018/07/24/java/java-param/"/>
    <id>http://yoursite.com/2018/07/24/java/java-param/</id>
    <published>2018-07-24T14:29:00.000Z</published>
    <updated>2018-07-24T14:44:42.856Z</updated>
    
    <content type="html"><![CDATA[<h3 id="查看JDK参数"><a href="#查看JDK参数" class="headerlink" title="查看JDK参数"></a>查看JDK参数</h3><p>查看并行收集线程<br>/usr/jdk1.8.0_101/bin/java -server -Xmx1024m -Xms1024m -XX:+UseConcMarkSweepGC -XX:+PrintFlagsFinal -version| grep ParallelGCThreads</p><a id="more"></a><h3 id="内存控制参数"><a href="#内存控制参数" class="headerlink" title="内存控制参数"></a>内存控制参数</h3><table><thead><tr><th>参数名</th><th>说明</th></tr></thead><tbody><tr><td>-Xms128m</td><td>初始的内存</td></tr><tr><td>-Xmx256m</td><td>最大堆内存</td></tr><tr><td>-Xmn128m</td><td>设置新生代内存,和NewRatio有相同的作用,可以不用设置</td></tr><tr><td>-Xss256k</td><td>线程栈Stack Space,一般情况下可以给256k,实测最小228k,JDK默认给的是1MB的内存</td></tr><tr><td></td><td>如果多层递归发现StackOverFlow,可以适当调大,调小这个参数可以开启更多的线程</td></tr><tr><td></td><td></td></tr><tr><td></td></tr></tbody></table><h3 id="XX参数"><a href="#XX参数" class="headerlink" title="-XX参数"></a>-XX参数</h3><p>-XX后面有部分参数可以跟+或者-,+表示开启,-表示禁用<br>比如<br>-XX:+UseGCOverheadLimit,就表示GC时间过长抛OOM<br>-XX:-UseGCOverheadLimit,就表示GC时间过长不要抛OOM</p><table><thead><tr><th>参数名</th><th>说明</th></tr></thead><tbody><tr><td>-XX:PermSize=64m</td><td>初始的永久区大小,&lt;=JDK7可用</td></tr><tr><td>-XX:MaxMaxPermSize=128m</td><td>最大的永久区大小,&lt;=JDK7可用</td></tr><tr><td>-XX:MetaspaceSize=64m</td><td>初始的永久区大小,&gt;=JDK8可用</td></tr><tr><td>-XX:MaxMetaspaceSize=256m</td><td>最大的永久区大小,&gt;=JDK8可用</td></tr><tr><td></td><td></td></tr><tr><td>-XX:NewRatio=1</td><td>新生代占1/2,默认情况,新生代占1/3,公式为1/(1+n)</td></tr><tr><td></td><td></td></tr><tr><td>-XX:-UseBiasedLocking</td><td>取消偏向锁,在synchronized上的 优化,取消对性能有提升</td></tr><tr><td>-XX:AutoBoxCacheMax=20000</td><td>Integer i = 3;这语句有着 int自动装箱成Integer的过程，JDK默认只缓存 -128 ~ +127的int 和 long</td></tr><tr><td>-XX:+PerfDisablesharedMem</td><td>禁止在/tmp/hperf[用户名] 路径上写入进程统计文件,jps,jstat,远程查看jvm状态也不能用了</td></tr><tr><td></td><td></td></tr><tr><td>-XX:-UseGCOverheadLimit</td><td>限制GC的运行时间。如果GC耗时过长，就抛OOM</td></tr><tr><td>-XX:-OmitStackTraceInFastThrow</td><td>2W次异常后,不打印异常栈,建议禁用,不方便查问题</td></tr><tr><td></td><td></td></tr><tr><td>-XX:+UseParNewGC</td><td>新生代并行收集</td></tr><tr><td>-XX:+UseConcMarkSweepGC</td><td>老年代并发收集</td></tr><tr><td>-XX:CMSInitiatingOccupancyFraction=75</td><td>老年代收集的触发的比例</td></tr><tr><td>-XX:+UseCMSInitiatingOccupancyOnly</td><td>强制使用上面的触发比例</td></tr><tr><td>-XX:MaxTenuringThreshold=2</td><td>对象在Survivor区熬过多少次Young GC后晋升到年老代</td></tr><tr><td></td><td></td></tr><tr><td>-XX:+HeapDumpOnOutOfMemoryError</td><td>如果出现OutOfMemory,dump内存到文件中,默认情况下dump到当前用户目录</td></tr><tr><td>-XX:HeapDumpPath=/home/checkin/dump</td><td>指定dump的目录</td></tr><tr><td></td><td></td></tr><tr><td>-XX:+ExplicitGCInvokesConcurrent</td><td>显式的System.gc()的调用会使用CMS方式</td></tr><tr><td>-XX:+DisableExplicitGC</td><td>不允许显式调用System.gc(),此选项最好不要加,有些堆外内存分配依赖显式 gc 调用,关闭之后容易导致 OOM</td></tr><tr><td></td><td></td></tr><tr><td>-XX:+PrintGCDetails</td><td>打印GC详细情况</td></tr><tr><td>-XX:+PrintGCDateStamps</td><td>打印GC的时间戳</td></tr><tr><td>-verbose:gc</td><td>打印gc日志</td></tr><tr><td>-Xloggc:gc.log</td><td>日志存放在当前路径下,文件名为gc.log,可以使用相对路径和绝对路径</td></tr><tr><td></td></tr></tbody></table><p>JDK1.7及以下版本参数<br>-Xms128m -Xmx512m -Xss256k -server -XX:PermSize=32m -XX:MaxPermSize=256m -XX:NewRatio=1 -XX:-UseBiasedLocking -XX:AutoBoxCacheMax=20000 -XX:-UseGCOverheadLimit -XX:-OmitStackTraceInFastThrow -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:MaxTenuringThreshold=2 -XX:+HeapDumpOnOutOfMemoryError -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+PrintGCDateStamps -verbose:gc -Xloggc:gc.log  </p><p>JDK1.8及以上版本参数<br>-Xms128m -Xmx512m -Xss256k -server -XX:MetaspaceSize=32m -XX:MaxMetaspaceSize=256m -XX:NewRatio=1 -XX:-UseBiasedLocking -XX:AutoBoxCacheMax=20000 -XX:-UseGCOverheadLimit -XX:-OmitStackTraceInFastThrow -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:MaxTenuringThreshold=2 -XX:+HeapDumpOnOutOfMemoryError -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+PrintGCDateStamps -verbose:gc -Xloggc:gc.log  </p><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><li>关键业务系统的JVM参数推荐(2016热冬版) <a href="http://calvin1978.blogcn.com/articles/jvmoption-2.html" target="_blank" rel="noopener">http://calvin1978.blogcn.com/articles/jvmoption-2.html</a></li><br><li>JVM调优总结 -Xms -Xmx -Xmn -Xss <a href="http://unixboy.iteye.com/blog/174173/" target="_blank" rel="noopener">http://unixboy.iteye.com/blog/174173/</a></li><br><li>java高分局之JVM命令参数大全（高级垃圾回收选项）<a href="http://blog.csdn.net/maosijunzi/article/details/46562489" target="_blank" rel="noopener">http://blog.csdn.net/maosijunzi/article/details/46562489</a></li>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;查看JDK参数&quot;&gt;&lt;a href=&quot;#查看JDK参数&quot; class=&quot;headerlink&quot; title=&quot;查看JDK参数&quot;&gt;&lt;/a&gt;查看JDK参数&lt;/h3&gt;&lt;p&gt;查看并行收集线程&lt;br&gt;/usr/jdk1.8.0_101/bin/java -server -Xmx1024m -Xms1024m -XX:+UseConcMarkSweepGC -XX:+PrintFlagsFinal -version| grep ParallelGCThreads&lt;/p&gt;
    
    </summary>
    
      <category term="java" scheme="http://yoursite.com/categories/java/"/>
    
    
      <category term="jvm" scheme="http://yoursite.com/tags/jvm/"/>
    
  </entry>
  
  <entry>
    <title>MySQL索引原理</title>
    <link href="http://yoursite.com/2018/07/15/mysql/index/"/>
    <id>http://yoursite.com/2018/07/15/mysql/index/</id>
    <published>2018-07-15T15:53:00.000Z</published>
    <updated>2018-07-24T14:53:06.500Z</updated>
    
    <content type="html"><![CDATA[<h3 id="数据结构"><a href="#数据结构" class="headerlink" title="数据结构"></a>数据结构</h3><h4 id="二叉排序树（Binary-Sort-Tree）"><a href="#二叉排序树（Binary-Sort-Tree）" class="headerlink" title="二叉排序树（Binary Sort Tree）"></a>二叉排序树（Binary Sort Tree）</h4><h5 id="规则"><a href="#规则" class="headerlink" title="规则"></a>规则</h5><ol><li>若左子树不空，则左子树上所有节点的值均小于它的根节点的值</li><li>若右子树不空，则右子树上所有节点的值均大于它的根节点的值</li><li>它的左、右子树也分别为二叉排序树（递归定义）</li></ol><h5 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h5><p>二叉查找树查找比较方便，因为每次经过一次节点时，最多减少一半的可能。极端情况下，会出现所有节点位于同一侧的情况，直观上看就是一条直线，这种情况的查询效率比较低。因此需要对二叉树左右子树的高度作平衡化处理，这就是平衡二叉树。</p><a id="more"></a><h4 id="平衡二叉树（Balance-Binary-Tree）"><a href="#平衡二叉树（Balance-Binary-Tree）" class="headerlink" title="平衡二叉树（Balance Binary Tree）"></a>平衡二叉树（Balance Binary Tree）</h4><h5 id="规则-1"><a href="#规则-1" class="headerlink" title="规则"></a>规则</h5><ol><li>左右子树的高度差的绝对值不超过1</li><li>左右子树都是平衡二叉树（递归定义）</li></ol><h5 id="说明-1"><a href="#说明-1" class="headerlink" title="说明"></a>说明</h5><p>常见的实现方式为：红黑树，平衡二叉查找树（AVL）,替罪羊树，树堆（Treap），伸展树。在这样的平衡树中进行查找，总共比较节点的次数不超过树的高度，查询效率得到提高，时间复杂度为O(logn)。</p><h4 id="平衡多路查找树（B树或B-树）"><a href="#平衡多路查找树（B树或B-树）" class="headerlink" title="平衡多路查找树（B树或B-树）"></a>平衡多路查找树（B树或B-树）</h4><h5 id="规则-2"><a href="#规则-2" class="headerlink" title="规则"></a>规则</h5><ol><li>每个节点至多可以拥有m棵子树</li><li>根节点，只有至少2个节点</li><li>非根非叶的节点至少有Ceil(m/2)个子树（Ceil表示向上取整），例如5阶B树，每个节点至少3个子树</li><li>所有叶子节点位于同一层，意思是从根到叶子节点的每一条路径都有同样的长度</li></ol><h5 id="说明-2"><a href="#说明-2" class="headerlink" title="说明"></a>说明</h5><p>B树查询与二叉排序树类似，从根节点依次比较每个节点，<strong>因为每个节点中关键字和左右子树都是有序的</strong>。</p><h4 id="B-树"><a href="#B-树" class="headerlink" title="B+树"></a>B+树</h4><h5 id="规则-3"><a href="#规则-3" class="headerlink" title="规则"></a>规则</h5><ol><li>有n棵子树的节点含有n个关键字，每个关键字不保存数据，只用来索引，所有数据保存在叶子节点</li><li>所有的叶子结点中包含了全部关键字的信息，及指向含这些关键字记录的指针，且叶子结点本身依关键字的大小自小而大顺序链接</li><li>非叶子节点看成是索引部分，结点中仅含其子树（根节点）中的最大（或最小）关键字</li></ol><h5 id="说明-3"><a href="#说明-3" class="headerlink" title="说明"></a>说明</h5><p>B+树查找过程与B树类似，<strong>只不过查找时，如果在非叶子节点上的关键字等于给定值，并不终止，而是继续沿着指针直到叶子节点</strong>。因此对于B+树，不管查找成功或失败，每次查找都是走了一条从根到叶子节点的路径。<br><br>通常在B+Tree上有两个头指针，一个指向根节点，另一个指向关键字最小的叶子节点，而且所有叶子节点（即数据节点）之间是一种链式环结构（<strong>双向循环链表</strong>）。因此可以对B+Tree进行两种查找运算：一种是对于主键的<strong>范围查找</strong>和<strong>分页查找</strong>，另一种是从根节点开始，进行<strong>随机查找</strong>。</p><h3 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h3><h4 id="MyISAM索引实现"><a href="#MyISAM索引实现" class="headerlink" title="MyISAM索引实现"></a>MyISAM索引实现</h4><p>MyISAM引擎使用B+Tree作为索引结构，叶结点的data域存放的是数据记录的地址。</p><h5 id="主索引-主键"><a href="#主索引-主键" class="headerlink" title="主索引(主键)"></a>主索引(主键)</h5><p><img src="/images/mysql/myisam_main_index.png" alt=""></p><h5 id="辅助索引"><a href="#辅助索引" class="headerlink" title="辅助索引"></a>辅助索引</h5><p>在 MyISAM 中,主索引和辅助索引(Secondary key)在结构上没有任何区别，只是主索引要求 key 是唯一的,而辅助索引的 key 可以重复。<br><img src="/images/mysql/myisam_secondary_index.png" alt=""></p><h5 id="说明-4"><a href="#说明-4" class="headerlink" title="说明"></a>说明</h5><p>同样也是一颗B+Tree，data域保存数据记录的地址。因此，MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引，如果指定的Key存在，则取出其data域的值，然后以data域的值为地址，读取相应数据记录。<br><br>MyISAM的索引方式也叫做“非聚集”的，之所以这么称呼是为了与InnoDB的聚集索引区分。</p><h4 id="InnoDB索引实现"><a href="#InnoDB索引实现" class="headerlink" title="InnoDB索引实现"></a>InnoDB索引实现</h4><p>虽然InnoDB也使用B+Tree作为索引结构，但具体实现方式却与MyISAM截然不同。<br><br>第一个重大区别是<strong>InnoDB的数据文件本身就是索引文件</strong>。从上文知道，MyISAM索引文件和数据文件是分离的，索引文件仅保存数据记录的地址。而在InnoDB中，表数据文件本身就是按B+Tree组织的一个索引结构，这棵树的叶结点data域保存了完整的数据记录。这个索引的key是数据表的主键，因此<strong>InnoDB表数据文件本身就是主索引</strong>。</p><h5 id="主索引-主键-1"><a href="#主索引-主键-1" class="headerlink" title="主索引(主键)"></a>主索引(主键)</h5><p><img src="/images/mysql/innodb_main_index.png" alt=""></p><h5 id="辅助索引-1"><a href="#辅助索引-1" class="headerlink" title="辅助索引"></a>辅助索引</h5><p><img src="/images/mysql/innodb_secondary_index.png" alt=""></p><h5 id="说明-5"><a href="#说明-5" class="headerlink" title="说明"></a>说明</h5><p>InnoDB存储引擎中页的大小为<strong>16KB</strong>，一般表的主键类型为INT（占用4个字节）或BIGINT（占用8个字节），指针类型也一般为4或8个字节，也就是说一个页（B+Tree中的一个节点）中大概存储16KB/(8B+8B)=1K个键值（因为是估值，为方便计算，这里的K取值为10^3）。也就是说一个深度为3的B+Tree索引可以维护10^3 <em> 10^3 </em> 10^3 = 10亿 条记录。实际情况中每个节点可能不能填充满，因此在数据库中，<strong>B+Tree的高度一般都在2~4层</strong>。mysql的InnoDB存储引擎在设计时是将根节点常驻内存的，也就是说查找某一键值的行记录时最多只需要1~3次磁盘I/O操作。<br><br>InnoDB <strong>要求表必须有主键(MyISAM 可以没有)</strong>，如果没有显式指定，则 MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列，则MySQL 自动为 InnoDB 表生成一个隐含字段作为主键,类型为长整形。<br><br>同时,请尽量在 InnoDB 上采用自增字段做表的主键。因为 InnoDB 数据文件本身是一棵B+Tree，非单调的主键会造成在插入新记录时数据文件为了维持 B+Tree 的特性而频繁的分裂调整，十分低效,而使用自增字段作为主键则是一个很好的选择。如果表使用自增主键,那么每次插入新的记录，记录就会顺序添加到当前索引节点的后续位置，当一页写满,就会自动开辟一个新的页。</p><h4 id="聚簇索引"><a href="#聚簇索引" class="headerlink" title="聚簇索引"></a>聚簇索引</h4><p>InnoDB 使用的是聚簇索引，将主键组织到一棵B+树中， 而行数据就储存在叶子节点上， 若使用”where id = 14”这样的条件查找主键，则按照 B+树的检索算法即可查找到对应的叶节点，之后获得行数据。 若对 Name 列进行条件搜索，则需要两个步骤:</p><ul><li>第一步、在辅助索引 B+树中检索 Name，到达其叶子节点获取对应的主键。</li><li>第二步、使用主键在主索引B+树种再执行一次 B+树检索操作，最终到达叶子节点即可获取整行数据。</li></ul><h4 id="非聚簇索引"><a href="#非聚簇索引" class="headerlink" title="非聚簇索引"></a>非聚簇索引</h4><p>MyISM 使用的是非聚簇索引, 非聚簇索引的两棵 B+树看上去没什么不同, 节点的结构完全一致只是存储的内容不同而已, 主键索引 B+树的节点存储了主键, 辅助键索引B+树存储了辅助键。 表数据存储在独立的地方, 这两颗 B+树的叶子节点都使用一个地址指向真正的表数据, 对于表数据来说, 这两个键没有任何差别。 由于索引树是独立的, 通过辅助键检索无需访问主键的索引树。</p>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;数据结构&quot;&gt;&lt;a href=&quot;#数据结构&quot; class=&quot;headerlink&quot; title=&quot;数据结构&quot;&gt;&lt;/a&gt;数据结构&lt;/h3&gt;&lt;h4 id=&quot;二叉排序树（Binary-Sort-Tree）&quot;&gt;&lt;a href=&quot;#二叉排序树（Binary-Sort-Tree）&quot; class=&quot;headerlink&quot; title=&quot;二叉排序树（Binary Sort Tree）&quot;&gt;&lt;/a&gt;二叉排序树（Binary Sort Tree）&lt;/h4&gt;&lt;h5 id=&quot;规则&quot;&gt;&lt;a href=&quot;#规则&quot; class=&quot;headerlink&quot; title=&quot;规则&quot;&gt;&lt;/a&gt;规则&lt;/h5&gt;&lt;ol&gt;
&lt;li&gt;若左子树不空，则左子树上所有节点的值均小于它的根节点的值&lt;/li&gt;
&lt;li&gt;若右子树不空，则右子树上所有节点的值均大于它的根节点的值&lt;/li&gt;
&lt;li&gt;它的左、右子树也分别为二叉排序树（递归定义）&lt;/li&gt;
&lt;/ol&gt;
&lt;h5 id=&quot;说明&quot;&gt;&lt;a href=&quot;#说明&quot; class=&quot;headerlink&quot; title=&quot;说明&quot;&gt;&lt;/a&gt;说明&lt;/h5&gt;&lt;p&gt;二叉查找树查找比较方便，因为每次经过一次节点时，最多减少一半的可能。极端情况下，会出现所有节点位于同一侧的情况，直观上看就是一条直线，这种情况的查询效率比较低。因此需要对二叉树左右子树的高度作平衡化处理，这就是平衡二叉树。&lt;/p&gt;
    
    </summary>
    
      <category term="mysql" scheme="http://yoursite.com/categories/mysql/"/>
    
    
      <category term="索引" scheme="http://yoursite.com/tags/%E7%B4%A2%E5%BC%95/"/>
    
  </entry>
  
  <entry>
    <title>缓存常见问题</title>
    <link href="http://yoursite.com/2018/04/19/cache/cache-question/"/>
    <id>http://yoursite.com/2018/04/19/cache/cache-question/</id>
    <published>2018-04-18T16:00:00.000Z</published>
    <updated>2018-04-18T17:04:25.471Z</updated>
    
    <content type="html"><![CDATA[<h3 id="缓存穿透"><a href="#缓存穿透" class="headerlink" title="缓存穿透"></a>缓存穿透</h3><h4 id="含义"><a href="#含义" class="headerlink" title="含义"></a>含义</h4><p>缓存穿透是指查询一个一定不存在的数据，由于缓存是不命中时需要从数据库查询，查不到数据则不写入缓存，这将导致这个不存在的数据每次请求都要到数据库去查询，造成缓存穿透。</p><a id="more"></a><p><img src="/images/cache/cache01.png" alt=""></p><blockquote><p>缓存穿透问题可能会使后端存储负载加大，由于很多后端存储不具备高并发性，甚至可能造成后端存储宕掉。通常可以在程序中分别统计总调用数、缓存层命中数、存储层命中数，如果发现大量存储层空命中，可能就是出现了缓存穿透问题。</p></blockquote><h4 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h4><h5 id="缓存空对象"><a href="#缓存空对象" class="headerlink" title="缓存空对象"></a>缓存空对象</h5><p>存储层不命中后，仍然将空对象保留到缓存层中，之后再访问这个数据将会从缓存中获取，保护了后端数据源。</p><p><img src="/images/cache/cache02.png" alt=""></p><p>缓存空对象会有两个问题：</p><p>第一，空值做了缓存，意味着缓存层中存了更多的键，需要更多的内存空间 ( 如果是攻击，问题更严重 )，比较有效的方法是针对这类数据设置一个较短的过期时间，让其自动剔除。</p><p>第二，缓存层和存储层的数据会有一段时间窗口的不一致，可能会对业务有一定影响。例如过期时间设置为 5 分钟，如果此时存储层添加了这个数据，那此段时间就会出现缓存层和存储层数据的不一致，此时可以利用消息系统或者其他方式清除掉缓存层中的空对象。</p><p>实现代码：<br><img src="/images/cache/cache03.png" alt=""></p><h5 id="布隆过滤"><a href="#布隆过滤" class="headerlink" title="布隆过滤"></a>布隆过滤</h5><p>对所有可能查询的参数以hash形式存储，在控制层先进行校验，不符合则丢弃。还有最常见的则是采用布隆过滤器，将所有可能存在的数据哈希到一个足够大的bitmap中，一个一定不存在的数据会被这个bitmap拦截掉，从而避免了对底层存储系统的查询压力。</p><p>例如： 一个个性化推荐系统有 4 亿个用户 ID，每个小时算法工程师会根据每个用户之前历史行为做出来的个性化放到存储层中，但是最新的用户由于没有历史行为，就会发生缓存穿透的行为，为此可以将所有有个性化推荐数据的用户做成布隆过滤器。如果布隆过滤器认为该用户 ID 不存在，那么就不会访问存储层，在一定程度保护了存储层。</p><p>有关布隆过滤器的相关知识，可以参考： <a href="https://en.wikipedia.org/wiki/Bloom_filter" target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Bloom_filter</a></p><p>可以利用 Redis 的 Bitmaps 实现布隆过滤器，GitHub 上已经开源了类似的方案：<br><a href="https://github.com/erikdubbelboer/Redis-Lua-scaling-bloom-filter" target="_blank" rel="noopener">https://github.com/erikdubbelboer/Redis-Lua-scaling-bloom-filter</a></p><p><img src="/images/cache/cache04.png" alt=""></p><p><strong>这种方法适用于数据命中不高，数据相对固定实时性低（通常是数据集较大）的应用场景</strong>，代码维护较为复杂，但是缓存空间占用少。</p><h5 id="对比"><a href="#对比" class="headerlink" title="对比"></a>对比</h5><p><img src="/images/cache/cache05.png" alt=""></p><h3 id="缓存雪崩"><a href="#缓存雪崩" class="headerlink" title="缓存雪崩"></a>缓存雪崩</h3><h4 id="含义-1"><a href="#含义-1" class="headerlink" title="含义"></a>含义</h4><p>缓存雪崩是指在我们设置缓存时采用了相同的过期时间，导致缓存在某一时刻同时失效，请求全部转发到DB，DB瞬时压力过重雪崩。</p><h4 id="解决方案-1"><a href="#解决方案-1" class="headerlink" title="解决方案"></a>解决方案</h4><h5 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h5><p>预防和解决缓存雪崩问题，可以从以下三个方面进行着手。</p><p>1）保证缓存层服务高可用性。</p><p>和飞机都有多个引擎一样，如果缓存层设计成高可用的，即使个别节点、个别机器、甚至是机房宕掉，依然可以提供服务，例如前面介绍过的 Redis Sentinel 和 Redis Cluster 都实现了高可用。</p><p>2）依赖隔离组件为后端限流并降级。</p><p>无论是缓存层还是存储层都会有出错的概率，可以将它们视同为资源。作为并发量较大的系统，假如有一个资源不可用，可能会造成线程全部 hang 在这个资源上，造成整个系统不可用。降级在高并发系统中是非常正常的：比如推荐服务中，如果个性化推荐服务不可用，可以降级补充热点数据，不至于造成前端页面是开天窗。</p><p>在实际项目中，我们需要对重要的资源 ( 例如 Redis、 MySQL、 Hbase、外部接口 ) 都进行隔离，让每种资源都单独运行在自己的线程池中，即使个别资源出现了问题，对其他服务没有影响。但是线程池如何管理，比如如何关闭资源池，开启资源池，资源池阀值管理，这些做起来还是相当复杂的，这里推荐一个 Java 依赖隔离工具 Hystrix(<a href="https://github.com/Netflix/Hystrix" target="_blank" rel="noopener">https://github.com/Netflix/Hystrix</a>)</p><h5 id="方案"><a href="#方案" class="headerlink" title="方案"></a>方案</h5><h6 id="加锁排队"><a href="#加锁排队" class="headerlink" title="加锁排队"></a>加锁排队</h6><p>在缓存失效后，通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存，其他线程等待。</p><p>业界比较常用的做法，是使用mutex。简单地来说，就是在缓存失效的时候（判断拿出来的值为空），不是立即去load db，而是先使用缓存工具的某些带成功操作返回值的操作（比如Redis的SETNX或者Memcache的ADD）去set一个mutex key，当操作返回成功时，再进行load db的操作并回设缓存；否则，就重试整个get缓存的方法</p><h6 id="数据预热"><a href="#数据预热" class="headerlink" title="数据预热"></a>数据预热</h6><p>可以通过缓存reload机制，预先去更新缓存，再即将发生大并发访问前手动触发加载缓存不同的key，设置不同的过期时间，让缓存失效的时间点尽量均匀</p><h6 id="做二级缓存，或者双缓存策略。"><a href="#做二级缓存，或者双缓存策略。" class="headerlink" title="做二级缓存，或者双缓存策略。"></a>做二级缓存，或者双缓存策略。</h6><p>A1为原始缓存，A2为拷贝缓存，A1失效时，可以访问A2，A1缓存失效时间设置为短期，A2设置为长期。</p><h6 id="缓存永远不过期"><a href="#缓存永远不过期" class="headerlink" title="缓存永远不过期"></a>缓存永远不过期</h6><p>这里的“永远不过期”包含两层意思：</p><p>1）从缓存上看，确实没有设置过期时间，这就保证了，不会出现热点key过期问题，也就是“物理”不过期。</p><p>2）从功能上看，如果不过期，那不就成静态的了吗？所以我们把过期时间存在key对应的value里，如果发现要过期了，通过一个后台的异步线程进行缓存的构建，也就是“逻辑”过期.</p><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><li><a href="http://mp.weixin.qq.com/s/TBCEwLVAXdsTszRVpXhVug" target="_blank" rel="noopener">http://mp.weixin.qq.com/s/TBCEwLVAXdsTszRVpXhVug</a></li><br><li><a href="https://blog.csdn.net/fei33423/article/details/79027790" target="_blank" rel="noopener">https://blog.csdn.net/fei33423/article/details/79027790</a></li>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;缓存穿透&quot;&gt;&lt;a href=&quot;#缓存穿透&quot; class=&quot;headerlink&quot; title=&quot;缓存穿透&quot;&gt;&lt;/a&gt;缓存穿透&lt;/h3&gt;&lt;h4 id=&quot;含义&quot;&gt;&lt;a href=&quot;#含义&quot; class=&quot;headerlink&quot; title=&quot;含义&quot;&gt;&lt;/a&gt;含义&lt;/h4&gt;&lt;p&gt;缓存穿透是指查询一个一定不存在的数据，由于缓存是不命中时需要从数据库查询，查不到数据则不写入缓存，这将导致这个不存在的数据每次请求都要到数据库去查询，造成缓存穿透。&lt;/p&gt;
    
    </summary>
    
      <category term="cache" scheme="http://yoursite.com/categories/cache/"/>
    
    
      <category term="cache" scheme="http://yoursite.com/tags/cache/"/>
    
  </entry>
  
  <entry>
    <title>数据库中的锁</title>
    <link href="http://yoursite.com/2018/04/17/mysql/lock/"/>
    <id>http://yoursite.com/2018/04/17/mysql/lock/</id>
    <published>2018-04-17T15:19:00.000Z</published>
    <updated>2018-04-17T16:02:32.942Z</updated>
    
    <content type="html"><![CDATA[<h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>在数据库的锁机制中介绍过，数据库管理系统（DBMS）中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。</p><p><strong>乐观并发控制(乐观锁)</strong> 和 <strong>悲观并发控制（悲观锁）</strong> 是并发控制主要采用的技术手段。</p><a id="more"></a><p>无论是悲观锁还是乐观锁，都是人们定义出来的概念，可以认为是一种思想。其实不仅仅是关系型数据库系统中有乐观锁和悲观锁的概念，像memcache、hibernate、tair等都有类似的概念。</p><p>针对于不同的业务场景，应该选用不同的并发控制方式。所以，不要把乐观并发控制和悲观并发控制狭义的理解为DBMS中的概念，更不要把他们和数据中提供的锁机制（行锁、表锁、排他锁、共享锁）混为一谈。其实，在DBMS中，悲观锁正是利用数据库本身提供的锁机制来实现的。</p><h3 id="悲观锁"><a href="#悲观锁" class="headerlink" title="悲观锁"></a>悲观锁</h3><blockquote><p>在关系数据库管理系统里，悲观并发控制（又名“悲观锁”，Pessimistic Concurrency Control，缩写“PCC”）是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作都某行数据应用了锁，那只有当这个事务把锁释放，其他事务才能够执行与该锁冲突的操作。<br>悲观并发控制主要用于数据争用激烈的环境，以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。</p></blockquote><h4 id="含义"><a href="#含义" class="headerlink" title="含义"></a>含义</h4><p>悲观锁，正如其名，它指的是对数据被外界（包括本系统当前的其他事务，以及来自外部系统的事务处理）修改持保守态度(悲观)，因此，在整个数据处理过程中，将数据处于锁定状态。 悲观锁的实现，往往依靠数据库提供的锁机制 （也只有数据库层提供的锁机制才能真正保证数据访问的排他性，否则，即使在本系统中实现了加锁机制，也无法保证外部系统不会修改数据）</p><h4 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h4><p>在数据库中，悲观锁的流程如下：</p><ol><li>在对任意记录进行修改前，先尝试为该记录加上<strong>排他锁</strong>（exclusive locking）。</li><li>如果加锁失败，说明该记录正在被修改，那么当前查询可能要等待或者抛出异常。 具体响应方式由开发者根据实际需要决定。</li><li>如果成功加锁，那么就可以对记录做修改，事务完成后就会解锁了。</li><li>其间如果有其他对该记录做修改或加排他锁的操作，都会等待我们解锁或直接抛出异常。</li></ol><h4 id="应用（MySQL-InnoDB中使用悲观锁）"><a href="#应用（MySQL-InnoDB中使用悲观锁）" class="headerlink" title="应用（MySQL InnoDB中使用悲观锁）"></a>应用（MySQL InnoDB中使用悲观锁）</h4><blockquote><p>要使用悲观锁，我们必须关闭mysql数据库的自动提交属性，因为MySQL默认使用autocommit模式，也就是说，当你执行一个更新操作后，MySQL会立刻将结果进行提交。set autocommit=0;</p></blockquote><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">//0.开始事务</span><br><span class="line"><span class="keyword">begin</span>;/<span class="keyword">begin</span> <span class="keyword">work</span>;/<span class="keyword">start</span> <span class="keyword">transaction</span>; (三者选一就可以)</span><br><span class="line">//1.查询出商品信息</span><br><span class="line"><span class="keyword">select</span> <span class="keyword">status</span> <span class="keyword">from</span> t_goods <span class="keyword">where</span> <span class="keyword">id</span>=<span class="number">1</span> <span class="keyword">for</span> <span class="keyword">update</span>;</span><br><span class="line">//2.根据商品信息生成订单</span><br><span class="line"><span class="keyword">insert</span> <span class="keyword">into</span> t_orders (<span class="keyword">id</span>,goods_id) <span class="keyword">values</span> (<span class="literal">null</span>,<span class="number">1</span>);</span><br><span class="line">//3.修改商品status为2</span><br><span class="line"><span class="keyword">update</span> t_goods <span class="keyword">set</span> <span class="keyword">status</span>=<span class="number">2</span>;</span><br><span class="line">//4.提交事务</span><br><span class="line"><span class="keyword">commit</span>;/<span class="keyword">commit</span> <span class="keyword">work</span>;</span><br></pre></td></tr></table></figure><p>上面的查询语句中，我们使用了select…for update的方式，这样就通过开启排他锁的方式实现了悲观锁。此时在t_goods表中，id为1的 那条数据就被我们锁定了，其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。</p><blockquote><p>上面我们提到，使用select…for update会把数据给锁住，不过我们需要注意一些锁的级别，MySQL InnoDB默认行级锁。行级锁都是基于索引的，如果一条SQL语句用不到索引是不会使用行级锁的，会使用表级锁把整张表锁住，这点需要注意。</p></blockquote><h4 id="优点与不足"><a href="#优点与不足" class="headerlink" title="优点与不足"></a>优点与不足</h4><p>悲观并发控制实际上是“先取锁再访问”的保守策略，为数据处理的安全提供了保证。但是在效率方面，处理加锁的机制会让数据库产生额外的开销，还有增加产生死锁的机会；另外，在只读型事务处理中由于不会产生冲突，也没必要使用锁，这样做只能增加系统负载；还有会降低了并行性，一个事务如果锁定了某行数据，其他事务就必须等待该事务处理完才可以处理那行数</p><h3 id="乐观锁"><a href="#乐观锁" class="headerlink" title="乐观锁"></a>乐观锁</h3><blockquote><p>在关系数据库管理系统里，乐观并发控制（又名“乐观锁”，Optimistic Concurrency Control，缩写“OCC”）是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响，各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前，每个事务会先检查在该事务读取数据后，有没有其他事务又修改了该数据。如果其他事务有更新的话，正在提交的事务会进行回滚。乐观事务控制最早是由孔祥重（H.T.Kung）教授提出。</p></blockquote><h4 id="含义-1"><a href="#含义-1" class="headerlink" title="含义"></a>含义</h4><p>乐观锁（ Optimistic Locking ） 相对悲观锁而言，乐观锁假设认为数据一般情况下不会造成冲突，所以在数据进行提交更新的时候，才会正式对数据的冲突与否进行检测，如果发现冲突了，则让返回用户错误的信息，让用户决定如何去做。</p><p>相对于悲观锁，在对数据库进行处理的时候，乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。</p><blockquote><p>数据版本,为数据增加的一个版本标识。当读取数据时，将版本标识的值一同读出，数据每更新一次，同时对版本标识进行更新。当我们提交更新的时候，判断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对，如果数据库表当前版本号与第一次取出来的版本标识值相等，则予以更新，否则认为是过期数据。</p></blockquote><h4 id="实现-1"><a href="#实现-1" class="headerlink" title="实现"></a>实现</h4><p>实现数据版本有两种方式，第一种是使用版本号，第二种是使用时间戳。</p><p>使用版本号时，可以在数据初始化时指定一个版本号，每次对数据的更新操作都对版本号执行+1操作。并判断当前版本号是不是该数据的最新的版本号。</p><h4 id="应用（MySQL中使用乐观锁）"><a href="#应用（MySQL中使用乐观锁）" class="headerlink" title="应用（MySQL中使用乐观锁）"></a>应用（MySQL中使用乐观锁）</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">1.查询出商品信息</span><br><span class="line"><span class="keyword">select</span> (<span class="keyword">status</span>,<span class="keyword">status</span>,<span class="keyword">version</span>) <span class="keyword">from</span> t_goods <span class="keyword">where</span> <span class="keyword">id</span>=#&#123;<span class="keyword">id</span>&#125;</span><br><span class="line"><span class="number">2.</span>根据商品信息生成订单</span><br><span class="line"><span class="number">3.</span>修改商品<span class="keyword">status</span>为<span class="number">2</span></span><br><span class="line"><span class="keyword">update</span> t_goods</span><br><span class="line"><span class="keyword">set</span> <span class="keyword">status</span>=<span class="number">2</span>,<span class="keyword">version</span>=<span class="keyword">version</span>+<span class="number">1</span></span><br><span class="line"><span class="keyword">where</span> <span class="keyword">id</span>=#&#123;<span class="keyword">id</span>&#125; <span class="keyword">and</span> <span class="keyword">version</span>=#&#123;<span class="keyword">version</span>&#125;;</span><br></pre></td></tr></table></figure><h4 id="优点与不足-1"><a href="#优点与不足-1" class="headerlink" title="优点与不足"></a>优点与不足</h4><p>乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的，因此尽可能直接做下去，直到提交的时候才去锁定，所以不会产生任何锁和死锁。但如果直接简单这么做，还是有可能会遇到不可预期的结果，例如两个事务都读取了数据库的某一行，经过修改以后写回数据库，这时就遇到了问题。</p>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;简介&quot;&gt;&lt;a href=&quot;#简介&quot; class=&quot;headerlink&quot; title=&quot;简介&quot;&gt;&lt;/a&gt;简介&lt;/h3&gt;&lt;p&gt;在数据库的锁机制中介绍过，数据库管理系统（DBMS）中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;乐观并发控制(乐观锁)&lt;/strong&gt; 和 &lt;strong&gt;悲观并发控制（悲观锁）&lt;/strong&gt; 是并发控制主要采用的技术手段。&lt;/p&gt;
    
    </summary>
    
      <category term="mysql" scheme="http://yoursite.com/categories/mysql/"/>
    
    
      <category term="lock" scheme="http://yoursite.com/tags/lock/"/>
    
  </entry>
  
  <entry>
    <title>spring-boot整合kafka</title>
    <link href="http://yoursite.com/2018/03/28/kafka/springboot-kafka/"/>
    <id>http://yoursite.com/2018/03/28/kafka/springboot-kafka/</id>
    <published>2018-03-27T17:09:00.000Z</published>
    <updated>2018-04-01T05:46:34.780Z</updated>
    
    <content type="html"><![CDATA[<h3 id="生产者"><a href="#生产者" class="headerlink" title="生产者"></a>生产者</h3><h4 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h4><p>KafkaTemplate封装了一个生成器，并提供了方便的方法来发送数据到kafka主题。 提供了异步和同步方法，异步方法返回一个Future。</p><a id="more"></a><p>其构造方法有：</p><figure class="highlight mathematica"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">ListenableFuture&lt;SendResult&lt;<span class="keyword">K</span>, V&gt;&gt; sendDefault(V data);</span><br><span class="line"></span><br><span class="line">ListenableFuture&lt;SendResult&lt;<span class="keyword">K</span>, V&gt;&gt; sendDefault(<span class="keyword">K</span> key, V data);</span><br><span class="line"></span><br><span class="line">ListenableFuture&lt;SendResult&lt;<span class="keyword">K</span>, V&gt;&gt; sendDefault(int partition, <span class="keyword">K</span> key, V data);</span><br><span class="line"></span><br><span class="line">ListenableFuture&lt;SendResult&lt;<span class="keyword">K</span>, V&gt;&gt; send(<span class="keyword">String</span> topic, V data);</span><br><span class="line"></span><br><span class="line">ListenableFuture&lt;SendResult&lt;<span class="keyword">K</span>, V&gt;&gt; send(<span class="keyword">String</span> topic, <span class="keyword">K</span> key, V data);</span><br><span class="line"></span><br><span class="line">ListenableFuture&lt;SendResult&lt;<span class="keyword">K</span>, V&gt;&gt; send(<span class="keyword">String</span> topic, int partition, V data);</span><br><span class="line"></span><br><span class="line">ListenableFuture&lt;SendResult&lt;<span class="keyword">K</span>, V&gt;&gt; send(<span class="keyword">String</span> topic, int partition, <span class="keyword">K</span> key, V data);</span><br><span class="line"></span><br><span class="line">ListenableFuture&lt;SendResult&lt;<span class="keyword">K</span>, V&gt;&gt; send(<span class="keyword">Message</span>&lt;?&gt; message);</span><br></pre></td></tr></table></figure><blockquote><p>前3个方法需要向Temple提供默认主题</p></blockquote><h4 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h4><p>使用Producer配置类</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableKafka</span></span><br><span class="line">public <span class="class"><span class="keyword">class</span> <span class="title">ProducerConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value</span>(<span class="string">"<span class="subst">$&#123;kafka.producer.servers&#125;</span>"</span>)</span><br><span class="line">private <span class="built_in">String</span> servers;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value</span>(<span class="string">"<span class="subst">$&#123;kafka.producer.retries&#125;</span>"</span>)</span><br><span class="line">private <span class="built_in">int</span> retries;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value</span>(<span class="string">"<span class="subst">$&#123;kafka.producer.batch.size&#125;</span>"</span>)</span><br><span class="line">private <span class="built_in">int</span> batchSize;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value</span>(<span class="string">"<span class="subst">$&#123;kafka.producer.linger&#125;</span>"</span>)</span><br><span class="line">private <span class="built_in">int</span> linger;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value</span>(<span class="string">"<span class="subst">$&#123;kafka.producer.buffer.memory&#125;</span>"</span>)</span><br><span class="line">private <span class="built_in">int</span> bufferMemory;</span><br><span class="line"></span><br><span class="line">public <span class="built_in">Map</span>&lt;<span class="built_in">String</span>, <span class="built_in">Object</span>&gt; producerConfigs() &#123;</span><br><span class="line"><span class="built_in">Map</span>&lt;<span class="built_in">String</span>, <span class="built_in">Object</span>&gt; props = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line">props.put(Config.BOOTSTRAP_SERVERS_CONFIG, servers);</span><br><span class="line">props.put(Config.RETRIES_CONFIG, retries);</span><br><span class="line">props.put(Config.BATCH_SIZE_CONFIG, batchSize);</span><br><span class="line">props.put(Config.LINGER_MS_CONFIG, linger);</span><br><span class="line">props.put(Config.BUFFER_MEMORY_CONFIG, bufferMemory);</span><br><span class="line">props.put(Config.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.<span class="keyword">class</span>);</span><br><span class="line">props.put(Config.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.<span class="keyword">class</span>);</span><br><span class="line">props.put(Config.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.<span class="keyword">class</span>);</span><br><span class="line"><span class="keyword">return</span> props;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public ProducerFactory&lt;<span class="built_in">String</span>, <span class="built_in">String</span>&gt; producerFactory() &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> DefaultKafkaProducerFactory&lt;&gt;(producerConfigs());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line">public KafkaTemplate&lt;<span class="built_in">String</span>, <span class="built_in">String</span>&gt; kafkaTemplate() &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> KafkaTemplate&lt;<span class="built_in">String</span>, <span class="built_in">String</span>&gt;(producerFactory());</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h4><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(<span class="meta-string">"/kafka/producer"</span>)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProducerController</span> </span>&#123;</span><br><span class="line"><span class="keyword">private</span> static Logger logger = LoggerFactory.getLogger(ProducerController.<span class="keyword">class</span>);</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value(<span class="meta-string">"<span class="subst">$&#123;topic.name&#125;</span>"</span>)</span></span><br><span class="line"><span class="keyword">private</span> String topicName;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Autowired</span></span><br><span class="line"><span class="keyword">private</span> KafkaTemplate&lt;String, String&gt; kafkaTemplate;</span><br><span class="line"></span><br><span class="line"><span class="meta">@RequestMapping(<span class="meta-string">"/send"</span>)</span></span><br><span class="line"><span class="keyword">public</span> Object sendKafka(String message) &#123;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">logger.info(<span class="string">"send kafka message: &#123;&#125;"</span>, message);</span><br><span class="line">kafkaTemplate.send(topicName, UUID.randomUUID().toString(), message);</span><br><span class="line"><span class="keyword">return</span> <span class="string">"success"</span>;</span><br><span class="line">&#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">logger.error(<span class="string">"发送kafka失败"</span>, e);</span><br><span class="line"><span class="keyword">return</span> <span class="string">"fail"</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="消费者"><a href="#消费者" class="headerlink" title="消费者"></a>消费者</h3><h4 id="说明-1"><a href="#说明-1" class="headerlink" title="说明"></a>说明</h4><p>可以通过配置MessageListenerContainer并提供MessageListener或通过使用@KafkaListener注释来接收消息。<br>MessageListenerContainer有两个实现：</p><li>KafkaMessageListenerContainer：从单个线程上的所有主题/分区接收所有消息</li><br><li>ConcurrentMessageListenerContainer：委托给1个或多个KafkaMessageListenerContainer以提供多线程消费。通过container.setConcurrency(3)，来设置多个线程</li><h4 id="配置-1"><a href="#配置-1" class="headerlink" title="配置"></a>配置</h4><p>使用Consumer配置类</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableKafka</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConsumerConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value(<span class="meta-string">"<span class="subst">$&#123;kafka.consumer.servers&#125;</span>"</span>)</span></span><br><span class="line"><span class="keyword">private</span> String servers;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value(<span class="meta-string">"<span class="subst">$&#123;kafka.consumer.enable.auto.commit&#125;</span>"</span>)</span></span><br><span class="line"><span class="keyword">private</span> boolean enableAutoCommit;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value(<span class="meta-string">"<span class="subst">$&#123;kafka.consumer.session.timeout&#125;</span>"</span>)</span></span><br><span class="line"><span class="keyword">private</span> String sessionTimeout;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value(<span class="meta-string">"<span class="subst">$&#123;kafka.consumer.auto.commit.interval&#125;</span>"</span>)</span></span><br><span class="line"><span class="keyword">private</span> String autoCommitInterval;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value(<span class="meta-string">"<span class="subst">$&#123;kafka.consumer.group.id&#125;</span>"</span>)</span></span><br><span class="line"><span class="keyword">private</span> String groupId;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value(<span class="meta-string">"<span class="subst">$&#123;kafka.consumer.topic&#125;</span>"</span>)</span></span><br><span class="line"><span class="keyword">private</span> String topic;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value(<span class="meta-string">"<span class="subst">$&#123;kafka.consumer.auto.offset.reset&#125;</span>"</span>)</span></span><br><span class="line"><span class="keyword">private</span> String autoOffsetReset;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value(<span class="meta-string">"<span class="subst">$&#123;kafka.consumer.concurrency&#125;</span>"</span>)</span></span><br><span class="line"><span class="keyword">private</span> int concurrency;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * KafkaMessageListenerContainer： 从单个线程上的所有主题/分区接收所有消息</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"><span class="doctag">@Bean</span>(initMethod = "doStart")</span></span><br><span class="line"><span class="comment">public KafkaMessageListenerContainer&lt;String, String&gt; kafkaMessageListenerContainer() &#123;</span></span><br><span class="line"><span class="comment">KafkaMessageListenerContainer&lt;String, String&gt; container = new KafkaMessageListenerContainer&lt;&gt;(consumerFactory(), containerProperties());</span></span><br><span class="line"><span class="comment">return container;</span></span><br><span class="line"><span class="comment">&#125;</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * ConcurrentMessageListenerContainer：</span></span><br><span class="line"><span class="comment"> * 委托给1个或多个KafkaMessageListenerContainer以提供多线程消费。</span></span><br><span class="line"><span class="comment"> * 通过container.setConcurrency(3)，来设置多个线程</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Bean(initMethod = <span class="meta-string">"doStart"</span>)</span></span><br><span class="line"><span class="keyword">public</span> ConcurrentMessageListenerContainer&lt;String, String&gt; concurrentMessageListenerContainer() &#123;</span><br><span class="line">ConcurrentMessageListenerContainer&lt;String, String&gt; container = new ConcurrentMessageListenerContainer&lt;&gt;(consumerFactory(), containerProperties());</span><br><span class="line">container.setConcurrency(concurrency);</span><br><span class="line"><span class="keyword">return</span> container;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> ConsumerFactory&lt;String, String&gt; consumerFactory() &#123;</span><br><span class="line"><span class="keyword">return</span> new DefaultKafkaConsumerFactory&lt;&gt;(consumerConfigs());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> ContainerProperties containerProperties() &#123;</span><br><span class="line">ContainerProperties containerProperties = new ContainerProperties(topic);</span><br><span class="line">containerProperties.setMessageListener(messageListener());</span><br><span class="line"><span class="keyword">return</span> containerProperties;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> Map&lt;String, Object&gt; consumerConfigs() &#123;</span><br><span class="line">Map&lt;String, Object&gt; propsMap = new HashMap&lt;&gt;();</span><br><span class="line">propsMap.put(Config.BOOTSTRAP_SERVERS_CONFIG, servers);</span><br><span class="line">propsMap.put(Config.ENABLE_AUTO_COMMIT_CONFIG, enableAutoCommit);</span><br><span class="line">propsMap.put(Config.AUTO_COMMIT_INTERVAL_MS_CONFIG, autoCommitInterval);</span><br><span class="line">propsMap.put(Config.SESSION_TIMEOUT_MS_CONFIG, sessionTimeout);</span><br><span class="line">propsMap.put(Config.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.<span class="keyword">class</span>);</span><br><span class="line">propsMap.put(Config.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.<span class="keyword">class</span>);</span><br><span class="line">propsMap.put(Config.GROUP_ID_CONFIG, groupId);</span><br><span class="line">propsMap.put(Config.AUTO_OFFSET_RESET_CONFIG, autoOffsetReset);</span><br><span class="line"><span class="keyword">return</span> propsMap;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> MessageListener&lt;String, String&gt; messageListener() &#123;</span><br><span class="line"><span class="keyword">return</span> new CustomMessageListener();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="消息接收"><a href="#消息接收" class="headerlink" title="消息接收"></a>消息接收</h4><h5 id="Java实现"><a href="#Java实现" class="headerlink" title="Java实现"></a>Java实现</h5><p>直接使用kafka0.10 client去收发消息</p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">@Test</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> receive()&#123;</span><br><span class="line">    Properties props = <span class="keyword">new</span> Properties();</span><br><span class="line">    props.<span class="built_in">put</span>(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, broker);</span><br><span class="line">    props.<span class="built_in">put</span>(ConsumerConfig.GROUP_ID_CONFIG, groupId);</span><br><span class="line">    props.<span class="built_in">put</span>(ConsumerConfig.CLIENT_ID_CONFIG, clientId);</span><br><span class="line">    props.<span class="built_in">put</span>(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, <span class="string">"true"</span>);</span><br><span class="line">    props.<span class="built_in">put</span>(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, <span class="string">"1000"</span>);</span><br><span class="line">    props.<span class="built_in">put</span>(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, <span class="string">"earliest"</span>);</span><br><span class="line">    props.<span class="built_in">put</span>(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());</span><br><span class="line">    props.<span class="built_in">put</span>(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());</span><br><span class="line">    KafkaConsumer&lt;<span class="keyword">String</span>, <span class="keyword">String</span>&gt; consumer = <span class="keyword">new</span> KafkaConsumer&lt;&gt;(props);</span><br><span class="line">    <span class="built_in">try</span>&#123;</span><br><span class="line">        consumer.subscribe(Arrays.asList(topic));</span><br><span class="line">        <span class="built_in">while</span> (true) &#123;</span><br><span class="line">            ConsumerRecords&lt;<span class="keyword">String</span>, <span class="keyword">String</span>&gt; records = consumer.poll(<span class="number">10000</span>);</span><br><span class="line">            records.forEach(record -&gt; &#123;</span><br><span class="line">                System.out.printf(<span class="string">"client : %s , topic: %s , partition: %d , offset = %d, key = %s, value = %s%n"</span>, clientId, record.topic(),</span><br><span class="line">                        record.partition(), record.offset(), record.key(), record.value());</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;<span class="built_in">catch</span> (Exception e)&#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;finally &#123;</span><br><span class="line">        consumer.<span class="built_in">close</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="使用MessageListener接口"><a href="#使用MessageListener接口" class="headerlink" title="使用MessageListener接口"></a>使用MessageListener接口</h5><p>继承MessageListener接口</p><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CustomMessageListener</span> <span class="keyword">implements</span> <span class="title">MessageListener</span>&lt;<span class="title">Integer</span>, <span class="title">String</span>&gt; &#123;</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> Logger logger = LoggerFactory.getLogger(CustomMessageListener.<span class="keyword">class</span>);</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> onMessage(ConsumerRecord&lt;Integer, String&gt; data) &#123;</span><br><span class="line">logger.info(<span class="string">"received key: &#123;&#125;, value: &#123;&#125;"</span>, data.key(), data.value());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//或包含消费者的onMessage方法，以手动提交ofset</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="使用-KafkaListener注解"><a href="#使用-KafkaListener注解" class="headerlink" title="使用@KafkaListener注解"></a>使用@KafkaListener注解</h5><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">@KafkaListener</span>(id = <span class="string">"foo"</span>, topics = <span class="string">"myTopic"</span>)</span><br><span class="line">public void listen(String data) &#123;</span><br><span class="line"> ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">@KafkaListener</span>(id = <span class="string">"bar"</span>, topicPartitions =</span><br><span class="line">        &#123; <span class="variable">@TopicPartition</span>(topic = <span class="string">"topic1"</span>, partitions = &#123; <span class="string">"0"</span>, <span class="string">"1"</span> &#125;),</span><br><span class="line">          <span class="variable">@TopicPartition</span>(topic = <span class="string">"topic2"</span>, partitions = <span class="string">"0"</span>,</span><br><span class="line">             partitionOffsets = <span class="variable">@PartitionOffset</span>(partition = <span class="string">"1"</span>, initialOffset = <span class="string">"100"</span>))</span><br><span class="line">        &#125;)</span><br><span class="line">public void listen(ConsumerRecord&lt;?, ?&gt; record) &#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><ul><li>对于生产者来说，封装KafkaProducer到KafkaTemplate相对简单</li><li>对于消费者来说，由于spring是采用注解的形式去标注消息处理方法<ul><li>先在KafkaListenerAnnotationBeanPostProcessor中扫描bean，然后注册到KafkaListenerEndpointRegistrar</li><li>而KafkaListenerEndpointRegistrar在afterPropertiesSet的时候去创建MessageListenerContainer</li><li>messageListener包含了原始endpoint携带的bean以及method转换成的InvocableHandlerMethod</li><li>ConcurrentMessageListenerContainer这个衔接上，根据配置的spring.kafka.listener.concurrency来生成多个并发的KafkaMessageListenerContainer实例</li><li>每个KafkaMessageListenerContainer都自己创建一个ListenerConsumer，然后自己创建一个独立的kafka consumer，每个ListenerConsumer在线程池里头运行，这样来实现并发</li><li>每个ListenerConsumer里头都有一个recordsToProcess队列，从原始的kafka consumer poll出来的记录会放到这个队列里头，</li><li>然后有一个ListenerInvoker线程循环超时等待从recordsToProcess取出记录，然后调用messageListener的onMessage方法(即KafkaListener注解标准的方法)</li></ul></li></ul><h5 id="项目源码"><a href="#项目源码" class="headerlink" title="项目源码"></a>项目源码</h5><li><a href="https://github.com/scjqwe/spring-kafka-examples" target="_blank" rel="noopener">https://github.com/scjqwe/spring-kafka-examples</a></li><h5 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h5><li><a href="https://docs.spring.io/spring-kafka/docs/1.0.4.RELEASE/reference/html/_reference.html" target="_blank" rel="noopener">https://docs.spring.io/spring-kafka/docs/1.0.4.RELEASE/reference/html/_reference.html</a></li><br><li><a href="https://segmentfault.com/a/1190000011471181" target="_blank" rel="noopener">https://segmentfault.com/a/1190000011471181</a></li><br><li><a href="http://www.2bowl.info/apache-kafka%E7%BC%96%E7%A8%8B%E5%85%A5%E9%97%A8%E4%BA%8C-spring%E6%95%B4%E5%90%88kafka/" target="_blank" rel="noopener">http://www.2bowl.info/apache-kafka%E7%BC%96%E7%A8%8B%E5%85%A5%E9%97%A8%E4%BA%8C-spring%E6%95%B4%E5%90%88kafka/</a></li>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;生产者&quot;&gt;&lt;a href=&quot;#生产者&quot; class=&quot;headerlink&quot; title=&quot;生产者&quot;&gt;&lt;/a&gt;生产者&lt;/h3&gt;&lt;h4 id=&quot;说明&quot;&gt;&lt;a href=&quot;#说明&quot; class=&quot;headerlink&quot; title=&quot;说明&quot;&gt;&lt;/a&gt;说明&lt;/h4&gt;&lt;p&gt;KafkaTemplate封装了一个生成器，并提供了方便的方法来发送数据到kafka主题。 提供了异步和同步方法，异步方法返回一个Future。&lt;/p&gt;
    
    </summary>
    
      <category term="kafka" scheme="http://yoursite.com/categories/kafka/"/>
    
    
      <category term="kafka" scheme="http://yoursite.com/tags/kafka/"/>
    
      <category term="spring-boot" scheme="http://yoursite.com/tags/spring-boot/"/>
    
  </entry>
  
  <entry>
    <title>Git知识</title>
    <link href="http://yoursite.com/2018/03/18/git/"/>
    <id>http://yoursite.com/2018/03/18/git/</id>
    <published>2018-03-18T15:21:00.000Z</published>
    <updated>2018-03-19T16:27:44.035Z</updated>
    
    <content type="html"><![CDATA[<h4 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h4><p>git是一个开源的<strong>分布式版本控制系统</strong>，用以有效、高速的处理从很小到非常大的项目版本管理。<strong>git是个工具</strong>，在linux里面也就类似gcc这样的工具一样，是一个shell命令。</p><a id="more"></a><h4 id="Git与GitHub区别"><a href="#Git与GitHub区别" class="headerlink" title="Git与GitHub区别"></a>Git与GitHub区别</h4><li>Git是版本控制工具</li><br><li>Github是一个平台，提供给用户创建git仓储空间，保存（托管）用户的一些数据文档或者代码等。</li><h4 id="Git与CVS、SVN的区别"><a href="#Git与CVS、SVN的区别" class="headerlink" title="Git与CVS、SVN的区别"></a>Git与CVS、SVN的区别</h4><li>Git是分布式版本控制系统，代码提交是在本地的（如此速度就快），当然生成补丁（patch）然后push到远程服务器上是需要联网的</li><br><li>CVS、SVN是集中式版本控制系统，代码提交都是提交到远程服务器上</li><br><li>CVS、SVN这样的集中式版本控制系统，它的完整代码仓库（代码仓库不仅仅只包含了代码，还包含各个历史版本的信息等）在中心服务器上，一旦这个中心服务器挂了，也就是完整的代码仓库挂了</li><br><li>Git没有中心服务器的概念，每一个git客户端（git节点）都含有一个完整的代码仓库（前提是你之前从远程git仓库fetch过代码）</li><h4 id="远程仓库、工作区、版本库和暂存区"><a href="#远程仓库、工作区、版本库和暂存区" class="headerlink" title="远程仓库、工作区、版本库和暂存区"></a>远程仓库、工作区、版本库和暂存区</h4><p><img src="/images/git/git.jpg" alt=""></p><h5 id="远程仓库"><a href="#远程仓库" class="headerlink" title="远程仓库"></a>远程仓库</h5><p>就是在github或者在gitlab上的代码。可以用<strong>git pull</strong>和<strong>git push</strong>来进行本地仓库和远程仓库的同步操作</p><h5 id="工作区（Working-Directory）"><a href="#工作区（Working-Directory）" class="headerlink" title="工作区（Working Directory）"></a>工作区（Working Directory）</h5><p>从项目中取出某个版本的所有文件和目录，用以开始后续工作的叫做工作目录，也就是工作区</p><h5 id="版本库（Repository）"><a href="#版本库（Repository）" class="headerlink" title="版本库（Repository）"></a>版本库（Repository）</h5><p>工作区有一个隐藏目录.git，这个不算工作区，而是Git的版本库。</p><li>Git的版本库里存了很多东西，其中最重要的就是称为stage（或者叫index）的<strong>暂存区</strong>，还有Git为我们自动创建的第一个分支<strong>master</strong>，以及指向master的一个指针叫<strong>HEAD</strong>。</li><h5 id="暂存区（Stage）"><a href="#暂存区（Stage）" class="headerlink" title="暂存区（Stage）"></a>暂存区（Stage）</h5><p>暂存区就是版本库中的一个区域，具体参见上面的结构图</p><h5 id="工作区、版本库、暂存区之间的关系"><a href="#工作区、版本库、暂存区之间的关系" class="headerlink" title="工作区、版本库、暂存区之间的关系"></a>工作区、版本库、暂存区之间的关系</h5><li>使用<strong>git add</strong>把文件从工作区添加到版本库中的暂存区，git add命令可以多次用，或者使用git add file1 file2 …</li><br><li>使用<strong>git commit</strong>提交代码，就是把暂存区的所有内容提交到当前分支</li><br><li>需要提交的文件修改通通放到暂存区（可能有多次的git add），然后，一次性提交暂存区的所有修改到当前分支（git commit）</li><h4 id="版本回退"><a href="#版本回退" class="headerlink" title="版本回退"></a>版本回退</h4><p>前提条件：已经执行git commit命令了，但是没有push到远程仓库，用以下命令可以回退<br><br>其实这个回退就是将本地的HEAD指针移动到某个版本上而已，所以这个操作是非常快的。</p><h5 id="回退到上一个版本"><a href="#回退到上一个版本" class="headerlink" title="回退到上一个版本"></a>回退到上一个版本</h5><li>在Git中，用<strong>HEAD</strong>表示当前版本，也就是最新的提交3628164…882e1e0，上一个版本就是<strong>HEAD^</strong>，上上一个版本就是<strong>HEAD^^</strong>，当然往上100个版本写100个^比较容易数不过来，所以写成<strong>HEAD~100</strong></li><br><li>版本号没必要写全，前几位就可以了，Git会自动去找。当然也不能只写前一两位，因为Git可能会找到多个版本号，就无法确定是哪一个了</li><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git <span class="keyword">reset</span> <span class="comment">--hard HEAD^</span></span><br><span class="line"><span class="keyword">HEAD</span> <span class="keyword">is</span> <span class="keyword">now</span> <span class="keyword">at</span> ea34578 <span class="keyword">add</span> <span class="keyword">distributed</span></span><br></pre></td></tr></table></figure><h5 id="回退到未来的某个版本"><a href="#回退到未来的某个版本" class="headerlink" title="回退到未来的某个版本"></a>回退到未来的某个版本</h5><li>使用<strong>git log</strong>命令查看提交记录，找到需要回退版本的<strong>commit id</strong></li><br><li>如果在回退以后又想再次回到之前的版本，<strong>git reflog</strong> 可以查看所有分支的所有操作记录（包括commit和reset的操作），包括已经被删除的commit记录</li><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git <span class="keyword">reset</span> <span class="comment">--hard 3628164</span></span><br><span class="line"><span class="keyword">HEAD</span> <span class="keyword">is</span> <span class="keyword">now</span> <span class="keyword">at</span> <span class="number">3628164</span> append GPL</span><br></pre></td></tr></table></figure><blockquote><p>git log则不能查看已经删除了的commit记录</p></blockquote><h5 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h5><li>HEAD指向的版本就是当前版本，因此，Git允许我们在版本的历史之间穿梭，使用命令git reset –hard commit_id</li><br><li>穿梭前，用git log可以查看提交历史，以便确定要回退到哪个版本</li><br><li>要重返未来，用git reflog查看命令历史，以便确定要回到未来的哪个版本</li><blockquote><p><strong>git reset –-soft</strong>：回退到某个版本，只回退了commit的信息，不会恢复到index file一级。如果还要提交，直接commit即可<br><strong>git reset -–hard</strong>：彻底回退到某个版本，本地的源码也会变为上一个版本的内容，撤销的commit中所包含的更改被冲掉</p></blockquote><h4 id="撤销修改"><a href="#撤销修改" class="headerlink" title="撤销修改"></a>撤销修改</h4><h5 id="含义"><a href="#含义" class="headerlink" title="含义"></a>含义</h5><li>命令git checkout – file意思就是，把file文件在工作区的修改全部撤销，这里有两种情况：</li><br><li>一种是file自修改后还没有被放到暂存区，现在，撤销修改就回到和版本库一模一样的状态；</li><br><li>一种是file已经添加到暂存区后，又作了修改，现在，撤销修改就回到添加到暂存区后的状态。</li><br><li>总之，就是让这个文件回到最近一次git commit或git add时的状态。</li><h5 id="场景"><a href="#场景" class="headerlink" title="场景"></a>场景</h5><li>场景1：当你改乱了工作区某个文件的内容，想直接丢弃工作区的修改时，用命令git checkout – file</li><br><li>场景2：当你不但改乱了工作区某个文件的内容，还添加到了暂存区时，想丢弃修改，分两步，第一步用命令git reset HEAD file，就回到了场景1，第二步按场景1操作</li><br><li>场景3：已经提交了不合适的修改到版本库时，想要撤销本次提交，版本回退一节，不过前提是没有推送到远程库</li><h4 id="git-stash"><a href="#git-stash" class="headerlink" title="git stash"></a>git stash</h4><p>场景:当你接到一个修复一个代号101的bug的任务时，你想创建一个分支issue-101来修复它，但是，当前正在dev上进行的工作还没有提交，Git还提供了一个stash功能，可以把当前工作现场“储藏”起来，等以后恢复现场后继续工作</p><h5 id="git-stash-list"><a href="#git-stash-list" class="headerlink" title="git stash list"></a>git stash list</h5><p>查看暂存区的所有暂存修改记录</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git stash <span class="keyword">list</span></span><br><span class="line">stash@&#123;<span class="number">0</span>&#125;: WIP <span class="keyword">on</span> de<span class="variable">v:</span> <span class="number">6224937</span> <span class="built_in">add</span> merge</span><br></pre></td></tr></table></figure><h5 id="git-stash-apply-stash-X"><a href="#git-stash-apply-stash-X" class="headerlink" title="git stash apply stash@{X}"></a>git stash apply stash@{X}</h5><p>取出相应的暂存</p><h5 id="git-stash-drop-stash-X"><a href="#git-stash-drop-stash-X" class="headerlink" title="git stash drop stash@{X}"></a>git stash drop stash@{X}</h5><p>将记录列表中取出的对应暂存记录删除</p><h5 id="git-stash-pop-推荐"><a href="#git-stash-pop-推荐" class="headerlink" title="git stash pop(推荐)"></a>git stash pop(推荐)</h5><p>取出最近一次暂存并删除记录列表中对应记录</p><h4 id="分支管理"><a href="#分支管理" class="headerlink" title="分支管理"></a>分支管理</h4><table><thead><tr><th>命令</th><th>含义</th></tr></thead><tbody><tr><td>git branch</td><td>查看分支</td></tr><tr><td>git branch <name></name></td><td>创建分支</td></tr><tr><td>git checkout <name></name></td><td>切换分支</td></tr><tr><td>git checkout -b <name></name></td><td>创建+切换分支</td></tr><tr><td>git merge <name></name></td><td>合并某分支到当前分支</td></tr><tr><td>git branch -d <name></name></td><td>删除分支</td></tr></tbody></table><h5 id="Bug分支"><a href="#Bug分支" class="headerlink" title="Bug分支"></a>Bug分支</h5><p>在Git中，由于分支是如此的强大，所以，每个bug都可以通过一个新的临时分支来修复，修复后，合并分支，然后将临时分支删除。</p><h5 id="Feature分支"><a href="#Feature分支" class="headerlink" title="Feature分支"></a>Feature分支</h5><p>添加一个新功能时，你肯定不希望因为一些实验性质的代码，把主分支搞乱了，所以，每添加一个新功能，最好新建一个feature分支，在上面开发，完成后，合并，最后，删除该feature分支。</p>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;介绍&quot;&gt;&lt;a href=&quot;#介绍&quot; class=&quot;headerlink&quot; title=&quot;介绍&quot;&gt;&lt;/a&gt;介绍&lt;/h4&gt;&lt;p&gt;git是一个开源的&lt;strong&gt;分布式版本控制系统&lt;/strong&gt;，用以有效、高速的处理从很小到非常大的项目版本管理。&lt;strong&gt;git是个工具&lt;/strong&gt;，在linux里面也就类似gcc这样的工具一样，是一个shell命令。&lt;/p&gt;
    
    </summary>
    
      <category term="git" scheme="http://yoursite.com/categories/git/"/>
    
    
      <category term="git" scheme="http://yoursite.com/tags/git/"/>
    
  </entry>
  
  <entry>
    <title>Redis高级知识</title>
    <link href="http://yoursite.com/2018/03/15/redis/redis/"/>
    <id>http://yoursite.com/2018/03/15/redis/redis/</id>
    <published>2018-03-15T15:05:00.000Z</published>
    <updated>2018-03-19T16:17:31.950Z</updated>
    
    <content type="html"><![CDATA[<h4 id="Redis-发布订阅"><a href="#Redis-发布订阅" class="headerlink" title="Redis 发布订阅"></a>Redis 发布订阅</h4><p>Redis 发布订阅(pub/sub)是一种消息通信模式：发送者(pub)发送消息，订阅者(sub)接收消息。<br><br>Redis 客户端可以订阅任意数量的频道。<br></p><a id="more"></a><p>下图展示了频道 channel1 ， 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系：<br><br><img src="http://www.runoob.com/wp-content/uploads/2014/11/pubsub1.png" alt=""><br>当有新消息通过 publish 命令发送给频道 channel1 时， 这个消息就会被发送给订阅它的三个客户端：<br><br><img src="http://www.runoob.com/wp-content/uploads/2014/11/pubsub2.png" alt=""></p><h6 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h6><p>以下实例演示了发布订阅是如何工作的。在我们实例中我们创建了订阅频道名为 redisChat:<br></p><figure class="highlight gams"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">redis <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; SUBSCRIBE redisChat</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">Reading</span></span> messages... (press Ctrl-C to quit)</span><br><span class="line"><span class="number">1</span>) <span class="string">"subscribe"</span></span><br><span class="line"><span class="number">2</span>) <span class="string">"redisChat"</span></span><br><span class="line"><span class="number">3</span>) (<span class="keyword">integer</span>) <span class="number">1</span></span><br></pre></td></tr></table></figure><p>现在，我们先重新开启个 redis 客户端，然后在同一个频道 redisChat 发布两次消息，订阅者就能接收到消息。<br></p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">redis</span> <span class="number">127.0.0.1:6379</span>&gt; publish redisChat <span class="string">"Redis is a great caching technique"</span></span><br><span class="line"></span><br><span class="line">(integer) <span class="number">1</span></span><br><span class="line"></span><br><span class="line">redis <span class="number">127.0.0.1:6379</span>&gt; publish redisChat <span class="string">"Learn redis by runoob.com"</span></span><br><span class="line"></span><br><span class="line">(integer) <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 订阅者的客户端会显示如下消息</span></span><br><span class="line"><span class="number">1</span>) <span class="string">"message"</span></span><br><span class="line"><span class="number">2</span>) <span class="string">"redisChat"</span></span><br><span class="line"><span class="number">3</span>) <span class="string">"Redis is a great caching technique"</span></span><br><span class="line"><span class="number">1</span>) <span class="string">"message"</span></span><br><span class="line"><span class="number">2</span>) <span class="string">"redisChat"</span></span><br><span class="line"><span class="number">3</span>) <span class="string">"Learn redis by runoob.com"</span></span><br></pre></td></tr></table></figure><h6 id="命令"><a href="#命令" class="headerlink" title="命令"></a>命令</h6><table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td>PSUBSCRIBE pattern [pattern …]</td><td>订阅一个或多个符合给定模式的频道</td></tr><tr><td>PUBSUB subcommand [argument [argument …]]</td><td>查看订阅与发布系统状态</td></tr><tr><td>PUBLISH channel message</td><td>将信息发送到指定的频道</td></tr><tr><td>PUNSUBSCRIBE [pattern [pattern …]]</td><td>退订所有给定模式的频道</td></tr><tr><td>SUBSCRIBE channel [channel …]</td><td>订阅给定的一个或多个频道的信息</td></tr><tr><td>UNSUBSCRIBE [channel [channel …]]</td><td>指退订给定的频道</td></tr></tbody></table><h4 id="Redis-事务"><a href="#Redis-事务" class="headerlink" title="Redis 事务"></a>Redis 事务</h4><p>Redis 事务可以一次执行多个命令， 并且带有以下两个重要的保证：<br></p><li>批量操作在发送 EXEC 命令前被放入队列缓存。</li><br><li>收到 EXEC 命令后进入事务执行，事务中任意命令执行失败，其余的命令依然被执行。</li><br><li>在事务执行过程，其他客户端提交的命令请求不会插入到事务执行命令序列中。</li><h6 id="实例-1"><a href="#实例-1" class="headerlink" title="实例"></a>实例</h6><p>以下是一个事务的例子， 它先以 MULTI 开始一个事务， 然后将多个命令入队到事务中， 最后由 EXEC 命令触发事务， 一并执行事务中的所有命令：<br></p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">MULTI</span></span><br><span class="line"><span class="selector-tag">OK</span></span><br><span class="line"></span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">SET</span> <span class="selector-tag">book-name</span> "<span class="selector-tag">Mastering</span> <span class="selector-tag">C</span>++ <span class="selector-tag">in</span> 21 <span class="selector-tag">days</span>"</span><br><span class="line"><span class="selector-tag">QUEUED</span></span><br><span class="line"></span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">GET</span> <span class="selector-tag">book-name</span></span><br><span class="line"><span class="selector-tag">QUEUED</span></span><br><span class="line"></span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">SADD</span> <span class="selector-tag">tag</span> "<span class="selector-tag">C</span>++" "<span class="selector-tag">Programming</span>" "<span class="selector-tag">Mastering</span> <span class="selector-tag">Series</span>"</span><br><span class="line"><span class="selector-tag">QUEUED</span></span><br><span class="line"></span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">SMEMBERS</span> <span class="selector-tag">tag</span></span><br><span class="line"><span class="selector-tag">QUEUED</span></span><br><span class="line"></span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">EXEC</span></span><br><span class="line">1) <span class="selector-tag">OK</span></span><br><span class="line">2) "<span class="selector-tag">Mastering</span> <span class="selector-tag">C</span>++ <span class="selector-tag">in</span> 21 <span class="selector-tag">days</span>"</span><br><span class="line">3) (<span class="selector-tag">integer</span>) 3</span><br><span class="line">4) 1) "<span class="selector-tag">Mastering</span> <span class="selector-tag">Series</span>"</span><br><span class="line">   2) "<span class="selector-tag">C</span>++"</span><br><span class="line">   3) "<span class="selector-tag">Programming</span>"</span><br></pre></td></tr></table></figure><p>单个 Redis 命令的执行是原子性的，但 Redis 没有在事务上增加任何维持原子性的机制，所以 Redis 事务的执行并不是原子性的。<br><br>事务可以理解为一个打包的批量执行脚本，但批量指令并非原子化的操作，中间某条指令的失败不会导致前面已做指令的回滚，也不会造成后续的指令不做。<br></p><h6 id="命令-1"><a href="#命令-1" class="headerlink" title="命令"></a>命令</h6><table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td>DISCARD</td><td>取消事务，放弃执行事务块内的所有命令</td></tr><tr><td>EXEC</td><td>执行所有事务块内的命令</td></tr><tr><td>MULTI</td><td>标记一个事务块的开始</td></tr><tr><td>UNWATCH</td><td>取消 WATCH 命令对所有 key 的监视</td></tr><tr><td>WATCH key [key …]</td><td>监视一个(或多个) key ，如果在事务执行之前这个(或这些) key 被其他命令所改动，那么事务将被打断</td></tr></tbody></table><li>Redis 在事务失败时不进行回滚，而是继续执行余下的命令</li><br><li>WATCH 命令可以为 Redis 事务提供 check-and-set （CAS）行为</li><blockquote><p>具体内容参见<a href="http://www.redis.cn/topics/transactions.html" target="_blank" rel="noopener">事物-官网文档</a></p></blockquote><h4 id="Redis-脚本"><a href="#Redis-脚本" class="headerlink" title="Redis 脚本"></a>Redis 脚本</h4><p>Redis 脚本使用 Lua 解释器来执行脚本。 Reids 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。<br></p><h6 id="实例-2"><a href="#实例-2" class="headerlink" title="实例"></a>实例</h6><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">redis <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; EVAL <span class="string">"return &#123;KEYS[1],KEYS[2],ARGV[1],ARGV[2]&#125;"</span> <span class="number">2</span> key1 key2 <span class="keyword">first</span> <span class="keyword">second</span></span><br><span class="line"></span><br><span class="line"><span class="number">1</span>) <span class="string">"key1"</span></span><br><span class="line"><span class="number">2</span>) <span class="string">"key2"</span></span><br><span class="line"><span class="number">3</span>) <span class="string">"first"</span></span><br><span class="line"><span class="number">4</span>) <span class="string">"second"</span></span><br></pre></td></tr></table></figure><h6 id="命令-2"><a href="#命令-2" class="headerlink" title="命令"></a>命令</h6><table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td>EVAL script numkeys key [key …] arg [arg …]</td><td>执行 Lua 脚本</td></tr><tr><td>EVALSHA sha1 numkeys key [key …] arg [arg …]</td><td>执行 Lua 脚本</td></tr><tr><td>SCRIPT EXISTS script [script …]</td><td>查看指定的脚本是否已经被保存在缓存当中</td></tr><tr><td>SCRIPT FLUSH</td><td>从脚本缓存中移除所有脚本</td></tr><tr><td>SCRIPT KILL</td><td>杀死当前正在运行的 Lua 脚本</td></tr><tr><td>SCRIPT LOAD script</td><td>将脚本 script 添加到脚本缓存中，但并不立即执行这个脚本</td></tr></tbody></table><h4 id="Redis-数据备份与恢复"><a href="#Redis-数据备份与恢复" class="headerlink" title="Redis 数据备份与恢复"></a>Redis 数据备份与恢复</h4><p>Redis 提供了不同级别的持久化方式:</p><li>RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储.</li><br><li>AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.</li><h5 id="RDB"><a href="#RDB" class="headerlink" title="RDB"></a>RDB</h5><h6 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h6><li>RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集.</li><br><li>RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3（可能加密），非常适用于灾难恢复.</li><br><li>RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做，父进程不需要再做其他IO操作，所以RDB持久化方式可以最大化redis的性能.</li><br><li>与AOF相比,在恢复大的数据集的时候，RDB方式会更快一些.</li><h6 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h6><li>如果你希望在redis意外停止工作（例如电源中断）的情况下丢失的数据最少的话，那么RDB不适合你.虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据.</li><br><li>RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求.如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度.</li><h5 id="AOF"><a href="#AOF" class="headerlink" title="AOF"></a>AOF</h5><h6 id="优点-1"><a href="#优点-1" class="headerlink" title="优点"></a>优点</h6><li>使用AOF 会让你的Redis更加耐久: 你可以使用不同的fsync策略：无fsync,每秒fsync,每次写的时候fsync.使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障，你最多丢失1秒的数据.</li><br><li>AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满，写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题.</li><br><li>Redis 可以在 AOF 文件体积变得过大时，自动地在后台对 AOF 进行重写： 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的，因为 Redis 在创建新 AOF 文件的过程中，会继续将命令追加到现有的 AOF 文件里面，即使重写过程中发生停机，现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕，Redis 就会从旧 AOF 文件切换到新 AOF 文件，并开始对新 AOF 文件进行追加操作。</li><br><li>AOF 文件有序地保存了对数据库执行的所有写入操作， 这些写入操作以 Redis 协议的格式保存， 因此 AOF 文件的内容非常容易被人读懂， 对文件进行分析（parse）也很轻松。 导出（export） AOF 文件也非常简单： 举个例子， 如果你不小心执行了 FLUSHALL 命令， 但只要 AOF 文件未被重写， 那么只要停止服务器， 移除 AOF 文件末尾的 FLUSHALL 命令， 并重启 Redis ， 就可以将数据集恢复到 FLUSHALL 执行之前的状态。</li><h6 id="缺点-1"><a href="#缺点-1" class="headerlink" title="缺点"></a>缺点</h6><li>对于相同的数据集来说，AOF 文件的体积通常要大于 RDB 文件的体积。</li><br><li>根据所使用的 fsync 策略，AOF 的速度可能会慢于 RDB 。 在一般情况下， 每秒 fsync 的性能依然非常高， 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快， 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时，RDB 可以提供更有保证的最大延迟时间（latency）。</li><h5 id="命令-3"><a href="#命令-3" class="headerlink" title="命令"></a>命令</h5><table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td>SAVE</td><td>创建当前数据库的备份,该命令将在 redis 安装目录中创建dump.rdb文件</td></tr><tr><td>BGSAVE</td><td>创建 redis 备份文件也可以使用命令 BGSAVE，该命令在后台执行</td></tr></tbody></table><blockquote><p>如果需要恢复数据，只需将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可</p></blockquote><h4 id="Redis-管道技术"><a href="#Redis-管道技术" class="headerlink" title="Redis 管道技术"></a>Redis 管道技术</h4><p>Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤：<br></p><li>客户端向服务端发送一个查询请求，并监听Socket返回，通常是以阻塞模式，等待服务端响应。</li><br><li>服务端处理命令，并将结果返回给客户端。</li><p>更多知识详见<a href="http://www.redis.cn/documentation.html" target="_blank" rel="noopener">官方文档</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;Redis-发布订阅&quot;&gt;&lt;a href=&quot;#Redis-发布订阅&quot; class=&quot;headerlink&quot; title=&quot;Redis 发布订阅&quot;&gt;&lt;/a&gt;Redis 发布订阅&lt;/h4&gt;&lt;p&gt;Redis 发布订阅(pub/sub)是一种消息通信模式：发送者(pub)发送消息，订阅者(sub)接收消息。&lt;br&gt;&lt;br&gt;Redis 客户端可以订阅任意数量的频道。&lt;br&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="redis" scheme="http://yoursite.com/categories/redis/"/>
    
    
      <category term="redis" scheme="http://yoursite.com/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>Redis入门</title>
    <link href="http://yoursite.com/2018/03/15/redis/redis-introduction/"/>
    <id>http://yoursite.com/2018/03/15/redis/redis-introduction/</id>
    <published>2018-03-15T15:05:00.000Z</published>
    <updated>2018-03-18T13:09:42.929Z</updated>
    
    <content type="html"><![CDATA[<h4 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h4><p>Remote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。<br>Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库，并提供多种语言的API。<br><br>它通常被称为数据结构服务器，因为值（value）可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。</p><a id="more"></a><h4 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h4><h5 id="String（字符串）"><a href="#String（字符串）" class="headerlink" title="String（字符串）"></a>String（字符串）</h5><p>string是redis最基本的类型，你可以理解成与Memcached一模一样的类型，一个key对应一个value。<br><br>string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。<br><br>string类型是Redis最基本的数据类型，一个键最大能存储512MB。<br></p><h6 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h6><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">SET</span> <span class="selector-tag">name</span> "<span class="selector-tag">runoob</span>"</span><br><span class="line"><span class="selector-tag">OK</span></span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">GET</span> <span class="selector-tag">name</span></span><br><span class="line">"<span class="selector-tag">runoob</span>"</span><br></pre></td></tr></table></figure><p>在以上实例中我们使用了 Redis 的 <strong>SET</strong> 和 <strong>GET</strong> 命令。键为 name，对应的值为 runoob。<br><br><strong>注意</strong>：一个键最大能存储<strong>512MB</strong>。</p><h5 id="Hash（哈希）"><a href="#Hash（哈希）" class="headerlink" title="Hash（哈希）"></a>Hash（哈希）</h5><p>Redis hash 是一个键值(key=&gt;value)对集合。<br><br>Redis hash是一个string类型的field和value的映射表，hash特别适合用于存储对象。</p><h6 id="实例-1"><a href="#实例-1" class="headerlink" title="实例"></a>实例</h6><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">redis&gt;</span><span class="bash"> HMSET myhash field1 <span class="string">"Hello"</span> field2 <span class="string">"World"</span></span></span><br><span class="line">"OK"</span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> HGET myhash field1</span></span><br><span class="line">"Hello"</span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"> HGET myhash field2</span></span><br><span class="line">"World"</span><br></pre></td></tr></table></figure><p>以上实例中 hash 数据类型存储了包含用户脚本信息的用户对象。 实例中我们使用了 Redis <strong>HMSET</strong>, <strong>HGETALL</strong> 命令，user:1 为键值。<br><br>每个 hash 可以存储 <strong>2^32 -1 键值对</strong>（<strong>40多亿</strong>）。</p><h5 id="List（列表）"><a href="#List（列表）" class="headerlink" title="List（列表）"></a>List（列表）</h5><p>Redis 列表是简单的字符串列表，按照插入顺序排序。你可以添加一个元素到列表的头部（左边）或者尾部（右边）。</p><h6 id="实例-2"><a href="#实例-2" class="headerlink" title="实例"></a>实例</h6><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">lpush</span> <span class="selector-tag">runoob</span> <span class="selector-tag">redis</span></span><br><span class="line">(<span class="selector-tag">integer</span>) 1</span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">lpush</span> <span class="selector-tag">runoob</span> <span class="selector-tag">mongodb</span></span><br><span class="line">(<span class="selector-tag">integer</span>) 2</span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">lpush</span> <span class="selector-tag">runoob</span> <span class="selector-tag">rabitmq</span></span><br><span class="line">(<span class="selector-tag">integer</span>) 3</span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">lrange</span> <span class="selector-tag">runoob</span> 0 10</span><br><span class="line">1) "<span class="selector-tag">rabitmq</span>"</span><br><span class="line">2) "<span class="selector-tag">mongodb</span>"</span><br><span class="line">3) "<span class="selector-tag">redis</span>"</span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt;</span><br></pre></td></tr></table></figure><p>列表最多可存储 <strong>2^32 - 1</strong> 元素 (4294967295, <strong>每个列表可存储40多亿</strong>)。</p><h5 id="Set（集合）"><a href="#Set（集合）" class="headerlink" title="Set（集合）"></a>Set（集合）</h5><p>Redis的Set是string类型的无序集合。<br><br>集合是通过哈希表实现的，所以添加，删除，查找的复杂度都是O(1)。</p><h6 id="sadd-命令"><a href="#sadd-命令" class="headerlink" title="sadd 命令"></a>sadd 命令</h6><p>添加一个string元素到,key对应的set集合中，成功返回1,如果元素已经在集合中返回0,key对应的set不存在返回错误。</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sadd <span class="built_in">key</span> <span class="built_in">member</span></span><br></pre></td></tr></table></figure><h6 id="实例-3"><a href="#实例-3" class="headerlink" title="实例"></a>实例</h6><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">sadd</span> <span class="selector-tag">runoob</span> <span class="selector-tag">redis</span></span><br><span class="line">(<span class="selector-tag">integer</span>) 1</span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">sadd</span> <span class="selector-tag">runoob</span> <span class="selector-tag">mongodb</span></span><br><span class="line">(<span class="selector-tag">integer</span>) 1</span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">sadd</span> <span class="selector-tag">runoob</span> <span class="selector-tag">rabitmq</span></span><br><span class="line">(<span class="selector-tag">integer</span>) 1</span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">sadd</span> <span class="selector-tag">runoob</span> <span class="selector-tag">rabitmq</span></span><br><span class="line">(<span class="selector-tag">integer</span>) 0</span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">smembers</span> <span class="selector-tag">runoob</span></span><br><span class="line"></span><br><span class="line">1) "<span class="selector-tag">rabitmq</span>"</span><br><span class="line">2) "<span class="selector-tag">mongodb</span>"</span><br><span class="line">3) "<span class="selector-tag">redis</span>"</span><br></pre></td></tr></table></figure><p><strong>注意</strong>：以上实例中 rabitmq 添加了两次，但根据<strong>集合内元素的唯一性</strong>，第二次插入的元素将被忽略。<br><br>集合中最大的成员数为 <strong>2^32 - 1</strong>(4294967295, <strong>每个集合可存储40多亿个成员</strong>)。</p><h5 id="zset-sorted-set：有序集合"><a href="#zset-sorted-set：有序集合" class="headerlink" title="zset(sorted set：有序集合)"></a>zset(sorted set：有序集合)</h5><p>Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。<br><br>不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。<br><br>zset的成员是唯一的,但分数(score)却可以重复。</p><h6 id="zadd-命令"><a href="#zadd-命令" class="headerlink" title="zadd 命令"></a>zadd 命令</h6><p>添加元素到集合，元素在集合中存在则更新对应score</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zadd <span class="built_in">key</span> score <span class="built_in">member</span></span><br></pre></td></tr></table></figure><h6 id="实例-4"><a href="#实例-4" class="headerlink" title="实例"></a>实例</h6><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">zadd</span> <span class="selector-tag">runoob</span> 0 <span class="selector-tag">redis</span></span><br><span class="line">(<span class="selector-tag">integer</span>) 1</span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">zadd</span> <span class="selector-tag">runoob</span> 0 <span class="selector-tag">mongodb</span></span><br><span class="line">(<span class="selector-tag">integer</span>) 1</span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">zadd</span> <span class="selector-tag">runoob</span> 0 <span class="selector-tag">rabitmq</span></span><br><span class="line">(<span class="selector-tag">integer</span>) 1</span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; <span class="selector-tag">zadd</span> <span class="selector-tag">runoob</span> 0 <span class="selector-tag">rabitmq</span></span><br><span class="line">(<span class="selector-tag">integer</span>) 0</span><br><span class="line"><span class="selector-tag">redis</span> 127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span>&gt; &gt; <span class="selector-tag">ZRANGEBYSCORE</span> <span class="selector-tag">runoob</span> 0 1000</span><br><span class="line">1) "<span class="selector-tag">mongodb</span>"</span><br><span class="line">2) "<span class="selector-tag">rabitmq</span>"</span><br><span class="line">3) "<span class="selector-tag">redis</span>"</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;介绍&quot;&gt;&lt;a href=&quot;#介绍&quot; class=&quot;headerlink&quot; title=&quot;介绍&quot;&gt;&lt;/a&gt;介绍&lt;/h4&gt;&lt;p&gt;Remote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。&lt;br&gt;Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库，并提供多种语言的API。&lt;br&gt;&lt;br&gt;它通常被称为数据结构服务器，因为值（value）可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。&lt;/p&gt;
    
    </summary>
    
      <category term="redis" scheme="http://yoursite.com/categories/redis/"/>
    
    
      <category term="redis" scheme="http://yoursite.com/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>Kaptcha验证码介绍</title>
    <link href="http://yoursite.com/2018/03/15/captcha/kaptcha/"/>
    <id>http://yoursite.com/2018/03/15/captcha/kaptcha/</id>
    <published>2018-03-15T15:00:00.000Z</published>
    <updated>2018-04-18T16:52:50.296Z</updated>
    
    <content type="html"><![CDATA[<h2 id="kaptcha"><a href="#kaptcha" class="headerlink" title="kaptcha"></a>kaptcha</h2><h3 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h3><p>kaptcha 是一个很有用的验证码生成工具。有了它，你能够生成各种样式的验证码，由于它是可配置的。<br><br>kaptcha工作的原理是调用com.google.code.kaptcha.servlet.KaptchaServlet，生成一个图片。<br><br>同一时候将生成的验证码字符串放到 HttpSession中。</p><a id="more"></a><h3 id="配置项"><a href="#配置项" class="headerlink" title="配置项"></a>配置项</h3><table><thead><tr><th>配置项</th><th>描述</th><th>可选值</th><th>默认值</th></tr></thead><tbody><tr><td>kaptcha.border</td><td>图片边框</td><td>yes,no</td><td>yes</td></tr><tr><td>kaptcha.border.color</td><td>边框颜色</td><td>r,g,b(and optional alpha) 或者 white,black,blue</td><td>black</td></tr><tr><td>kaptcha.border.thickness</td><td>边框厚度</td><td>&gt;0</td><td>1</td></tr><tr><td>kaptcha.image.width</td><td>图片宽</td><td>&gt;0</td><td>200</td></tr><tr><td>kaptcha.image.height</td><td>图片高</td><td>&gt;0</td><td>50</td></tr><tr><td>kaptcha.producer.impl</td><td>图片实现类</td><td></td><td>com.google.code.kaptcha.impl.DefaultKaptcha</td></tr><tr><td>kaptcha.textproducer.impl</td><td>文本实现类</td><td></td><td>com.google.code.kaptcha.text.impl.DefaultTextCreator</td></tr><tr><td>kaptcha.textproducer.char.string</td><td>文本集合</td><td></td><td>abcde2345678gfynmnpwx</td></tr><tr><td>kaptcha.textproducer.char.length</td><td>验证码长度</td><td></td><td>5</td></tr><tr><td>kaptcha.textproducer.font.names</td><td>字体</td><td></td><td>Arial, Courier</td></tr><tr><td>kaptcha.textproducer.font.size</td><td>字体大小</td><td></td><td>40px</td></tr><tr><td>kaptcha.textproducer.font.color</td><td>字体颜色</td><td>r,g,b  或者 white,black,blue</td><td>black</td></tr><tr><td>kaptcha.textproducer.char.space</td><td>文字间隔</td><td></td><td>2</td></tr><tr><td>kaptcha.noise.impl</td><td>干扰实现类</td><td></td><td>com.google.code.kaptcha.impl.DefaultNoise</td></tr><tr><td>kaptcha.noise.color</td><td>干扰颜色</td><td>r,g,b 或者 white,black,blue</td><td>black</td></tr><tr><td>kaptcha.obscurificator.impl</td><td>图片样式</td><td>水纹com.google.code.kaptcha.impl.WaterRipple<br>鱼眼com.google.code.kaptcha.impl.FishEyeGimpy<br>阴影com.google.code.kaptcha.impl.ShadowGimpy</td><td>com.google.code.kaptcha.impl.WaterRipple</td></tr><tr><td>kaptcha.background.impl</td><td>背景实现类</td><td></td><td>com.google.code.kaptcha.impl.DefaultBackground</td></tr><tr><td>kaptcha.background.clear.from</td><td>背景颜色渐变，开始颜色</td><td></td><td>lightGray</td></tr><tr><td>kaptcha.background.clear.to</td><td>背景颜色渐变，结束颜色</td><td></td><td>white</td></tr><tr><td>kaptcha.word.impl</td><td>文字渲染器</td><td></td><td>com.google.code.kaptcha.text.impl.DefaultWordRenderer</td></tr><tr><td>kaptcha.session.key</td><td>session key</td><td></td><td>KAPTCHA_SESSION_KEY</td></tr><tr><td>kaptcha.session.date</td><td>session date</td><td></td><td>KAPTCHA_SESSION_DATE</td></tr></tbody></table>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;kaptcha&quot;&gt;&lt;a href=&quot;#kaptcha&quot; class=&quot;headerlink&quot; title=&quot;kaptcha&quot;&gt;&lt;/a&gt;kaptcha&lt;/h2&gt;&lt;h3 id=&quot;介绍&quot;&gt;&lt;a href=&quot;#介绍&quot; class=&quot;headerlink&quot; title=&quot;介绍&quot;&gt;&lt;/a&gt;介绍&lt;/h3&gt;&lt;p&gt;kaptcha 是一个很有用的验证码生成工具。有了它，你能够生成各种样式的验证码，由于它是可配置的。&lt;br&gt;&lt;br&gt;kaptcha工作的原理是调用com.google.code.kaptcha.servlet.KaptchaServlet，生成一个图片。&lt;br&gt;&lt;br&gt;同一时候将生成的验证码字符串放到 HttpSession中。&lt;/p&gt;
    
    </summary>
    
      <category term="captcha" scheme="http://yoursite.com/categories/captcha/"/>
    
    
      <category term="kaptcha" scheme="http://yoursite.com/tags/kaptcha/"/>
    
  </entry>
  
  <entry>
    <title>Jcaptcha验证码介绍</title>
    <link href="http://yoursite.com/2018/03/15/captcha/jcaptcha/"/>
    <id>http://yoursite.com/2018/03/15/captcha/jcaptcha/</id>
    <published>2018-03-15T15:00:00.000Z</published>
    <updated>2018-04-18T16:58:13.461Z</updated>
    
    <content type="html"><![CDATA[<h1 id="jcaptcha"><a href="#jcaptcha" class="headerlink" title="jcaptcha"></a>jcaptcha</h1><h2 id="组件介绍"><a href="#组件介绍" class="headerlink" title="组件介绍"></a>组件介绍</h2><table><thead><tr><th>组件</th><th>作用</th></tr></thead><tbody><tr><td>FontGenerator</td><td>设置<strong>字体</strong>随机大小范围，字体名称</td></tr><tr><td>BackgroundGenerator</td><td>设置<strong>背景颜色</strong>，<strong>图片大小</strong></td></tr><tr><td>TextPaster</td><td>设置<strong>单词</strong>的最小最大<strong>长度</strong>，设置字的<strong>颜色</strong>，设置单词在图像中的位置是固定还是随机</td></tr><tr><td>ImageDeformation、ImageFilter</td><td>非必须，指定为图片<strong>变形</strong>的类，可以用变形类和过滤器两种方式</td></tr><tr><td>WordToImage</td><td>将FontGenerator、BackgroundGenerator、TextPaster、ImageDeformation和ImageFilter组装到一起，属于中间环节的类，类似一个容器</td></tr><tr><td>WordGenerator</td><td>设置<strong>取词的范围</strong>，设置一定范围，或从本地词库读取</td></tr><tr><td>CaptchaFactory</td><td>将配置的类，组成工程类， 也属于包装类</td></tr></tbody></table><a id="more"></a><p>结构图如下：<br><br><img src="/images/captcha/component.jpg" alt=""></p><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><h3 id="FontGenerator"><a href="#FontGenerator" class="headerlink" title="FontGenerator"></a>FontGenerator</h3><p>Font类，用于设置字体的样式和字体随机的大小范围<br><br>在com.octo.captcha.component.image.fontgenerator包中包含了几种Font类</p><table><thead><tr><th>Font</th><th>作用</th><th>示例</th><th>效果</th></tr></thead><tbody><tr><td>RandomFontGenerator</td><td>设置<strong>随机出现的字体样式</strong>和<strong>随机的字体大小范围</strong></td><td>RandomFontGenerator fonts = new RandomFontGenerator (new Integer(20), new Integer(30));</td><td></td></tr><tr><td>TwistedRandomFontGenerator</td><td>设置<strong>字体的随机大小范围</strong>，并对字体进行<strong>简单的扭曲变形</strong>，字体<strong>左右倾斜</strong>的效果</td><td>TwistedRandomFontGenerator fonts = new TwistedRandomFontGenerator(new Integer(20), new Integer(30));</td><td><img src="/images/captcha/TwistedRandomFontGenerator.jpg" alt=""></td></tr><tr><td>TwistedAndShearedRandomFontGenerator</td><td>TwistedRandomFontGenerator的子类，和 TwistedRandomFontGenerator类的构造器参数也相同，显示<strong>效果相近</strong>，采用的算法有所不同</td><td></td><td><img src="/images/captcha/TwistedAndShearedRandomFontGenerator.jpg" alt=""></td></tr><tr><td>DeformedRandomFontGenerator</td><td>构造器的参数和上两个类相同，采用的方式也类似</td><td></td><td><img src="/images/captcha/DeformedRandomFontGenerator.jpg" alt=""></td></tr></tbody></table><h3 id="BackgroundGenerator"><a href="#BackgroundGenerator" class="headerlink" title="BackgroundGenerator"></a>BackgroundGenerator</h3><p>Background设置背景颜色或背景图片、设置图片大小<br><br>在com.octo.captcha.component.image.backgroundgenerator包中包含了的Background类</p><table><thead><tr><th>Background</th><th>作用</th><th>示例</th><th>效果</th></tr></thead><tbody><tr><td>UniColorBackgroundGenerator</td><td>设置<strong>图片大小</strong>和以<strong>单一颜色为背景的图片背景</strong>，可以设置一个或多个备选颜色。<br>如果想让颜色更加多变，则可以使用RandomRangeColorGenerator类，设置随机取得的RGB值，让RGB值分别的随机取得，则RGB值随机搭配的颜色将会更加多变</td><td>UniColorBackgroundGenerator background = new UniColorBackgroundGenerator(new Integer(200), new Integer(100), Color.BLUE);</td><td></td></tr><tr><td>MultipleShapeBackgroundGenerator</td><td>设置一种<strong>多变的背景花纹</strong></td><td>MultipleShapeBackgroundGenerator background = new MultipleShapeBackgroundGenerator (200,100,Color.RED,Color.BLUE,10,10,8,8,Color.WHITE,Color.YELLOW,3);</td><td><img src="/images/captcha/MultipleShapeBackgroundGenerator.jpg" alt=""></td></tr><tr><td>GradientBackgroundGenerator</td><td>设置一种<strong>渐变颜色的背景</strong>，也可以设置<strong>多种颜色的渐变背景</strong></td><td>GradientBackgroundGenerator background =  new GradientBackgroundGenerator(200, 100, Color.BLACK, Color.WHITE);</td><td><img src="/images/captcha/GradientBackgroundGenerator.jpg" alt=""></td></tr><tr><td>FunkyBackgroundGenerator</td><td>设置<strong>四种不同颜色混杂</strong>在一起的背景。<br>如果创建过程中，给SingleColorGenerator类改为使用RandomListColorGenerator或其他类，也可以让<strong>四种颜色随机变化</strong></td><td>SingleColorGenerator leftUpColor = new SingleColorGenerator(Color.RED); SingleColorGenerator leftDownColor = new SingleColorGenerator(Color.YELLOW); SingleColorGenerator rightUpColor = new SingleColorGenerator(Color.BLUE); SingleColorGenerator rightDownColor = new SingleColorGenerator(Color.GREEN); FunkyBackgroundGenerator background = new FunkyBackgroundGenerator( 200, 100, leftUpColor, leftDownColor, rightUpColor, rightDownColor, 0.5f);</td><td><img src="/images/captcha/FunkyBackgroundGenerator.jpg" alt=""></td></tr><tr><td>FileReaderRandomBackgroundGenerator</td><td>设置从<strong>指定目录读取图片作为生成图片的背景</strong>，目录中的图片，会在<strong>类构造期间就全部读取到内存</strong>中</td><td>FileReaderRandomBackgroundGenerator background =  new FileReaderRandomBackgroundGenerator(200, 100, “E:¥¥testproject¥¥jcaptcha¥¥images¥¥backgrounds”);</td><td></td></tr><tr><td>EllipseBackgroundGenerator</td><td>一种<strong>花样的背景</strong>，该类到目前为止没有更多的配置选项，只能设置图片大小，颜色和其他相关的值都不能设置</td><td></td><td><img src="/images/captcha/EllipseBackgroundGenerator.jpg" alt=""></td></tr></tbody></table><h3 id="TextPaster"><a href="#TextPaster" class="headerlink" title="TextPaster"></a>TextPaster</h3><p>TextPaster可以设置单词的最小和最大长度和设置字的颜色（前景颜色），设置单词在图像中的位置是固定还是随机<br> 在com.octo.captcha.component.image.textpaster包中包含了的常用TextPaster类</p><table><thead><tr><th>TextPaster</th><th>作用</th><th>示例</th><th>效果</th></tr></thead><tbody><tr><td>RandomTextPaster</td><td>最常用的TextPaster，可以设置生成单词的<strong>随机长度范围</strong>，和设置<strong>一种或多种前景颜色</strong>，生成的字符串在图像中的<strong>位置是随机</strong>的</td><td>RandomTextPaster textPaster = new RandomTextPaster(new Integer(5), new Integer(10),  Color.BLACK);<br><br>也可以设置多种随机前景颜色：<br>Color[] colors = new Color[]{Color.RED,Color.YELLOW,Color.BLUE}; RandomListColorGenerator randomColors = new RandomListColorGenerator(colors); RandomTextPaster textPaster =  new RandomTextPaster(new Integer(5), new Integer(10), randomColors);</td><td></td></tr><tr><td>SimpleTextPaster</td><td>和RandomTextPaster用法和参数都相同，但是<strong>生成的字符串位置不变</strong></td><td></td><td></td></tr><tr><td>NonLinearTextPaster</td><td>设置<strong>非直线</strong>的TextPaster，最后<strong>生成的文本成非直线的排列</strong></td><td>NonLinearTextPaster textPaster =   new NonLinearTextPaster  (new Integer(5), new Integer(10), Color.BLACK);</td><td><img src="/images/captcha/NonLinearTextPaster.jpg" alt=""></td></tr><tr><td>DoubleTextPaster</td><td>可以形成两行好像<strong>重影效果</strong>的文本，单词<strong>位置固定</strong></td><td>DoubleTextPaster textPaster = new DoubleTextPaster(new Integer(5),new Integer(10), Color.BLACK);</td><td><img src="/images/captcha/DoubleTextPaster.jpg" alt=""></td></tr><tr><td>DoubleRandomTextPaster</td><td>和DoubleTextPaster类类似的效果，但是单词在<strong>图像中出现的位置和第二行字上下位置，是随机的</strong></td><td></td><td><img src="/images/captcha/DoubleRandomTextPaster.jpg" alt=""></td></tr><tr><td>DecoratedRandomTextPaster</td><td>为生成的文本加上修饰，例如<strong>干扰线</strong>、<strong>干扰点</strong></td><td>SingleColorGenerator lineColor = new SingleColorGenerator(Color.BLUE); TextDecorator decorator1 = new LineTextDecorator(new Integer(1), lineColor, AlphaComposite.SRC_OVER);  TextDecorator[] decorators = new TextDecorator[] { decorator1 }; SingleColorGenerator color = new SingleColorGenerator(Color.RED); DecoratedRandomTextPaster textPaster = new DecoratedRandomTextPaster(new Integer(5), new Integer(10), color, decorators);<br><br>LineTextDecorator的构造器的最后一个参数是<strong>干扰线的类型</strong>，在该参数上进行修改，也能取得不错的效果，例如将AlphaComposite.SRC_OVER改为AlphaComposite.CLEAR、AlphaComposite.SRC_IN、AlphaComposite.XOR<br><br>LineTextDecorator类是线型的装饰器，Captcha还提供了一种点状的装饰类：<br>SingleColorGenerator dColor = new SingleColorGenerator(Color.BLUE); TextDecorator decorator2 = new BaffleTextDecorator(new Integer(1),       dColor, AlphaComposite.SRC_OUT);  TextDecorator[] decorators = new TextDecorator[] { decorator2 }; SingleColorGenerator color = new SingleColorGenerator(Color.RED); DecoratedRandomTextPaster textPaster = new DecoratedRandomTextPaster(new Integer(5), new Integer(10), color, decorators);</td><td><img src="/images/captcha/DecoratedRandomTextPaster.jpg" alt=""><br><br><img src="/images/captcha/DecoratedRandomTextPaster_1.jpg" alt=""><br><img src="/images/captcha/DecoratedRandomTextPaster_2.jpg" alt=""><br><img src="/images/captcha/DecoratedRandomTextPaster_3.jpg" alt=""><br><br><img src="/images/captcha/DecoratedRandomTextPaster_4.jpg" alt=""><br><img src="/images/captcha/DecoratedRandomTextPaster_5.jpg" alt=""><br><img src="/images/captcha/DecoratedRandomTextPaster_6.jpg" alt=""></td></tr></tbody></table><h3 id="Deformation和ImageFilter"><a href="#Deformation和ImageFilter" class="headerlink" title="Deformation和ImageFilter"></a>Deformation和ImageFilter</h3><p>&nbsp;&nbsp;&nbsp;&nbsp;这两种类，都提供了对图片字体进行进一步变形的功能，Deformation类是Captcha框架中提供的变形类 和接口，ImageFilter是一个使用java提供图形图像方面服务的公司提供的通用的图形变形过滤器，可以实现的效果很多。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;Deformation和ImageFilter都分为3层，一种是在<strong>文字级别</strong>的，一种是在<strong>背景级别</strong>的，还有在<strong>整个图片级别</strong>的，用于在图片生成不同的阶段对图片进行变形。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;Deformation类，当前Captcha只提供了一种选择，就是PuzzleImageDeformation类，该类的效果是将 图片旋转，然后直接贴在原来图片的上面，所以效果不是很好，相信以后Captcha会提供更多的Deformation类，并且会不断完善，但是目前，不推荐使用。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;ImageFilter过滤器，有很多现有的资源可以供选择使用，是目前最好的选择。</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// font  </span></span><br><span class="line">RandomFontGenerator fonts = <span class="keyword">new</span> <span class="type">RandomFontGenerator</span>(<span class="keyword">new</span> <span class="type">Integer</span>(<span class="number">20</span>), <span class="keyword">new</span> <span class="type">Integer</span>(<span class="number">20</span>));  </span><br><span class="line"></span><br><span class="line"><span class="comment">// background  </span></span><br><span class="line">BackgroundGenerator background = <span class="keyword">new</span> <span class="type">UniColorBackgroundGenerator</span>(<span class="keyword">new</span> <span class="type">Integer</span>(<span class="number">400</span>), <span class="keyword">new</span> <span class="type">Integer</span>(<span class="number">300</span>), Color.white);  </span><br><span class="line"></span><br><span class="line"><span class="comment">// textPaster  </span></span><br><span class="line">RandomTextPaster textPaster =  <span class="keyword">new</span> <span class="type">RandomTextPaster</span>(<span class="keyword">new</span> <span class="type">Integer</span>(<span class="number">5</span>), <span class="keyword">new</span> <span class="type">Integer</span>(<span class="number">5</span>), Color.BLACK);  </span><br><span class="line"></span><br><span class="line"><span class="comment">// no deformation  </span></span><br><span class="line">ImageDeformation noneDeformation = <span class="keyword">new</span> <span class="type">ImageDeformationByFilters</span>( <span class="keyword">new</span> <span class="type">ImageFilter</span>[] &#123;&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// filter创建过滤器  </span></span><br><span class="line">ImageDeformation filters =  <span class="keyword">new</span> <span class="type">ImageDeformationByFilters</span>( <span class="keyword">new</span> <span class="type">ImageFilter</span>[] &#123; <span class="keyword">new</span> <span class="type">TwirlFilter</span>() &#125;);  DeformedComposedWordToImage cwti =   <span class="keyword">new</span> <span class="type">DeformedComposedWordToImage</span>(fonts, background, textPaster, noneDeformation, filters, noneDeformation);  </span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置随机取词范围</span></span><br><span class="line">RandomWordGenerator words = <span class="keyword">new</span> <span class="type">RandomWordGenerator</span>(<span class="string">"ABCDEFGHIJKLMNOPQRSTUVWXYZ"</span>);</span><br><span class="line">GimpyFactory gimpy = <span class="keyword">new</span> <span class="type">GimpyFactory</span>(words, cwti);  </span><br><span class="line">SimpleListImageCaptchaEngine engine = <span class="keyword">new</span> <span class="type">SimpleListImageCaptchaEngine</span>();</span><br><span class="line">engine.setFactories(<span class="keyword">new</span> <span class="type">CaptchaFactory</span>[] &#123; gimpy &#125;);  </span><br><span class="line">FastHashMapCaptchaStore captchaStore = <span class="keyword">new</span> <span class="type">FastHashMapCaptchaStore</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建Captcha服务  </span></span><br><span class="line">DefaultManageableImageCaptchaService defaultService = <span class="keyword">new</span>  <span class="type">DefaultManageableImageCaptchaService</span>(captchaStore, engine, <span class="number">180</span>, <span class="number">100000</span>, <span class="number">75000</span>);  </span><br><span class="line"><span class="keyword">return</span> defaultService;</span><br></pre></td></tr></table></figure><p>&nbsp;&nbsp;&nbsp;&nbsp;上述的代码，是在创建一个Captcha服务器的过程中，将过滤器加入其中，其中使用的font、background、 和testPaster类，都使用了最简单的，没有变形效果的类，意在只查看过滤器对整个图片的映像。</p><p><img src="/images/captcha/sample.jpg" alt=""></p><p>更多过滤器效果，可以参见<a href="http://www.jhlabs.com/ip/filters/index.html" target="_blank" rel="noopener">Java Image Filters</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;jcaptcha&quot;&gt;&lt;a href=&quot;#jcaptcha&quot; class=&quot;headerlink&quot; title=&quot;jcaptcha&quot;&gt;&lt;/a&gt;jcaptcha&lt;/h1&gt;&lt;h2 id=&quot;组件介绍&quot;&gt;&lt;a href=&quot;#组件介绍&quot; class=&quot;headerlink&quot; title=&quot;组件介绍&quot;&gt;&lt;/a&gt;组件介绍&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;组件&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FontGenerator&lt;/td&gt;
&lt;td&gt;设置&lt;strong&gt;字体&lt;/strong&gt;随机大小范围，字体名称&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BackgroundGenerator&lt;/td&gt;
&lt;td&gt;设置&lt;strong&gt;背景颜色&lt;/strong&gt;，&lt;strong&gt;图片大小&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TextPaster&lt;/td&gt;
&lt;td&gt;设置&lt;strong&gt;单词&lt;/strong&gt;的最小最大&lt;strong&gt;长度&lt;/strong&gt;，设置字的&lt;strong&gt;颜色&lt;/strong&gt;，设置单词在图像中的位置是固定还是随机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ImageDeformation、ImageFilter&lt;/td&gt;
&lt;td&gt;非必须，指定为图片&lt;strong&gt;变形&lt;/strong&gt;的类，可以用变形类和过滤器两种方式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WordToImage&lt;/td&gt;
&lt;td&gt;将FontGenerator、BackgroundGenerator、TextPaster、ImageDeformation和ImageFilter组装到一起，属于中间环节的类，类似一个容器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WordGenerator&lt;/td&gt;
&lt;td&gt;设置&lt;strong&gt;取词的范围&lt;/strong&gt;，设置一定范围，或从本地词库读取&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CaptchaFactory&lt;/td&gt;
&lt;td&gt;将配置的类，组成工程类， 也属于包装类&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
    
    </summary>
    
      <category term="captcha" scheme="http://yoursite.com/categories/captcha/"/>
    
    
      <category term="jcaptcha" scheme="http://yoursite.com/tags/jcaptcha/"/>
    
  </entry>
  
  <entry>
    <title>Kafka知识探索</title>
    <link href="http://yoursite.com/2018/03/11/kafka/kafka/"/>
    <id>http://yoursite.com/2018/03/11/kafka/kafka/</id>
    <published>2018-03-11T03:08:00.000Z</published>
    <updated>2018-03-12T15:07:23.176Z</updated>
    
    <content type="html"><![CDATA[<h3 id="环境搭建-Linux"><a href="#环境搭建-Linux" class="headerlink" title="环境搭建(Linux)"></a>环境搭建(Linux)</h3><h4 id="1-Kafka下载"><a href="#1-Kafka下载" class="headerlink" title="1. Kafka下载"></a>1. Kafka下载</h4><ul><li>下载地址：<a href="http://kafka.apache.org/downloads" target="_blank" rel="noopener">http://kafka.apache.org/downloads</a></li><li>wget <a href="http://apache.fayea.com/kafka/0.10.1.0/kafka_2.11-0.10.1.0.tgz" target="_blank" rel="noopener">http://apache.fayea.com/kafka/0.10.1.0/kafka_2.11-0.10.1.0.tgz</a></li><li>tar -xvf kafka_2.11-0.10.1.0.tgz<br>cd kafka_2.11-0.10.1.0</li></ul><h4 id="2-Zookeeper安装"><a href="#2-Zookeeper安装" class="headerlink" title="2. Zookeeper安装"></a>2. Zookeeper安装</h4><p>Kafka需要Zookeeper的监控，所以先要安装Zookeeper，如何安装请传送至：<br>     <a href="http://blog.csdn.net/trigl/article/details/52401847" target="_blank" rel="noopener">hadoop</a>、<br>     <a href="http://blog.csdn.net/trigl/article/details/52401847" target="_blank" rel="noopener">zookeeper</a>、<br>     <a href="http://blog.csdn.net/trigl/article/details/52401847" target="_blank" rel="noopener">hbase</a>、<a href="http://blog.csdn.net/trigl/article/details/52401847" target="_blank" rel="noopener">spark集群环境搭建</a> ，<br>安装完成以后依次启动各个节点</p><a id="more"></a><h4 id="3-配置kafka-broker集群"><a href="#3-配置kafka-broker集群" class="headerlink" title="3. 配置kafka broker集群"></a>3. 配置kafka broker集群</h4><ul><li>首先把Kafka解压后的目录复制到集群的各台服务器</li><li>然后修改各个服务器的配置文件：进入Kafka的config目录，修改server.properties<figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># brokerid就是指各台服务器对应的id，所以各台服务器值不同</span></span><br><span class="line">broker.<span class="attribute">id</span>=0</span><br><span class="line"><span class="comment"># 端口号，无需改变</span></span><br><span class="line"><span class="attribute">port</span>=9092</span><br><span class="line"><span class="comment"># 当前服务器的IP，各台服务器值不同</span></span><br><span class="line">host.<span class="attribute">name</span>=192.168.0.10</span><br><span class="line"><span class="comment"># Zookeeper集群的ip和端口号</span></span><br><span class="line">zookeeper.<span class="attribute">connect</span>=192.168.0.10:2181,192.168.0.11:2181,192.168.0.12:2181</span><br><span class="line"><span class="comment"># 日志目录</span></span><br><span class="line">log.<span class="attribute">dirs</span>=/home/www/kafka-logs</span><br></pre></td></tr></table></figure></li></ul><h4 id="4-启动Kafka"><a href="#4-启动Kafka" class="headerlink" title="4. 启动Kafka"></a>4. 启动Kafka</h4><ul><li>在每台服务器上进入Kafka目录，分别执行以下命令：<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bin/kafka-server-start<span class="selector-class">.sh</span> config/server<span class="selector-class">.properties</span> &amp;</span><br></pre></td></tr></table></figure></li></ul><h4 id="5-Kafka常用命令"><a href="#5-Kafka常用命令" class="headerlink" title="5. Kafka常用命令"></a>5. Kafka常用命令</h4><ul><li><h5 id="5-1-新建topic"><a href="#5-1-新建topic" class="headerlink" title="5.1 新建topic"></a>5.1 新建topic</h5><figure class="highlight brainfuck"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">bin/kafka</span><span class="literal">-</span><span class="comment">topics</span><span class="string">.</span><span class="comment">sh</span> <span class="literal">-</span><span class="literal">-</span><span class="comment">create</span> <span class="literal">-</span><span class="literal">-</span><span class="comment">zookeeper</span> <span class="comment">hxf:2181</span><span class="string">,</span><span class="comment">cfg:2181</span><span class="string">,</span><span class="comment">jqs:2181</span><span class="string">,</span><span class="comment">jxf:2181</span><span class="string">,</span><span class="comment">sxtb:2181</span> <span class="literal">-</span><span class="literal">-</span><span class="comment">replication</span><span class="literal">-</span><span class="comment">factor</span> <span class="comment">2</span> <span class="literal">-</span><span class="literal">-</span><span class="comment">partitions</span> <span class="comment">2</span> <span class="literal">-</span><span class="literal">-</span><span class="comment">topic</span> <span class="comment">test</span></span><br></pre></td></tr></table></figure></li></ul><blockquote><p>test有两个复制因子和两个分区</p></blockquote><ul><li><h5 id="5-2-查看某个topic主题"><a href="#5-2-查看某个topic主题" class="headerlink" title="5.2 查看某个topic主题"></a>5.2 查看某个topic主题</h5><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bin/kafka-topics.sh --describe --zookeeper <span class="string">hxf:</span><span class="number">2181</span>,<span class="string">cfg:</span><span class="number">2181</span>,<span class="string">jqs:</span><span class="number">2181</span>,<span class="string">jxf:</span><span class="number">2181</span>,<span class="string">sxtb:</span><span class="number">2181</span> --topic test</span><br></pre></td></tr></table></figure></li></ul><p>其中第一行是所有分区的信息，下面的每一行对应一个分区<br><br>Leader：负责某个分区所有读写操作的节点<br><br>Replicas：复制因子节点<br><br>Isr：存活节点<br></p><ul><li><h5 id="5-3-查看Kafka所有的主题"><a href="#5-3-查看Kafka所有的主题" class="headerlink" title="5.3 查看Kafka所有的主题"></a>5.3 查看Kafka所有的主题</h5><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bin/kafka-topics.<span class="keyword">sh</span> --<span class="keyword">list</span> --zookeeper hxf:<span class="number">2181</span>,<span class="keyword">cf</span><span class="variable">g:2181</span>,jq<span class="variable">s:2181</span>,jxf:<span class="number">2181</span>,sxt<span class="variable">b:2181</span></span><br></pre></td></tr></table></figure></li><li><h5 id="5-4-终端发送消息"><a href="#5-4-终端发送消息" class="headerlink" title="5.4 终端发送消息"></a>5.4 终端发送消息</h5><figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bin/kafka-console-producer.<span class="keyword">sh</span> --broker-<span class="keyword">list</span> localhost:9092 --topic <span class="keyword">test</span></span><br></pre></td></tr></table></figure></li><li><h5 id="5-5-终端接收（消费）消息"><a href="#5-5-终端接收（消费）消息" class="headerlink" title="5.5 终端接收（消费）消息"></a>5.5 终端接收（消费）消息</h5><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bin/kafka-console-consumer.sh --zookeeper <span class="string">hxf:</span><span class="number">2181</span>,<span class="string">cfg:</span><span class="number">2181</span>,<span class="string">jqs:</span><span class="number">2181</span>,<span class="string">jxf:</span><span class="number">2181</span>,<span class="string">sxtb:</span><span class="number">2181</span> --bootstrap-server <span class="string">localhost:</span><span class="number">9092</span> --topic test --from-beginning</span><br></pre></td></tr></table></figure></li></ul><h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><h4 id="1-基本术语"><a href="#1-基本术语" class="headerlink" title="1. 基本术语"></a>1. 基本术语</h4><h5 id="消息"><a href="#消息" class="headerlink" title="消息"></a>消息</h5><p>在Kafka中，每一个消息由键、值和一个时间戳组成</p><h5 id="主题和日志"><a href="#主题和日志" class="headerlink" title="主题和日志"></a>主题和日志</h5><p> Kafka集群存储同一类别的消息流称为主题</p><br><p> 主题会有多个订阅者（0个1个或多个），当<strong>主题发布消息时，会向订阅者推送记录</strong></p><br><p> 针对每一个主题，Kafka集群维护了一个像下面这样的分区日志：</p><br><img src="http://img.blog.csdn.net/20170520173825028?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvVHJpZ2w=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt=""><br><p> 这些分区位于不同的服务器上，每一个分区可以看做是一个结构化的提交日志，<strong>每写入一条记录都会记录到其中一个分区并且分配一个唯一地标识其位置的数字称为偏移量offset</strong></p><br><p> <strong>Kafka集群会将发布的消息保存一段时间，不管是否被消费</strong>。例如，如果设置保存天数为2天，那么从消息发布起的两天之内，该消息一直可以被消费，但是超过两天后就会被丢弃以节省空间。其次，Kafka的数据持久化性能很好，所以长时间存储数据不是问题</p><br><p>如下图所示，<strong>生产者每发布一条消息就会向分区log写入一条记录的offset，而消费者就是通过offset来读取对应的消息的，一般来说每读取一条消息，消费者对应要读取的offset就加1</strong>，例如最后一条读到offset=12，那么下条offset就为13.由于消费者通过offset来读取消息，所以可以重复读取已经读过的记录，或者跳过某些记录不读</p><br><img src="http://img.blog.csdn.net/20170520180436658?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvVHJpZ2w=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt=""><br><p>Kafka中采用分区的设计有几个目的。一是<strong>可以处理更多的消息，不受单台服务器的限制</strong>。Topic拥有多个分区意味着它可以不受限的处理更多的数据。第二，<strong>分区可以作为并行处理的单元</strong></p><h5 id="分布式"><a href="#分布式" class="headerlink" title="分布式"></a>分布式</h5><p>Log的分区被分布到集群中的多个服务器上。每个服务器处理它分到的分区。 根据配置每个分区还可以复制到其它服务器作为备份容错</p><br><p>每个分区有一个leader，零或多个follower。Leader处理此分区的所有的读写请求，而follower被动的复制数据。如果leader宕机，其它的一个follower会被推举为新的leader。 <strong>一台服务器可能同时是一个分区的leader，另一个分区的follower。 这样可以平衡负载，避免所有的请求都只让一台或者某几台服务器处理</strong></p><h5 id="生产者"><a href="#生产者" class="headerlink" title="生产者"></a>生产者</h5><p>生产者往某个Topic上发布消息。生产者还可以选择将消息分配到Topic的哪个节点上。<strong>最简单的方式是轮询分配到各个分区以平衡负载，也可以根据某种算法依照权重选择分区</strong></p><h5 id="消费者"><a href="#消费者" class="headerlink" title="消费者"></a>消费者</h5><p>Kafka有一个消费者组的概念，生产者把消息发到的是消费者组，在消费者组里面可以有很多个消费者实例，如下图所示： </p><br><img src="http://img.blog.csdn.net/20170520180325504?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvVHJpZ2w=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt=""><br><p>Kafka集群有两台服务器，四个分区，此外有两个消费者组A和B，消费者组A具有2个消费者实例C1-2，消费者B具有4个消费者实例C3-6</p><br><p>那么Kafka发送消息的过程是怎样的呢？</p><br><p>例如此时我们创建了一个主题test，有两个分区，分别是Server1的P0和Server2的P1，假设此时我们通过test发布了一条消息，那么这条消息是发到P0还是P1呢，或者是都发呢？答案是只会发到P0或P1其中之一，也就是消息只会发给其中的一个分区</p><br><p>分区接收到消息后会记录在分区日志中，记录的方式我们讲过了，就是通过offset，正因为有这个偏移量的存在，所以一个分区内的消息是有先后顺序的，即offset大的消息比offset小的消息后到。但是注意，由于消息随机发往主题的任意一个分区，因此虽然同一个分区的消息有先后顺序，但是不同分区之间的消息就没有先后顺序了，那么如果我们要求消费者顺序消费主题发的消息那该怎么办呢，此时只要在创建主题的时候只提供一个分区即可</p><br><p>讲完了主题发消息，接下来就该消费者消费消息了，假设上面test的消息发给了分区P0，此时从图中可以看到，有两个消费者组，那么P0将会把消息发到哪个消费者组呢？从图中可以看到，P0把消息既发给了消费者组A也发给了B，但是A中消息仅被C1消费，B中消息仅被C3消费。这就是我们要讲的，主题发出的消息会发往所有的消费者组，而每一个消费者组下面可以有很多消费者实例，这条消息只会被他们中的一个消费掉</p><h4 id="2-核心API"><a href="#2-核心API" class="headerlink" title="2. 核心API"></a>2. 核心API</h4><p>Kafka具有4个核心API：</p><ol><li>Producer API：用于向Kafka主题发送消息。</li><li>Consumer API：用于从订阅主题接收消息并且处理这些消息。</li><li>Streams API：作为一个流处理器，用于从一个或者多个主题中消费消息流然后为其他主题生产消息流，高效地将输入流转换为输出流。</li><li>Connector API：用于构建和运行将Kafka主题和已有应用或者数据系统连接起来的可复用的生产者或消费者。例如一个主题到一个关系型数据库的连接能够捕获表的任意变化。<br><img src="http://img.blog.csdn.net/20170523104303032?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvVHJpZ2w=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt=""></li></ol><h4 id="3-应用场景"><a href="#3-应用场景" class="headerlink" title="3. 应用场景"></a>3. 应用场景</h4><h5 id="Kafka用作消息系统"><a href="#Kafka用作消息系统" class="headerlink" title="Kafka用作消息系统"></a>Kafka用作消息系统</h5><p>Kafka流的概念与传统企业消息系统有什么异同？</p><br><p>传统消息系统有两个模型：队列和发布-订阅系统。在队列模式中，每条服务器的消息会被消费者池中的一个所读取；而发布-订阅系统中消息会广播给所有的消费者。这两种模式各有优劣。队列模式的优势是可以将消息数据让多个消费者处理以实现程序的可扩展，然而这就导致其没有多个订阅者，只能用于一个进程。发布-订阅模式的好处在于数据可以被多个进程消费使用，但是却无法使单一程序扩展性能</p><br><p>Kafka中消费者组的概念同时涵盖了这两方面。对应于队列的概念，Kafka中每个消费者组中有多个消费者实例可以接收消息；对应于发布-订阅模式，Kafka中可以指定多个消费者组来订阅消息<br></p><p>相对传统消息系统，Kafka可以提供更强的顺序保证</p><h5 id="Kafka用作存储系统"><a href="#Kafka用作存储系统" class="headerlink" title="Kafka用作存储系统"></a>Kafka用作存储系统</h5><p>任何发布消息与消费消息解耦的消息队列其实都可以看做是用来存放发布的消息的存储系统，而Kafka是一个非常高效的存储系统<br></p><p>写入Kafka的数据会被存入磁盘并且复制到集群中以容错。Kafka允许生产者等待数据完全复制并且确保持久化到磁盘的确认应答</p><br><p>Kafka使用的磁盘结构扩容性能很好——不管服务器上有50KB还是50TB，Kafka的表现都是一样的</p><br><p>由于能够精致的存储并且供客户端程序进行读操作，你可以把Kafka看做是一个用于高性能、低延迟的存储提交日志、复制及传播的分布式文件系统</p><h5 id="Kafka的流处理"><a href="#Kafka的流处理" class="headerlink" title="Kafka的流处理"></a>Kafka的流处理</h5><p>仅仅读、写、存储流数据是不够的，Kafka的目的是实现实时流处理。</p><br><p>在Kafka中一个流处理器的处理流程是首先持续性的从输入主题中获取数据流，然后对其进行一些处理，再持续性地向输出主题中生产数据流。例如一个销售商应用，接收销售和发货量的输入流，输出新订单和调整后价格的输出流</p><br><p>可以直接使用producer和consumer API进行简单的处理。对于复杂的转换，Kafka提供了更强大的Streams API。可构建聚合计算或连接流到一起的复杂应用程序</p><br><p>流处理有助于解决这类应用面临的硬性问题：处理无序数据、代码更改的再处理、执行状态计算等</p><br><p>Streams API所依托的都是Kafka的核心内容：使用producer和consumer API作为输入，使用Kafka作为状态存储，在流处理实例上使用相同的组机制来实现容错</p><h3 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h3><h4 id="消费者自动提交"><a href="#消费者自动提交" class="headerlink" title="消费者自动提交"></a>消费者自动提交</h4><p>使用如下api自动提交：</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">properties</span>.<span class="built_in">put</span>(<span class="string">"enable.auto.commit"</span>, <span class="string">"false"</span>);</span><br></pre></td></tr></table></figure><h4 id="消费者手动提交"><a href="#消费者手动提交" class="headerlink" title="消费者手动提交"></a>消费者手动提交</h4><p>每个消费者和对应的patition建立对应的流来读取kafka上面的数据，如果comsumer得到数据，那么kafka就会自动去维护该comsumer的offset，例如在获取到kafka的消息后正准备入库（未入库），但是消费者挂了，那么如果让kafka自动去维护offset，它就会认为这条数据已经被消费了，那么会造成数据丢失。</p><br><p>但是kafka可以让你自己去手动提交，如果在上面的场景中，那么需要我们手动commit，如果comsumer挂了 那么程序就不会执行commit这样的话 其他同group的消费者又可以消费这条数据，保证数据不丢，先要做如下设置：</p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//设置不自动提交，自己手动更新offset</span></span><br><span class="line">properties.<span class="built_in">put</span>(<span class="string">"enable.auto.commit"</span>, <span class="string">"false"</span>);</span><br></pre></td></tr></table></figure><p>使用如下api提交：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">consumer.commitSync()<span class="comment">;</span></span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;环境搭建-Linux&quot;&gt;&lt;a href=&quot;#环境搭建-Linux&quot; class=&quot;headerlink&quot; title=&quot;环境搭建(Linux)&quot;&gt;&lt;/a&gt;环境搭建(Linux)&lt;/h3&gt;&lt;h4 id=&quot;1-Kafka下载&quot;&gt;&lt;a href=&quot;#1-Kafka下载&quot; class=&quot;headerlink&quot; title=&quot;1. Kafka下载&quot;&gt;&lt;/a&gt;1. Kafka下载&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;下载地址：&lt;a href=&quot;http://kafka.apache.org/downloads&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://kafka.apache.org/downloads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;wget &lt;a href=&quot;http://apache.fayea.com/kafka/0.10.1.0/kafka_2.11-0.10.1.0.tgz&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://apache.fayea.com/kafka/0.10.1.0/kafka_2.11-0.10.1.0.tgz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;tar -xvf kafka_2.11-0.10.1.0.tgz&lt;br&gt;cd kafka_2.11-0.10.1.0&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;2-Zookeeper安装&quot;&gt;&lt;a href=&quot;#2-Zookeeper安装&quot; class=&quot;headerlink&quot; title=&quot;2. Zookeeper安装&quot;&gt;&lt;/a&gt;2. Zookeeper安装&lt;/h4&gt;&lt;p&gt;Kafka需要Zookeeper的监控，所以先要安装Zookeeper，如何安装请传送至：&lt;br&gt;     &lt;a href=&quot;http://blog.csdn.net/trigl/article/details/52401847&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;hadoop&lt;/a&gt;、&lt;br&gt;     &lt;a href=&quot;http://blog.csdn.net/trigl/article/details/52401847&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;zookeeper&lt;/a&gt;、&lt;br&gt;     &lt;a href=&quot;http://blog.csdn.net/trigl/article/details/52401847&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;hbase&lt;/a&gt;、&lt;a href=&quot;http://blog.csdn.net/trigl/article/details/52401847&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;spark集群环境搭建&lt;/a&gt; ，&lt;br&gt;安装完成以后依次启动各个节点&lt;/p&gt;
    
    </summary>
    
      <category term="kafka" scheme="http://yoursite.com/categories/kafka/"/>
    
    
      <category term="kafka" scheme="http://yoursite.com/tags/kafka/"/>
    
  </entry>
  
  <entry>
    <title>ELK环境搭建</title>
    <link href="http://yoursite.com/2017/08/04/ELK/"/>
    <id>http://yoursite.com/2017/08/04/ELK/</id>
    <published>2017-08-04T15:05:00.000Z</published>
    <updated>2018-03-11T04:27:17.096Z</updated>
    
    <content type="html"><![CDATA[<h4 id="一、配置"><a href="#一、配置" class="headerlink" title="一、配置"></a>一、配置</h4><pre><code>系统： Windows 8.1elasticsearch：5.5.1logstash：2.0.0kibana：5.5.1</code></pre><blockquote><p>注：由于实验性搭建，选择windows系统，但选择Linux系统效果更佳</p></blockquote><h4 id="二、部署方案"><a href="#二、部署方案" class="headerlink" title="二、部署方案"></a>二、部署方案</h4><h5 id="1-ELK-Redis"><a href="#1-ELK-Redis" class="headerlink" title="1.ELK+Redis"></a>1.ELK+Redis</h5><h5 id="2-ELK-Kafka"><a href="#2-ELK-Kafka" class="headerlink" title="2.ELK+Kafka"></a>2.ELK+Kafka</h5><blockquote><p>注：本次搭建选用第一种方案</p></blockquote><a id="more"></a><h4 id="三、安装"><a href="#三、安装" class="headerlink" title="三、安装"></a>三、安装</h4><blockquote><p>前提:<a href="https://nssm.cc/release/nssm-2.24.zip" target="_blank" rel="noopener">下载nssm</a></p></blockquote><h5 id="1-Elasticsearch"><a href="#1-Elasticsearch" class="headerlink" title="1. Elasticsearch"></a>1. Elasticsearch</h5><h6 id="下载：-download"><a href="#下载：-download" class="headerlink" title="下载： download"></a>下载： <a href="https://www.elastic.co/downloads/elasticsearch" target="_blank" rel="noopener">download</a></h6><h5 id="2-logstash"><a href="#2-logstash" class="headerlink" title="2. logstash"></a>2. logstash</h5><h6 id="下载：-download-1"><a href="#下载：-download-1" class="headerlink" title="下载： download"></a>下载： <a href="https://www.elastic.co/downloads/logstash" target="_blank" rel="noopener">download</a></h6><h5 id="3-kibana"><a href="#3-kibana" class="headerlink" title="3. kibana"></a>3. kibana</h5><h6 id="下载：-download-2"><a href="#下载：-download-2" class="headerlink" title="下载： download"></a>下载： <a href="https://www.elastic.co/downloads/kibana" target="_blank" rel="noopener">download</a></h6><h5 id="注册为windows服务"><a href="#注册为windows服务" class="headerlink" title="注册为windows服务"></a>注册为windows服务</h5><h6 id="a-将下载的nssm-exe分别拷贝到Elasticsearch、logstash和kibana解压后的bin目录下，然后CMD进入bin执行nssm-install-服务名-例如Elasticsearch-的执行nssm-install-elasticsearch-service"><a href="#a-将下载的nssm-exe分别拷贝到Elasticsearch、logstash和kibana解压后的bin目录下，然后CMD进入bin执行nssm-install-服务名-例如Elasticsearch-的执行nssm-install-elasticsearch-service" class="headerlink" title="(a) 将下载的nssm.exe分别拷贝到Elasticsearch、logstash和kibana解压后的bin目录下，然后CMD进入bin执行nssm install 服务名,例如Elasticsearch 的执行nssm install elasticsearch-service.."></a>(a) 将下载的nssm.exe分别拷贝到Elasticsearch、logstash和kibana解压后的bin目录下，然后CMD进入bin执行nssm install 服务名,例如Elasticsearch 的执行nssm install elasticsearch-service..<br></h6><h6 id="b-分析选择path为各压缩包的bin目录下的elasticsearch-bat、logstash-bat和kibana-bat"><a href="#b-分析选择path为各压缩包的bin目录下的elasticsearch-bat、logstash-bat和kibana-bat" class="headerlink" title="(b) 分析选择path为各压缩包的bin目录下的elasticsearch.bat、logstash.bat和kibana.bat"></a>(b) 分析选择path为各压缩包的bin目录下的elasticsearch.bat、logstash.bat和kibana.bat</h6><h6 id="c-Details选项卡设置显示名为Windows名"><a href="#c-Details选项卡设置显示名为Windows名" class="headerlink" title="(c) Details选项卡设置显示名为Windows名"></a>(c) Details选项卡设置显示名为Windows名</h6><h6 id="d-最后选择Install-service"><a href="#d-最后选择Install-service" class="headerlink" title="(d) 最后选择Install service"></a>(d) 最后选择Install service</h6><h4 id="四、部署"><a href="#四、部署" class="headerlink" title="四、部署"></a>四、部署</h4><h5 id="1-创建Maven项目elk-log-可另外取名-，pom文件为："><a href="#1-创建Maven项目elk-log-可另外取名-，pom文件为：" class="headerlink" title="1. 创建Maven项目elk-log(可另外取名)，pom文件为："></a>1. 创建Maven项目elk-log(可另外取名)，pom文件为：</h5><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span> <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag"><span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.suncj<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>elk-log<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">name</span>&gt;</span>elk-log<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">description</span>&gt;</span>elk日志生成项目<span class="tag">&lt;/<span class="name">description</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>4.2.8.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.eclipse.jetty.aggregate<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jetty-all<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>8.1.19.v20160209<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.slf4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>slf4j-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>1.7.12<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>ch.qos.logback<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>logback-core<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>1.2.3<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>net.logstash.logback<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>logstash-logback-encoder<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>4.9<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!--实现slf4j接口并整合 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>ch.qos.logback<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>logback-classic<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>1.2.3<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.fasterxml.jackson.core<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jackson-databind<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>2.7.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>javax.servlet<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>javax.servlet-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>3.1.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><h5 id="2-配置logback-logback-xml文件为："><a href="#2-配置logback-logback-xml文件为：" class="headerlink" title="2. 配置logback,logback.xml文件为："></a>2. 配置logback,logback.xml文件为：</h5><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="php"><span class="meta">&lt;?</span>xml version=<span class="string">"1.0"</span> encoding=<span class="string">"UTF-8"</span><span class="meta">?&gt;</span></span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">configuration</span> <span class="attr">debug</span>=<span class="string">"false"</span>&gt;</span></span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">appender</span> <span class="attr">name</span>=<span class="string">"console"</span> <span class="attr">class</span>=<span class="string">"ch.qos.logback.core.ConsoleAppender"</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">encoder</span> <span class="attr">class</span>=<span class="string">"ch.qos.logback.classic.encoder.PatternLayoutEncoder"</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="comment">&lt;!-- 格式化输出：%d表示日期，%thread表示线程名，%-5level：级别从左显示5个字符宽度%msg：日志消息，%n是换行符 --&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">pattern</span>&gt;</span>%d</span><span class="template-variable">&#123;HH:mm:ss.SSS&#125;</span><span class="xml"> [%thread] %-5level %c</span><span class="template-variable">&#123;1&#125;</span><span class="xml">.%M:%L - %m%n</span></span><br><span class="line"><span class="xml"><span class="tag">&lt;/<span class="name">pattern</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;/<span class="name">encoder</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;/<span class="name">appender</span>&gt;</span></span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">appender</span> <span class="attr">name</span>=<span class="string">"stash"</span></span></span></span><br><span class="line"><span class="xml">class="net.logstash.logback.appender.LogstashTcpSocketAppender"&gt;</span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">destination</span>&gt;</span>127.0.0.1:9250<span class="tag">&lt;/<span class="name">destination</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">encoder</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span></span></span></span><br><span class="line"><span class="xml">class="net.logstash.logback.encoder.LogstashEncoder" /&gt;</span></span><br><span class="line"><span class="xml"><span class="tag">&lt;/<span class="name">appender</span>&gt;</span></span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">logger</span> <span class="attr">name</span>=<span class="string">"com.suncj"</span> <span class="attr">level</span>=<span class="string">"INFO"</span> /&gt;</span></span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">root</span> <span class="attr">level</span>=<span class="string">"INFO"</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">appender-ref</span> <span class="attr">ref</span>=<span class="string">"console"</span> /&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">appender-ref</span> <span class="attr">ref</span>=<span class="string">"stash"</span> /&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;/<span class="name">root</span>&gt;</span></span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span></span><br></pre></td></tr></table></figure><h5 id="3-设置项目定时任务-打日志"><a href="#3-设置项目定时任务-打日志" class="headerlink" title="3.设置项目定时任务(打日志)"></a>3.设置项目定时任务(打日志)</h5><p>定时任务类LogProducer:<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">package com.suncj.elk;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.Random;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.slf4j.Logger;</span><br><span class="line"><span class="keyword">import</span> org.slf4j.LoggerFactory;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 日志生成器&lt;br&gt;</span></span><br><span class="line"><span class="comment"> * 版权：Copyright (c) 2015-2016&lt;br&gt;</span></span><br><span class="line"><span class="comment"> * 创建日期：2017年8月5日&lt;br&gt;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LogProducer</span> &#123;</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> final Logger <span class="built_in">log</span> = LoggerFactory.getLogger(LogProducer.class);</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> Random rand = <span class="keyword">new</span> Random();</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> logId = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">produce</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="built_in">log</span>.info(<span class="string">"log_id: &#123;&#125; , content:&#123;&#125;"</span>, logId, String.format(<span class="string">"I am %s"</span>, logId + rand.nextInt(<span class="number">100000</span>)));</span><br><span class="line">logId++;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>项目启动类:<br><figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">package com.suncj.elk;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.slf4j.Logger;</span><br><span class="line"><span class="keyword">import</span> org.slf4j.LoggerFactory;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.ApplicationContext;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Application</span> </span>&#123;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="type">Logger</span> logger = <span class="type">LoggerFactory</span>.getLogger(<span class="type">Application</span>.<span class="keyword">class</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">ApplicationContext</span> appContext;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> void main(<span class="type">String</span>[] args) &#123;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">logger.info(<span class="string">"准备加载程序"</span>);</span><br><span class="line">appContext = new <span class="type">ClassPathXmlApplicationContext</span>(<span class="string">"app-*.xml"</span>);</span><br><span class="line">logger.info(<span class="string">"加载完成"</span>);</span><br><span class="line">&#125; <span class="keyword">catch</span> (<span class="type">Exception</span> e) &#123;</span><br><span class="line">logger.error(<span class="string">"主程序出错:"</span>, e);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>其他配置文件：app-task.xml<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="php"><span class="meta">&lt;?</span>xml version=<span class="string">"1.0"</span> encoding=<span class="string">"UTF-8"</span><span class="meta">?&gt;</span></span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">"http://www.springframework.org/schema/beans"</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span> <span class="attr">xmlns:util</span>=<span class="string">"http://www.springframework.org/schema/util"</span></span></span><br><span class="line"><span class="tag"><span class="attr">xmlns:task</span>=<span class="string">"http://www.springframework.org/schema/task"</span> <span class="attr">xmlns:context</span>=<span class="string">"http://www.springframework.org/schema/context"</span></span></span><br><span class="line"><span class="tag"><span class="attr">xsi:schemaLocation</span>=<span class="string">"http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd</span></span></span><br><span class="line"><span class="tag"><span class="string">http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="tag"><span class="string">http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd</span></span></span><br><span class="line"><span class="tag"><span class="string">http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">"logProducer"</span> <span class="attr">class</span>=<span class="string">"com.suncj.elk.LogProducer"</span>&gt;</span><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">task:scheduled-tasks</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">task:scheduled</span> <span class="attr">ref</span>=<span class="string">"logProducer"</span> <span class="attr">method</span>=<span class="string">"produce"</span></span></span><br><span class="line"><span class="tag"><span class="attr">cron</span>=<span class="string">"0/5 * * * * *"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">task:scheduled-tasks</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></p><h5 id="2-logstash配置"><a href="#2-logstash配置" class="headerlink" title="2. logstash配置"></a>2. logstash配置</h5><h6 id="a-run-es-bat，run-redis-bat"><a href="#a-run-es-bat，run-redis-bat" class="headerlink" title="(a) run_es.bat，run_redis.bat"></a>(a) run_es.bat，run_redis.bat</h6><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">logstash</span><span class="selector-class">.bat</span> <span class="selector-tag">agent</span> <span class="selector-tag">-f</span> <span class="selector-tag">logstash_es</span><span class="selector-class">.conf</span></span><br></pre></td></tr></table></figure><h6 id="b-logstash-redis-conf"><a href="#b-logstash-redis-conf" class="headerlink" title="(b) logstash_redis.conf"></a>(b) logstash_redis.conf</h6><figure class="highlight puppet"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">input</span> &#123;</span><br><span class="line">tcp &#123;</span><br><span class="line"><span class="attr">host</span> =&gt; <span class="string">"127.0.0.1"</span></span><br><span class="line"><span class="attr">port</span> =&gt; <span class="number">9250</span></span><br><span class="line"><span class="attr">mode</span> =&gt; <span class="string">"server"</span></span><br><span class="line"><span class="attr">codec</span> =&gt; json_lines</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">output</span> &#123;</span><br><span class="line">redis &#123;</span><br><span class="line"><span class="attr">host</span> =&gt; <span class="string">"127.0.0.1"</span></span><br><span class="line">        <span class="attr">port</span> =&gt; <span class="number">6379</span></span><br><span class="line">        <span class="attr">db</span> =&gt; <span class="number">1</span></span><br><span class="line"><span class="attr">data_type</span> =&gt; <span class="string">"list"</span></span><br><span class="line"><span class="attr">key</span> =&gt; <span class="string">"log:es"</span></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h6 id="c-logstash-es-conf"><a href="#c-logstash-es-conf" class="headerlink" title="(c) logstash_es.conf"></a>(c) logstash_es.conf</h6><figure class="highlight puppet"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">input</span> &#123;</span><br><span class="line">  redis &#123;</span><br><span class="line">    <span class="attr">data_type</span> =&gt; <span class="string">"list"</span></span><br><span class="line">    <span class="attr">key</span> =&gt; <span class="string">"log:es"</span></span><br><span class="line">    <span class="attr">host</span> =&gt; <span class="string">"127.0.0.1"</span></span><br><span class="line">    <span class="attr">db</span> =&gt; <span class="number">1</span></span><br><span class="line">    <span class="attr">port</span> =&gt; <span class="number">6379</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">output</span> &#123;</span><br><span class="line">  stdout&#123;</span><br><span class="line">    <span class="attr">codec</span> =&gt; rubydebug</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">elasticsearch</span> &#123;</span><br><span class="line">    <span class="attr">hosts</span> =&gt; [<span class="string">"127.0.0.1:9200"</span>]</span><br><span class="line">    <span class="attr">index</span> =&gt; <span class="string">"log-es-%&#123;+YYYY.MM.dd&#125;"</span></span><br><span class="line">    <span class="attr">flush_size</span> =&gt; <span class="number">1000</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>注: logstash注册为windows服务时需要<br>创建两个bat文件，一个用于项目日志存储到redis；另外一个用户读取redis,输出到elasticsearch，因此需要注册两个服务名不同的windows服务</p></blockquote><h4 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h4><p><a href="https://kibana.logstash.es/content/kibana/index.html" target="_blank" rel="noopener">https://kibana.logstash.es/content/kibana/index.html</a></p><p><a href="http://blog.csdn.net/tulizi/article/details/52972824" target="_blank" rel="noopener">http://blog.csdn.net/tulizi/article/details/52972824</a></p><p><a href="http://udn.yyuap.com/doc/logstash-best-practice-cn/input/redis.html" target="_blank" rel="noopener">http://udn.yyuap.com/doc/logstash-best-practice-cn/input/redis.html</a></p><p><a href="https://www.elastic.co/guide/en/logstash/current/codec-plugins.html" target="_blank" rel="noopener">https://www.elastic.co/guide/en/logstash/current/codec-plugins.html</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;一、配置&quot;&gt;&lt;a href=&quot;#一、配置&quot; class=&quot;headerlink&quot; title=&quot;一、配置&quot;&gt;&lt;/a&gt;一、配置&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;系统： Windows 8.1
elasticsearch：5.5.1
logstash：2.0.0
kibana：5.5.1
&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt;注：由于实验性搭建，选择windows系统，但选择Linux系统效果更佳&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;二、部署方案&quot;&gt;&lt;a href=&quot;#二、部署方案&quot; class=&quot;headerlink&quot; title=&quot;二、部署方案&quot;&gt;&lt;/a&gt;二、部署方案&lt;/h4&gt;&lt;h5 id=&quot;1-ELK-Redis&quot;&gt;&lt;a href=&quot;#1-ELK-Redis&quot; class=&quot;headerlink&quot; title=&quot;1.ELK+Redis&quot;&gt;&lt;/a&gt;1.ELK+Redis&lt;/h5&gt;&lt;h5 id=&quot;2-ELK-Kafka&quot;&gt;&lt;a href=&quot;#2-ELK-Kafka&quot; class=&quot;headerlink&quot; title=&quot;2.ELK+Kafka&quot;&gt;&lt;/a&gt;2.ELK+Kafka&lt;/h5&gt;&lt;blockquote&gt;
&lt;p&gt;注：本次搭建选用第一种方案&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="ELK" scheme="http://yoursite.com/categories/ELK/"/>
    
    
      <category term="ELK" scheme="http://yoursite.com/tags/ELK/"/>
    
  </entry>
  
  <entry>
    <title>Spring-Shiro介绍及其使用</title>
    <link href="http://yoursite.com/2017/08/01/spring/spring-shiro/"/>
    <id>http://yoursite.com/2017/08/01/spring/spring-shiro/</id>
    <published>2017-08-01T03:17:11.000Z</published>
    <updated>2017-08-07T16:46:29.570Z</updated>
    
    <content type="html"><![CDATA[<h5 id="What-is-Apache-Shiro"><a href="#What-is-Apache-Shiro" class="headerlink" title="What is Apache Shiro?"></a>What is Apache Shiro?</h5><p>Apache Shiro是一个功能强大、灵活的，开源的安全框架。它可以干净利落地处理身份验证、授权、企业会话管理和加密。</p><p>Apache Shiro的首要目标是易于使用和理解。安全通常很复杂，甚至让人感到很痛苦，但是Shiro却不是这样子的。一个好的安全框架应该屏蔽复杂性，向外暴露简单、直观的API，来简化开发人员实现应用程序安全所花费的时间和精力。</p><br><a id="more"></a><br><br><p>Shiro能做什么呢？</p><ul><br>  <li>验证用户身份</li><br>  <li>用户访问权限控制，比如：1、判断用户是否分配了一定的安全角色。2、判断用户是否被授予完成某个操作的权限</li><br>  <li>在非 web 或 EJB 容器的环境下可以任意使用Session API</li><br>  <li>可以响应认证、访问控制，或者 Session 生命周期中发生的事件</li><br>  <li>可将一个或以上用户安全数据源数据组合成一个复合的用户 “view”(视图)</li><br>  <li>支持单点登录(SSO)功能</li><br>  <li>支持提供“Remember Me”服务，获取用户关联信息而无需登录<br><br>…</li><br></ul><p>等等——都集成到一个有凝聚力的易于使用的API。</p><p>Shiro 致力在所有应用环境下实现上述功能，小到命令行应用程序，大到企业应用中，而且不需要借助第三方框架、容器、应用服务器等。当然 Shiro 的目的是尽量的融入到这样的应用环境中去，但也可以在它们之外的任何环境下开箱即用。</p><h5 id="Apache-Shiro-Features-特性"><a href="#Apache-Shiro-Features-特性" class="headerlink" title="Apache Shiro Features 特性"></a>Apache Shiro Features 特性</h5><p>Apache Shiro是一个全面的、蕴含丰富功能的安全框架。下图为描述Shiro功能的框架图：</p><!-- <img src="img/ShiroFeatures.png" /> --><p>Authentication（认证）, Authorization（授权）, Session Management（会话管理）, Cryptography（加密）被 Shiro 框架的开发团队称之为应用安全的四大基石。那么就让我们来看看它们吧：</p><ul><br>  <li><strong>Authentication（认证）：</strong>用户身份识别，通常被称为用户“登录”</li><br>  <li><strong>Authorization（授权）：</strong>访问控制。比如某个用户是否具有某个操作的使用权限。</li><br>  <li><strong>Session Management（会话管理）：</strong>特定于用户的会话管理,甚至在非web 或 EJB 应用程序。</li><br>  <li><strong>Cryptography（加密）：</strong>在对数据源使用加密算法加密的同时，保证易于使用。</li><br></ul><p>还有其他的功能来支持和加强这些不同应用环境下安全领域的关注点。特别是对以下的功能支持：</p><ul><br>  <li>Web支持：Shiro 提供的 web 支持 api ，可以很轻松的保护 web 应用程序的安全。</li><br>  <li>缓存：缓存是 Apache Shiro 保证安全操作快速、高效的重要手段。</li><br>  <li>并发：Apache Shiro 支持多线程应用程序的并发特性。</li><br>  <li>测试：支持单元测试和集成测试，确保代码和预想的一样安全。</li><br>  <li>“Run As”：这个功能允许用户假设另一个用户的身份(在许可的前提下)。</li><br>  <li>“Remember Me”：跨 session 记录用户的身份，只有在强制需要时才需要登录。</li><br></ul><blockquote><br>  <p>注意： Shiro不会去维护用户、维护权限，这些需要我们自己去设计/提供，然后通过相应的接口注入给Shiro</p><br></blockquote><h5 id="High-Level-Overview-高级概述"><a href="#High-Level-Overview-高级概述" class="headerlink" title="High-Level Overview 高级概述"></a>High-Level Overview 高级概述</h5><p>在概念层，Shiro 架构包含三个主要的理念：Subject,SecurityManager和 Realm。下面的图展示了这些组件如何相互作用，我们将在下面依次对其进行描述。</p><!-- <img src="img/ShiroBasicArchitecture.png" /> --><ul><br>  <li>Subject：当前用户，Subject 可以是一个人，但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。</li><br>  <li>SecurityManager：管理所有Subject，SecurityManager 是 Shiro 架构的核心，配合内部安全组件共同组成安全伞。</li><br>  <li>Realms：用于进行权限信息的验证，我们自己实现。Realm 本质上是一个特定的安全 DAO：它封装与数据源连接的细节，得到Shiro 所需的相关的数据。在配置 Shiro 的时候，你必须指定至少一个Realm 来实现认证（authentication）和/或授权（authorization）。</li><br></ul><p>我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份，Authorization 是授权访问控制，用于对用户进行的操作授权，证明该用户是否允许进行当前操作，如访问某个链接，某个资源文件等。</p><h5 id="快速上手"><a href="#快速上手" class="headerlink" title="快速上手"></a>快速上手</h5><h6 id="pom-xml"><a href="#pom-xml" class="headerlink" title="pom.xml"></a>pom.xml</h6><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-jpa<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-thymeleaf<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>net.sourceforge.nekohtml<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>nekohtml<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>1.9.22<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>shiro-spring<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>1.4.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">scope</span>&gt;</span>runtime<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><h6 id="Shiro-配置"><a href="#Shiro-配置" class="headerlink" title="Shiro 配置"></a>Shiro 配置</h6><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">@Configuration</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ShiroConfig</span> </span>&#123;</span><br><span class="line">@Bean</span><br><span class="line"><span class="keyword">public</span> ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) &#123;</span><br><span class="line">System.out.println(<span class="string">"ShiroConfiguration.shirFilter()"</span>);</span><br><span class="line">ShiroFilterFactoryBean shiroFilterFactoryBean = <span class="keyword">new</span> <span class="type">ShiroFilterFactoryBean</span>();</span><br><span class="line">shiroFilterFactoryBean.setSecurityManager(securityManager);</span><br><span class="line"><span class="comment">//拦截器.</span></span><br><span class="line">Map&lt;<span class="keyword">String</span>,<span class="keyword">String</span>&gt; filterChainDefinitionMap = <span class="keyword">new</span> <span class="type">LinkedHashMap</span>&lt;<span class="keyword">String</span>,<span class="keyword">String</span>&gt;();</span><br><span class="line"><span class="comment">// 配置不会被拦截的链接 顺序判断</span></span><br><span class="line">filterChainDefinitionMap.put(<span class="string">"/static/**"</span>, <span class="string">"anon"</span>);</span><br><span class="line"><span class="comment">//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了</span></span><br><span class="line">filterChainDefinitionMap.put(<span class="string">"/logout"</span>, <span class="string">"logout"</span>);</span><br><span class="line"><span class="comment">//&lt;!-- 过滤链定义，从上向下顺序执行，一般将/**放在最为下边 --&gt;:这是一个坑呢，一不小心代码就不好使了;</span></span><br><span class="line"><span class="comment">//&lt;!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--&gt;</span></span><br><span class="line">filterChainDefinitionMap.put(<span class="string">"/**"</span>, <span class="string">"authc"</span>);</span><br><span class="line"><span class="comment">// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面</span></span><br><span class="line">shiroFilterFactoryBean.setLoginUrl(<span class="string">"/login"</span>);</span><br><span class="line"><span class="comment">// 登录成功后要跳转的链接</span></span><br><span class="line">shiroFilterFactoryBean.setSuccessUrl(<span class="string">"/index"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//未授权界面;</span></span><br><span class="line">shiroFilterFactoryBean.setUnauthorizedUrl(<span class="string">"/403"</span>);</span><br><span class="line">shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);</span><br><span class="line"><span class="keyword">return</span> shiroFilterFactoryBean;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@Bean</span><br><span class="line"><span class="keyword">public</span> MyShiroRealm myShiroRealm()&#123;</span><br><span class="line">MyShiroRealm myShiroRealm = <span class="keyword">new</span> <span class="type">MyShiroRealm</span>();</span><br><span class="line"><span class="keyword">return</span> myShiroRealm;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">@Bean</span><br><span class="line"><span class="keyword">public</span> SecurityManager securityManager()&#123;</span><br><span class="line">DefaultWebSecurityManager securityManager =  <span class="keyword">new</span> <span class="type">DefaultWebSecurityManager</span>();</span><br><span class="line">securityManager.setRealm(myShiroRealm());</span><br><span class="line"><span class="keyword">return</span> securityManager;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h6 id="Filter-Chain定义说明："><a href="#Filter-Chain定义说明：" class="headerlink" title="Filter Chain定义说明："></a>Filter Chain定义说明：</h6><ul><br>  <li>1、一个URL可以配置多个Filter，使用逗号分隔</li><br>  <li>2、当设置多个过滤器时，全部验证通过，才视为通过</li><br>  <li>3、部分过滤器可指定参数，如perms，roles</li><br></ul><h6 id="Shiro内置的FilterChain"><a href="#Shiro内置的FilterChain" class="headerlink" title="Shiro内置的FilterChain:"></a>Shiro内置的FilterChain:</h6><table><thead><tr><th>Filter Name</th><th>Class</th></tr></thead><tbody><tr><td>anon</td><td>org.apache.shiro.web.filter.authc.AnonymousFilter</td></tr><tr><td>authc</td><td>org.apache.shiro.web.filter.authc.FormAuthenticationFilter</td></tr><tr><td>authcBasic</td><td>org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter</td></tr><tr><td>perms</td><td>org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter</td></tr><tr><td>port</td><td>org.apache.shiro.web.filter.authz.PortFilter</td></tr><tr><td>rest</td><td>org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter</td></tr><tr><td>roles</td><td>org.apache.shiro.web.filter.authz.RolesAuthorizationFilter</td></tr><tr><td>ssl</td><td>org.apache.shiro.web.filter.authz.SslFilter</td></tr><tr><td>user</td><td>org.apache.shiro.web.filter.authc.UserFilter</td></tr></tbody></table><ul><br>  <li>anon:所有url都都可以匿名访问</li><br>  <li>authc: 需要认证才能进行访问</li><br>  <li>user:配置记住我或认证通过可以访问</li><br></ul><p><strong>登录认证实现</strong></p><p>在认证、授权内部实现机制中都有提到，最终处理都将交给Real进行处理。因为在Shiro中，最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下，在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说，Realm是专用于安全框架的DAO.<br>Shiro的认证过程最终会交由Realm执行，这时会调用Realm的<code class="highlighter-rouge">getAuthenticationInfo(token)</code>方法。</p><p>该方法主要执行以下操作:</p><ul><br>  <li>1、检查提交的进行认证的令牌信息</li><br>  <li>2、根据令牌信息从数据源(通常为数据库)中获取用户信息</li><br>  <li>3、对用户信息进行匹配验证。</li><br>  <li>4、验证通过将返回一个封装了用户信息的<code class="highlighter-rouge">AuthenticationInfo</code>实例。</li><br>  <li>5、验证失败则抛出<code class="highlighter-rouge">AuthenticationException</code>异常信息。</li><br></ul><p>而在我们的应用程序中要做的就是自定义一个Realm类，继承AuthorizingRealm抽象类，重载doGetAuthenticationInfo()，重写获取用户信息的方法。</p><p>doGetAuthenticationInfo的重写</p><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line"><span class="keyword">protected</span> AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)</span><br><span class="line">        <span class="keyword">throws</span> AuthenticationException &#123;</span><br><span class="line">    System.out.<span class="built_in">println</span>(<span class="string">"MyShiroRealm.doGetAuthenticationInfo()"</span>);</span><br><span class="line">    <span class="comment">//获取用户的输入的账号.</span></span><br><span class="line">    <span class="keyword">String</span> username = (<span class="keyword">String</span>)token.getPrincipal();</span><br><span class="line">    System.out.<span class="built_in">println</span>(token.getCredentials());</span><br><span class="line">    <span class="comment">//通过username从数据库中查找 User对象，如果找到，没找到.</span></span><br><span class="line">    <span class="comment">//实际项目中，这里可以根据实际情况做缓存，如果不做，Shiro自己也是有时间间隔机制，2分钟内不会重复执行该方法</span></span><br><span class="line">    UserInfo userInfo = userInfoService.findByUsername(username);</span><br><span class="line">    System.out.<span class="built_in">println</span>(<span class="string">"-----&gt;&gt;userInfo="</span>+userInfo);</span><br><span class="line">    <span class="keyword">if</span>(userInfo == <span class="keyword">null</span>)&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    SimpleAuthenticationInfo authenticationInfo = <span class="keyword">new</span> SimpleAuthenticationInfo(</span><br><span class="line">            userInfo, <span class="comment">//用户名</span></span><br><span class="line">            userInfo.getPassword(), <span class="comment">//密码</span></span><br><span class="line">            ByteSource.Util.bytes(userInfo.getCredentialsSalt()),<span class="comment">//salt=username+salt</span></span><br><span class="line">            getName()  <span class="comment">//realm name</span></span><br><span class="line">    );</span><br><span class="line">    <span class="keyword">return</span> authenticationInfo;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>链接权限的实现</strong></p><p>shiro的权限授权是通过继承<code class="highlighter-rouge">AuthorizingRealm</code>抽象类，重载<code class="highlighter-rouge">doGetAuthorizationInfo();</code>当访问到页面的时候，链接配置了相应的权限或者shiro标签才会执行此方法否则不会执行，所以如果只是简单的身份认证没有权限的控制的话，那么这个方法可以不进行实现，直接返回null即可。在这个方法中主要是使用类：<code class="highlighter-rouge">SimpleAuthorizationInfo</code>进行角色的添加和权限的添加。</p><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">protected</span> AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) &#123;</span><br><span class="line">    System.out.println(<span class="string">"权限配置--&gt;MyShiroRealm.doGetAuthorizationInfo()"</span>);</span><br><span class="line">    SimpleAuthorizationInfo authorizationInfo = <span class="keyword">new</span> SimpleAuthorizationInfo();</span><br><span class="line">    UserInfo userInfo  = (UserInfo)principals.getPrimaryPrincipal();</span><br><span class="line">    <span class="keyword">for</span>(SysRole <span class="string">role:</span>userInfo.getRoleList())&#123;</span><br><span class="line">        authorizationInfo.addRole(role.getRole());</span><br><span class="line">        <span class="keyword">for</span>(SysPermission <span class="string">p:</span>role.getPermissions())&#123;</span><br><span class="line">            authorizationInfo.addStringPermission(p.getPermission());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> authorizationInfo;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当然也可以添加set集合：roles是从数据库查询的当前用户的角色，stringPermissions是从数据库查询的当前用户对应的权限</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">authorizationInfo.setRoles(roles)<span class="comment">;</span></span><br><span class="line">authorizationInfo.setStringPermissions(stringPermissions)<span class="comment">;</span></span><br></pre></td></tr></table></figure><p>就是说如果在shiro配置文件中添加了<code class="highlighter-rouge">filterChainDefinitionMap.put(“/add”, “perms[权限添加]”);</code>就说明访问/add这个链接必须要有“权限添加”这个权限才可以访问，如果在shiro配置文件中添加了<code class="highlighter-rouge">filterChainDefinitionMap.put(“/add”, “roles[100002]，perms[权限添加]”);</code>就说明访问<code class="highlighter-rouge">/add</code>这个链接必须要有“权限添加”这个权限和具有“100002”这个角色才可以访问。</p><p><strong>参考附录:</strong></p><br><p><a href="https://waylau.gitbooks.io/apache-shiro-1-2-x-reference/content/" target="_blank" rel="noopener">Apache Shiro中文手册</a> <br><br><a href="http://412887952-qq-com.iteye.com/blog/2299777" target="_blank" rel="noopener">Spring Boot Shiro权限管理【从零开始学Spring Boot】</a><br><br><a href="http://z77z.oschina.io/2017/02/13/SpringBoot+shiro%E6%95%B4%E5%90%88%E5%AD%A6%E4%B9%A0%E4%B9%8B%E7%99%BB%E5%BD%95%E8%AE%A4%E8%AF%81%E5%92%8C%E6%9D%83%E9%99%90%E6%8E%A7%E5%88%B6/" target="_blank" rel="noopener">SpringBoot+shiro整合学习之登录认证和权限控制</a> <br><br><a href="http://www.ityouknow.com/springboot/2017/06/26/springboot-shiro.html" target="_blank" rel="noopener">springboot整合shiro-登录认证和权限管理</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h5 id=&quot;What-is-Apache-Shiro&quot;&gt;&lt;a href=&quot;#What-is-Apache-Shiro&quot; class=&quot;headerlink&quot; title=&quot;What is Apache Shiro?&quot;&gt;&lt;/a&gt;What is Apache Shiro?&lt;/h5&gt;&lt;p&gt;Apache Shiro是一个功能强大、灵活的，开源的安全框架。它可以干净利落地处理身份验证、授权、企业会话管理和加密。&lt;/p&gt;

&lt;p&gt;Apache Shiro的首要目标是易于使用和理解。安全通常很复杂，甚至让人感到很痛苦，但是Shiro却不是这样子的。一个好的安全框架应该屏蔽复杂性，向外暴露简单、直观的API，来简化开发人员实现应用程序安全所花费的时间和精力。&lt;/p&gt;&lt;br&gt;
    
    </summary>
    
      <category term="spring" scheme="http://yoursite.com/categories/spring/"/>
    
    
      <category term="spring" scheme="http://yoursite.com/tags/spring/"/>
    
      <category term="shiro" scheme="http://yoursite.com/tags/shiro/"/>
    
  </entry>
  
  <entry>
    <title>ThreadLocal</title>
    <link href="http://yoursite.com/2017/07/22/java/thread-local/"/>
    <id>http://yoursite.com/2017/07/22/java/thread-local/</id>
    <published>2017-07-22T10:54:25.000Z</published>
    <updated>2018-07-24T17:15:49.945Z</updated>
    
    <content type="html"><![CDATA[<h3 id="ThreadLocal用法"><a href="#ThreadLocal用法" class="headerlink" title="ThreadLocal用法"></a>ThreadLocal用法</h3><p>Java中线程的同步机制保证了多线程访问共享变量的安全性，通常我们使用synchronized关键字来实现。在多个线程对共享变量进行读写操作时，同步锁保证了同一时间只有一个线程对共享变量进行操作，概括地说，这是一种“以时间换空间”的解决策略。</p><p>在JDK1.2中引入了ThreadLocal类来提供了一种“以空间换时间”的同步解决策略。ThreadLocal内部维护了一份类似Map的静态变量ThreadLocalMap，其中key为当前线程，value为共享变量。JDK1.5引入泛型，ThreadLocal也同时支持泛型。<br><a id="more"></a><br>其具体实现如下<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadLocal</span>&lt;T&gt; &#123;</span></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * ThreadLocals rely on per-thread hash maps attached to each thread</span></span><br><span class="line"><span class="comment">   * (Thread.threadLocals and inheritableThreadLocals).  The ThreadLocal</span></span><br><span class="line"><span class="comment">   * objects act as keys, searched via threadLocalHashCode.  This is a</span></span><br><span class="line"><span class="comment">   * custom hash code (useful only within ThreadLocalMaps) that eliminates</span></span><br><span class="line"><span class="comment">   * collisions in the common case where consecutively constructed</span></span><br><span class="line"><span class="comment">   * ThreadLocals are used by the same threads, while remaining well-behaved</span></span><br><span class="line"><span class="comment">   * in less common cases.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">private</span> final <span class="keyword">int</span> threadLocalHashCode = nextHashCode();</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * The next hash code to be given out. Accessed only by like-named method.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> nextHashCode = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * The difference between successively generated hash codes - turns</span></span><br><span class="line"><span class="comment">   * implicit sequential thread-local IDs into near-optimally spread</span></span><br><span class="line"><span class="comment">   * multiplicative hash values for power-of-two-sized tables.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> final <span class="keyword">int</span> HASH_INCREMENT = <span class="number">0x61c88647</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Compute the next hash code. The static synchronization used here</span></span><br><span class="line"><span class="comment">   * should not be a performance bottleneck. When ThreadLocals are</span></span><br><span class="line"><span class="comment">   * generated in different threads at a fast enough rate to regularly</span></span><br><span class="line"><span class="comment">   * contend on this lock, memory contention is by far a more serious</span></span><br><span class="line"><span class="comment">   * problem than lock contention.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> synchronized <span class="keyword">int</span> <span class="title">nextHashCode</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      <span class="keyword">int</span> h = nextHashCode;</span><br><span class="line">      nextHashCode = h + HASH_INCREMENT;</span><br><span class="line">      <span class="keyword">return</span> h;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Creates a thread local variable.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">ThreadLocal</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Returns the value in the current thread's copy of this thread-local</span></span><br><span class="line"><span class="comment">   * variable.  Creates and initializes the copy if this is the first time</span></span><br><span class="line"><span class="comment">   * the thread has called this method.</span></span><br><span class="line"><span class="comment">   *</span></span><br><span class="line"><span class="comment">   * @return the current thread's value of this thread-local</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> T <span class="title">get</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      Thread t = Thread.currentThread();</span><br><span class="line">      ThreadLocalMap <span class="built_in">map</span> = getMap(t);</span><br><span class="line">      <span class="keyword">if</span> (<span class="built_in">map</span> != null)</span><br><span class="line">          <span class="keyword">return</span> (T)<span class="built_in">map</span>.get(<span class="keyword">this</span>);</span><br><span class="line"></span><br><span class="line">      <span class="comment">// Maps are constructed lazily.  if the map for this thread</span></span><br><span class="line">      <span class="comment">// doesn't exist, create it, with this ThreadLocal and its</span></span><br><span class="line">      <span class="comment">// initial value as its only entry.</span></span><br><span class="line">      T value = initialValue();</span><br><span class="line">      createMap(t, value);</span><br><span class="line">      <span class="keyword">return</span> value;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Sets the current thread's copy of this thread-local variable</span></span><br><span class="line"><span class="comment">   * to the specified value.  Many applications will have no need for</span></span><br><span class="line"><span class="comment">   * this functionality, relying solely on the &#123;@link #initialValue&#125;</span></span><br><span class="line"><span class="comment">   * method to set the values of thread-locals.</span></span><br><span class="line"><span class="comment">   *</span></span><br><span class="line"><span class="comment">   * @param value the value to be stored in the current threads' copy of</span></span><br><span class="line"><span class="comment">   *        this thread-local.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(T value)</span> </span>&#123;</span><br><span class="line">      Thread t = Thread.currentThread();</span><br><span class="line">      ThreadLocalMap <span class="built_in">map</span> = getMap(t);</span><br><span class="line">      <span class="keyword">if</span> (<span class="built_in">map</span> != null)</span><br><span class="line">          <span class="built_in">map</span>.<span class="built_in">set</span>(<span class="keyword">this</span>, value);</span><br><span class="line">      <span class="keyword">else</span></span><br><span class="line">          createMap(t, value);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Get the map associated with a ThreadLocal. Overridden in</span></span><br><span class="line"><span class="comment">   * InheritableThreadLocal.</span></span><br><span class="line"><span class="comment">   *</span></span><br><span class="line"><span class="comment">   * @param  t the current thread</span></span><br><span class="line"><span class="comment">   * @return the map</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function">ThreadLocalMap <span class="title">getMap</span><span class="params">(Thread t)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> t.threadLocals;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Create the map associated with a ThreadLocal. Overridden in</span></span><br><span class="line"><span class="comment">   * InheritableThreadLocal.</span></span><br><span class="line"><span class="comment">   *</span></span><br><span class="line"><span class="comment">   * @param t the current thread</span></span><br><span class="line"><span class="comment">   * @param firstValue value for the initial entry of the map</span></span><br><span class="line"><span class="comment">   * @param map the map to store.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">createMap</span><span class="params">(Thread t, T firstValue)</span> </span>&#123;</span><br><span class="line">      t.threadLocals = <span class="keyword">new</span> ThreadLocalMap(<span class="keyword">this</span>, firstValue);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  .......</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * ThreadLocalMap is a customized hash map suitable only for</span></span><br><span class="line"><span class="comment">   * maintaining thread local values. No operations are exported</span></span><br><span class="line"><span class="comment">   * outside of the ThreadLocal class. The class is package private to</span></span><br><span class="line"><span class="comment">   * allow declaration of fields in class Thread.  To help deal with</span></span><br><span class="line"><span class="comment">   * very large and long-lived usages, the hash table entries use</span></span><br><span class="line"><span class="comment">   * WeakReferences for keys. However, since reference queues are not</span></span><br><span class="line"><span class="comment">   * used, stale entries are guaranteed to be removed only when</span></span><br><span class="line"><span class="comment">   * the table starts running out of space.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadLocalMap</span> &#123;</span></span><br><span class="line"></span><br><span class="line">  ........</span><br><span class="line"></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>从中很清晰的可以看出，多个线程拥有自己一份单独的ThreadLocalMap，共享变量对于每个线程都是单独的一份，因此不会造成线程的安全问题。</p><p>JDBC的ConnectionManager类就是以这种方式来实现数据库连接Connection对象线程隔离。</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">import java.sql.Connection;</span><br><span class="line">import java.sql.DriverManager;</span><br><span class="line">import java.sql.SQLException;</span><br><span class="line"></span><br><span class="line">public class ConnectionManager &#123;</span><br><span class="line"></span><br><span class="line">private static ThreadLocal&lt;Connection&gt; connectionHolder = new ThreadLocal&lt;Connection&gt;() &#123;</span><br><span class="line">@Override</span><br><span class="line">protected<span class="built_in"> Connection </span>initialValue() &#123;</span><br><span class="line"><span class="built_in">Connection </span>conn = <span class="literal">null</span>;</span><br><span class="line">try &#123;</span><br><span class="line">conn = DriverManager.getConnection(</span><br><span class="line"><span class="string">"jdbc:mysql://localhost:3306/test"</span>, <span class="string">"username"</span>,</span><br><span class="line"><span class="string">"password"</span>);</span><br><span class="line">&#125; catch (SQLException e) &#123;</span><br><span class="line">e.printStackTrace();</span><br><span class="line">&#125;</span><br><span class="line">return conn;</span><br><span class="line">&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">public static<span class="built_in"> Connection </span>getConnection() &#123;</span><br><span class="line">return connectionHolder.<span class="builtin-name">get</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public static void setConnection(Connection conn) &#123;</span><br><span class="line">connectionHolder.<span class="builtin-name">set</span>(conn);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>但是，有些情况ThreadLocal可能并不适用，例如存储大量数据的共享变量，或共享变量只能被创建一次时，就只能通过synchronized来实现了。</p><p><a href="https://my.oschina.net/lichhao/blog/111362" target="_blank" rel="noopener">推荐阅读</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;ThreadLocal用法&quot;&gt;&lt;a href=&quot;#ThreadLocal用法&quot; class=&quot;headerlink&quot; title=&quot;ThreadLocal用法&quot;&gt;&lt;/a&gt;ThreadLocal用法&lt;/h3&gt;&lt;p&gt;Java中线程的同步机制保证了多线程访问共享变量的安全性，通常我们使用synchronized关键字来实现。在多个线程对共享变量进行读写操作时，同步锁保证了同一时间只有一个线程对共享变量进行操作，概括地说，这是一种“以时间换空间”的解决策略。&lt;/p&gt;
&lt;p&gt;在JDK1.2中引入了ThreadLocal类来提供了一种“以空间换时间”的同步解决策略。ThreadLocal内部维护了一份类似Map的静态变量ThreadLocalMap，其中key为当前线程，value为共享变量。JDK1.5引入泛型，ThreadLocal也同时支持泛型。&lt;br&gt;
    
    </summary>
    
      <category term="JAVA" scheme="http://yoursite.com/categories/JAVA/"/>
    
    
      <category term="ThreadLocal" scheme="http://yoursite.com/tags/ThreadLocal/"/>
    
  </entry>
  
  <entry>
    <title>Spring-cloud入门介绍</title>
    <link href="http://yoursite.com/2017/07/22/spring/spring-boot/spring-introduction/"/>
    <id>http://yoursite.com/2017/07/22/spring/spring-boot/spring-introduction/</id>
    <published>2017-07-22T10:52:56.000Z</published>
    <updated>2017-08-07T15:53:31.568Z</updated>
    
    <content type="html"><![CDATA[<h3 id="Spring-cloud入门介绍"><a href="#Spring-cloud入门介绍" class="headerlink" title="Spring-cloud入门介绍"></a>Spring-cloud入门介绍</h3><p><a href="https://projects.spring.io/spring-cloud/" target="_blank" rel="noopener">Spring Cloud官网</a></p><p><a href="https://springcloud.cc/" target="_blank" rel="noopener">Spring Cloud中文网</a></p><h4 id="一、Spring-Cloud-Netflix"><a href="#一、Spring-Cloud-Netflix" class="headerlink" title="一、Spring Cloud Netflix"></a>一、Spring Cloud Netflix</h4><h4 id="二、服务提供与调用"><a href="#二、服务提供与调用" class="headerlink" title="二、服务提供与调用"></a>二、服务提供与调用</h4><a id="more"></a><h4 id="三、熔断器Hystrix"><a href="#三、熔断器Hystrix" class="headerlink" title="三、熔断器Hystrix"></a>三、熔断器Hystrix</h4><h4 id="四、熔断监控Hystrix-Dashboard和Turbine"><a href="#四、熔断监控Hystrix-Dashboard和Turbine" class="headerlink" title="四、熔断监控Hystrix Dashboard和Turbine"></a>四、熔断监控Hystrix Dashboard和Turbine</h4><h4 id="五、配置中心git示例"><a href="#五、配置中心git示例" class="headerlink" title="五、配置中心git示例"></a>五、配置中心git示例</h4>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;Spring-cloud入门介绍&quot;&gt;&lt;a href=&quot;#Spring-cloud入门介绍&quot; class=&quot;headerlink&quot; title=&quot;Spring-cloud入门介绍&quot;&gt;&lt;/a&gt;Spring-cloud入门介绍&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://projects.spring.io/spring-cloud/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Spring Cloud官网&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://springcloud.cc/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Spring Cloud中文网&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;一、Spring-Cloud-Netflix&quot;&gt;&lt;a href=&quot;#一、Spring-Cloud-Netflix&quot; class=&quot;headerlink&quot; title=&quot;一、Spring Cloud Netflix&quot;&gt;&lt;/a&gt;一、Spring Cloud Netflix&lt;/h4&gt;&lt;h4 id=&quot;二、服务提供与调用&quot;&gt;&lt;a href=&quot;#二、服务提供与调用&quot; class=&quot;headerlink&quot; title=&quot;二、服务提供与调用&quot;&gt;&lt;/a&gt;二、服务提供与调用&lt;/h4&gt;
    
    </summary>
    
      <category term="spring-cloud" scheme="http://yoursite.com/categories/spring-cloud/"/>
    
    
      <category term="spring" scheme="http://yoursite.com/tags/spring/"/>
    
      <category term="spring-cloud" scheme="http://yoursite.com/tags/spring-cloud/"/>
    
  </entry>
  
  <entry>
    <title>第一篇博客</title>
    <link href="http://yoursite.com/2017/07/16/My-first-post/"/>
    <id>http://yoursite.com/2017/07/16/My-first-post/</id>
    <published>2017-07-16T03:17:11.000Z</published>
    <updated>2018-03-15T16:19:49.226Z</updated>
    
    <content type="html"><![CDATA[<h3 id="为什么我要开始要写博客"><a href="#为什么我要开始要写博客" class="headerlink" title="为什么我要开始要写博客"></a>为什么我要开始要写博客</h3><p>&nbsp;&nbsp;&nbsp;&nbsp;从15年11月份以来，这一年多在企业工作的日子里，我收获许多。作为一个渴望学技术的程序员，我慢慢摆脱了学校的那种安逸的生活，开始走上了技术宅的道路。</p><p>&nbsp;&nbsp;&nbsp;&nbsp;在企业中，前几个月的时间里，我每天都像是海绵一样吸收着养分，学习企业的架构，项目的开发，部署，优化以及维护工作。我每天都痛苦并快乐着，虽然加班，但是我能感觉到自己一点一点的在往上爬。我学会SpringMVC架构，学会使用Maven构建项目，用Ant来实现自动部署项目，用Groovy脚本来编写告警任务。学会了很多软件，诸如MongoDB，Redis等常用开发软件。这个过程中，我很快乐，并且每天都在进步。<br><a id="more"></a><br>&nbsp;&nbsp;&nbsp;&nbsp;但是到了17年的3,4月份，我熟悉了团队的各种业务，也明白了项目中所用到的框架和各种技术。我每天做的除了日常的开发和维护，似乎陷入了重复造轮子的困境。虽然在这过程中，我学会了怎样去考虑到新业务或新场景的设计流程和后续的维护过程，但是我始终感觉到了自己的进步慢慢的缓下了，这是我不希望看到的，我渴望进步和成功。</p><p>&nbsp;&nbsp;&nbsp;&nbsp;最终，我看到了一句话，“种一棵树，最好的时间是十年前，其次，是现在”，我开始领悟到我必须改变点什么。我开始看基础的JAVA进阶等书籍，开始每天看技术博客或推文，培养自己的兴趣，并且从现在开始，写博客。我以前似乎总在犹豫，我常常害怕自己的技术不够好，但是，从另外一个方面想，这又有什么关系，那就学吧。</p><p>&nbsp;&nbsp;&nbsp;&nbsp;为什么写博客，因为它是对你看到，用到知识的升华。在写博客的过程中，不仅会让你review以前的代码，考虑更好的设计方案，也会让你对知识的理解更上一层楼，并记忆深刻。</p><p>&nbsp;&nbsp;&nbsp;&nbsp;写代码的时候，往往避免不了遇到各种BUG和技术难点，但是没关系，多请教别人，大多数人愿意和你分享自己的所得。和优秀的人多交流，你们会相互收益。最后，最重要的是，告诉自己不要怕，并且时刻保持一个谦卑的心。</p>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;为什么我要开始要写博客&quot;&gt;&lt;a href=&quot;#为什么我要开始要写博客&quot; class=&quot;headerlink&quot; title=&quot;为什么我要开始要写博客&quot;&gt;&lt;/a&gt;为什么我要开始要写博客&lt;/h3&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;从15年11月份以来，这一年多在企业工作的日子里，我收获许多。作为一个渴望学技术的程序员，我慢慢摆脱了学校的那种安逸的生活，开始走上了技术宅的道路。&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;在企业中，前几个月的时间里，我每天都像是海绵一样吸收着养分，学习企业的架构，项目的开发，部署，优化以及维护工作。我每天都痛苦并快乐着，虽然加班，但是我能感觉到自己一点一点的在往上爬。我学会SpringMVC架构，学会使用Maven构建项目，用Ant来实现自动部署项目，用Groovy脚本来编写告警任务。学会了很多软件，诸如MongoDB，Redis等常用开发软件。这个过程中，我很快乐，并且每天都在进步。&lt;br&gt;
    
    </summary>
    
      <category term="随笔" scheme="http://yoursite.com/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
      <category term="随笔" scheme="http://yoursite.com/tags/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
</feed>
