首页 文章 精选 留言 我的

精选列表

搜索[教程],共9166篇文章
优秀的个人博客,低调大师

logstash的使用教程

一、简单使用 cd logstash_HOME bin/logstash -e 'input { stdin { } } output { stdout {} }' 启动 Logstash 后,再键入 Hello hiekay,结果如下: image.png 在生产环境中,Logstash 的管道要复杂很多,可能需要配置多个输入、过滤器和输出插件。 因此,需要一个配置文件管理输入、过滤器和输出相关的配置。配置文件内容格式如下: # 输入 input { ... } # 过滤器 filter { ... } # 输出 output { ... } 二、配置使用 插件用法 在使用插件之前,我们先了解一个概念:事件。 Logstash 每读取一次数据的行为叫做事件。 在 Logstach_HOME 目录中创建一个配置文件,名为 logstash.conf(名字任意)。 1、 输入插件 输入插件允许一个特定的事件源可以读取到 Logstash 管道中,配置在 input {} 中,且可以设置多个。 修改配置文件: input { # 从文件读取日志信息 file { path => "/var/log/syslog" type => "system" start_position => "beginning" } } # filter { # # } output { # 标准输出 stdout { codec => rubydebug } } 其中,syslog 为系统日志。保存文件。 运行 bin/logstash -f logstash.conf 在控制台结果如下: image.png 2、 输出插件 输出插件将事件数据发送到特定的目的地,配置在 output {} 中,且可以设置多个。 修改配置文件: input { # 从文件读取日志信息 file { path => "/var/log/syslog" type => "error" start_position => "beginning" } } # filter { # # } output { # 输出到 elasticsearch elasticsearch { hosts => ["127.0.0.1:9201"] index => "syslog-%{+YYYY.MM.dd}" } } 配置文件中使用 elasticsearch 输出插件。输出的日志信息将被保存到 Elasticsearch 中,索引名称为 index 参数设置的格式。保存文件。 运行 bin/logstash -f logstash.conf 打开浏览器访问 http://127.0.0.1:9100 使用 head 插件查看 Elasticsearch 数据,结果如下图: image.png 3、 编码解码插件 编码解码插件本质是一种流过滤器,配合输入插件或输出插件使用。 从上图中,我们发现一个问题:Java 异常日志被拆分成单行事件记录到 Elasticsearch 中,这不符合开发者或运维人员的查看习惯。因此,我们需要对日志信息进行编码将多行事件转成单行事件记录起来。 我们需要配置 Multiline codec 插件,这个插件可以将多行日志信息合并成一行,作为一个事件处理。 Logstash 默认没有安装该插件,需要开发者自行安装。 bin/logstash-plugin install logstash-codec-multiline 修改配置文件: input { # 从文件读取日志信息 file { path => "/var/log/syslog" type => "error" start_position => "beginning" # 使用 multiline 插件 codec => multiline { # 通过正则表达式匹配,具体配置根据自身实际情况而定 pattern => "^\d" negate => true what => "previous" } } } # filter { # # } output { # 输出到 elasticsearch elasticsearch { hosts => ["127.0.0.1:9201"] index => "syslog-%{+YYYY.MM.dd}" } } 保存文件。 运行 bin/logstash -f logstash.conf 使用 head 插件查看 Elasticsearch 数据,结果如下图: image.png 4 过滤器插件 过滤器插件位于 Logstash 管道的中间位置,对事件执行过滤处理,配置在 filter {},且可以配置多个。 本次测试使用 grok 插件演示,grok 插件用于过滤杂乱的内容,将其结构化,增加可读性。 安装: bin/logstash-plugin install logstash-filter-grok 修改配置文件: input { stdin {} } filter { grok { match => { "message" => "%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER :duration}" } } } output { stdout { codec => "rubydebug" } } 保存文件。 运行 bin/logstash -f logstash.conf 启动成功后,我们输入: 127.0.0.1 GET /index.html 16688 0.066 控制台返回: image.png 输入的内容被匹配到相应的名字中。

优秀的个人博客,低调大师

Web Worker 使用教程

一、概述 JavaScript 语言采用的是单线程模型,也就是说,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着。随着电脑计算能力的增强,尤其是多核 CPU 的出现,单线程带来很大的不便,无法充分发挥计算机的计算能力。 Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。 Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。 Web Worker 有以下几个使用注意点。 (1)同源限制 分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。 (2)DOM 限制 Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。 (3)通信联系 Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。 (4)脚本限制 Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。 (5)文件限制 Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。 二、基本用法 2.1 主线程 主线程采用new命令,调用Worker()构造函数,新建一个 Worker 线程。 var worker = new Worker('work.js'); Worker()构造函数的参数是一个脚本文件,该文件就是 Worker 线程所要执行的任务。由于 Worker 不能读取本地文件,所以这个脚本必须来自网络。如果下载没有成功(比如404错误),Worker 就会默默地失败。 然后,主线程调用worker.postMessage()方法,向 Worker 发消息。 worker.postMessage('Hello World'); worker.postMessage({method: 'echo', args: ['Work']}); worker.postMessage()方法的参数,就是主线程传给 Worker 的数据。它可以是各种数据类型,包括二进制数据。 接着,主线程通过worker.onmessage指定监听函数,接收子线程发回来的消息。 worker.onmessage = function (event) { console.log('Received message ' + event.data); doSomething(); } function doSomething() { // 执行任务 worker.postMessage('Work done!'); } 上面代码中,事件对象的data属性可以获取 Worker 发来的数据。 Worker 完成任务以后,主线程就可以把它关掉。 worker.terminate(); 2.2 Worker 线程 Worker 线程内部需要有一个监听函数,监听message事件。 self.addEventListener('message', function (e) { self.postMessage('You said: ' + e.data); }, false); 上面代码中,self代表子线程自身,即子线程的全局对象。因此,等同于下面两种写法。 // 写法一 this.addEventListener('message', function (e) { this.postMessage('You said: ' + e.data); }, false); // 写法二 addEventListener('message', function (e) { postMessage('You said: ' + e.data); }, false); 除了使用self.addEventListener()指定监听函数,也可以使用self.onmessage指定。监听函数的参数是一个事件对象,它的data属性包含主线程发来的数据。self.postMessage()方法用来向主线程发送消息。 根据主线程发来的数据,Worker 线程可以调用不同的方法,下面是一个例子。 self.addEventListener('message', function (e) { var data = e.data; switch (data.cmd) { case 'start': self.postMessage('WORKER STARTED: ' + data.msg); break; case 'stop': self.postMessage('WORKER STOPPED: ' + data.msg); self.close(); // Terminates the worker. break; default: self.postMessage('Unknown command: ' + data.msg); }; }, false); 上面代码中,self.close()用于在 Worker 内部关闭自身。 2.3 Worker 加载脚本 Worker 内部如果要加载其他脚本,有一个专门的方法importScripts()。 importScripts('script1.js'); 该方法可以同时加载多个脚本。 importScripts('script1.js', 'script2.js'); 2.4 错误处理 主线程可以监听 Worker 是否发生错误。如果发生错误,Worker 会触发主线程的error事件。 worker.onerror(function (event) { console.log([ 'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message ].join('')); }); // 或者 worker.addEventListener('error', function (event) { // ... }); 2.5 关闭 Worker 使用完毕,为了节省系统资源,必须关闭 Worker。 // 主线程 worker.terminate(); // Worker 线程 self.close(); 三、数据通信 前面说过,主线程与 Worker 之间的通信内容,可以是文本,也可以是对象。需要注意的是,这种通信是拷贝关系,即是传值而不是传址,Worker 对通信内容的修改,不会影响到主线程。事实上,浏览器内部的运行机制是,先将通信内容串行化,然后把串行化后的字符串发给 Worker,后者再将它还原。 主线程与 Worker 之间也可以交换二进制数据,比如 File、Blob、ArrayBuffer 等类型,也可以在线程之间发送。下面是一个例子。 // 主线程 var uInt8Array = new Uint8Array(new ArrayBuffer(10)); for (var i = 0; i < uInt8Array.length; ++i) { uInt8Array[i] = i * 2; // [0, 2, 4, 6, 8,...] } worker.postMessage(uInt8Array); // Worker 线程 self.onmessage = function (e) { var uInt8Array = e.data; postMessage('Inside worker.js: uInt8Array.toString() = ' + uInt8Array.toString()); postMessage('Inside worker.js: uInt8Array.byteLength = ' + uInt8Array.byteLength); }; 但是,拷贝方式发送二进制数据,会造成性能问题。比如,主线程向 Worker 发送一个 500MB 文件,默认情况下浏览器会生成一个原文件的拷贝。为了解决这个问题,JavaScript 允许主线程把二进制数据直接转移给子线程,但是一旦转移,主线程就无法再使用这些二进制数据了,这是为了防止出现多个线程同时修改数据的麻烦局面。这种转移数据的方法,叫做Transferable Objects。这使得主线程可以快速把数据交给 Worker,对于影像处理、声音处理、3D 运算等就非常方便了,不会产生性能负担。 如果要直接转移数据的控制权,就要使用下面的写法。 // Transferable Objects 格式 worker.postMessage(arrayBuffer, [arrayBuffer]); // 例子 var ab = new ArrayBuffer(1); worker.postMessage(ab, [ab]); 四、同页面的 Web Worker 通常情况下,Worker 载入的是一个单独的 JavaScript 脚本文件,但是也可以载入与主线程在同一个网页的代码。 <!DOCTYPE html> <body> <script id="worker" type="app/worker"> addEventListener('message', function () { postMessage('some message'); }, false); </script> </body> </html> 上面是一段嵌入网页的脚本,注意必须指定 然后,读取这一段嵌入页面的脚本,用 Worker 来处理。 var blob = new Blob([document.querySelector('#worker').textContent]); var url = window.URL.createObjectURL(blob); var worker = new Worker(url); worker.onmessage = function (e) { // e.data === 'some message' }; 上面代码中,先将嵌入网页的脚本代码,转成一个二进制对象,然后为这个二进制对象生成 URL,再让 Worker 加载这个 URL。这样就做到了,主线程和 Worker 的代码都在同一个网页上面。 五、实例:Worker 线程完成轮询 有时,浏览器需要轮询服务器状态,以便第一时间得知状态改变。这个工作可以放在 Worker 里面。 function createWorker(f) { var blob = new Blob([f.toString()]); var url = window.URL.createObjectURL(blob); var worker = new Worker(url); return worker; } var pollingWorker = createWorker(function (e) { var cache; function compare(new, old) { ... }; setInterval(function () { fetch('/my-api-endpoint').then(function (res) { var data = res.json(); if (!compare(data, cache)) { cache = data; self.postMessage(data); } }) }, 1000) }); pollingWorker.onmessage = function () { // render data } pollingWorker.postMessage('init'); 上面代码中,Worker 每秒钟轮询一次数据,然后跟缓存做比较。如果不一致,就说明服务端有了新的变化,因此就要通知主线程。 六、实例: Worker 新建 Worker Worker 线程内部还能再新建 Worker 线程。下面的例子是将一个计算密集的任务,分配到10个 Worker。 主线程代码如下。 var worker = new Worker('worker.js'); worker.onmessage = function (event) { document.getElementById('result').textContent = event.data; }; Worker 线程代码如下。 // worker.js // settings var num_workers = 10; var items_per_worker = 1000000; // start the workers var result = 0; var pending_workers = num_workers; for (var i = 0; i < num_workers; i += 1) { var worker = new Worker('core.js'); worker.postMessage(i * items_per_worker); worker.postMessage((i + 1) * items_per_worker); worker.onmessage = storeResult; } // handle the results function storeResult(event) { result += event.data; pending_workers -= 1; if (pending_workers <= 0) postMessage(result); // finished! } 上面代码中,Worker 线程内部新建了10个 Worker 线程,并且依次向这10个 Worker 发送消息,告知了计算的起点和终点。计算任务脚本的代码如下。 // core.js var start; onmessage = getStart; function getStart(event) { start = event.data; onmessage = getEnd; } var end; function getEnd(event) { end = event.data; onmessage = null; work(); } function work() { var result = 0; for (var i = start; i < end; i += 1) { // perform some complex calculation here result += 1; } postMessage(result); close(); } 七、API 7.1 主线程 浏览器原生提供Worker()构造函数,用来供主线程生成 Worker 线程。 var myWorker = new Worker(jsUrl, options); Worker()构造函数,可以接受两个参数。第一个参数是脚本的网址(必须遵守同源政策),该参数是必需的,且只能加载 JS 脚本,否则会报错。第二个参数是配置对象,该对象可选。它的一个作用就是指定 Worker 的名称,用来区分多个 Worker 线程。 // 主线程 var myWorker = new Worker('worker.js', { name : 'myWorker' }); // Worker 线程 self.name // myWorker Worker()构造函数返回一个 Worker 线程对象,用来供主线程操作 Worker。Worker 线程对象的属性和方法如下。 Worker.onerror:指定 error 事件的监听函数。 Worker.onmessage:指定 message 事件的监听函数,发送过来的数据在Event.data属性中。 Worker.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。 Worker.postMessage():向 Worker 线程发送消息。 Worker.terminate():立即终止 Worker 线程。 7.2 Worker 线程 Web Worker 有自己的全局对象,不是主线程的window,而是一个专门为 Worker 定制的全局对象。因此定义在window上面的对象和方法不是全部都可以使用。 Worker 线程有一些自己的全局属性和方法。 self.name: Worker 的名字。该属性只读,由构造函数指定。 self.onmessage:指定message事件的监听函数。 self.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。 self.close():关闭 Worker 线程。 self.postMessage():向产生这个 Worker 线程发送消息。 self.importScripts():加载 JS 脚本。 (完) 原文发布时间为:2018-07-31本文作者:阮一峰本文来自云栖社区合作伙伴“数据与算法之美”,了解相关信息可以关注“ 数据与算法之美”。

优秀的个人博客,低调大师

Thrift简明教程

Thrift是个RPC框架,它的目标是为各种流行的开发语言提供便利的RPC调用机制,而不需要使用那些开销巨大的方式,比如SOAP。 Thrift使用语言中立的服务定义文件(IDL,接口描述语言),描述数据类型和服务接口。这个文件会被用作引擎的输入,编译器生成代码为每种支持的语言生成RPC客户端代码库。这种静态生成的设计让它非常容易被开发者所使用,而且因为类型验证都发生在编译期而非运行期,所以代码可以很有效率地运行。 Apache Thrift,它采用接口描述语言(IDL)定义并创建服务,支持可扩展的跨语言服务开发,所包含的代码生成引擎可以在多种语言中,如 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk 等创建高效的、无缝的服务,其传输数据采用二进制格式,相对 XML 和 JSON 体积更小,对于高并发、大数据量和多语言的环境更有优势。 Thrift 服务开发流程,简单概述如下: 1、定义IDL文件(xxx.thrift文件) 2、用xxx.thrift文件生成Java代码(服务接口文件) 3、服务端实现(创建服务):实现服务接口,开启服务器 4、客户端实现(服务消费):引入接口,进行远程调用。 Thrift 环境 总体感觉,安装thrift环境,不算简单,而且很多人不清楚安装的那些,到底能干啥? Mac 安装thrift可参考:https://blog.csdn.net/u010827436/article/details/44303897 目前感觉,安装thrift的目的,就是能够使用thrift 命令,将xxx.thrift文件生成Java代码(仅个人简单的理解);而生成Java代码有多种方式,安装thrift对初学者来说,占据学习的一部分事件,不推荐。 下面快速开始一个简单的Demo,供初学者对thrift有一个直观的了解。 1、定义一个IDL接口描述文件 namespace java com.ljheee.thrift service QueryService{ string query(1:string query) } 这个IDL接口描述文件,采用平台中立、语言中立的方式,描述数据类型和服务接口;即RPC调用过程中,调用方与服务提供方之间通信,传输的什么数据结构、调用的哪个服务接口,是需要在这个.thrift文件中约定清楚的。 具体文件含义参考: 2、用xxx.thrift文件生成Java代码(服务接口文件) 如果已经安装了thrift环境,那么可以直接使用命令 thrift -gen java demoHello.thrift 生成Java代码的命令,win、Mac下都相同。命令执行后,会生成一个gen-java文件夹。 如果还没有安装thrift,不要紧,我这里提供一个下载,先快速上手,预览全观。(复制里面全部内容,新建文件到对QueryService.java)https://github.com/ljheee/thrift-demo/blob/master/src/main/java/com/ljheee/thrift/service/QueryService.java 其实由第1步的QueryService.thrift文件,大概可以看出,定义了一个Java service,里面有一个方法query(String query)。可以预测,它大概是要定义一个接口: public interface QueryService { public String query(String query) ; } 事实上,就是这样。该接口就是服务提供方和服务调用方之间的约定。服务端通过完成接口的具体实现,对外提供服务,客户端引入该Java接口,进行远程调用。当然这只是简单描述,thrift生成的QueryService接口里面也有很多它特定的东西,如传输协议、序列化方式等。 3、服务端实现(创建服务) 实现服务接口,开启服务器。 把第2步生成的Java文件(QueryService.java),copy到服务器端工程,注意package不要错,然后新建类implements QueryService.Iface package com.ljheee.thrift.service.impl; import com.ljheee.thrift.service.QueryService; import org.apache.thrift.TException; public class QueryServiceImpl implements QueryService.Iface { @Override public String query(String query) throws TException { return "Hello,"+query; } } 开启服务器,等待客户端远程调用 package com.ljheee.thrift; import com.ljheee.thrift.service.QueryService; import com.ljheee.thrift.service.impl.QueryServiceImpl; import org.apache.thrift.TProcessor; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TSimpleServer; import org.apache.thrift.transport.TServerSocket; public class Server { public static final int SERVER_PORT = 9999; public static final String SERVER_IP = "localhost"; public static final int TIMEOUT = 30000; public static void main(String[] args) { try { System.out.println("QueryService TSimpleServer start ...."); TProcessor tprocessor = new QueryService.Processor<QueryService.Iface>(new QueryServiceImpl()); // 简单的单线程服务模型,一般用于测试 TServerSocket serverTransport = new TServerSocket(SERVER_PORT); TServer.Args tArgs = new TServer.Args(serverTransport); tArgs.processor(tprocessor); tArgs.protocolFactory(new TBinaryProtocol.Factory()); // tArgs.protocolFactory(new TCompactProtocol.Factory()); // tArgs.protocolFactory(new TJSONProtocol.Factory()); TServer server = new TSimpleServer(tArgs); server.serve(); } catch (Exception e) { System.out.println("Server start error!!!"); e.printStackTrace(); } } } 4、客户端实现(服务消费) 引入生成接口,进行远程调用。 同样把第2步生成的Java文件(QueryService.java),copy到客户端工程里面。本系统为演示方便,在同一个工程中放入了server 和Client的类。 package com.ljheee.thrift; import com.ljheee.thrift.service.QueryService; import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; /** * Created by lijianhua04 on 2018/7/27. */ public class Client { public static final int SERVER_PORT = 9999; public static final String SERVER_IP = "localhost"; public static final int TIMEOUT = 30000; public static void main(String[] args) { TTransport transport = null; try { transport = new TSocket(SERVER_IP, SERVER_PORT, TIMEOUT); // 协议要和服务端一致 TProtocol protocol = new TBinaryProtocol(transport); // TProtocol protocol = new TCompactProtocol(transport); // TProtocol protocol = new TJSONProtocol(transport); QueryService.Client client = new QueryService.Client(protocol); transport.open(); String result = client.query("ljh"); System.out.println("Thrify client result =: " + result); } catch (TTransportException e) { e.printStackTrace(); } catch (TException e) { e.printStackTrace(); } finally { if (null != transport) { transport.close(); } } } } 工程结构目录 image.png https://github.com/ljheee/thrift-demo

优秀的个人博客,低调大师

Kafka入门经典教程

一、基本概念 介绍 Kafka是一个分布式的、可分区的、可复制的消息系统。它提供了普通消息系统的功能,但具有自己独特的设计。 这个独特的设计是什么样的呢? 首先让我们看几个基本的消息系统术语: Kafka将消息以topic为单位进行归纳。 将向Kafka topic发布消息的程序成为producers. 将预订topics并消费消息的程序成为consumer. Kafka以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个broker. producers通过网络将消息发送到Kafka集群,集群向消费者提供消息,如下图所示: 客户端和服务端通过TCP协议通信。Kafka提供了Java客户端,并且对多种语言都提供了支持。 Topics 和Logs 先来看一下Kafka提供的一个抽象概念:topic. 一个topic是对一组消息的归纳。对每个topic,Kafka 对它的日志进行了分区,如下图所示: 每个分区都由一系列有序的、不可变的消息组成,这些消息被连续的追加到分区中。分区中的每个消息都有一个连续的序列号叫做offset,用来在分区中唯一的标识这个消息。 在一个可配置的时间段内,Kafka集群保留所有发布的消息,不管这些消息有没有被消费。比如,如果消息的保存策略被设置为2天,那么在一个消息被发布的两天时间内,它都是可以被消费的。之后它将被丢弃以释放空间。Kafka的性能是和数据量无关的常量级的,所以保留太多的数据并不是问题。 实际上每个consumer唯一需要维护的数据是消息在日志中的位置,也就是offset.这个offset有consumer来维护:一般情况下随着consumer不断的读取消息,这offset的值不断增加,但其实consumer可以以任意的顺序读取消息,比如它可以将offset设置成为一个旧的值来重读之前的消息。 以上特点的结合,使Kafka consumers非常的轻量级:它们可以在不对集群和其他consumer造成影响的情况下读取消息。你可以使用命令行来"tail"消息而不会对其他正在消费消息的consumer造成影响。 将日志分区可以达到以下目的:首先这使得每个日志的数量不会太大,可以在单个服务上保存。另外每个分区可以单独发布和消费,为并发操作topic提供了一种可能。 分布式 每个分区在Kafka集群的若干服务中都有副本,这样这些持有副本的服务可以共同处理数据和请求,副本数量是可以配置的。副本使Kafka具备了容错能力。 每个分区都由一个服务器作为“leader”,零或若干服务器作为“followers”,leader负责处理消息的读和写,followers则去复制leader.如果leader down了,followers中的一台则会自动成为leader。集群中的每个服务都会同时扮演两个角色:作为它所持有的一部分分区的leader,同时作为其他分区的followers,这样集群就会据有较好的负载均衡。 Producers Producer将消息发布到它指定的topic中,并负责决定发布到哪个分区。通常简单的由负载均衡机制随机选择分区,但也可以通过特定的分区函数选择分区。使用的更多的是第二种。 Consumers 发布消息通常有两种模式:队列模式(queuing)和发布-订阅模式(publish-subscribe)。队列模式中,consumers可以同时从服务端读取消息,每个消息只被其中一个consumer读到;发布-订阅模式中消息被广播到所有的consumer中。Consumers可以加入一个consumer 组,共同竞争一个topic,topic中的消息将被分发到组中的一个成员中。同一组中的consumer可以在不同的程序中,也可以在不同的机器上。如果所有的consumer都在一个组中,这就成为了传统的队列模式,在各consumer中实现负载均衡。如果所有的consumer都不在不同的组中,这就成为了发布-订阅模式,所有的消息都被分发到所有的consumer中。更常见的是,每个topic都有若干数量的consumer组,每个组都是一个逻辑上的“订阅者”,为了容错和更好的稳定性,每个组由若干consumer组成。这其实就是一个发布-订阅模式,只不过订阅者是个组而不是单个consumer。 由两个机器组成的集群拥有4个分区 (P0-P3) 2个consumer组. A组有两个consumerB组有4个 相比传统的消息系统,Kafka可以很好的保证有序性。 传统的队列在服务器上保存有序的消息,如果多个consumers同时从这个服务器消费消息,服务器就会以消息存储的顺序向consumer分发消息。虽然服务器按顺序发布消息,但是消息是被异步的分发到各consumer上,所以当消息到达时可能已经失去了原来的顺序,这意味着并发消费将导致顺序错乱。为了避免故障,这样的消息系统通常使用“专用consumer”的概念,其实就是只允许一个消费者消费消息,当然这就意味着失去了并发性。 在这方面Kafka做的更好,通过分区的概念,Kafka可以在多个consumer组并发的情况下提供较好的有序性和负载均衡。将每个分区分只分发给一个consumer组,这样一个分区就只被这个组的一个consumer消费,就可以顺序的消费这个分区的消息。因为有多个分区,依然可以在多个consumer组之间进行负载均衡。注意consumer组的数量不能多于分区的数量,也就是有多少分区就允许多少并发消费。 Kafka只能保证一个分区之内消息的有序性,在不同的分区之间是不可以的,这已经可以满足大部分应用的需求。如果需要topic中所有消息的有序性,那就只能让这个topic只有一个分区,当然也就只有一个consumer组消费它。 二、环境搭建 Step 1: 下载Kafka 点击下载最新的版本并解压. tar -xzf kafka_2.9.2-0.8.1.1.tgz cd kafka_2.9.2-0.8.1.1 复制代码 Step 2: 启动服务 Kafka用到了Zookeeper,所有首先启动Zookper,下面简单的启用一个单实例的Zookkeeper服务。可以在命令的结尾加个&符号,这样就可以启动后离开控制台。 bin/zookeeper-server-start.sh config/zookeeper.properties & [2013-04-22 15:01:37,495] INFO Reading configuration from: config/zookeeper.properties (org.apache.zookeeper.server.quorum.QuorumPeerConfig) ... 复制代码 现在启动Kafka: bin/kafka-server-start.sh config/server.properties [2013-04-22 15:01:47,028] INFO Verifying properties (kafka.utils.VerifiableProperties) [2013-04-22 15:01:47,051] INFO Property socket.send.buffer.bytes is overridden to 1048576 (kafka.utils.VerifiableProperties) ... 复制代码 Step 3: 创建 topic 创建一个叫做“test”的topic,它只有一个分区,一个副本。 bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test 复制代码 可以通过list命令查看创建的topic: bin/kafka-topics.sh --list --zookeeper localhost:2181 test 复制代码 除了手动创建topic,还可以配置broker让它自动创建topic. Step 4:发送消息. Kafka 使用一个简单的命令行producer,从文件中或者从标准输入中读取消息并发送到服务端。默认的每条命令将发送一条消息。 运行producer并在控制台中输一些消息,这些消息将被发送到服务端: bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test This is a messageThis is another message 复制代码 ctrl+c可以退出发送。 Step 5: 启动consumer Kafka also has a command line consumer that will dump out messages to standard output. Kafka也有一个命令行consumer可以读取消息并输出到标准输出: bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning This is a message This is another message 复制代码 你在一个终端中运行consumer命令行,另一个终端中运行producer命令行,就可以在一个终端输入消息,另一个终端读取消息。 这两个命令都有自己的可选参数,可以在运行的时候不加任何参数可以看到帮助信息。 Step 6: 搭建一个多个broker的集群 刚才只是启动了单个broker,现在启动有3个broker组成的集群,这些broker节点也都是在本机上的: 首先为每个节点编写配置文件: cp config/server.properties config/server-1.properties cp config/server.properties config/server-2.properties 复制代码 在拷贝出的新文件中添加以下参数: config/server-1.properties: broker.id=1 port=9093 log.dir=/tmp/kafka-logs-1 复制代码 config/server-2.properties: broker.id=2 port=9094 log.dir=/tmp/kafka-logs-2 复制代码 broker.id在集群中唯一的标注一个节点,因为在同一个机器上,所以必须制定不同的端口和日志文件,避免数据被覆盖。 We already have Zookeeper and our single node started, so we just need to start the two new nodes: 刚才已经启动可Zookeeper和一个节点,现在启动另外两个节点: bin/kafka-server-start.sh config/server-1.properties & ... bin/kafka-server-start.sh config/server-2.properties & ... 复制代码 创建一个拥有3个副本的topic: bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 1 --topic my-replicated-topic 复制代码 现在我们搭建了一个集群,怎么知道每个节点的信息呢?运行“"describe topics”命令就可以了: bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic 复制代码 Topic:my-replicated-topic PartitionCount:1 ReplicationFactor:3 Configs: Topic: my-replicated-topic Partition: 0 Leader: 1 Replicas: 1,2,0 Isr: 1,2,0 复制代码 下面解释一下这些输出。第一行是对所有分区的一个描述,然后每个分区都会对应一行,因为我们只有一个分区所以下面就只加了一行。 leader:负责处理消息的读和写,leader是从所有节点中随机选择的. replicas:列出了所有的副本节点,不管节点是否在服务中. isr:是正在服务中的节点. 在我们的例子中,节点1是作为leader运行。 向topic发送消息: bin/kafka-console-producer.sh --broker-list localhost:9092 --topic my-replicated-topic 复制代码 ... my test message 1my test message 2^C 复制代码 消费这些消息: bin/kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic my-replicated-topic ... my test message 1 my test message 2 ^C 测试一下容错能力.Broker 1作为leader运行,现在我们kill掉它: ps | grep server-1.properties7564 ttys002 0:15.91 /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/bin/java... kill -9 7564 复制代码 另外一个节点被选做了leader,node 1 不再出现在 in-sync 副本列表中: bin/kafka-topics.sh --describe --zookeeper localhost:218192 --topic my-replicated-topic Topic:my-replicated-topic PartitionCount:1 ReplicationFactor:3 Configs: Topic: my-replicated-topic Partition: 0 Leader: 2 Replicas: 1,2,0 Isr: 2,0 复制代码 虽然最初负责续写消息的leader down掉了,但之前的消息还是可以消费的: bin/kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic my-replicated-topic ... my test message 1 my test message 2 复制代码 看来Kafka的容错机制还是不错的。 三、搭建Kafka开发环境 我们搭建了kafka的服务器,并可以使用Kafka的命令行工具创建topic,发送和接收消息。下面我们来搭建kafka的开发环境。 添加依赖 搭建开发环境需要引入kafka的jar包,一种方式是将Kafka安装包中lib下的jar包加入到项目的classpath中,这种比较简单了。不过我们使用另一种更加流行的方式:使用maven管理jar包依赖。 创建好maven项目后,在pom.xml中添加以下依赖: org.apache.kafka kafka_2.10 0.8.0 复制代码 添加依赖后你会发现有两个jar包的依赖找不到。没关系我都帮你想好了,点击这里下载这两个jar包,解压后你有两种选择,第一种是使用mvn的install命令将jar包安装到本地仓库,另一种是直接将解压后的文件夹拷贝到mvn本地仓库的com文件夹下,比如我的本地仓库是d:mvn,完成后我的目录结构是这样的: 配置程序 首先是一个充当配置文件作用的接口,配置了Kafka的各种连接参数: package com.sohu.kafkademon; public interface KafkaProperties { final static String zkConnect = "10.22.10.139:2181"; final static String groupId = "group1"; final static String topic = "topic1"; final static String kafkaServerURL = "10.22.10.139"; final static int kafkaServerPort = 9092; final static int kafkaProducerBufferSize = 64 * 1024; final static int connectionTimeOut = 20000; final static int reconnectInterval = 10000; final static String topic2 = "topic2"; final static String topic3 = "topic3"; final static String clientId = "SimpleConsumerDemoClient"; } 复制代码 producer package com.sohu.kafkademon; import java.util.Properties; import kafka.producer.KeyedMessage; import kafka.producer.ProducerConfig; /** @author leicui bourne_cui@163.com */ public class KafkaProducer extends Thread { private final kafka.javaapi.producer.Producer producer; private final String topic; private final Properties props = new Properties(); public KafkaProducer(String topic) { props.put("serializer.class", "kafka.serializer.StringEncoder"); props.put("metadata.broker.list", "10.22.10.139:9092"); producer = new kafka.javaapi.producer.Producer(new ProducerConfig(props)); this.topic = topic; } @Override public void run() { int messageNo = 1; while (true) { String messageStr = new String("Message_" + messageNo); System.out.println("Send:" + messageStr); producer.send(new KeyedMessage(topic, messageStr)); messageNo++; try { sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } 复制代码 consumer package com.sohu.kafkademon; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import kafka.consumer.ConsumerConfig; import kafka.consumer.ConsumerIterator; import kafka.consumer.KafkaStream; import kafka.javaapi.consumer.ConsumerConnector; /** @author leicui bourne_cui@163.com */ public class KafkaConsumer extends Thread { private final ConsumerConnector consumer; private final String topic; public KafkaConsumer(String topic) { consumer = kafka.consumer.Consumer.createJavaConsumerConnector( createConsumerConfig()); this.topic = topic; } private static ConsumerConfig createConsumerConfig() { Properties props = new Properties(); props.put("zookeeper.connect", KafkaProperties.zkConnect); props.put("group.id", KafkaProperties.groupId); props.put("zookeeper.session.timeout.ms", "40000"); props.put("zookeeper.sync.time.ms", "200"); props.put("auto.commit.interval.ms", "1000"); return new ConsumerConfig(props); } @Override public void run() { Map topicCountMap = new HashMap(); topicCountMap.put(topic, new Integer(1)); Map>> consumerMap = consumer.createMessageStreams(topicCountMap); KafkaStream stream = consumerMap.get(topic).get(0); ConsumerIterator it = stream.iterator(); while (it.hasNext()) { System.out.println("receive:" + new String(it.next().message())); try { sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } } 复制代码 简单的发送接收 运行下面这个程序,就可以进行简单的发送接收消息了: package com.sohu.kafkademon; /** @author leicui bourne_cui@163.com */ public class KafkaConsumerProducerDemo { public static void main(String[] args) { KafkaProducer producerThread = new KafkaProducer(KafkaProperties.topic); producerThread.start(); KafkaConsumer consumerThread = new KafkaConsumer(KafkaProperties.topic); consumerThread.start(); } } 复制代码 高级别的consumer 下面是比较负载的发送接收的程序: package com.sohu.kafkademon; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import kafka.consumer.ConsumerConfig; import kafka.consumer.ConsumerIterator; import kafka.consumer.KafkaStream; import kafka.javaapi.consumer.ConsumerConnector; /** @author leicui bourne_cui@163.com */ public class KafkaConsumer extends Thread { private final ConsumerConnector consumer; private final String topic; public KafkaConsumer(String topic) { consumer = kafka.consumer.Consumer.createJavaConsumerConnector( createConsumerConfig()); this.topic = topic; } private static ConsumerConfig createConsumerConfig() { Properties props = new Properties(); props.put("zookeeper.connect", KafkaProperties.zkConnect); props.put("group.id", KafkaProperties.groupId); props.put("zookeeper.session.timeout.ms", "40000"); props.put("zookeeper.sync.time.ms", "200"); props.put("auto.commit.interval.ms", "1000"); return new ConsumerConfig(props); } @Override public void run() { Map topicCountMap = new HashMap(); topicCountMap.put(topic, new Integer(1)); Map>> consumerMap = consumer.createMessageStreams(topicCountMap); KafkaStream stream = consumerMap.get(topic).get(0); ConsumerIterator it = stream.iterator(); while (it.hasNext()) { System.out.println("receive:" + new String(it.next().message())); try { sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } } 四、数据持久化 不要畏惧文件系统! Kafka大量依赖文件系统去存储和缓存消息。对于硬盘有个传统的观念是硬盘总是很慢,这使很多人怀疑基于文件系统的架构能否提供优异的性能。实际上硬盘的快慢完全取决于使用它的方式。设计良好的硬盘架构可以和内存一样快。 在6块7200转的SATA RAID-5磁盘阵列的线性写速度差不多是600MB/s,但是随即写的速度却是100k/s,差了差不多6000倍。现代的操作系统都对次做了大量的优化,使用了 read-ahead 和 write-behind的技巧,读取的时候成块的预读取数据,写的时候将各种微小琐碎的逻辑写入组织合并成一次较大的物理写入。对此的深入讨论可以查看这里,它们发现线性的访问磁盘,很多时候比随机的内存访问快得多。 为了提高性能,现代操作系统往往使用内存作为磁盘的缓存,现代操作系统乐于把所有空闲内存用作磁盘缓存,虽然这可能在缓存回收和重新分配时牺牲一些性能。所有的磁盘读写操作都会经过这个缓存,这不太可能被绕开除非直接使用I/O。所以虽然每个程序都在自己的线程里只缓存了一份数据,但在操作系统的缓存里还有一份,这等于存了两份数据。 另外再来讨论一下JVM,以下两个事实是众所周知的: •Java对象占用空间是非常大的,差不多是要存储的数据的两倍甚至更高。 •随着堆中数据量的增加,垃圾回收回变的越来越困难。 基于以上分析,如果把数据缓存在内存里,因为需要存储两份,不得不使用两倍的内存空间,Kafka基于JVM,又不得不将空间再次加倍,再加上要避免GC带来的性能影响,在一个32G内存的机器上,不得不使用到28-30G的内存空间。并且当系统重启的时候,又必须要将数据刷到内存中( 10GB 内存差不多要用10分钟),就算使用冷刷新(不是一次性刷进内存,而是在使用数据的时候没有就刷到内存)也会导致最初的时候新能非常慢。但是使用文件系统,即使系统重启了,也不需要刷新数据。使用文件系统也简化了维护数据一致性的逻辑。 所以与传统的将数据缓存在内存中然后刷到硬盘的设计不同,Kafka直接将数据写到了文件系统的日志中。 常量时间的操作效率 在大多数的消息系统中,数据持久化的机制往往是为每个cosumer提供一个B树或者其他的随机读写的数据结构。B树当然是很棒的,但是也带了一些代价:比如B树的复杂度是O(log N),O(log N)通常被认为就是常量复杂度了,但对于硬盘操作来说并非如此。磁盘进行一次搜索需要10ms,每个硬盘在同一时间只能进行一次搜索,这样并发处理就成了问题。虽然存储系统使用缓存进行了大量优化,但是对于树结构的性能的观察结果却表明,它的性能往往随着数据的增长而线性下降,数据增长一倍,速度就会降低一倍。 直观的讲,对于主要用于日志处理的消息系统,数据的持久化可以简单的通过将数据追加到文件中实现,读的时候从文件中读就好了。这样做的好处是读和写都是 O(1) 的,并且读操作不会阻塞写操作和其他操作。这样带来的性能优势是很明显的,因为性能和数据的大小没有关系了。 既然可以使用几乎没有容量限制(相对于内存来说)的硬盘空间建立消息系统,就可以在没有性能损失的情况下提供一些一般消息系统不具备的特性。比如,一般的消息系统都是在消息被消费后立即删除,Kafka却可以将消息保存一段时间(比如一星期),这给consumer提供了很好的机动性和灵活性,这点在今后的文章中会有详述。 五、消息传输的事务定义 之前讨论了consumer和producer是怎么工作的,现在来讨论一下数据传输方面。数据传输的事务定义通常有以下三种级别: 最多一次: 消息不会被重复发送,最多被传输一次,但也有可能一次不传输。 最少一次: 消息不会被漏发送,最少被传输一次,但也有可能被重复传输. 精确的一次(Exactly once): 不会漏传输也不会重复传输,每个消息都传输被一次而且仅仅被传输一次,这是大家所期望的。 大多数消息系统声称可以做到“精确的一次”,但是仔细阅读它们的的文档可以看到里面存在误导,比如没有说明当consumer或producer失败时怎么样,或者当有多个consumer并行时怎么样,或写入硬盘的数据丢失时又会怎么样。kafka的做法要更先进一些。当发布消息时,Kafka有一个“committed”的概念,一旦消息被提交了,只要消息被写入的分区的所在的副本broker是活动的,数据就不会丢失。关于副本的活动的概念,下节文档会讨论。现在假设broker是不会down的。 如果producer发布消息时发生了网络错误,但又不确定实在提交之前发生的还是提交之后发生的,这种情况虽然不常见,但是必须考虑进去,现在Kafka版本还没有解决这个问题,将来的版本正在努力尝试解决。 并不是所有的情况都需要“精确的一次”这样高的级别,Kafka允许producer灵活的指定级别。比如producer可以指定必须等待消息被提交的通知,或者完全的异步发送消息而不等待任何通知,或者仅仅等待leader声明它拿到了消息(followers没有必要)。 现在从consumer的方面考虑这个问题,所有的副本都有相同的日志文件和相同的offset,consumer维护自己消费的消息的offset,如果consumer不会崩溃当然可以在内存中保存这个值,当然谁也不能保证这点。如果consumer崩溃了,会有另外一个consumer接着消费消息,它需要从一个合适的offset继续处理。这种情况下可以有以下选择: consumer可以先读取消息,然后将offset写入日志文件中,然后再处理消息。这存在一种可能就是在存储offset后还没处理消息就crash了,新的consumer继续从这个offset处理,那么就会有些消息永远不会被处理,这就是上面说的“最多一次”。 consumer可以先读取消息,处理消息,最后记录offset,当然如果在记录offset之前就crash了,新的consumer会重复的消费一些消息,这就是上面说的“最少一次”。 “精确一次”可以通过将提交分为两个阶段来解决:保存了offset后提交一次,消息处理成功之后再提交一次。但是还有个更简单的做法:将消息的offset和消息被处理后的结果保存在一起。比如用Hadoop ETL处理消息时,将处理后的结果和offset同时保存在HDFS中,这样就能保证消息和offser同时被处理了。

优秀的个人博客,低调大师

android studio安装教程

下载地址 http://www.android-studio.org/ 注意: 安装主要分两种情况,下载的自带SDK和不带SDK两种 然后又分为安装版,就是.exe和解压版 两种的区别...解压版,,就是安装板初始化安装完之后的,一会下面的图说明什么是安装版初始化安装完之后的 自带SDK的可以参考,我这里说一下不带SDK的怎么安装,主要是注意安装的位置,因为软件默认安装到C盘,这是让人不能忍受的,因为如果大部分内容安装到C盘会导致电脑卡.... https://blog.csdn.net/qq_33505204/article/details/78449246 自带SDK 有些人会问从哪里可以看出带不带SDK 不带SDK的安装方法也有两种,第一种是让软件自己安装SDK,自己规定路径,,另一种是自己下载SDK,然后也是更改软件SDK的路径 SDK下载地址和软件下载的地址在一个界面内,在下面 先说第一种,安装版的,不自带SDK的,让软件自己安装SDK 下载 双击安装 我安装好以后 现在安装以后,其实就是解压版的解压之后的样子 然后我不选择启动,我要更改软件的一些安装路径,,,,如果您不更改安装路径就跳过这部分 大家如果不想改可以不用该,默认安装在C盘 然后安装 安装版的因为安装了,所以也可以找到安装的快捷方式,然后接着按照 记住不要新建工程,,接着往下看 然后看一下自己的文件夹 现在还要修改一个地方 这个.Gradle我试了试每一个版本都会安装一些东西,如果安装到C盘...... 其实我一开始默认了C盘了........ E:/androidStudio/.gradle 这样的话安装后以后就会变成这样,是建完工程,然后选择了SDK版本以后才会下载gradel,然后把下载的东西放到了咱的路径里面 现在建一个工程 要等很长时间............... 给大家一个更改一些配置的链接 https://blog.csdn.net/lidiwo/article/details/74531057 其实现在就能用了 咱先这样下载到手机 说一下哈,,大家的可能报错说,没有自己手机的SDK 也可以自己安装相应的SDK 有可能会问,,这么多选择哪些呢.... 所以呢这个界面必选的就一个, Android SDK Platform (假设不用模拟器仿真的话),,我用自己的手机,所以就不下载.... 有可能会问,,如果我用仿真下哪些,,,我感觉哈除了TV,,,,, 我不用仿真器就不下了,,.......... 等着太无聊.................... 软件一开始默认用最高版本的编译程序,,(按理说:应该或建议用最高版本的编译) ,但是呢!!!有人呢就想,我就是不想用最高版本的编译,,,,, 选择以后,其实还要修改一个地方,,,按理说应该自动的更改的............. 但是呢会报错,,,,,,,,,,,,,,,,,,,,, 算啦算啦...........还是默认最高版本编译吧....修改了好多地方,还是有错误,,,,,,,,,,,,,,,,改天再抽时间修改,看看需要修改哪些地方,然后写篇文章.................. 还是都修改为原来的27吧......................感觉......................没有Eclipse好弄.............. 然后下载把 这个.....我一开始建了一个工程,,然后安装到手机了,,然后我删除了工程,然后建了一模一样的,,,,告诉我已经有啦..是不是卸载以前的重新安装.......我就 OK 大家的应该不会这种情况 好啦

优秀的个人博客,低调大师

Navicat 12激活教程

Navicat是一个非常好用的数据库管理软件,支持多种数据库。今天我偶然发现了Navicat 12的激活工具,试验了一下果然成功了,所以不道德地向大家介绍一下。当然,如果大家有条件的话还是请多支持正版。 首先第一步,先到官网下载Navicat高级版,然后安装。 然后,到Github上下载作者发布的开源破解工具,破解工具的架构(32位或64位)要和安装的Navicat版本一致。解压破解工具,然后在命令提示符中输入以下命令,假设Navicat的安装路径是D:\Program Files\PremiumSoft\Navicat Premium 12。 navicat-patcher.exe "D:\Program Files\PremiumSoft\Navicat Premium 12" RegPrivateKey.pem 然后应该会提示类似下面的信息。 D:\Program Files\PremiumSoft\Navicat Premium 12\libcc.dll has been backed up. Public key has been replaced. Success! 然后再输入以下命令。 navicat-keygen.exe RegPrivateKey.pem 这个命令会给你一个snKey,并且让你输入姓名及组织名,然后会提醒你输入请求码,这时候不要关闭窗口。 接下来就很简单了,首先断网,然后打开Navicat,然后点击注册按钮,输入刚才给你的snKey,然后点击激活按钮,稍等片刻,由于无法连接网络,Navicat会要求你进行手动激活,这时候将Navicat上出现的请求码输入到激活工具中,以空行结束。这样激活工具就会生成最后的激活码,将激活码输入到Navicat窗口中,完成激活。 激活完成后,可以重新连接网络了。这样Navicat就激活完毕了。 Navicat激活完成 有兴趣的同学可以关注一下作者的Github链接,最后再次提醒一下,有条件的同学请一定要支持正版。

优秀的个人博客,低调大师

REST API教程(PHP)

很高兴有人能看到这篇博客!我希望你已经做好心理准备,在这里我们将从0开始构建REST API。这不是一件简单的事情:我们接下来要做很多事情,不仅仅是coding还包括去理清一些理论知识。但我向你保证,你会为你付出努力而感到高兴。 接下来,我们会学习REST的一些理论并遵循 best practices 去开发,但也不会墨守陈规。因为如果你固执于太多的条条框框,就可能会被这些繁文缛节所困扰。构建一个完美的RESTful API是不太可能的,这反而会引起更多的麻烦。 因此,一个友好的API更符合实际,我们会遵循REST的最佳部分,而当我们违规或纠结时会告诉你。我们不会在意那些细节。不,这只不过是在攻击API中那些丑陋的地方,比如如何定义方法以及文档应该保存哪里,为什么等等。 开发项目: Resources and Links 项目?代码大战( Code Battles):一个超级牛逼的网站,程序员与项目进行着殊死搏斗。当你注册之后,你可以创建一个程序员,并为他选择头像。 REST的思想在于资源(resources)。好,我现在说的是资源(resource)!一定要清醒。这对REST是非常重要的,这里醉了的话,你将通不过第二章。表示法(representations)同样如此。 你可以对这个程序员resource进行某些操作,比如充电。基于运气因素,这可能会增加或减少程序员的等级,接下来还可以去战斗,和项目去进行战斗,项目又是一个resource。我们的程序员终究会取得战斗的胜利,战斗同样也是一个resource。 接下来,我将解释下他们明明看起来很自然,而要说他是资源(resource)。 我们项目的计划是创建一个API允许HTTP客户端可以完成上述这些操作甚至更多。但是创建和编辑程序员的请求地址是什么样子?客户端以JSON形式把数据发送给我们,我们是不是也应该以JSON返回数据?我们怎么样验证错误,进行查询的url又如何定义比如程序员列表和程序员详情HTTP,那么HTTP methods 和状态码呢?我们怎么把这些都记录下来呢?客户端怎么知道创建程序员需要哪些字段?通过哪个URL去和项目战斗? 噢,好多。。。所以构建一个可用的,一致的API所涉及的不止是定义一个请求地址,但也正因如此,你在这里,一起继续吧!

优秀的个人博客,低调大师

PyTorch入门教程

介绍 PyTorch是一个非常有可能改变深度学习领域前景的Python库。我尝试使用了几星期PyTorch,然后被它的易用性所震惊,在我使用过的各种深度学习库中,PyTorch是最灵活、最容易掌握的。 在本文中,我们将讲解如何入门PyTorch,包括基础知识和案例研究。还将分别在numpy和PyTorch中从零开始构建神经网络,以了解它们在实践中的相似处与区别。 目录 ·PyTorch的概述 ·深入研究技术细节 ·在Numpy和PyTorch中分别构建神经网络并进行对比 ·与其它深度学习库比较 ·案例研究——用PyTorch解决图像识别问题 PyTorch的概述 PyTorch的创始人说过他们创作的一个准则——他们想成为当务之急。这意味着我们可以立即执行计算。这正好符合Python的编程方法,不需要完成全部代码才能运行,可以轻松的运行部分代码并实时检

优秀的个人博客,低调大师

CentOS 7安装教程

使用U盘安装 1、CentOS 7系统镜像制作U盘启动盘 1)、下载CentOS 7系统镜像 下载地址:http://mirrors.aliyun.com/centos/7/isos/x86_64/ 下载地址:http://mirrors.sina.cn/centos/7/isos/x86_64/ 下载地址:http://www.centoscn.com/(CentOS中文站) 2)、制作U盘启动盘 使用软件:UltraISO软碟通 2、安装CentOS 7 开机按F12,从界面中选择U盘启动。如何设置U盘启动就不一一赘述了,不会的小伙伴去百度一下了。 上下箭头,选中第一项,按Tab键,将显示内容修改为: vmlinuz initrd=initrd.img linux dd quiet 回车执行命令,查看U盘路径名称,如下图: U盘名称sda4,记住这个就行了。输入‘c’继续,也有可能没反应,直接手动重启电脑或者输入命令‘reboot’重启电脑。 重启后,此时的命令修改为: vmlinuz initrd=initrd.img inst.stage2=hd:/dev/sdb4 quiet 进安装界面,选择语言。 点击软件选择图标,选择安装图形界面,如图所示: 点击完成,返回到菜单界面,选择安装位置 其中一个是我们的U盘(空间比较小),选择另外一个磁盘。存储选项选择‘我想让额外的空间可用’,点击完成。 如果此项为不可选中状态,先选择‘我要配置分区’,点击完成,再次进入页面就可以选中了。 删除不要的空间,点击回收空间。如果都不要了就全部删除 再次进入安装位置界面,选择‘我要配置分区’,点击完成。 分区方案可以根据需要自行选择,点击‘+’号可以添加挂载点,容量例如:10G、100M等等,根据需求方案,自行设定。不要的挂载点,点‘-’号可以移除。 点击完成,接受更改。 完成后返回菜单界面,可以继续配置网路和主机名,也可以以后配置。完成后开始安装 设置root账户密码和自定义账户设置,等待安装结束,重启电脑,完成最后配置。 重启电脑后会进入一个图形许可界面,接受许可,完成配置。如果进入的是命令界面,按Ctrl+Alt+F1切换到图形界面。

优秀的个人博客,低调大师

Jquery基础教程

1. jQuery概述 - jQuery是一个优秀的JavaScript框架 - 主要提供如下功能 - 访问页面框架的局部 - 修改页面的表现 - 更改页面的内容 - 响应事件 - 为页面添加动画 - 与服务器异步交互 - 简化常用的JavaScript操作 - 下载并使用jQuery - 官方网站(http://jquery.com/) - 不需要任何安装过程 2. jQuery的“$” - 选择器 - 功能函数前缀 - window.onload - window.onload的冲突 - ready()方法 - 创建DOM元素 3. 选择器 - 属性选择器 - 位置选择器 3. 实例 实例一 :一行代码实现隔行变色效果: Code 运行效果: 实例二 :选择器 Code 运行效果: 实例三:字符串操作: Code 实例4:获取浏览器的型号和版本号“ Code 四:核心部分 $(expr):该函数可以通过css选择器,Xpath或html代码来匹配目标元素,所有的jQuery操作都以此为基础。expr:字符串,一个查询表达式或一段html字符串 例子: < p > one </ p > < div > < p > two </ p > </ div > < p > three </ p > < a href ="#" id ="test" onClick ="jq()" > jQuery </ a > functionjq(){ alert($("div>p").html()); } 运行:当点击id为test的元素时,弹出对话框文字为two,即div标签下p元素的内容 functionjq(){ $(" < div >< p > Hello </ p ></ div > ").appendTo("body"); } 当点击id为test的元素时,向body中添加“<div><p>Hello</p></div>” $(elem) 说明:限制jQuery作用于一个特定的dom元素,这个函数也接受xml文档和windows对象 参数: elem:通过jQuery对象压缩的DOM元素 例子: 未执行jQuery前: < p > one </ p > < div > < p > two </ p > </ div >< p > three </ p > < a href ="#" id ="test" onClick ="jq()" > jQuery </ a > functionjq(){ alert($(document).find("div>p").html()); } 运行:当点击id为test的元素时,弹出对话框文字为two,即div标签下p元素的内容 function jq(){ $(document.body).background( " black " ); } 运行:当点击id为test的元素时,背景色变成黑色$(elems) 说明:限制jQuery作用于一组特定的DOM元素 参数: elem:一组通过jQuery对象压缩的DOM元素 例子: 未执行jQuery前: < form id ="form1" > < input type ="text" name ="textfield" > < input type ="submit" name ="Submit" value ="提交" > </ form > < a href ="#" id ="test" onClick ="jq()" > jQuery </ a > function jq(){ $(form1.elements).hide(); } 运行:当点击id为test的元素时,隐藏form1表单中的所有元素。 jQuery代码及功能: 四:总结 通过Jquery使我们操作更加方便. 本文转自麒麟博客园博客,原文链接:http://www.cnblogs.com/zhuqil/archive/2008/08/22/1273929.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

TensorFlow图像分类教程

深度学习算法与计算机硬件性能的发展,使研究人员和企业在图像识别、语音识别、推荐引擎和机器翻译等领域取得了巨大的进步。六年前,视觉模式识别领域取得了第一个超凡的成果。两年前,Google大脑团队开发了TensorFlow,并将深度学习巧妙的应用于各个领域。现在,TensorFlow则超越了很多用于深度学习的复杂工具。 利用TensorFlow,你可以获得具有强大能力的复杂功能,其强大的基石来自于TensorFlow的易用性。 在这个由两部分组成的系列中,我将讲述如何快速的创建一个应用于图像识别的卷积神经网络。TensorFlow计算步骤是并行的,可对其配置进行逐帧视频分析,也可对其扩展进行时间感知视频分析。 本系列文章直接切入关键的部分,只需要对命令行和Python有最基本的了解,就可以在家快速地创建一些令你激动不已的项目。本文不会深入探

优秀的个人博客,低调大师

springboot使用RabbitMQ教程

1、安装rabbitmq docker安装,拉取镜像 docker pull rabbitmq:management 创建容器并启动 docker run -d --name rabbitmq --publish 5671:5671 --publish 5672:5672 --publish 4369:4369 --publish 25672:25672 --publish 15671:15671 --publish 15672:15672 rabbitmq:management 管理地址:http://localhost:15672/用户名:guest 密码:guest 2、配置springboot (1)pom.xml添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> (2)添加rabbit配置文件,配置server信息 import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** @Author 冯战魁 @Date 2018/1/12 下午2:50br/>*/ @Configuration public class AmqpConfig {br/>@Bean public ConnectionFactory connectionFactory() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); connectionFactory.setAddresses("127.0.0.1:5672"); connectionFactory.setUsername("guest"); connectionFactory.setPassword("guest"); connectionFactory.setVirtualHost("/"); connectionFactory.setPublisherConfirms(true); //必须要设置 return connectionFactory;br/>} @Bean public Queue helloQueue() { return new Queue("hello"); } } (3)创建生产者,循环下发四个任务,用sleep模拟任务处理时间,一个.代码任务处理1s的时长,分别有四个不同时长的任务下发 import org.springframework.amqp.core.AmqpTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** @Author 冯战魁 @Date 2018/1/12 上午11:21 br/>*/ @RestController public class RabbitSenderController { br/>@Autowired private AmqpTemplate rabbitTemplate; br/>@RequestMapping("/sender") public void sender(){ String[] tasks = {"1.","2..","3...","4...."}; for (int i=0;i<tasks.length;i++){ String content = tasks[i]; System.out.println("Sender : " + content); this.rabbitTemplate.convertAndSend("hello",content); } } } (4)创建两个消费者Receiver1,Receiver2处理队列的任务 Receiver1: import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; /** @Author 冯战魁 @Date 2018/1/12 下午3:06 br/>*/ @Component @RabbitListener(queues = "hello") public class RabbitReceiver1 { br/>@RabbitHandler public void process(String content){ System.out.println("Receiver1 : " + content); try { dowork(content); } catch (InterruptedException e) { e.printStackTrace(); } } public void dowork(String content) throws InterruptedException{ for (char ch: content.toCharArray()) { if (ch == '.'){ Thread.sleep(1000); } } } } Receiver2: import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; /** @Author 冯战魁 @Date 2018/1/12 下午3:06 br/>*/ @Component @RabbitListener(queues = "hello") public class RabbitReceiver2 { br/>@RabbitHandler public void process(String content){ System.out.println("Receiver2 : " + content); try { dowork(content); } catch (InterruptedException e) { e.printStackTrace(); } } public void dowork(String content) throws InterruptedException{ for (char ch: content.toCharArray()) { if (ch == '.'){ Thread.sleep(1000); } } } } (5)验证代码 启动springboot,执行生产者的接口 http://localhost:8080/sender 然后查看终端输出的信息,如图所示 可以看到最先完成的消费者去执行下一个任务,是按任务时长执行,并不是按任务个数平分,防止消费者无意义的等待,提高效率。 查看rabbit的管理端http://localhost:15672/ 可以看到已经生成队列 至此整个任务完成! 本文转自 无心低语 51CTO博客,原文链接:http://blog.51cto.com/fengzhankui/2060275,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册