-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
82 lines (47 loc) · 24.8 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>知也无涯</title>
<link href="/atom.xml" rel="self"/>
<link href="http://yoursite.com/"/>
<updated>2017-11-18T17:56:51.879Z</updated>
<id>http://yoursite.com/</id>
<author>
<name>blessing si</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>kafka系列之生产者</title>
<link href="http://yoursite.com/2017/11/18/kafka%E7%B3%BB%E5%88%97%E4%B9%8B%E7%94%9F%E4%BA%A7%E8%80%85/"/>
<id>http://yoursite.com/2017/11/18/kafka系列之生产者/</id>
<published>2017-11-18T13:29:06.000Z</published>
<updated>2017-11-18T17:56:51.879Z</updated>
<content type="html"><![CDATA[<h2 id="概要"><a href="#概要" class="headerlink" title="概要"></a>概要</h2><p>在不同的生产场景下,我们对生产者的要求是不同的。在使用生产者写消息之前,首先要明确我们能否接受数据丢失?能否接受数据重复发送?系统延迟和数据准确性哪个更重要?对于不同的要求,我们要对Kafka进行不同的配置。</p><p>首先来看生产者发送消息的过程<br><a id="more"></a><br><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="overview.png" alt="" title=""> </div> <div class="image-caption"></div> </figure><br>如上图所示,生产者写数据的过程主要有如下几个步骤。</p><ol><li>创建一个ProduceRecord</li><li>调用send()方法进行发送操作,会将消息序列化为字节数组</li><li>Partitioner来确定写入的partition。如果我们在第一步中指定了partition,partitioner会直接返回我们指定的partition,否则,Partitioner会根据消息的Key来确定要写入的partition。生产者在确定了消息的topic和partition后,会把它加到相同partition下的批量消息中,会有一个单独的线程来负责将这些消息发送给对应的broker</li><li>当broker收到消息后,它会返回一个响应。如果消息被成功写进Kafka,会返回一个包含topic,partition和offset的消息给生产者,否则会返回错误消息。生产者接收到错误消息后,会重试几次,知道重试失败,返回错误信息给调用者</li></ol><h2 id="创建一个生产者"><a href="#创建一个生产者" class="headerlink" title="创建一个生产者"></a>创建一个生产者</h2><p>首先上代码。</p><p>在Kafka的Apach客户端中,KafkaProducer提供了如下构造方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="title">KafkaProducer</span><span class="params">(Map<String, Object> configs)</span></span>{}</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="title">KafkaProducer</span><span class="params">(Map<String, Object> configs, Serializer<K> keySerializer, </span></span></div><div class="line">Serializer<V> valueSerializer){}</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="title">KafkaProducer</span><span class="params">(Properties properties)</span></span>{}</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="title">KafkaProducer</span><span class="params">(Properties properties, Serializer<K> keySerializer, Serializer<V> valueSerializer)</span></span>{}</div></pre></td></tr></table></figure></p><p>从构造方法中可以看出,构造一个生产者,我们需要提供若干配置信息。其中必须的有如下三个。</p><ul><li><em>bootstrap.servers</em><blockquote><p>Kafka broker列表,是一组host:port格式的列表。生产者在初始化连接到集群后会从集群获得更多的元数据信息,所以我们不必传入集群中所有的broker,为了防止有机器正好挂掉,传入两三个broker就可以了。</p></blockquote></li><li><em>key.serializer</em><blockquote><p>kafka消息是一个<strong>K-V</strong>对,。这个参数的值就是生产者对Key进行序列化时所用的序列化器。这个类需要实现<em>org.apache.kafka.common.serialization.Serializer</em>接口。Kafka客户端中内置了ByteArray,String和Integer三种实现,所以如果你需要写入的消息属于以上三种,那么直接使用內建的序列化实现就可以了,否则,你需要自己实现接口,或者使用第三方的序列化库,<a href="#ser">后面</a>会继续讨论。注意,Kafka允许我们只写入value,而不写入key,但即使这样,<strong>这个参数也是必须的</strong>。</p></blockquote></li><li><em>value.serializer</em><blockquote><p>顾名思义,这个参数是生产者对消息value进行序列化时所用的序列化器,具体要求同上。</p></blockquote></li></ul><p>自己的代码实现<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> Producer<String, String> <span class="title">newProducer</span><span class="params">()</span> </span>{</div><div class="line"> Properties kafkaProps = <span class="keyword">new</span> Properties();</div><div class="line"> kafkaProps.put(<span class="string">"bootstrap.servers"</span>, <span class="string">"broker1:9092,broker2:9092"</span>);</div><div class="line"> kafkaProps.put(<span class="string">"key.serializer"</span>, <span class="string">"org.apache.kafka.common.serialization.StringSerializer"</span>);</div><div class="line"> kafkaProps.put(<span class="string">"value.serializer"</span>, <span class="string">"org.apache.kafka.common.serialization.StringSerializer"</span>);</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> KafkaProducer<String, String>(kafkaProps);</div><div class="line">}</div></pre></td></tr></table></figure></p><p>除了上面提到的三个必须的配置参数,生产者还有其他若干参数可以配置,具体情况请看后面。</p><h2 id="发送消息"><a href="#发送消息" class="headerlink" title="发送消息"></a>发送消息</h2><p>构造好生产者后,我们就可以尝试发送消息了。Kafka提供了三种方式来进行发送操作。</p><ul><li>管杀不管埋(<em>Fire-and-forget</em>)<blockquote><p>顾名思义,发完消息后就不管了。即不关注消息是否成功写入了。</p></blockquote></li><li>同步发送<blockquote><p>send()方法会返回一个<em>Future</em>对象,我们可以通过<em>get()</em>方法来同步地获取发送结果。</p></blockquote></li><li>异步发送<blockquote><p>我们可以给send()方法传入一个回调函数参数,当消息返回后,就会触发这个回调方法。</p></blockquote></li></ul><h3 id="最简单的方式"><a href="#最简单的方式" class="headerlink" title="最简单的方式"></a>最简单的方式</h3><p>先上代码<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">send</span><span class="params">()</span></span>{</div><div class="line"> Producer<String, String> producer = newProducer();</div><div class="line"> ProducerRecord<String, String> record = <span class="keyword">new</span> ProducerRecord<String, String>(<span class="string">"topic"</span>, <span class="string">"this is key"</span>,</div><div class="line"> <span class="string">"I am value"</span>);</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> producer.send(record);</div><div class="line"> }<span class="keyword">catch</span> (Exception e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p><p>这就是上面所提到的管杀不管埋方式。调用send()方法后不对返回消息进行任何处理。</p><p>Producer接受<em>ProducerRecord</em>对象作为消息载体,因此在发送消息前,我们首先要构造一个。我们这里传入了<em>topic</em>,<em>key</em>,<em>value</em>三个参数。</p><p>尽管我们决定了管杀不管埋,但是在消息发送之前,仍然可能发生其他异常让我们“杀”不成,比如序列化异常,Buffer耗尽异常等,因此仍然进行了try-catch处理</p><h3 id="同步发送"><a href="#同步发送" class="headerlink" title="同步发送"></a>同步发送</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">send</span><span class="params">()</span></span>{</div><div class="line"> Producer<String, String> producer = newProducer();</div><div class="line"> ProducerRecord<String, String> record = <span class="keyword">new</span> ProducerRecord<String, String>(<span class="string">"topic"</span>, <span class="string">"this is key"</span>,</div><div class="line"> <span class="string">"I am value"</span>);</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> producer.send(record).get();</div><div class="line"> }<span class="keyword">catch</span> (Exception e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><p>代码和前一种方式很像,只是调用了<em>get()</em>方法来取回写入的结果。如果写入成功,我们就会得到一个RecordMetadata对象,携带了包括 <em>offset</em>在内的一系列元数据信息。如果写入失败,Kafka会视错误的种类决定是否重试。比如,当发生连接超时错误时,重新发送就可能奏效,相反,当错误信息为消息长度太大时,重试是没有意义的。</p><h3 id="异步发送"><a href="#异步发送" class="headerlink" title="异步发送"></a>异步发送</h3><p>同步发送比管杀不管埋更加可靠,但是当我们对系统延迟要求比较高,或消息数量太多时,同步发送的等待时间就会影响系统的可用性。所以,我们再进一步,来看看异步发送。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">send</span><span class="params">()</span></span>{</div><div class="line"> Producer<String, String> producer = newProducer();</div><div class="line"> ProducerRecord<String, String> record = <span class="keyword">new</span> ProducerRecord<String, String>(<span class="string">"topic"</span>, <span class="string">"this is key"</span>,<span class="string">"I am value"</span>);</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> producer.send(record, <span class="keyword">new</span> Callback() {</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCompletion</span><span class="params">(RecordMetadata recordMetadata, Exception e)</span> </span>{</div><div class="line"> <span class="keyword">if</span> (e!=<span class="keyword">null</span>){</div><div class="line"> e.printStackTrace();</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"> System.out.print(recordMetadata.offset());</div><div class="line"> }</div><div class="line"> });</div><div class="line"> }<span class="keyword">catch</span> (Exception e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><p>在异步发送方式中,我们传入了一个<em>org.apache.kafka.clients.producer.Callback</em>实例,作为send()方法返回后的回调,在这个回调方法中,我们可以对成功后的RecordMetadata和失败后的异常分别进行处理。</p><h2 id="生产者配置参数"><a href="#生产者配置参数" class="headerlink" title="生产者配置参数"></a>生产者配置参数</h2><p>除了上面提到的构造所必须的三个参数外,生产者还有其他一系列配置参数,具体请查阅<a href="https://kafka.apache.org/documentation/#producerconfigs" target="_blank" rel="external">官方文档</a>。这里列出了一些重要的配置项</p><ul><li><p><em>acks</em></p><p> 决定多少个partition replias写入后,一次写入被生产者认为写入成功,有三种取值,默认为1。</p><ul><li>0 生产者发送消息即认为写入成功,不会等待broker的返回。显然,这种情况下,系统的吞吐量最大,但可靠性最差。</li><li>1 生产者在消息成功写入leader replica并收到来自broker的消息后,认为本次写入成功。如果leader写入失败,生产者会收到失败信息,并进行重试。但是,当leader挂掉,并且一个还未被写入本条消息的partition被选为leader时,这条消息仍会丢失。在这种配置下,系统的吞吐将取决于我们的发送方式。当我们同步发送时,相对前一种,延迟会大大增加,如果使用异步发送方式时,延迟则上升不明显,系统的吞吐量则受到正在发送中的消息数量的限制。</li><li>all/-1 两种值等效。生产者需要等待消息成功写入所有partition replicas后才认为此次写入成功。显然,这种情况下,系统可靠性得到最大保障,但速度最慢。</li></ul></li><li><p><em>buffer.memory</em></p></li><li><p><em>compression.type</em></p><p> 压缩方式,默认为不压缩。可以被设置为<em>“snappy”</em>,<em>“gzip”</em>,<em>lz4</em>三种方式。其中<em>snappy</em>来自Google,占用资源最低,<em>gzip</em>占用资源较高,但是压缩比率最好。设置压缩可以降低系统在网络传输和存储方面的限制,而这二者通常是kafka系统的瓶颈所在。</p></li><li><p><em>retries</em></p><p> 生产者失败后的重试次数。这个参数和<em>retry.backoff.ms</em>共同发挥作用。默认情况下,后者为100ms,即生产者会每隔100ms重试一次。推荐将重试间隔时间比partition选举leader的时间稍长。</p></li><li><p><em>batch.size</em> && <em>linger.ms</em></p><p> 生产者会将发送到同一partition的消息一次批量发送过去。这两个参数分别决定了一批次的最大大小(bytes)和最长等待时间。两个条件达到任一,就会触发一次发送。默认情况下,linger.ms为0。</p></li></ul><h2 id="序列化"><a href="#序列化" class="headerlink" title=" 序列化"></a><a name="ser"> 序列化</a></h2><p>在前面的例子中,我们提到了发送消息时需要对消息进行序列化,但是kafka客户端内部只提供了三种基本实现,如果我们要传输其他对象,就需要自己实现序列化,或使用其他序列化库。由于自己实现序列化有一系列不便之处,所以我们推荐使用第三方的序列化方式,比如JSON,Protobuf,Apache Avro,Thrift等。这里重点讨论Avro。</p>]]></content>
<summary type="html">
<h2 id="概要"><a href="#概要" class="headerlink" title="概要"></a>概要</h2><p>在不同的生产场景下,我们对生产者的要求是不同的。在使用生产者写消息之前,首先要明确我们能否接受数据丢失?能否接受数据重复发送?系统延迟和数据准确性哪个更重要?对于不同的要求,我们要对Kafka进行不同的配置。</p>
<p>首先来看生产者发送消息的过程<br>
</summary>
<category term="kafka" scheme="http://yoursite.com/tags/kafka/"/>
</entry>
<entry>
<title>线程池</title>
<link href="http://yoursite.com/2017/11/12/%E7%BA%BF%E7%A8%8B%E6%B1%A0/"/>
<id>http://yoursite.com/2017/11/12/线程池/</id>
<published>2017-11-11T16:39:52.000Z</published>
<updated>2017-11-11T16:50:17.659Z</updated>
<content type="html"><![CDATA[<h1 id="线程池"><a href="#线程池" class="headerlink" title="线程池"></a>线程池</h1><p>线程池,是指管理一组同构工作线程的资源池。线程池与工作队列密切相关,在工作队列中保存了需要执行的任务。工作线程在工作队列中取出一个任务,执行任务,然后返回线程池,等待下一个任务。</p><h3 id="线程池的优势"><a href="#线程池的优势" class="headerlink" title="线程池的优势"></a>线程池的优势</h3><ul><li>通过重用池中已有的线程,而不是创建新的线程,可以在处理多个请求时分摊线程创建、销毁的巨大开销。</li><li>当请求到达时,工作线程通常已经存在了,因此不会由于等待创建线程而延迟任务的执行。</li><li>通过调整线程池大小,可以创建足够多的线程是处理器保持忙碌状态,提高处理器的利用率,同时可以防止过多线程相互竞争资源而使应用程序耗尽内存。</li></ul><h3 id="创建线程池"><a href="#创建线程池" class="headerlink" title="创建线程池"></a>创建线程池</h3><p>我们可以通过调用Executors类中的静态方法创建线程池:</p><ul><li>newFixedThreadPool。newFixedThreadPool将创建一个固定长度的线程池,每当提交一个任务时就创建一个新线程,直到线程池满。<blockquote><p>如果池中某线程因为异常而终止,那么线程池将会补充一个新线程。</p></blockquote></li><li>newCachedThreadPool。newCachedThreadPool将会创建一个可缓存的线程池。如果线程池的规模超过了工作任务数量时,那么将回收多余的线程,而当需求增加时,则可以新建线程,线程池的规模不受限制。</li><li>newSingleThreadExecutor。newSingleThreadExecutor是一个单线程的线程池,它创建单个线程来执行任务,如果线程因为异常而结束,则会创建一个新的线程来取代它。它能够确保任务按照工作队列中的顺序串行执行。<blockquote><p>单线程的Executor还提供了内部同步机制,即使线程会因为异常等原因被其他线程所取代,但对象是安全地封闭在工作线程内的。</p></blockquote></li><li>newScheduledThreadPool。newScheduledThreadPool创建了定长的线程池,它的功能类似Timer,用于定时的执行任务。</li></ul><h3 id="线程池的生命周期"><a href="#线程池的生命周期" class="headerlink" title="线程池的生命周期"></a>线程池的生命周期</h3><p>ExecutorService生命周期有三种状态:运行、关闭和终止。<br>当ExecutorService新建时处于运行状态。<br>为了解决线程池的生命周期问题,Java提供了ExecutorService接口,其中关于生命周期的方法如下。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ExecutorService</span> <span class="keyword">extends</span> <span class="title">Executor</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">void</span> <span class="title">shutdown</span><span class="params">()</span></span>;</div><div class="line"> <span class="function">List<Runnable> <span class="title">shutdownNow</span><span class="params">()</span></span>;</div><div class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">isShutdown</span><span class="params">()</span></span>;</div><div class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">isTerminated</span><span class="params">()</span></span>;</div><div class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">awaitTermination</span><span class="params">(<span class="keyword">long</span> timeout, TimeUnit unit)</span></span></div><div class="line"> <span class="keyword">throws</span> InterruptedException;</div><div class="line"> <span class="comment">//......其他用于任务提交的简便方法</span></div><div class="line">}</div></pre></td></tr></table></figure></p><ul><li>shutdown()方法将会平缓地关闭线程池</li><li>shutdownNow()方法将会粗暴地进行关闭–尝试取消所有正在运行的任务,并不再启动工作队列中待执行的工作。<blockquote><p>当线程池关闭后,再尝试进行提交的任务将会由“拒绝执行处理器(<strong>Rejected Execution Handler</strong>)”来处理,它会抛弃任务,或使executor()方法抛出RejectedExecutionExection。在所有任务都完成后,ExecutorService将进入终止状态。</p></blockquote></li><li>isShutdown()方法返回是否处于关闭状态</li><li>isTerminated()返回是否处于终止状态。但是,除非已经调用过shutdown()或shutdownNow()方法,否则一定返回false。当线程池shutdown后,所有任务都完成时,返回true。</li><li>awaitTermination()方法。这个方法会一直阻塞,直到以下事件之一发生:<ul><li>在shutdown请求后所有任务完成</li><li>到达超时时间</li><li>当前线程中断</li></ul></li></ul>]]></content>
<summary type="html">
<h1 id="线程池"><a href="#线程池" class="headerlink" title="线程池"></a>线程池</h1><p>线程池,是指管理一组同构工作线程的资源池。线程池与工作队列密切相关,在工作队列中保存了需要执行的任务。工作线程在工作队列中取出一个任
</summary>
</entry>
<entry>
<title>第一篇</title>
<link href="http://yoursite.com/2017/11/12/%E7%AC%AC%E4%B8%80%E7%AF%87/"/>
<id>http://yoursite.com/2017/11/12/第一篇/</id>
<published>2017-11-11T16:00:46.000Z</published>
<updated>2017-11-11T16:12:35.722Z</updated>
<content type="html"><![CDATA[<h2 id="第一篇"><a href="#第一篇" class="headerlink" title="第一篇"></a>第一篇</h2><p>一直想自己写点东西,折腾了好久一直没有付诸实践,这次希望可以坚持下去。主要关于java、python开发,偶尔写点读后感。恩,就是这样。</p>]]></content>
<summary type="html">
<h2 id="第一篇"><a href="#第一篇" class="headerlink" title="第一篇"></a>第一篇</h2><p>一直想自己写点东西,折腾了好久一直没有付诸实践,这次希望可以坚持下去。主要关于java、python开发,偶尔写点读后感。恩,就是这
</summary>
</entry>
</feed>