ethereum钱包交易

前面一篇已经把私有链部署起来,如果要开发创新创新场景的应用,一般是需要后台智能合约及前端展示,作为大众用户,一般不会去关心后台啥啥的,也不会去下载个什么专业的桌面钱包来管理“合约”相关交易,并且现在一般手机应用也是混合居多,所以我们现在就直接基于JS把Web前端开发做个示例。后台智能合约开发后续再详细讲下。

链服务端

要直接操作以太坊链,服务端需要启动一个链的节点,并开放RPC端口供客户端调用,开启命令为进入geth

[bath]admin.startRPC( ‘192.168.226.88’,’8545′,’*’)[/bath]

web3.js

以太坊区块链 提供了jason的数据交互格式及httprpc 的访问协议供前端应用的开发,并且封装到了web3.js.

步骤

1.加载初始化web3.js,并连接到后端rpc端口地址

1) 引入依赖
<script type="text/javascript" src=".../web3.min.js"></script>

2) 创建实例
web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545'));

2.使用web3使用各个接口进行交易操作

  • 同步调用
    var balance = web3.eth.getBalance(address);
  • 异步回调
    web3.eth.getBalance(address, function(error, result){...});

4) 批量调用
var batch = web3.createBatch(); batch.add(web3.eth.getBalance.request('0x0000000000000000000000000000000000000000', 'latest', callback)); batch.add(web3.eth.contract(abi).at(address).balance.request(address, callback2)); batch.execute();

web3.js 接口用法详见https://github.com/ethereum/wiki/wiki/JavaScript-API

web3.js 主要针对区块链交易等,而还有一个js库ethereumjs-wallet提供钱包功能,如果要做在线钱包功能可以使用该库。

 

该地址提供了ethereum 不同JS功能相关库信息资料http://ethereumjs.github.io/

ethereum私有链部署

本篇主要给开发人员阅读,要了解以太坊,区块链等概念基本知识,网络上搜索下就好。本篇进行私有链部署,当然你也可以直接在以太坊的测试链上进行,就省去私有链部署过程。

工具

我们需要如下客户端:

geth(以太坊节点客户端):

通过geth可以加入一个以太坊链,也可以建立一个自己的私有链然后加入。

mist(依托geth的图形化钱包管理客户端):

可在钱包中进行账户管理及合约操作并测试

链元数据(创世块)

新建一个文件genesis.json内容如下:

{ "alloc": { "bd2d69e3e68e1ab3944a865b3e566ca5c48740da": { "balance": "4000000000000000000000000000000" }, "ca9f427df31a1f5862968fad1fe98c0a9ee068c4": { "balance": "5000000000000000000000000000000" } }, "nonce": "0x0000000000000042", "difficulty": "0x060000", "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000", "timestamp": "0x00", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "extraData": "missummerwillenyuan", "gasLimit": "0x4c4b40" }

 

参数

描述

mixhash

与nonce配合用于挖矿,由上一个区块的一部分生成的hash。注意他和nonce的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件。

nonce

nonce就是一个64位随机数,用于挖矿,注意他和mixhash的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件。

difficulty

设置当前区块的难度,如果难度过大,cpu挖矿就很难,这里设置较小难度

alloc

用来预置账号以及账号的以太币数量,因为私有链挖矿比较容易,所以我们不需要预置有币的账号,需要的时候自己创建即可以。

coinbase

矿工的账号,随便填

timestamp

设置创世块的时间戳

parentHash

上一个区块的hash值,因为是创世块,所以这个值是0

extraData

附加信息,随便填,可以填你的个性信息

gasLimit

该值设置对GAS的消耗总量限制,用来限制区块能包含的交易信息总和,因为我们是私有链,所以填最大。

步骤

1.使用命令行通过geth 设置创世区块及区块链保存目录

[bath]geth –datadir “C:\Users\zhouwr\AppData\Roaming\Ethereum\willenchain” init genesis.json [/bath]

2.通过geth 启动该区块链

[bath]geth –identity “willenetherum” –rpc –rpccorsdomain “*” –datadir “C:\Users\zhouwr\AppData\Roaming\Ethereum\willenchain” –port “30303” –rpcapi “db,eth,net,web3″ –networkid 95518 console[/bath]

上面的步骤一个区块链已经创建成功,那么我们加入账号使用他们

账号

添加账号,当然测试用,我们就把创始分配有以太币的账号加进来

[bath]geth –datadir “C:\Users\zhouwr\AppData\Roaming\Ethereum\willenchain”  account import ca9f427df31a1f5862968fad1fe98c0a9ee068c4.key[/bath]

使用如下脚本查看账号

[bath]geth -datadir “C:\Users\zhouwr\AppData\Roaming\Ethereum\willenchain” account list[/bath]

或者启动区块链后使用如下命令查看账号:

[bath]eth.accounts[/bath]

当然同时我们可以使用图形化钱包来查看账号

参数

描述

identity

区块链的标示,随便填写,用于标示目前网络的名字

init

指定创世块文件的位置,并创建初始块

datadir

设置当前区块链网络数据存放的位置

port

网络监听端口

rpc

启动rpc通信,可以进行智能合约的部署和调试

rpcapi

设置允许连接的rpc的客户端,一般为db,eth,net,web3

networkid

设置当前区块链的网络ID,用于区分不同的网络,是一个数字

console

启动命令行模式,可以在Geth中执行命令

nodiscover

禁止被网络中其它节点发现,需要手动添加该节点到网络

verbosity

打印详细的日志信息

图型化钱包及交易

可以启动钱包Ethereum-Wallet-win64-0-8-5了,我们可以看到有个主账户并有好多ether,现在我们在钱包中新建一个账户,然后主账户转账1000ether给新建的账户,输入密码之后我们看到状态是确认中,那么我们需要在geth的命令行界面启动挖矿命令:miner.start(),将该笔交易记录到区块中,因为我们设置的挖矿难度很低,所以开启后看到挖出几个区块之后我们要停止挖矿,miner.stop(),不然会浪费好多区块,再回到图形化钱包,我们已经看到新账户已经到账ether 了。

网络

使用VirtualBox创建以太windows 7虚拟机,然后重复第一个节点的创建步骤在虚拟机上部署geth和以太坊钱包,同样需要初始化文件hdgenesis.json(同一个文件拷贝一份),并运行初始化命令,然后再运行启动命令,启动之后运行admin.nodeInfo.enode命令获取新节点的enode信息,然后在第一个节点上运行 admin.addPeer(enode URL)来添加新节点,注意将enode URL中[::]替换为新节点的IP地址,添加成功后运行admin.peers命令即可查看对等节点信息。

至此,一套基于geth的本地简单私有网络创建完成,可以在这套环境上进行简单的智能合约创建和测试。

智能合约

基本上,现今我们的应用创新场景有如下可行的:

底层创新:基本上是区块链算法上的创新,如修改一下共识机制之类的创新。

平台创新:Baas,区块链即服务,就是能够帮助企业快速部署一条私有链,将链作为服务提供给需要的企业。

场景创新:再一条固定的以太坊区块链上,将可行的应用场景引入进来,解决一些问题,比如信息保密需求,工作量确认并不可抵赖等需求场景,然后根据场景建模数字化成响应的智能合约,就创建了不同场景的应用,解决了问题,提供人们服务。

我觉得个人可以尝试的就是场景创新,虽然这个门框比较低,但是抓住历史机会也是会成功的,因为过了一段时间再看,虽然技术门槛是低,但是进入时间恰当的话,经过时间积累,会形成其他壁垒,如用户习惯,服务质量,大众效应。

 

部署使用过程参考https://my.oschina.net/stevex/blog/746669

Scrapy安装

windows10  安装scrapy, windows 下的scrapy 目前暂时不支持python3,因为scrapy 依赖的 Twisted  暂不支持python3,所以只能用python2,并且是win32版本。

目前已支持Windows X64版本 20160928 更新

安装

安装scrapy 前需要如下环境内容:

python2-7-12(win32):

https://www.python.org/ftp/python/2.7.12/python-2.7.12.msi

lxml: https://pypi.python.org/packages/ce/23/e734f2f1a4e3efb40ec60a2cfa6daa08e5d46240c256f9fb146a5b64a9c0/lxml-3.3.5.win32-py2.7.exe#md5=2c10ce9cab81e0155a019fb6c0c3e2e9

libxml2:

http://xmlsoft.org/sources/win32/python/libxml2-python-2.7.7.win32-py2.7.exe

VCForPython27.msi

https://download.microsoft.com/download/7/9/6/796EF2E4-801B-4FC4-AB28-B59FBF6D907B/VCForPython27.msi

安装上诉之后,整个环境已经准备妥当,直接用如下pip 即可将Scrapy 安装好了

[bath]py -2 -m pip install Scrapy[/bath]

python3.5 Scrapy1.1.3安装 20160928

需要使用wheel格式的文件直接本地安装lxml ,Twisted. 这个几个包可恶意从http://www.lfd.uci.edu/~gohlke/pythonlibs/  找到。

lxml:http://www.lfd.uci.edu/~gohlke/pythonlibs/dp2ng7en/lxml-3.6.4-cp35-cp35m-win_amd64.whl

Twisted:http://www.lfd.uci.edu/~gohlke/pythonlibs/dp2ng7en/Twisted-16.4.1-cp35-cp35m-win_amd64.whl

scrapy:https://pypi.python.org/packages/1f/91/81b32afce9676a0542ee42e8755ff1d61a80acd0101035929d7355b8cc50/Scrapy-1.1.3-py2.py3-none-any.whl#md5=eb35996066a3802dd9d2b2070098bdbb

依次安装上述下载的wheel 文件:

[bath]py -3 -m pip install lxml-3.6.4-cp35-cp35m-win_amd64.whl

py -3 -m pip install Twisted-16.4.1-cp35-cp35m-win_amd64.whl

py -3 -m pip install Scrapy-1.1.3-py2.py3-none-any.whl[/bath]

 

运行调试

创建项目

scrapy startproject tutorial

编写spider

运行

scrapy crawl spider_name

scrapy crawl dmoz

在pycharm 环境中,则通过配置 run configuration实现,

%_H8K{A)339[P2A67%V7J}P

 

配置完成后,直接run 就行了,可断点调试

java学习路线(转)

比较全,根据自己情况挑着学吧http://blog.csdn.net/pplcheer/article/details/12276999

  • 熟悉一种文本编辑器,比如Vim, Emacs, Notepad++, TextMate等。知道哪些是开源的,哪些是闭源的,哪些要收费。养成不用盗版软件的习惯。

  • 安装JDK(建议用你的Linux发行版自带的软件包管理器安装openjdk,过程中可能需要读发行版特定的文档)

  • 写一个Java的Helloworld程序,并用命令行工具javac编译,再用java命令运行这个程序。过程中熟悉源代码、字节码、虚拟机这些东西,以及Java的包(package)对.class文件所在的路径的影响。如果这两个命令行工具使用熟练了,可以开始选一个喜欢的集成开发环境,比如Eclipse。当然,养成不用盗版软件的习惯。熟悉一下如何建立“工程”,以及快捷键的使用。

  • 学习Java的面向过程编程,包括基本数据结构、表达式、语句、控制流、函数调用。

  • 学习Java的面向对象编程,包括类、引用类型和值类型的区别、成员、方法、访问控制、继承、多态、接口、接口实现。顺便学习一下面向对象的基本思想,即对象、消息、封装、继承、多态等,这些通用的内容不是Java特有的。这时候应该已经涉及了Java的垃圾回收。要留意即使有垃圾回收的情况下也会发生的内存泄露(如自己设计数组容器,元素是引用,逻辑上删除了元素,但并没有清成null)。注意垃圾回收只能回收内存中的对象,除了内存以外,其它资源不能依靠垃圾回收来关闭。比如,文件、管道、Socket、数据库连接等,垃圾回收是不会帮你关闭的。

  • 学习Java的异常处理,但更重要的是学习什么时候用特殊返回值而不使用异常,什么时候应该抛出异常而不处理异常,知道什么是pokemon catch及其危害,了解为什么Java的checked exception是一个糟糕的特性。如果愿意,同时学习一下Java1.7的try-with-resource语句和AutoCloseable接口。

  • 熟悉Java常用的数据结构,如基本的数组类型,以及泛型容器(java.util.*),尤其是java.util.List接口和java.util.ArrayList实现;以及java.util.Map接口和java.util.HashMap实现。(java1.5以前的没有泛型参数的就不用碰了)同时留意一下基本类型int, double等和装箱类型Integer和Double的区别,以及它们是如何自动转换的。

  • 熟悉Java标准库里的各种工具,包括日期时间、字符串格式化、IO等。**知道文件要自己在finally子句中close(),或者用Java1.7的try-with-resource,不要妄想垃圾回收器会帮你关掉文件。

  • 学习一下Java的命名习惯,以及JavaBeans的常规,知道为什么getter/setter比直接操作成员变量好。按这种方式给Java的变量、方法命名。同时看看你的IDE能不能自动帮你生成getter和setter。

  • 使用一个第三方的库(比如Apache Commons Lang通用工具库),让你的程序依赖于它的二进制jar包(而不是直接拷贝源代码),用命令行编译、运行(注意classpath等);也熟悉一下如何用你的集成开发环境添加第三方依赖。感受一下手动管理依赖关系的麻烦。

  • 学习Maven的使用,试着让Maven帮你解决依赖关系,再试着让Maven帮你创建一个Eclipse工程。再试试用Maven打包发布。

  • 学习软件测试,以及JUnit的使用,以及怎么在IDE中使用JUnit。有空看一下coverage工具。

  • 读读四人帮写的《设计模式》(这本书是用C++和Smalltalk语言为例子的,但仍然适合Java)。具体的是这本书,http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612图书馆应该能还借到英文原版,因为我借到过。


接下来就看具体要做哪方面的应用了,看需求。比如(下面的没有顺序)

  1. 关于语言

    • 如果学Java学得不舒服了,学Python。
    • 如果对面向对象编程的概念有点不习惯,学Smalltalk。(Ruby也行,但不如Smalltalk经典。Ruby的文档是一大硬伤。)
    • 如果嫌Java太啰嗦,学Python
    • 如果嫌Java太啰嗦,又想用JVM,自己又有精力,学Scala
    • 如果对对象之间的关系有点晕,学一学UML,以及它的一些图,可以对程序和运行进行直观的建模。你的IDE也许有插件可以生成UML图。但是不要太沉迷于这些方法论。
  2. 调试和辅助工具

    学习一下你的集成开发环境提供的调试工具,加一些断点试试。

    • 试试用jconsole或者VisualVM监控另一个jvm的状态。
    • 用profiling工具寻找程序中慢的地方。Eclipse有profiling工具。VisualVM也有这样的功能。(如果不介意使用闭源软件的话,也试试JProfiler和YourKit)
    • 有的JVM允许在运行时更新代码。Eclipse可以和某些JVM集成。这样你可以频繁修改代码而不用频繁重启JVM。对于某些“重型”工程很有用。(如果不介意使用闭源软件的话,也试试jRebel)
  3. 多线程

    学习Java的多线程编程以及lock、condition的用法(包括传统Java的synchronized语句以及新加的java.util.concurrent.*),学习一下如何让线程停下来,以及为什么要频繁确认isInterrupted()而不要用Thread.stop()。了解一下多核处理器、缓存、内存的关系,以及为什么多线程编程这么难。

    • 如果还舒服,学习一下Runnable的用法,以及自带的Executer等基本多线程工具。
    • 应该已经留意到java.util里面的很多容器不是线程安全的,但是java.util.Collections可以帮你创建一些安全的版本。另外关注一下java.util.concurrent里面有ConcurrentMap等容器可供使用。
    • 如果有空的话,看看memory model(内存一致性模型)和无锁同步(见java memory model和java.util.concurrent.atomic)。
    • 如果还有空,再了解一下除了“共享内存多线程编程”以外有没有别的模型(多进程multi-processing、消息传递message passing等)。
  4. 反射、元编程

    • 学习Java的反射机制,以及Annotation的用法。
    • 如果还舒服,试试java.lang.reflect.Proxy的用法。
    • 如果仍然还舒服,玩一玩CGLib(一个第三方的库)。
  5. 网络编程

    学习一下IP,TCP协议(计算机专业的应该学过,复习一下),学习Socket编程(注意垃圾回收器不会帮你关掉Socket)。

    • 如果不是很关心HTTP,看看java.nio,学习单线程轮询式IO复用(Selector)。
      1. 如果有点不明白nio的意图的话,了解一下c10k问题。 http://www.kegel.com/c10k.html
      2. 如果身体没有异样的话,大概了解一下操作系统(包括C语言)提供的select, poll, epoll, kqueue等接口。
      3. 如果身体仍然没有异样的话,试着用java.nio写一个文件服务器。
      4. 如果还有精力的话,上网扒一扒有没有其他的通信库,如netty等。
    • 如果关心Web还有HTTP,就学习一下HTTP协议,以及用Java进行HTTP的客户端编程。
      1. 如果还舒服,学学HTML,写写HTML的静态网页(不需要Java)
      2. 如果还舒服,用Java写一个基于DOM、XPath或者CSS Selector的网页解析器(爬网页)。
      3. 如果还舒服,学学Java的Servlet接口(先别学jsp)进行Web服务器端编程。学学标准的Servlet容器怎么用,包括web.xml的用法以及listener、filter等概念。以及某个Servlet容器(如Jetty或者Tomcat)的具体用法。
      4. 如果仍然还舒服,试着学一种模板语言(如haml, velocity, freemarker,【还有其他更好的框架吗?我不知道】, String.format,如果真的想学JSP的话JSP倒是也行,但不推荐)。
      5. 如果仍然觉得舒服,学学Spring框架中的Web框架,或者Struts,看你的口味。
      6. 如果还舒服,看看Spring Bean Container以及里面各种乱七八糟的工具。
      7. 如果还舒服,或者有需求,了解一下什么是RESTful Web Service,复习一下HTTP,找找适合的Java工具。
      8. 你可能会觉得Jackson是一个解析JSON用的好用的东西。
  6. 数据库

    学习一下关系数据库(计算机专业的应该学过,复习一下),包括SQL。选一个数据库管理系统熟悉一下(比如MariaDB,或者(如果你不讨厌Oracle的话)用被Oracle收购了的MySQL。先脱离Java单独学学)。然后看它们的官方文档教你怎么用Java连接这种数据库。这中间会涉及到JDBC接口。同时一定要知道SQL注入安全漏洞,以及掌握如何用PreparedStatement防止注入!!。建议看 http://bobby-tables.com/

    • 可能中间会涉及“事务”问题,让你不知不觉地开始去了解java transaction api(JTA)。
    • 如果还舒服,学一学对象关系转换(如Hibernate)。
    • 也可以学学非关系数据库,以及如何用Java访问它们。
  7. 日志记录

    学习一下slf4j和logback的用法。

    • 如果有精力的话,大概了解一下世界上有多少种Java日志框架,以及slf4j是怎么桥接这些框架的。
  8. 构建(build)系统

    学习一下Ant的用法。

    • 如果还舒服的话,学习一下用Ivy从Maven的仓库里下载软件包,解决依赖关系。
  9. 版本控制

    学习一种分布式版本控制器(如Git、Mercurial、Bzr、Darcs等,推荐Git)的基本用法,以及如何用它管理Java工程。希望你已经开始使用Maven了,并且知道为什么把IDE生成的工程文件(如eclipse的.project,.classpath和.metadata)放入版本控制器不好。然后了解一下如何在IDE中使用版本控制(Eclipse自带Git插件)。

    • 如果感觉很舒服的话,为你们实验室搭建一个Linux+SSH+Git服务器,装个GitLab(一种Web界面)。
    • 了解“集中式版本控制器”和“分布式版本控制器”的区别,并说服同事们不要再用SVN、CVS或者SourceSafe等老旧的“集中式版本控制器”了。
    • 开设一个GitHub账户。如果你不喜欢Git,就用BitBucket等。
  10. 持续集成

    自己(或者为你们实验室)搭建一个持续集成(Continuous Integration)服务器,如Jenkins,定期编译你的程序。建议同时使用Git等分布式版本控制器。

    • 如果你做开源软件,试试GitHub和Travis。
  11. 零碎工具

    淘一淘java.nio.files里面有什么好用的东东,然后再淘一淘Apache Commons Lang和Commons IO里有什么好用的工具。Commons Logging就不要再用了,用SLF4j和Logback。

  12. XML

    学学XML、DOM、XPath。XML这东西到处都可能用到。也可以用它写自己的配置文件。

    • 如果觉得不舒服了,就学学JSON和YAML。
    • 如果还是不舒服,就学学文本文件解析。
  13. 语法分析和编译器

    学学Antlr或者别的Parser Generator的用法

    • 如果觉得舒服,自己写一个计算器。
    • 如果还觉得舒服,自己写一种Domain-Specific Language (DSL)。
  14. 高效容器

    学学FastUtil或者Trove,如果你需要进行大量数值运算的话。

  15. 分布式计算

    学学MapReduce的思想以及它的实现。

    • 如果还舒服,学学Scala语言以及号称比MapReduce快得多的Apache Spark。
  16. 进程间通信

    看看ActiveMQ、MINA和RMI。

  17. 其他语言(JVM)

    学习另一门跑在JVM上的语言或者实现(如Groovy、Scala、Clojure、JRuby、Jython、JavaScript……)

  18. 其他语言(非JVM)

    学习另一门通用脚本语言(如Python、Ruby,其实perl也行,但不推荐),知道什么时候Java不是最好的选择。

  19. Java语言和Java虚拟机

    通读一遍(一目十行地读,不用细读)Java Language Specification,以及Java Virtual Machine Specification。

    • 了解以下解释器(interpreter)、编译器(compiler)、即时编译器(just-in-time compiler)和优化器(optimiser)的概念。
    • 如果对编译器的话题不感到畏惧,了解一下method JIT和tracing JIT的概念和区别。
  20. 内存管理

    学学垃圾回收的几种基本算法,包括mark-sweep、mark-compact、semi-space、generational、mark-region等,各自的性能,以及为什么朴素的reference counting是不完整的。知道为什么finalizer性能很糟糕,而且标准并不要求finalizer在程序退出前一定会执行。

    • 如果还舒服,了解一下如何设置Java虚拟机的堆大小限制(如HotSpot虚拟机的-Xmx选项等)。
    • 了解一下Java里的WeakReference以及SoftReference和PhantomReference,以及它们什么时候有用,以及为什么它们实现起来有些困难。
    • 如果有精力,了解一下Hotspot虚拟机的内存管理算法是什么样的。
  21. 动态装载

    学学Java的动态装载(class loading)

    • 如果还舒服的话,学学OSGI以及它的一种实现(如Felix或者Equinox)
    • 如果仍然很舒服的话,学学写基于Eclipse平台的程序。不是Eclipse集成开发环境,只是利用他们的图形框架,写自己的应用程序。
    • 如果还觉得舒服的话,写Eclipse集成开发环境的插件。
  22. 本地/外语接口

    学习一下Java Native Interface(JNI),试着写一个Java和C语言混合编程的程序。

    • 如果觉得不舒服了或者觉得欲仙欲死,就学一学Java Native Access(JNA),试一试不用任何胶水代码而从Java直接装载C库,直接调用C函数。
    • 如果连JNA也懒得学,就学一学SWIG,自动生成绑定。
    • 如果觉得舒服,就学一学Java Virtual Machine Tooling Interface(JVMTI),用C程序来监视JVM的状态。
  23. 密码学

    学一学密码学,包括编码、密码分析、攻击、对称密钥、公钥系统、数字签名、哈希算法等,看看Java有没有实现。

    • 如果觉得有点不舒服(你应该不会觉得舒服吧,除非你是学密码学的,要不然总会觉得自己写的程序有安全漏洞),就写一个“人品计算器”来放松一下,要求每次输入同一个姓名,得到的人品值是固定的,但又要让人无法用别人的人品值猜自己的人品值。
  24. 移动终端

    学一学Android开发。

    • 如果有精力的话,看看Dalvik虚拟机是怎么回事。
    • 建议买一个iPhone或者iPad,或许你再也不想用Android手机或者平板了。
  25. 历史

    如果觉得有些无聊,就去挖一挖历史上比较火的可能和Java相关技术,比如:

    • Applet,想想它比起html5+css3+javascript的缺点在哪里。
    • AWT、Swing,想想为什么很少有人用Java写图形界面程序。你觉得Swing的程序看上去舒服吗?中国人和残疾人喜欢用Swing程序吗?
    • JNDI,想想它比起Spring Bean Container的缺点在哪里。
    • JSP,想想它比起MVC结构的缺点在哪里。
    • WSDL/SOAP,把它们和XML-RPC、RESTful Web Service比较一下。
    • XSLT,以及为什么它是图灵完备的。可是它真的比Java本身更好用吗?
    • Log4j、java.util.logging、Apache Commons Logging,各自有什么问题,以及Log4j的作者本人为什么又开发了SLF4j和Logback?
    • Java最早是为什么设计的?
    • Type erasure是怎么回事?为什么ArrayList<int>不行但ArrayList<Integer>就可以?挖一挖历史。

其实Java不算完全面向对象的语言。Java更偏实用性,很多控制流(if语句、while循环、for循环等)来自面向过程的语言;基本数据类型(int, char, double等)也不是对象。但另一些语言,比如SmallTalk,更偏向纯粹的面向对象的设计,包括基本的数据类型都是对象,if/while/for等也用对象和方法来实现。比如:

基本数据类型也是对象,可以接受消息(即方法),比如:

(a + b) sqrt

这里面,a+b其实是向a发送“+”消息,参数是b。sqrt也是一个消息,发给一个数。

if分支是这样做的:

(x < y) ifTrue: [ 
       max := y. 
       i := j 
     ] ifFalse: [ 
       max := x. 
       i := k 
     ]

ifTrue:ifFalse: 是一个Boolean对象的一个方法,取两个参数,每个是一个“块”,分别在真和假的情况下执行。

还有while循环是这样做的:

[i < 100] whileTrue: [ 
       sum := sum + i. 
       i := i + 1 
     ]

whileTrue是“块”的一个方法,每次对块求值,如果是真,就反复执行第一个参数(也是“块”)。

所以,相对其他面向对象的语言,Java并不算很“面向对象”。所以需要补充一些对面向对象语言的了解。

文/Jabari(简书作者)
原文链接:http://www.jianshu.com/p/d51551b0a8ba
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

如何提高服务器并发处理能力

转发,看到的一篇总结得比较平实的博文。

August 16, 2014

说明

以下内容为入门级介绍,意在对老技术作较全的总结而不是较深的研究。主要参考《构建高性能Web站点》一书。

目录

  • 什么是服务器并发处理能力
  • 怎样衡量服务器并发处理能力
    1. 吞吐率
    2. 压力测试
  • 怎么提高服务器的并发处理能力
    1. 提高CPU并发计算能力
      • 多进程 & 多线程
      • 减少进程切换,使用线程,考虑进程绑定CPU
      • 减少使用不必要的锁,考虑无锁编程
      • 考虑进程优先级
      • 关注系统负载
      • 关注CPU使用率,除了用户空间和内核空间的CPU使用率以外,还要关注I/O wait
    2. 考虑减少内存分配和释放
      1. 改善数据结构和算法复杂度
      2. 使用内存池
      3. 考虑使用共享内存
    3. 考虑使用持久连接
    4. 改进I/O模型
      • DMA技术
      • 异步I/O
      • 多路I/O复用: epoll
      • sendfile
      • 内存映射
      • 直接I/O
    5. 改进服务器并发策略
      • 一个进程处理一个连接,非阻塞I/O
      • 一个线程处理一个连接,非阻塞IO
      • 一个进程处理多个连接,异步I/O
      • 一个线程处理多个连接,异步IO
    6. 改进硬件环境

什么是服务器并发处理能力

一台服务器在单位时间里能处理的请求越多,服务器的能力越高,也就是服务器并发处理能力越强

有什么方法衡量服务器并发处理能力

1. 吞吐率

吞吐率,单位时间里服务器处理的最大请求数,单位req/s

从服务器角度,实际并发用户数的可以理解为服务器当前维护的代表不同用户的文件描述符总数,也就是并发连接数。服务器一般会限制同时服务的最多用户数,比如apache的MaxClents参数。

这里再深入一下,对于服务器来说,服务器希望支持高吞吐率,对于用户来说,用户只希望等待最少的时间,显然,双方不能满足,所以双方利益的平衡点,就是我们希望的最大并发用户数。

2. 压力测试

有一个原理一定要先搞清楚,假如100个用户同时向服务器分别进行10个请求,与1个用户向服务器连续进行1000次请求,对服务器的压力是一样吗?实际上是不一样的,因对每一个用户,连续发送请求实际上是指发送一个请求并接收到响应数据后再发送下一个请求。这样对于1个用户向服务器连续进行1000次请求, 任何时刻服务器的网卡接收缓冲区中只有1个请求,而对于100个用户同时向服务器分别进行10个请求,服务器的网卡接收缓冲区最多有100个等待处理的请求,显然这时的服务器压力更大。

压力测试前提考虑的条件

  • 并发用户数: 指在某一时刻同时向服务器发送请求的用户总数(HttpWatch)
  • 总请求数
  • 请求资源描述
  • 请求等待时间(用户等待时间)
  • 用户平均请求的等待时间
  • 服务器平均请求处理的时间
  • 硬件环境

压力测试中关心的时间又细分以下2种:

  1. 用户平均请求等待时间(这里暂不把数据在网络的传输时间,还有用户PC本地的计算时间计算入内)
  2. 服务器平均请求处理时间

用户平均请求等待时间主要用于衡量服务器在一定并发用户数下,单个用户的服务质量;而服务器平均请求处理时间就是吞吐率的倒数,一般来说,用户平均请求等待时间 = 服务器平均请求处理时间 * 并发用户数

怎么提高服务器的并发处理能力

1. 提高CPU并发计算能力

服务器之所以可以同时处理多个请求,在于操作系统通过多执行流体系设计使得多个任务可以轮流使用系统资源,这些资源包括CPU,内存以及I/O. 这里的I/O主要指磁盘I/O, 和网络I/O。

多进程 & 多线程

多执行流的一般实现便是进程,多进程的好处可以对CPU时间的轮流使用,对CPU计算和IO操作重叠利用。这里的IO主要是指磁盘IO和网络IO,相对CPU而言,它们慢的可怜。

而实际上,大多数进程的时间主要消耗在I/O操作上。现代计算机的DMA技术可以让CPU不参与I/O操作的全过程,比如进程通过系统调用,使得CPU向网卡或者磁盘等I/O设备发出指令,然后进程被挂起,释放出CPU资源,等待I/O设备完成工作后通过中断来通知进程重新就绪。对于单任务而言,CPU大部分时间空闲,这时候多进程的作用尤为重要。

多进程不仅能够提高CPU的并发度。其优越性还体现在独立的内存地址空间和生命周期所带来的稳定性和健壮性,其中一个进程崩溃不会影响到另一个进程。

但是进程也有如下缺点:

  1. fork()系统调用开销很大: prefork
  2. 进程间调度和上下文切换成本: 减少进程数量
  3. 庞大的内存重复:共享内存
  4. IPC编程相对比较麻烦
减少进程切换

当硬件上下文频繁装入和移出时,所消耗的时间是非常可观的。可用Nmon工具监视服务器每秒的上下文切换次数。

为了尽量减少上下文切换次数,最简单的做法就是减少进程数,尽量使用线程并配合其它I/O模型来设计并发策略。

还可以考虑使用进程绑定CPU技术,增加CPU缓存的命中率。若进程不断在各CPU上切换,这样旧的CPU缓存就会失效。

减少使用不必要的锁

服务器处理大量并发请求时,多个请求处理任务时存在一些资源抢占竞争,这时一般采用“锁”机制来控制资源的占用,当一个任务占用资源时,我们锁住资源,这时其它任务都在等待锁的释放,这个现象称为锁竞争。

通过锁竞争的本质,我们要意识到尽量减少并发请求对于共享资源的竞争。比如在允许情况下关闭服务器访问日志,这可以大大减少在锁等待时的延迟时间。要最大程度减少无辜的等待时间。

这里说下无锁编程,就是由内核完成这个锁机制,主要是使用原子操作替代锁来实现对共享资源的访问保护 ,使用原子操作时,在进行实际的写操作时,使用了lock指令,这样就可以阻止其他任务写这块内存,避免出现数据竞争现象。原子操作速度比锁快,一般要快一倍以上。

例如fwrite(), fopen(),其是使用append方式写文件,其原理就是使用了无锁编程,无锁编程的复杂度高,但是效率快,而且发生死锁概率低。

考虑进程优先级

进程调度器会动态调整运行队列中进程的优先级,通过top观察进程的PR值

考虑系统负载

可在任何时刻查看/proc/loadavg, top中的load average也可看出

考虑CPU使用率

除了用户空间和内核空间的CPU使用率以外,还要关注I/O wait,它是指CPU空闲并且等待I/O操作完成的时间比例(top中查看wa的值)。

2. 考虑减少内存分配和释放

服务器的工作过程中,需要大量的内存,使得内存的分配和释放工作尤为重要。

可以通过改善数据结构和算法复制度来适当减少中间临时变量的内存分配及数据复制时间,而服务器本身也使用了各自的策略来提高效率。

例如Apache,在运行开始时一次申请大片的内存作为内存池,若随后需要时就在内存池中直接获取,不需要再次分配,避免了频繁的内存分配和释放引起的内存整理时间。

再如Nginx使用多线程来处理请求,使得多个线程之间可以共享内存资源,从而令它的内存总体使用量大大减少,另外,nginx分阶段的内存分配策略,按需分配,及时释放,使得内存使用量保持在很小的数量范围。

另外,还可以考虑共享内存。

共享内存指在多处理器的计算机系统中,可以被不同中央处理器(CPU)访问的大容量内存,也可以由不同进程共享,是非常快的进程通信方式。

但是使用共享内存也有不好的地方,就是对于多机器时数据不好统一。

shell命令ipcs可用来显示系统下共享内存的状态,函数shmget可以创建或打开一块共享内存区,函数shmat将一个存在的共享内存段连接到本进程空间, 函数shmctl可以对共享内存段进行多种操作,函数shmdt函数分离该共享内存。

3. 考虑使用持久连接

持久连接也为长连接,它本身是TCP通信的一种普通方式,即在一次TCP连接中持续发送多分数据而不断开连接,与它相反的方式称为短连接,也就是建立连接后发送一份数据就断开,然后再次建立连接发送下一份数据, 周而复始。是否采用持久连接,完全取决于应用特点。从性能角度看,建立TCP连接的操作本身是一项不小的开销,在允许的情况下,连接次数越少,越有利于性能的提升; 尤其对于密集型的图片或网页等小数据请求处理有明显的加速所用。

HTTP长连接需要浏览器和web服务器的共同协作,目前浏览器普遍支持长连接,表现在其发出的HTTP请求数据头中包含关于长连接的声明,如下: Connection: Keep-Alive

主流的web服务器都支持长连接,比如apache中,可以用KeepAlive off关闭长连接。

对于长连接的有效使用,还有关键一点在于长连接超时时间的设置,即长连接在什么时候关闭吗? Apache的默认设置为5s, 若这个时间设置过长,则可能导致资源无效占有,维持大量空闲进程,影响服务器性能。

4. 改进I/O 模型

I/O操作根据设备的不同分为很多类型,比如内存I/O, 网络I/O, 磁盘I/O. 对于网络I/O和磁盘I/O, 它们的速度要慢很多,尽管使用RAID磁盘阵列可通过并行磁盘磁盘来加快磁盘I/O速度,购买大连独享网络带宽以及使用高带宽网络适配器可以提高网络i/O的速度。但这些I/O操作需要内核系统调用来完成,这些需要CPU来调度,这使得CPU不得不浪费宝贵的时间来等待慢速I/O操作。我们希望让CPU足够少的时间在i/O操作的调度上,如何让高速的CPU和慢速的I/O设备更好地协调工作,是现代计算机一直探讨的话题。各种I/O模型的本质区别在于CPU的参与方式。

1. DMA技术

I/O设备和内存之间的数据传输方式由DMA控制器完成。在DMA模式下,CPU只需向DMA下达命令,让DMA控制器来处理数据的传送,这样可以大大节省系统资源。

2. 异步I/O

异步I/O指主动请求数据后便可以继续处理其它任务,随后等待I/O操作的通知,这样进程在数据读写时不发生阻塞。

异步I/O是非阻塞的,当函数返回时,真正的I/O传输已经完成,这让CPU处理和I/O操作达到很好的重叠。

3. I/O多路复用

epoll服务器同时处理大量的文件描述符是必不可少的,若采用同步非阻塞I/O模型,若同时接收TCP连接的数据,就必须轮流对每个socket调用接收数据的方法,不管这些socket有没有可接收的数据,都要询问一次。假如大部分socket并没有数据可以接收,那么进程便会浪费很多CPU时间用于检查这些socket有没有可以接收的数据。多路I/O就绪通知的出现,提供了对大量文件描述符就绪检查的高性能方案,它允许进程通过一种方法同时监视所有文件描述符,并可以快速获得所有就绪的文件描述符,然后只针对这些文件描述符进行数据访问。

epoll可以同时支持水平触发和边缘触发,理论上边缘触发性能更高,但是代码实现复杂,因为任何意外的丢失事件都会造成请求处理错误。

epoll主要有2大改进:

  1. epoll只告知就绪的文件描述符,而且当调用epoll_wait()获得文件描述符时,返回并不是实际的描述符,而是一个代表就绪描述符数量的值,然后只需去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里使用了内存映射(mmap)技术,这样彻底省掉了这些文件描述符在系统调用时复制的开销。
  2. epoll采用基于事件的就绪通知方式。其事先通过epoll_ctrl()注册每一个文件描述符,一旦某个文件描述符就绪时,内核会采用类似callback的回调机制,当进程调用epoll_wait()时得到通知

关于IO模型,可以参考笔者前面写的相关文章Java NIO.2; 关于epoll,可以参考笔者前面写的文章select、poll和epoll简介

4. Sendfile

大多数时候,我们都向服务器请求静态文件,比如图片,样式表等,在处理这些请求时,磁盘文件的数据先经过内核缓冲区,然后到用户内存空间,不需经过任何处理,其又被送到网卡对应的内核缓冲区,接着再被送入网卡进行发送。

Linux提供sendfile()系统调用,可以讲磁盘文件的特定部分直接传送到代表客户端的socket描述符,加快了静态文件的请求速度,同时减少CPU和内存的开销。

适用场景: 对于请求较小的静态文件,sendfile发挥的作用不那么明显,因发送数据的环节在整个过程中所占时间的比例相比于大文件请求时小很多。

5. 内存映射

Linux内核提供一种访问磁盘文件的特殊方式,它可以将内存中某块地址空间和我们指定的磁盘文件相关联,从而对这块内存的访问转换为对磁盘文件的访问。这种技术称为内存映射。

多数情况下,内存映射可以提高磁盘I/O的性能,无须使用read()或write()等系统调用来访问文件,而是通过mmap()系统调用来建立内存和磁盘文件的关联,然后像访问内存一样自由访问文件。

缺点:在处理较大文件时,内存映射会导致较大的内存开销,得不偿失。

6. 直接I/O

在linux 2.6中,内存映射和直接访问文件没有本质差异,因为数据需要经过2次复制,即在磁盘与内核缓冲区之间以及在内核缓冲区与用户态内存空间。

引入内核缓冲区的目的在于提高磁盘文件的访问性能,然而对于一些复杂的应用,比如数据库服务器,它们为了进一步提高性能,希望绕过内核缓冲区,由自己在用户态空间实现并管理I/O缓冲区,比如数据库可根据更加合理的策略来提高查询缓存命中率。另一方面,绕过内核缓冲区也可以减少系统内存的开销,因内核缓冲区本身就在使用系统内存。

Linux在open()系统调用中增加参数选项O_DIRECT,即可绕过内核缓冲区直接访问文件,实现直接I/O。

在Mysql中,对于Innodb存储引擎,自身进行数据和索引的缓存管理,可在my.cnf配置中分配raw分区跳过内核缓冲区,实现直接I/O。

改进服务器并发策略

服务器并发策略的目的,是让I/O操作和CPU计算尽量重叠进行,一方面让CPU在I/O等待时不要空闲,另一方面让CPU在I/O调度上尽量花最少的时间。

一个进程处理一个连接,非阻塞I/O

这样会存在多个并发请求同时到达时,服务器必然要准备多个进程来处理请求。其进程的开销限制了它的并发连接数。但从稳定性和兼容性的角度,则其相对安全,任何一个子进程的崩溃不会影响服务器本身,父进程可以创建新的子进程;这种策略典型的例子就是Apache的fork和prefork模式。对于并发数不高(如150以内)的站点同时依赖Apache其它功能时的应用选择Apache还是可以的。

一个线程处理一个连接,非阻塞IO

这种方式允许在一个进程中通过多个线程来处理多个连接,一个线程处理一个连接。Apache的worker模式就是这种典型例子,使其可支持更多的并发连接。不过这种模式的总体性能还不如prefork,所以一般不选用worker模式。

一个进程处理多个连接,异步I/O

一个线程同时处理多个连接,潜在的前提条件就是使用IO多路复用就绪通知。

这种情况下,将处理多个连接的进程叫做worker进程或服务进程。worker的数量可以配置,如Nginx中的worker_processes 4。

一个线程处理多个连接,异步IO

即使有高性能的IO多路复用就绪通知,但磁盘IO的等待还是无法避免的。更加高效的方法是对磁盘文件使用异步IO,目前很少有Web服务器真正意义上支持这种异步IO。

6. 改进硬件环境

还有一点要提及的是硬件环境,服务器的硬件配置对应用程序的性能提升往往是最直接,也是最简单的方式,这就是所谓的scale up。这里不做论述。

Delicious Save this on Delicious

Related Posts

乱七八糟的IT

自从踏入IT这一行,就不断的听到高大尚的各类名词,GOF设计模式,AOP架构,RESTFUL架构风格,刚开始听起来那是云里雾里,听完后赶紧GOOGLE,然后嗤之以鼻:也就这样!然后也码了些年代码,跳了不少坑,填了不少洞,回过头来再回顾下,却是感慨万分:如果把这些高大尚的东西都被运用上去我还会跳坑里而不自知么,却也不住的感概,看似高大尚的名称解释起来却也是简单,但是简单的东西其实也是不断跳坑的产悟,被各种牛人不断的推崇,却还是没多少人真正遵从,这是为什么啊!

git安装及私有服务仓库设置

git安装比较简单,没啥好说的。私有服务仓库之前装过一次,但是过了比较久远又有点忘记了,索性全部再记录一遍,防止以后大脑有间歇性遗忘,感觉频次越来越高,哈哈。

git安装

centos7,已经安装了yum。

1.root用户下执行:

[root@localhost ~]# yum install -y git

2.创建git用户作为git账户存放的目录等

useradd git

3.创建客户端登录证书

收集所有需要登录的用户的公钥,就是他们自己生成的id_rsa.pub文件,把所有公钥复制到/home/git/.ssh/authorized_keys文件里,一行一个。

客户端创建证书:

[git@localhost .ssh]$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/git/.ssh/id_rsa):
/home/git/.ssh/id_rsa already exists.
Overwrite (y/n)?
[git@localhost .ssh]$ ls
authorized_keys  id_rsa  id_rsa.pub

git仓库

创建一个目录作为仓库根目录 willen/git

然后进入该目录,初始化一个仓库:

git init --bare willen.git

Git 简易实用记

一些基础的命令

创建新仓库:在制定目录中执行

git init

检出仓库:

git clone <a href="mailto:username@host:/path/to/repository&#91;/as3">username@host:/path/to/repository[/as3</a>]</p> <p>其中一般username 为 git</p> <h4>添加与提交:</h4> <p>git add &lt;filename&gt; git add *

推送提交改动:

git push origin master

可以把 master 换成你想要推送的任何分支

添加新远程仓库:

git remote add origin &lt;server&gt;

更新:

git pull

 

设置多个远程仓库,同步更新远程仓库内容:

vim .git/config [remote "web"] url = ssh://server.example.org/home/ams/website.git url = ssh://other.exaple.org/home/foo/website.git

其他分支内容查看如下网址:

http://rogerdudler.github.io/git-guide/index.zh.html

http://www.bootcss.com/p/git-guide/

高性能服务器程序框架

将服务器解构为如下三个主要模块:

  • I/O处理单元。包含:四种I/O模型和两种高效事件处理模式
  • 逻辑单元。包含:两种高效并发模式,以及高效的逻辑处理方式—有限状态机
  • 存储单元。如db,文件等

将按照如下流程去学习:

  • 服务器模型
  • 服务器编程框架
  • I/O模型
  • 并发模式

如下思维导图:


一.服务器模型

分为:

  • C/S模型(大多数)
  • P2P模型(少数,点对点)

1. C/S模型

图1 CS模型

C/S模型的逻辑很简单。服务器启动后,首先创建一个或多个监听socket,并调用bind函数将其绑定到服务器指定的端口上,然后调用listen函数等待客户连接。服务器稳定运行之后,客户端就可以调用connect函数向服务器发起连接了。

由于客户连接请求是随机到达的异步事件,服务器需要使用某种I/O模型来监听这一事件。I/O模型有多种:如下是其中的一种。

图2 I/O复用技术之一的select系统调用

上图分析如下:

  • 当监听到连接请求后,服务器就调用accept函数接受它,并分配一个逻辑单元为新的连接服务。
  • 逻辑单元可以是新创建的子进程、子线程或者其他。上图服务器给客户端分配的逻辑单元是由fork系统调用创建的子进程
  • 逻辑单元读取客户请求,处理该请求,然后将处理结果返回给客户端。
  • 客户端接收到服务器反馈的结果之后,可以继续向服务器发送请求,也可以立即主动关闭连接。
  • 如果客户端主动关闭连接,则服务器执行被动关闭连接。至此,双方的通信结束。
  • 需要注意的是,服务器在处理一个客户请求的同时还会继续监听其他客户请求,否则就变成了效率低下的串行服务器了(必须先处理完前一个客户的请求,才能继续处理下一个客户请求)

CS模型的优缺点:

C/S模型非常适合资源相对集中的场合,并且它的实现也很简单,但其缺点也很明显:服务器是通信的中心,当访问量过大时,可能所有客户都将得到很慢的响应。下面讨论的P2P模型解决了这个问题。

2. P2P模型

P2P(Peer to Peer,点对点)模型比C/S模型更符合网络通信的实际情况。它摒弃了以服务器为中心的格局,让网络上所有主机重新回归对等的地位。

图3 P2P模型

P2P模型的优缺点:

  • 优点:使得每台机器在消耗服务的同时也给别人提供服务,这样资源能够充分、自由地共享。云计算机群可以看作P2P模型的一个典范。
  • 缺点:当用户之间传输的请求过多时,网络的负载将加重。

从编程角度来讲,P2P模型可以看作C/S模型的扩展:每台主机既是客户端,又是服务器。因此,我们仍然采用C/S模型来讨论网络编程。

二. 服务器编程框架

图4 服务器基础框架

该图既能用来描述一台服务器,也能用来描述一个服务器机群。两种情况下各个部件的含义和功能如表所示:


1. I/O处理单元

I/O处理单元是服务器管理客户连接的模块

它通常要完成以下工作:

  • 等待并接受新的客户连接
  • 接收客户数据
  • 将服务器响应数据返回给客户端。

但是,数据的收发不一定在I/O处理单元中执行,也可能在逻辑单元中执行,具体在何处执行取决于事件处理模式。

对于一个服务器机群来说,I/O处理单元是一个专门的接入服务器。它实现负载均衡,从所有逻辑服务器中选取负荷最小的一台来为新客户服务。(相当于负载均衡服务器)

2.逻辑单元

一个逻辑单元通常是一个进程或线程。

它的工作:

  • 分析并处理客户数据
  • 将结果传递给I/O处理单元或者直接发送给客户端(具体使用哪种方式取决于事件处理模式)
  • 对服务器机群而言,一个逻辑单元本身就是一台逻辑服务器。服务器通常拥有多个逻辑单元,以实现对多个客户任务的并行处理。

3.网络存储单元

网络存储单元可以是数据库、缓存和文件,甚至是一台独立的服务器。但它不是必须的,比如ssh、telnet等登录服务就不需要这个单元。

4.请求队列

请求队列是各单元之间的通信方式的抽象。

I/O处理单元接收到客户请求时,需要以某种方式通知一个逻辑单元来处理该请求。同样,多个逻辑单元同时访问一个存储单元时,也需要采用某种机制来协调处理竞态条件。请求队列通常被实现为的一部分。

对于服务器机群而言,请求队列是各台服务器之间预先建立的、静态的、永久的TCP连接。这种TCP连接能提高服务器之间交换数据的效率,因为它避免了动态建立TCP连接导致的额外的系统开销。

三. I/O模型

是服务器编程框架中I/O处理单元的一个细分

从两大方面开始:

  • 阻塞与非阻塞
  • 同步与异步

在知乎回答:“怎样理解阻塞非阻塞与同步异步的区别?”中理解两者的概念和区别。

1. 阻塞与非阻塞

基础socket默认是阻塞的,我们可以给socket系统调用的第2个参数传递SOCK_NONBLOCK标志,或者通过fcntl系统调用的F_SETFL命令,将其设置为非阻塞的。

注:阻塞和非阻塞的概念能应用于所有文件描述符,而不仅仅是socket。我们称阻塞的文件描述符为阻塞I/O,称非阻塞的文件描述符为非阻塞I/O。

1.1 阻塞I/O

针对阻塞I/O执行的系统调用可能因为无法立即完成而被操作系统挂起,直到等待的事件发生为止 socket的基础API中,可能被阻塞的系统调用包括acceptsendrecvconnect

1.2 非阻塞I/O

针对非阻塞I/O执行的系统调用则总是立即返回,而不管事件是否已经发生。

如果事件没有立即发生,这些系统调用就返回-1,和出错的情况一样。此时我们必须根据errno来区分这两种情况。对acceptsendrecv而言,事件未发生时errno通常被设置成EAGAIN(意为“再来一次”)或者EWOULDBLOCK(意为“期望阻塞”);对connect而言,errno则被设置成EINPROGRESS(意为“在处理中”)。

显然,我们只有在事件已经发生的情况下操作非阻塞I/O(读、写等),才能提高程序的效率。因此,非阻塞I/O通常要和其他I/O通知机制一起使用,比如I/O复用和SIGIO信号。

1). I/O复用

I/O复用是最常使用的I/O通知机制。

I/O复用使得程序能同时监听多个文件描述符。应用程序通过I/O复用函数向内核注册一组事件,内核通过I/O复用函数把其中就绪的事件通知给应用程序。

网络编程中使用I/O复用的场景:

  • 客户端程序需要同时处理多个socket时,如非阻塞connect程序
  • 客户端程序同时处理用户输入和网络连接时,如聊天室程序
  • TCP服务器要同时处理监听socket和连接socket时 (这是I/O复用使用最多的场合)
  • 服务器要同时处理TCP请求和UDP请求时

Linux上常用的I/O复用函数是selectpollepoll_wait

需要指出的是,I/O复用函数本身的调用是阻塞的,它们能提高程序效率的原因在于它们具有同时监听多个I/O事件的能力。

2).SIGIO信号(信号驱动I/O)

SIGIO信号也可以用来报告I/O事件

我们可以为一个目标文件描述符指定宿主进程,那么被指定的宿主进程将捕获到SIGIO信号。这样,当目标文件描述符上有事件发生时,SIGIO信号的信号处理函数将被触发,我们也就可以在该信号处理函数中对目标文件描述符执行非阻塞I/O操作了。

2. 同步与异步

从理论上说,阻塞I/O、I/O复用和信号驱动I/O都是同步I/O模型。因为在这三种I/O模型中,I/O的读写操作,都是在I/O事件发生之后,由应用程序来完成的。

而POSIX规范所定义的异步I/O模型则不同:

对异步I/O而言,用户可以直接对I/O执行读写操作,这些操作告诉内核 『用户读写缓冲区的位置,以及I/O操作完成之后内核通知应用程序的方式。』

异步I/O的读写操作总是立即返回,而不论I/O是否是阻塞的,因为真正的读写操作已经由内核接管。

也就是说:

  • 同步I/O模型要求用户代码自行执行I/O操作(将数据从内核缓冲区读入用户缓冲区,或将数据从用户缓冲区写入内核缓冲区)
  • 异步I/O机制则由内核来执行I/O操作(数据在内核缓冲区和用户缓冲区之间的移动是由内核在“后台”完成的)。

你也可以这样认为:

  • 同步I/O向应用程序通知的是I/O就绪事件
  • 异步I/O向应用程序通知的是I/O完成事件

Linux环境下,aio.h头文件中定义的函数提供了对异步I/O的支持

4种I/O模型的差异:


四.两种高效的事件处理模式

也是服务器编程框架中I/O处理单元的一个细分

服务器程序通常需要处理三类事件:

  • I/O事件
  • 信号
  • 定时事件

有两种高效的事件处理模式:ReactorProactor

同步I/O模型通常用于实现Reactor模式,异步I/O模型则用于实现Proactor模式。

1.Reactor模式

Reactor是这样一种模式,它要求主线程(I/O处理单元,下同)只负责监听文件描述上是否有事件发生,有的话就立即将该事件通知工作线程(逻辑单元,下同)。除此之外,主线程不做任何其他实质性的工作。读写数据,接受新的连接,以及处理客户请求均在工作线程中完成。

使用同步I/O模型(以epoll_wait为例)实现的Reactor模式的工作流程是:

  • 主线程往epoll内核事件表中注册socket上的读就绪事件
  • 主线程调用epoll_wait等待socket上有数据可读
  • 当socket上有数据可读时,epoll_wait通知主线程。主线程则将socket可读事件放入请求队列。
  • 睡眠在请求队列上的某个工作线程被唤醒,它从socket读取数据,并处理客户请求,然后往epoll内核事件表中注册该socket上的写就绪事件
  • 主线程调用epoll_wait等待socket可写。
  • 当socket可写时,epoll_wait通知主线程。主线程将socket可写事件放入请求队列。
  • 睡眠在请求队列上的某个工作线程被唤醒,它往socket上写入服务器处理客户请求的结果。

图5:Reactor模式的工作流程

上图工作线程从请求队列中取出事件后,将根据事件的类型来决定如何处理它:对于可读事件,执行读数据和处理请求的操作;对于可写事件,执行写数据的操作。因此,图所示的Reactor模式中,没必要区分所谓的“读工作线程”和“写工作线程”。

2.Proactor模式

与Reactor模式不同,Proactor模式将所有I/O操作都交给主线程和内核来处理,工作线程仅仅负责业务逻辑。因此,Proactor模式更符合图4所描述的服务器编程框架。

使用异步I/O模型(以aio_readaio_write为例)实现的Proactor模式的工作流程是:

  • 主线程调用aio_read函数向内核注册socket上的读完成事件,并告诉内核用户读缓冲区的位置,以及读操作完成时如何通知应用程序
  • 主线程继续处理其他逻辑。
  • 当socket上的数据被读入用户缓冲区后,内核将向应用程序发送一个信号,以通知应用程序数据已经可用。
  • 应用程序预先定义好的信号处理函数选择一个工作线程来处理客户请求。工作线程处理完客户请求之后,调用aio_write函数向内核注册socket上的写完成事件,并告诉内核用户写缓冲区的位置,以及写操作完成时如何通知应用程序(仍然以信号为例)。
  • 主线程继续处理其他逻辑。
  • 用户缓冲区的数据被写入socket之后,内核将向应用程序发送一个信号,以通知应用程序数据已经发送完毕。
  • 应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理,比如决定是否关闭socket。

图6:Proactor模式的工作流程

图6,连接socket上的读写事件是通过aio_read/aio_write向内核注册的,因此内核将通过信号来向应用程序报告连接socket上的读写事件。所以,主线程中的epoll_wait调用仅能用来检测监听socket上的连接请求事件,而不能用来检测连接socket上的读写事件。

3.使用同步I/O方式模拟Proactor模式

其原理是:主线程执行数据读写操作,读写完成之后,主线程向工作线程通知这一“完成事件”。那么从工作线程的角度来看,它们就直接获得了数据读写的结果,接下来要做的只是对读写的结果进行逻辑处理。 使用同步I/O模型(仍然以epoll_wait为例)模拟出的Proactor模式的工作流程如下:

  • 主线程往epoll内核事件表中注册socket上的读就绪事件。
  • 主线程调用epoll_wait等待socket上有数据可读。
  • 当socket上有数据可读时,epoll_wait通知主线程。主线程从socket循环读取数据,直到没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列。
  • 睡眠在请求队列上的某个工作线程被唤醒,它获得请求对象并处理客户请求,然后往epoll内核事件表中注册socket上的写就绪事件。
  • 主线程调用epoll_wait等待socket可写。
  • 当socket可写时,epoll_wait通知主线程。主线程往socket上写入服务器处理客户请求的结果。

图7 同步I/O模型模拟出的Proactor模式的工作流程


五.两种高效的并发模式

是逻辑单元的一个细分

并发编程的目的是让程序“同时”执行多个任务。

如果程序是计算密集型的,并发编程并没有优势,反而由于任务的切换使效率降低。但如果程序是I/O密集型的,比如经常读写文件,访问数据库等,则情况就不同了。

由于I/O操作的速度远没有CPU的计算速度快,所以让程序阻塞于I/O操作将浪费大量的CPU时间。如果程序有多个执行线程,则当前被I/O操作所阻塞的执行线程可主动放弃CPU(或由操作系统来调度),并将执行权转移到其他线程。这样一来,CPU就可以用来做更加有意义的事情(除非所有线程都同时被I/O操作所阻塞),而不是等待I/O操作完成,因此CPU的利用率显著提升。

  • 从实现上来说,并发编程主要有多进程和多线程两种方式
  • 服务器主要有两种并发编程模式:半同步/半异步(half-sync/half-async)模式和领导者/追随者(Leader/Followers)模式。

1.半同步/半异步模式

首先,半同步/半异步模式中的“同步”和“异步”与前面讨论的I/O模型中的“同步”和“异步”是完全不同的概念。

  • 在I/O模型中,“同步”和“异步”区分的是内核向应用程序通知的是何种I/O事件(是就绪事件还是完成事件),以及该由谁来完成I/O读写(是应用程序还是内核)
  • 在并发模式中,“同步”指的是程序完全按照代码序列的顺序执行;“异步”指的是程序的执行需要由系统事件来驱动。常见的系统事件包括中断、信号等。

图8.并发模式中的同步和异步读操作

那么什么是半同步/半异步模式呢??

按照同步方式运行的线程称为同步线程,按照异步方式运行的线程称为异步线程。

  • 显然,异步线程的执行效率高,实时性强,这是很多嵌入式程序采用的模型。但编写以异步方式执行的程序相对复杂,难于调试和扩展,而且不适合于大量的并发。
  • 同步线程则相反,它虽然效率相对较低,实时性较差,但逻辑简单。

因此,对于像服务器这种既要求较好的实时性,又要求能同时处理多个客户请求的应用程序,我们就应该同时使用同步线程和异步线程来实现,即采用半同步/半异步模式来实现。

半同步/半异步模式对应网络编程模型:

  • 同步线程用于处理客户逻辑,相当于图4中的逻辑单元;
  • 异步线程用于处理I/O事件,相当于图4中的I/O处理单元。

流程:

  • 异步线程监听到客户请求后,就将其封装成请求对象并插入请求队列中
  • 请求队列将通知某个工作在同步模式的工作线程来读取并处理该请求对象。

具体选择哪个工作线程来为新的客户请求服务,则取决于请求队列的设计。比如最简单的轮流选取工作线程的Round Robin算法,也可以通过条件变量或信号量来随机地选择一个工作线程。图9总结了半同步/半异步模式的工作流程。

图9:半同步/半异步模式的工作流程


(1).半同步/半反应堆(half-sync/half-reactive)模式

是半同步/半异步模式的变体

工作方式:

  • 异步线程只有一个,由主线程来充当, 负责监听所有socket上的事件
  • 如果监听socket上有可读事件发生(即有新的连接请求到来),主线程就接受之以得到新的连接socket,然后往epoll内核事件表中注册该socket上的读写事件。
  • 如果连接socket上有读写事件发生(即有新的客户请求到来或有数据要发送至客户端),主线程就将该连接socket插入请求队列中
  • 所有工作线程都sleep在请求队列上,当有任务到来时,它们将通过竞争(比如申请互斥锁)获得任务的接管权。这种竞争机制使得只有空闲的工作线程才有机会来处理新任务,这是很合理的。

图10:半同步/半反应堆模式

图10中,主线程插入请求队列中的任务是就绪的连接socket。这说明该图所示的半同步/半反应堆模式采用的事件处理模式是Reactor模式:它要求工作线程自己从socket上读取客户请求和往socket写入服务器应答。这就是该模式的名称中“half-reactive”的含义。

实际上,半同步/半反应堆模式也可以使用模拟的Proactor事件处理模式,即由主线程来完成数据的读写。在这种情况下,主线程一般会将应用程序数据、任务类型等信息封装为一个任务对象,然后将其(或者指向该任务对象的一个指针)插入请求队列。工作线程从请求队列中取得任务对象之后,即可直接处理之,而无须执行读写操作了。

半同步/半反应堆模式存在如下缺点:

  • 主线程和工作线程共享请求队列。主线程往请求队列中添加任务,或者工作线程从请求队列中取出任务,都需要对请求队列加锁保护,从而白白耗费CPU时间。
  • 每个工作线程在同一时间只能处理一个客户请求。如果客户数量较多,而工作线程较少,则请求队列中将堆积很多任务对象,客户端的响应速度将越来越慢。如果通过增加工作线程来解决这一问题,则工作线程的切换也将耗费大量CPU时间。

一种相对高效的半同步/半异步模式:它的每个工作线程都能同时处理多个客户连接

图11 高效的半同步/半异步模式

主线程只管理监听socket,连接socket由工作线程来管理。当有新的连接到来时,主线程就接受之并将新返回的连接socket派发给某个工作线程,此后该新socket上的任何I/O操作都由被选中的工作线程来处理,直到客户关闭连接。主线程向工作线程派发socket的最简单的方式,是往它和工作线程之间的管道里写数据。工作线程检测到管道上有数据可读时,就分析是否是一个新的客户连接请求到来。如果是,则把该新socket上的读写事件注册到自己的epoll内核事件表中。

每个线程(主线程和工作线程)都维持自己的事件循环,它们各自独立地监听不同的事件。因此,在这种高效的半同步/半异步模式中,每个线程都工作在异步模式,所以它并非严格意义上的半同步/半异步式中,每个线程都工作在异步模式,所以它并非严格意义上的半同步/半异步模式。

2.领导者/追随者模式

领导者/追随者模式是多个工作线程轮流获得事件源集合,轮流监听、分发并处理事件的一种模式。

在任意时间点,程序都仅有一个领导者线程,它负责监听I/O事件。而其他线程则都是追随者,它们休眠在线程池中等待成为新的领导者。

当前的领导者如果检测到I/O事件,首先要从线程池中推选出新的领导者线程,然后处理I/O事件。此时,新的领导者等待新的I/O事件,而原来的领导者则处理I/O事件,二者实现了并发。

领导者/追随者模式包含如下几个组件:

  • 句柄集(HandleSet)
  • 线程集(ThreadSet)
  • 事件处理器(EventHandler)
  • 具体的事件处理器(ConcreteEventHandler)

它们的关系如图12所示


(1).句柄集

句柄(Handle)用于表示I/O资源,在Linux下通常就是一个文件描述符。

句柄集管理众多句柄,它使用wait_for_event方法来监听这些句柄上的I/O事件,并将其中的就绪事件通知给领导者线程。领导者则调用绑定到Handle上的事件处理器来处理事件。领导者将Handle和事件处理器绑定是通过调用句柄集中的register_handle方法实现的。

(2).线程集

这个组件是所有工作线程(包括领导者线程和追随者线程)的管理者。它负责各线程之间的同步,以及新领导者线程的推选。

线程集中的线程在任一时间必处于如下三种状态之一:

  • Leader:线程当前处于领导者身份,负责等待句柄集上的I/O事件。
  • Processing:线程正在处理事件。领导者检测到I/O事件之后,可以转移到Processing状态来处理该事件,并调用promote_new_leader方法推选新的领导者;也可以指定其他追随者来处理事件(Event Handoff),此时领导者的地位不变。当处于Processing状态的线程处理完事件之后,如果当前线程集中没有领导者,则它将成为新的领导者,否则它就直接转变为追随者。
  • Follower:线程当前处于追随者身份,通过调用线程集的join方法等待成为新的领导者,也可能被当前的领导者指定来处理新的任务。

图13显示了这三种状态之间的转换关系


需要注意的是,领导者线程推选新的领导者和追随者等待成为新领导者这两个操作都将修改线程集,因此线程集提供一个成员Synchronizer来同步这两个操作,以避免竞态条件。

(3).事件处理器和具体的事件处理器

事件处理器通常包含一个或多个回调函数handle_event

这些回调函数用于处理事件对应的业务逻辑。事件处理器在使用前需要被绑定到某个句柄上,当该句柄上有事件发生时,领导者就执行与之绑定的事件处理器中的回调函数。具体的事件处理器是事件处理器的派生类。它们必须重新实现基类的handle_event方法,以处理特定的任务。

图14领导者/追随者模式的工作流程

由于领导者线程自己监听I/O事件并处理客户请求,因而领导者/追随者模式不需要在线程之间传递任何额外的数据,也无须像半同步/半反应堆模式那样在线程之间同步对请求队列的访问。但领导者/追随者的一个明显缺点是仅支持一个事件源集合,因此也无法像图11所示的那样,让每个工作线程独立地管理多个客户连接。

离线编写wordpress博客——windows live write

  • 心酸史

哥刚开始使用wordpress写博的时候,很兴奋的找了一堆插件,然后就登陆到后台开始写博客了,满腔热血,洋洋洒洒的写了一通!

要递交的时候,登陆缓存失效了,秀逗了就重新登陆,然后就没有然后了,,,,,,,不说了都是泪。于是求助谷哥君,找到了个离线写博神器,只能说,微软还是出了不少好软件的:WLW闪亮登场!

  • 快速入门

安装:

      啥都不说,下载 WLW,这个是windows live 包,去掉不需要的,傻瓜式安装好!

连接BLOG:

       打开软件,把博客站点地址设置好,

imageimage

你就可以开始写博客,再也不用担心网段了没保存,再也不用担心博客写完了,女票睡着了。。。。。。。

  • 进阶

安装好后,码码字足够了,但是我们是有高要求但很懒惰的程序猿,所以总是要加点小插件代码啥的高亮下才有逼格!

女票说要上传美美的图片,要加独一无二的签名,OK,接下来都给你弄好,满!足!不!满!足!!

高亮代码:

咱也就不装逼,就装个用的人最多的就OK,WP-Syntax是我们的选择。因为我的博客已经装过了,所以WLW下,咱也就装个 WP-Syntax Quick Insert用下就满足了,下好dll,放到安装目录的插件文件夹里面Windows Live\Writer\Plugins,你就可以用了,怎么高亮我就不说了,看这里的传送门,走你

整完自己的,那也给女票整个花哨的!

存好多的图片:

女票说我要上图片,高清的,超清的,可是咱的后台只有那了么点的空间,房子还是租的,还得攒钱买蜗牛壳啊!

土一点,但毕竟省空间,咱就先存到网上相册里面,然后通过地址直接导入网上的图片吧!

女票说,这样好烦啊,存这又存那,不玩了。那好吧,方法总比困难多,去找个免费的图床,然后

打开选项 – 账户 – 选中要设置的账户,点击编辑 – 图片 – FTP站点 – 更改FTP设置

把图床信息设置好,我们就可以愉快上图片了!

先写这么多吧,天黑了!!嘿嘿