Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15843 articles
Browse latest View live

设计趋势:网页设计中的幽灵按钮

$
0
0

我不确定我们是否意识到2014年最重大的趋势正在来临。它完全基于所有网站中微小的设计元素——按钮。

幽灵按钮——那些透明的、可点击的物体——忽然间就变得无处不在。以狂风暴雨之势席卷正网页设计领域。谁能想到,像按钮这么简单的事物,能够改变我们看待网页设计的方式?

参考: 预测网页设计趋势

 

什么是幽灵按钮?

幽灵按钮

nzk

幽灵按钮有着最简单的扁平外形——正方形、矩形、圆形、菱形——没有填充色,只有一条淡淡的轮廓。除了外框和文字,它完完全全(或者说几乎完全)透明。(因此得名“幽灵”)

这些按钮通常比网页上传统的可点击按钮大,也被置于显要位置,例如屏幕的正中央。

各种类型的网站(包括移动APP)中都能发现幽灵按钮的身影,它有着多种设计风格,却几乎都与单页网站有关联,还有那些极简风格或近扁平风格的设计方案。这种风格的按钮在全屏照片背景的网站中也大受欢迎,不像传统按钮那样,这种简洁的样式,是出于不干扰图片的考虑。

你有仔细观察过你的iPhone(运行iOS7)上的圆角按钮吗?每个设计元素都是幽灵按钮。

一位Designmodo设计师这样描绘这项新兴趋势:

“幽灵按钮的出现,从某种程度上来说,与人们热衷于在半透明全屏背景上放置界面与表单有关。于是,时机到来了,它在背景图片、产品形象和幽灵表单元素之间分配了用户的注意力,使得幽灵按钮不靠遮挡背景来体现自己的存在,却依然可见。”

设计元素

20 jeans

Couple

幽灵按钮有一系列典型的组成元素。尽管这并非完整的使用准则,但使用幽灵按钮时,其中多数都在发挥作用。

  • 按钮是中空的
  • 它外围有一圈轮廓,通常只有一点点厚度
  • 它包含了简短的文字
  • 颜色通常只有黑白
  • 按钮往往比传统按钮更大
  • 幽灵按钮一般占据页面显要位置
  • 幽灵按钮可以单独或成小组出现
  • 通常使用扁平或近扁平的设计方案
  • 幽灵按钮内部可以使用小的几何形图标,不过很少这么做

幽灵按钮的优势

Habr

Mixture

那么,是什么使得幽灵按钮如此管用?有必要把这项趋势用在你下个项目中吗?

幽灵按钮给人特别干净的外观和感受。简单自然的按钮,能使得页面的主体设计更加突出。(尤其在大幅图片上表现更好)

幽灵按钮几乎可以搭配任何设计方案,因为他们是透明的。这使得按钮可以从根本上承接周遭的设计特征。

幽灵按钮延续了“2013年度趋势—— 扁平设计”的演化。这样的设计趋势要成为当下主流,唯一的方法就是继续演变,接纳新的概念。

幽灵按钮提供了一种带有视觉惊喜的元素,因为这个按钮和用户预想的可不一样。

设计和创建幽灵按钮很简单。切记保持简约。幽灵按钮应该精细微妙,而非浮华炫目。

幽灵按钮无需过分扎眼,就足以创造出有感召力的视觉焦点。在众多网页设计中,幽灵按钮是屏幕上唯一的大型元素(这就是它如此有效的原因)。正因为如此,它抓住眼球,诱使用户点击按钮。而且这也是优秀用户界面的特征。

幽灵按钮有助于打造高端的设计风格。在设计中,简单往往就是高雅。

幽灵按钮的劣势

Creative ad

Exposure

虽说幽灵按钮在设计上体现出诸多优势,同时有些劣势也需要考虑。采用任何新趋势之前,都要掂量它的优势与劣势,然后决定是否在项目中使用。

幽灵按钮可能太溶于背景中,使用户产生困惑。并非所有用户都了解设计;有些人对难以识别非传统样式的按钮,也不会知道它是做什么的。

幽灵按钮在高对比度或者颜色丰富的图片上很难处理。这些按钮往往不是黑色就是白色。假如你用的背景图是黑白交替的,幽灵按钮几乎看不到,也无法阅读。

为了便于使用,幽灵按钮依赖特定尺寸与位置。放置按钮时要格外小心,让人容易发现它,并且不能遮盖图片的关键部分。

幽灵按钮有时会明显影响与它搭配的图片

幽灵按钮的文案比“点击此处”要复杂。其中的文字,需要足够清晰的构思与文案,并置于其余部分的上下文语境中。

幽灵按钮如今已无处不在。想要使自己看起来跟得上潮流趋势?选择时兴的事物前,请确保它确实对你的项目有帮助。

 

幽灵按钮的案例集

切记,把握任何趋势,关键是要用好它。正如一位Designmodo用户所说:

“我相信设计界诞生的每种趋势,都有它意义深远的用途。最重要的是不要沉迷其中,选择中庸之道。”

就此而言,它正是使用幽灵按钮和其他潮流趋势的关键所在。

接下来给你带来一组幽灵按钮的案例集,希望对你的创造力有所启发。这个案例集是从各种线上网站、进行中的项目、和Dribbble和Behance这类网站的作品集元素中收集而来。 (点击每张图片,可以查看来源深入了解)

Parrralax

Visage

Anyilu

Studio up

Audrey

off shore

Umbrella

Office

Feed & Sleep Tracker

Indiegogo

Paulvonexcite

Barclays

译者: 十萬個為什麼原作者:Carrie Cousins


© 推荐 for 互联网的那点事. | 猛击下载: iPhone客户端猛击下载: Android客户端


mongodb 实时监控工具motop

$
0
0

motop

mongodb实时监控工具,可以同时对多个MongoDB服务器进行监控。显示当前操作。 项目地址:https://github.com/tart/motop

使用

1. 使用easy_install安装
# wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py -O - | python
# easy_install motop
2. 源码安装
# wget https://github.com/tart/motop/archive/master.zip
# unzip master.zip
# ./setup.py install
3. 帮助
# motop -h
4. 监控多台
# motop 192.168.124.50 192.158.124.51

动作

q Quit p Pause e Explain the query k Kill operation using "mongo" executable K Kill operations older than given seconds using "mongo" executable r Try to reconnect to disconnected servers R Try to reconnect to all servers

依赖包

  • python 2.6 或以上
  • pymongo 2.0 或以上

配置(选项)

配置文件可以创建在/etc/motop.conf。可以有多个配置短,每一节都可以包含以下参数: address: 服务器的地址(必需) username: 登陆用户名 password:登陆用户密码 status:显示状态(默认开启) replicationInfo :显示复制状态(默认值:开启) replicaSet :显示副本集的状态(默认值:开启) operations:显示操作(默认值:开启) replicationOperations :不断展现主和从的复制操作(默认值:开启) “DEFAULT”是特殊的部分。本节参数可以设置为默认。 配置实例如下:
[MongoDB01]
address=10.42.2.121
replicationOperations=off

[MongoDB02]
address=10.42.2.122

[MongoDB03]
address=10.42.2.123

[MongoDB04]
address=10.42.2.124
username=foo
password=bar

实际使用

motop

MD5加密及第三方支付接口的技术比较

$
0
0

摘  要:第三方支付市场的发展前景乐观,但同时市场竞争也越来越激烈。随着第三方支付业务许可牌照的发放,第三方支付将很可能打破大型银行垄断电子金融的局面。本文将主要研究第三方支付的“网上支付接口”,比较分析各种不同的第三方支付接口的差异性。

关键词:电子支付 第三方支付 支付接口

 

电子支付是电子商务中重要的一个环节,其中第三方支付是电子支付的一种重要方式。随着第三方支付在网络交易中得到越来越广泛的使用,中国的互联网支付市场也得到发展迅速。根据Enfodesk易观智库数据报告显示,2010年中国第三方支付市场全年交易额达到11,342亿元,环比增长95%。同时,第三方支付市场依然保持较高的市场集中率, 支付宝以49%的份额占据半壁江山,支付宝、 财付通快钱Chinapay易宝支付五家企业占据整个市场的份额接近90%。根据《中华人民共和国中国人民银行法》等法律法规,中国人民银行制定了《非金融机构支付服务管理办法》,央行颁布《非金融机构支付服务管理办法》,通过申请支付牌照的方式把第三方支付企业正式纳入国家的监管体系下。2011年5月26日,中国人民银行发布公告称,已向国内27家单位颁发了首批非金融机构支付业务许可证。这27家机构包括支付宝、银联、财付通、 快钱盛付通、汇付天下等。

本文认为,第三方支付市场的发展前景乐观,但同时市场竞争也越来越激烈。随着第三方支付业务许可牌照的发放,第三方支付将很可能打破大型银行垄断电子金融的局面。本文将主要研究第三方支付的“网上支付接口”,比较分析各种不同的第三方支付接口的差异性。

1 支付接口定义及实现

  1.1支付接口定义

从技术角度讲,支付接口就是第三方支付平台提供的一段代码,商务需要将该代码配置到自己的服务器上去,并设置一些相关的接口参数。那么当客户选择使用第三方支付时,支付信息就会转到第三方支付平台的服务器上运行。


图1 第三方支付接口示意图 

具体来看,使用支付接口完成的支付流程如下:

(1)持卡客户(买方)选购好商品后,网上商城(卖方)为持卡客户生成订单;

(2)持卡客户和第三方服务器建立连接,将账号信息与订单信息发给第三方;

(3)第三方服务器要求顾客进行订单确认,收到确认信息后与所支持的银行进行支付交易处理,得到银行的支付确认后授权给商家可以发货;

(4)网上商城通知持卡客户发货信息。


图2 第三方支付流程图( 1.2支付接口实现

1.2.1实现条件

要实现接口,需要有一个网站、支付宝账户、支付宝的合作身份ID、安全校验码等。为了方便电子商务网站的集成,各支付网关在正式成为商家用户后,可下载ASP、.NET、JAVA等针对不同服务器类型的商务网站的集成接口程序。本文将分别以各种有代表性的第三方支付平台为例说明与JSP类型的电子商务网站的接口应用方法。

值得注意的是,参数简单的传过去是不行的,这些参数是经过了按一定排列顺序并区分大小写(参数为空也要传递)再加上一个MD5字符串。即使参数值为空字符串和没有传值也是有区别。

1.2.2实现过程

比如现在有一个页面要向第三方支付平台传递一个价格跟商品信息,则可以通过POST和GET两种方式传递进去。当第三方支付平台接收参数的同时,它还要判断身份,所以传递的时候身份信息也要一起传过去。当身份确认以后,第三方支付平台就开始处理商品信息跟价格信息了。无论消费者是否支付成功,它都会返回一个信息给消费者,这个返回页面就是第三方支付平台之前设置好了的,在这个返回页面里面写入相关的支付数据信息,这样就完成了一个简单支付接口。

2支付接口对比

目前市场上的第三方支付平台的运营模式可以将分为二种类型:独立的和非独立的第三方支付模式。(1)具备担保功能的非独立第三方支付模式,也称为信用中介型模式。该种运营模式,基本是由大型的电子交易平台独立开发或与其他投资人共同开发,凭借运营商的实力和信誉与各大银行合作,同时能够为买卖双方提供中间担保的第三方支付运营模式。这种模式的运营商主要是借助电子交易平台和中间担保支付平台与用户开展业务,在交易过中采用充当信用中介的模式,保证交易的正常进行。(2)独立的第三方网关模式,是指没有自己的电子商务交易网站,由第三方投资机构为网上签约商户提供围绕订单和支付等多种增值服务的共享平台。

根据第三方平台运营的两种模式,本文以非独立的第三方网关模式(支付宝)和第三方独立的网关模式(易宝)为比较对象,从开发环境、传递参数和安全性等三个方面进行比较分析。

2.1开发环境

在软件方面,为了方便电子商务网站的集成,各支付网关在用户正式成为商家用户后,都可以下载.NET、ASP、JAVA(JSP)、PHP等针对不同服务器类型的商务网站的集成接口程序。在硬件方面,基于高可靠硬件设备打造高性能平台,基于JAVA等语言开发高效安全的应用程序,系统采用分层架构,关键设备全部采用热备冗余,确保业务的不间断服务。

      2.  2传递参数

如在1.2.2小节中描述的支付实现过程,商家和第三方支付平台之间要通过支付接口传递一系列的购物信息,包括商品信息、用户信息等。本节将主要比较商家发送支付请求和商户接收支付完成数据的参数信息。

2.2.1商家发送支付请求相关参数

支付宝提供给商家的“发送支付请求”的接口参数共23项,易宝提供给商家的“发送支付请求”的接口参数共20项。请求参数信息(包括名称、含义、长度、是否为空、说明)不在本文做详细赘述,并将各种类型的信息进行分类描述,具体见表1。

 

交易信息

付完款后跳转的页面、交易过程中服务器通知的页面、防钓鱼时间戳、超时时间、字符编码格式、加密方式

订单信息

订单号、订单名称、订单描述、订单详细、订单备注、订单总金额、支付方式、网银代号

商品信息

展示网址

买家信息

买家支付宝账号、买家本地电脑的IP地址

卖家信息

卖家支付宝账号、合作ID、提成类型、提成信息集、安全校验码

其他信息

自定义参数

交易信息

交易签名串、请求命令、业务类型、是否需要应答机制、签名数据、交易请求地址、交易结果通知地址

订单信息

订单号、支付金额、交易币种、银行编号

商品信息

商品ID、名称、种类、描述

货运信息

是否需要填写送货信息

卖家信息

商户密钥、商户编号、商户扩展信息、退货地址

2.2.2商户接收支付完成数据的相关参数

支付宝提供给商家的“接收支付完成数据”的接口参数共10项,易宝提供给商家的“接收支付完成数据”的接口参数共18项。接收参数信息也不在本文做详细赘述,并将各种类型的信息进行分类描述,具体见表2。

 

交易信息

支付宝交易号、交易状态、验证状态、签名数据、通知任务ID

订单信息

订单号、订单金额

商品信息

商品标题、商品描述

买家信息

买家账号

交易信息

签名数据、业务类型、支付结果、易宝支付交易流水号、交易结果返回类型、支付成功时间、交易结果通知时间

订单信息

订单号、订单金额、支付金额、交易币种、银行编号、银行定单号

商品信息

名称、种类、描述

卖家信息

商户编号、商户密钥

 

从以上的支付请求参数表和支付返回参数表可以看出,虽然各种支付接口在参数的具体设置方面存在一定的差异,但是将各个参数进行分门别类之后,基本上都包括交易信息、订单信息、商品信息、买家信息、买家信息这五类信息。

本文由此认为,各种支付接口在传递参数的设置上存在一定的“同质化”现象。

2.3安全性能

系统安全体现在三个方面:系统层、应用层、运营层。(1)系统层平台部署有安全数据中心、防火墙、入侵检测等等多种安全设备,有专业的信息安全团队负责每天对系统进行安全分析;(2)应用层通过加密以及电子签名等多种身份鉴别、认证、访问控制以及安全变成等手段,力求将自产品逻辑设计和程序代码漏洞的风险降到最低;(3)运营层则利用自主开发的安全监控系统,对每笔交易进行实时监控,同时提供全天24小时的运营服务。本文节将主要从“应用层”来比较两种支付接口的安全性。

2.3.1参数加密算法

 

mysign = AlipayBase.BuildMysign(AlipayBase.ParaFilter(sPara),  key)

把参数存入Map,将其中空值和签名参数除去;再按照“参数=参数值”的模式用“&”字符拼接成字符串,最后把拼接后的字符串再与安全校验码直接连接起来。

// MD5加密方法,key为支付平台发放给商户的“密钥”

Hmac = PaymentForOnlineService. getReq Md5HmacForOnlinePayment

(String messageType, String merchantId, String orderId, String amount, String currency,

String productId, String productCat, String productDesc, String merchantCallbackURL,

String addressFlag, String sMctProperties, String needResponse, String frpID, String keyValue)

// MD5-HMAC加密方法,keyValue为支付平台发放给商户的“密钥”

注意:参数简单的传递是不行的,参数是经过一定排列顺序并区分大小写再加上一个MD5字符串。

从以上的支付加密信息表可以看出,一方面,两种支付接口基本上都采用的是MD5加密算法,该算法有很高的安全性;另一方面,在加密参数的具体设置上都使用了由支付平台发放给商户的“密钥”。

本文由此认为,各种支付接口在加密算法的选择上也存在一定的“同质化”现象。

2.3.2链接安全性

本文通过一个具体支付实例,来具体分析链接的安全性。

https://www.alipay.com/payto:merchanttool@alipay.com?cmd=0001

& subject=%D9%AA%C2%DE%BC%CD%B9%AB%D4%B0

& body=I%2CII%2CIII+%BC%AF& order_no=zoo00001& price=100.00

& ordinary_fee=5.00& express_fee=10.00

& ac=4d5010376411f5042868f238e8613c9b

https://www.yeepay.com/app-merchant-proxy/trxData.action?

paymentId=363526768& date=20101120210158

& sign=7b3a57af221df17b33a4705c5b205b4f

从以上的支付链接信息表可以看出,易宝的链接安全性较高,而支付宝在链接中明显透露了支付费用的部分信息。本文由此认为,两种支付接口在支付信息的安全性上都做的较为到位,支付技术较为安全,消费者可以比较放心使用。当然,本文并不排除这里没有发现的其他一些安全隐患。

3 未来展望

综上所述,第三方支付接口在技术实现上存在很多“同质化”现象。本文认为,技术上的“同质化”将降低竞争力。第三方支付应该在进一步完善支付接口的同时,进行一定的“商业模式”创新,提高行业竞争力。据本文调研发现,类似从综合搜索到垂直搜索、从综合网站到行业网站的发展趋势,目前,第三方支付也走上了“市场细分”的道路,互联网支付细分市场已经成为战略竞争的重点。以支付宝、财付通等为代表的第三方支付厂商继续拓展公共事业缴费业务、航空客票等领域。易宝支付等则在电信、教育交费等领域另辟蹊径。本文认为,走市场细分道路固然能够开拓新的市场,但不能在本质上提高行业竞争力,今天的“蓝海”很可能成为明天的“红海”。

从第三方支付市场上的具体行为来看,早在2007年淘宝网就率先推出的“卖家先行赔付”机制,不失为一种可贵的商业模式创新,在客观上起到了确保消费者利益的作用。2011年3月15日前夕,中国工商银行联合深圳本土购物网站走秀网设立专项赔付基金,推出先行赔付服务。随着银行开始介入网购售后领域,第三方支付市场将会面临更多的外部竞争压力。

本文没有对“先行赔付”机制的现实成效进行深入研究,但可以肯定,“先行赔付”(甚至“先行全额赔付”)将成为未来网络支付市场非常重要的一项竞争力指标。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



web开发利器之grunt

$
0
0

伴随着项目的多样化和复杂化,前端的代码维护和管理(打包、压缩等等)也越来越难以维护,人为的疏忽往往会导致不可预期的错误,对于这样的错误给我们带来了很多的麻烦和多余的工作量;对于前端的项目携同开发来说,我们每个人均会负责不同板块,将JS、CSS拆解成各自个的模块单独开发,为了方便管理这些模块均为独立的文件,而为了页面加载速度(增加了文件个数这时也产生过多的Http请求,影响页面加载速度)我们又不得不将这些文件进行合并、压缩等等,早期我们会用YUI做这些事但如今我们需要的却更多(如项目的源代码做单元测试和回归测试,以及JS语法自动化检测等等)YUI开始无法满足我们需求,这时grunt横空出世,它众多插件和灵活性满足了我们的需求,对于需要反复重复的任务,例如压缩、编译、单元测试、代码检查等,轻而易举的完成,今天主要说下grun的用法。

grunt不难,它主要依赖的是nodeJS的npm包管理器,和一个JSON及一个JS文件,先说说npm包管理器,玩过nodeJS的对它应该都很熟悉,在这里我们只需要安装nodeJS即可(新版的nodeJS基本都集成了npm),至于nodeJS的安装可以 点这里,这这篇文章就不做详细介绍,安装完后打开命令管理器(nodeJS安装完后的终端)输入:

npm install -g grunt-cli

在grunt中安装全局命令接口;

然后说说JSON,确切的说应该是package.json,它主要用于安装grunt依赖的插件,和nodeJS的pakeage.json一样,在执行npm install时,它就会作为主入口,npm通过这JSON中的devDependencies去安装这些插件,具体的关于package大家可以 点击这里看看。

最后也就是最重要和最难的gruntfile.js了,它是用于配置和定义任务的一个主文件,grunt运行任务接口主要就时依赖它了,它是由包含函数、任务配置、任务加载、自定义任务四部分组成,如下:

// 包装函数
module.exports = function(grunt) {

  // 任务配置
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    uglify: {
      build: {
        src: 'src/<%= pkg.name %>.js',
        dest: 'build/<%= pkg.name %>.min.js'
      }
    }
  });

  // 任务加载
  grunt.loadNpmTasks('grunt-contrib-uglify');

  // 自定义任务
  grunt.registerTask('default', ['uglify']);

};

其中包装函数没什么好说的,它和backbone这样的写法差不多,主要用于定义一个输出模块,在这个模块中处理grunt的相关事务;而任务配置大家可以看看 这里;任务加载则是加载你需要的插件如concat、jshint、uglify(合并、检测、压缩);自定义任务则是自定义grunt的执行命令;

具体的大家可以 点击这里,把代码clone到本地自己执行跑到看下,毕竟手动理解会比文字描述容易明白到多。好了今天写到这里吧,其实这篇文章描述多不是很多,给的更多是外链和自己git上的一个测试代码,多动动手再去结合理解比光看不练的好,大家想深入看看可以看下grunt的官网,另外如果碰到什么可以mail我大家交流。最后各位大大晚安

作者:nx8823520 发表于2014/7/8 0:01:23 原文链接
阅读:101 评论:0 查看评论

你可能并不需要消息队列

$
0
0

我是一个极简主义者,我不喜欢过早或者没必要地让软件复杂化。而往软件系统中添加组件就是严重增加复杂性的一种做法。我们来拿消息队列举个例子。

消息队列是一个能让你获得容错性,分布式,解耦等架构能力的系统。纸上谈兵的话,它看起来还不错。

或许消息列队在你的应用中有不少适用的场景。你可以看下这篇关于消息队列优点的 文章,看看到底有哪些合适的场景。但可不要因为说"能解耦那太好了”就轻易使用它。我们来看一个例子——你希望你的邮件发送和订单处理互相解耦。因此你发送一个消息到消息队列里,然后邮件处理系统取出这个消息并发送邮件。那你在一个独立的单classpath的应用中怎么实现呢?让你的订单处理服务依赖于一个邮件服务,然后调用sendEmail()方法,而不是sendToMQ()方法。如果你使用了消息队列,你需要定义一个两个系统都能识别的消息格式 ;如果你不使用消息队列,那么你得定义一个方法签名。它们有什么本质的区别吗?其实没有。

不过你可能还有别的消费者想要对某个指定的消息进行额外的处理?这的确是可能发生的,而并不只是针对我们这里说到的这个项目而已。尽管确有可能,但相比添加另一个方法调用而言,它可能并不值当。耦合?是的。不过这个耦合并没有什么不方便的。

那我应该如何处理峰值流量?你可以通过消息队列将请求放到一个持久化队列中,然后再一并处理它们。这是一个非常有用的特性,不过它也受限于几个 因素——你的请求是在UI后台处理,还是需要即时响应?serlvet容器的线程池某种程度上可以当作是一个队列,用户最终会拿到响应,但是得需要等待(如果线程的超时时间过短的话,请求可能会丢失)。你可以使用一个内存队列来存储那些较重的请求(得在UI后台进行处理)。不过注意了,你的队列并不是默认高可用的。比如说,如果一个消息队列节点挂掉了,你的消息就丢失了。因此,不去使用应用节点内的内存队列,而是去使用一个消息队列,这可能并没有什么优势。

消息队列使得我们可以进行异步处理——这的确是个有用的特性。你不希望在用户等待的时候做一些很重的操作。不过你也可以使用一个内存队列,或者简单地启动一个新的线程(比如Spring的@Async注解)。这样又有另一个问题——如果消息丢失的话是否有问题?如果你应用处理请求的节点挂了,你可以进行恢复吗?你会发现这事会经常发生,如果不保证所有消息都处理到的话,很难保证功能的正确性。因此,仅将较重的调用进行异步处理是比较可取的。

把消息放到队列以便让另一个组件来进行处理,对于这个场景,如果消息丢失是无法接受的 ,这也有一个很简单的解决方案——数据库。你可以把一条processed=false的数据存储到数据库中。然后再运行一个调度作业,将所有未处理的记录挑选出来,异步地进行处理。当处理完成的时候,将标记设为true。我经常用这个方法,包括在一些大型的线上系统中,它也工作得挺好的。

这样你还能不断地对你的应用节点进行扩展,只要它们的内存中没有任何的持久化状态的话。不管你是否使用了消息队列都可以(临时的内存处理队列并不属于持久化状态)。

为什么我要给经常用到的消息队列提供一些备选方案?因为如果你由于不恰当的原因选择了它,那么消息队列可能会成为一个负担。它们并非如想像中那样容易使用。首先,它有一个学习曲线。一般来说,你集成的组件切分得越多,就越容易出现问题。其次,还有一个设置及配置的成本。比如说,当消息队列需要在一个集群中运行的话,比如说多个数据中心,那么这就变得复杂了。高可用性并不是上来就有的——默认它是不会打开的。还有就是你的应用节点如何连接到消息队列?通过一个刷新的连接池,或者使用短生命周期的DNS记录,还是通过一个负载均衡器?你的队列可能还有许多配置项,大小是多少,行为是怎样的(消费者需不需要确认接受,要不要通知处理失败,多个消费者能够取到同一个消息吗,消息有没有TTL,等等)同时还有网络及消息传递的开销,尤其是现在大家都喜欢用XML或者JSON来传输消息。如果你过度地使用了消息队列,那么它会增加你系统的延时。最后一点,但并不是最次要的——如果出现问题的话,使用消息队列会让问题跟踪变得异常困难。你没法在IDE中看到所谓的调用层次,因为一旦你发送消息到队列里了,你就得自己去查找它在哪里处理的了。这可不是听起来那么简单的。你看到了吧,它会给你增加许多的复杂性,以及许多需要注意的东西。

通常而言,在某些上下文中,消息队列还是非常有用的。当它们的确适合的话,我也会在项目中使用它们——比方说,我们不想丢失消息,但又希望能快速地进行处理。我也见过它在一些不太常见的场景中使用的情况,比如说只有一个应用节点来进行消费,不管是哪个节点投递过来的消息。你还可以看下stackoverflow上的 这个问题。还有一些使用场景就是,或许你的确需要进行多语言间的通信,又或者你的数据流已经过于复杂了,不使用新的消息消费者而是增加新方法调用的话代价会很大。

我想说的是那句老掉牙的真理“杀鸡焉用牛刀”。如果你不是很确定已经没有别的更容易管理和维护的方法,一定要使用消息队列的话,最好不要使用它。不要因为”万一它有用呢“而去用它——只有你确实觉得需要的话再去使用。因为很有可能,就像这里说到的这个项目一样,消息队列其实是没有必要的。

原创文章转载请注明出处: 你可能并不需要消息队列

英文原文链接

android图片压缩方法

$
0
0
第一:我们先看下质量压缩方法
private Bitmap compressImage(Bitmap image) {  
        ByteArrayOutputStream baos = new ByteArrayOutputStream();  
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中  
        int options = 100;  
        while ( baos.toByteArray().length / 1024>100) {  //循环判断如果压缩后图片是否大于100kb,大于继续压缩         
            baos.reset();//重置baos即清空baos  
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中  
            options -= 10;//每次都减少10  
        }  
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中  
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片  
        return bitmap;  
    }  
 
第二:图片按比例大小压缩方法(根据路径获取图片并压缩)
private Bitmap getimage(String srcPath) {  
        BitmapFactory.Options newOpts = new BitmapFactory.Options();  
        //开始读入图片,此时把options.inJustDecodeBounds 设回true了  
        newOpts.inJustDecodeBounds = true;  
        Bitmap bitmap = BitmapFactory.decodeFile(srcPath,newOpts);//此时返回bm为空  
          
        newOpts.inJustDecodeBounds = false;  
        int w = newOpts.outWidth;  
        int h = newOpts.outHeight;  
        //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为  
        float hh = 800f;//这里设置高度为800f  
        float ww = 480f;//这里设置宽度为480f  
        //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可  
        int be = 1;//be=1表示不缩放  
        if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放  
            be = (int) (newOpts.outWidth / ww);  
        } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放  
            be = (int) (newOpts.outHeight / hh);  
        }  
        if (be <= 0)  
            be = 1;  
        newOpts.inSampleSize = be;//设置缩放比例  
        //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了  
        bitmap = BitmapFactory.decodeFile(srcPath, newOpts);  
        return compressImage(bitmap);//压缩好比例大小后再进行质量压缩  
    }  
 
第三:图片按比例大小压缩方法(根据Bitmap图片压缩)
private Bitmap comp(Bitmap image) {      
    ByteArrayOutputStream baos = new ByteArrayOutputStream();         
    image.compress(Bitmap.CompressFormat.JPEG, 100, baos);  
    if( baos.toByteArray().length / 1024>1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出    
        baos.reset();//重置baos即清空baos  
        image.compress(Bitmap.CompressFormat.JPEG, 50, baos);//这里压缩50%,把压缩后的数据存放到baos中  
    }  
    ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());  
    BitmapFactory.Options newOpts = new BitmapFactory.Options();  
    //开始读入图片,此时把options.inJustDecodeBounds 设回true了  
    newOpts.inJustDecodeBounds = true;  
    Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);  
    newOpts.inJustDecodeBounds = false;  
    int w = newOpts.outWidth;  
    int h = newOpts.outHeight;  
    //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为  
    float hh = 800f;//这里设置高度为800f  
    float ww = 480f;//这里设置宽度为480f  
    //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可  
    int be = 1;//be=1表示不缩放  
    if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放  
        be = (int) (newOpts.outWidth / ww);  
    } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放  
        be = (int) (newOpts.outHeight / hh);  
    }  
    if (be <= 0)  
        be = 1;  
    newOpts.inSampleSize = be;//设置缩放比例  
    //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了  
    isBm = new ByteArrayInputStream(baos.toByteArray());  
    bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);  
    return compressImage(bitmap);//压缩好比例大小后再进行质量压缩  
}  
作者:chuangzaozhe1 发表于2014/7/7 23:05:42 原文链接
阅读:98 评论:0 查看评论

HTML5 Canvas(画布)教程 – 图像处理

$
0
0

Canvas标记很多年前就被当作一个新的HTML标记成员加入到了HTML5标准中。在此之前,人们要想实现动态的网页应用,只能借助于第三方的 插件,比如Flash或Java,而引入了Canvas标记后,人们直接打通了通往神奇的动态应用网页的大门。本教程内容只覆盖了一小部分、但却是非常重 要的canvas标记的应用功能——图像显示和处理。

 

图像来源

最常见的在canvas上画图的方法是使用Javascript Image对象。所支持的来源图片格式依赖于浏览器的支持,然而,一些典型的图片格式(png,jpg,gif等)基本上都没有问题。

 

图片可以从DOM中已经加载的元素中抓取,也可以按需即时创建。

// 抓取页面上已有的图片。
var myImage = document.getElementById(‘myimageid’);// 或创建一个新图片。
myImage = new Image();
myImage.src = ‘image.png’;

 

大多数支持canvas标记的浏览器的当前版本中,当图片还没有加载完成时,如果你要去画它,结果是什么事情都不会发生。也就是说,如果你想画一个图片,你需要等它完全加载。你可以使用图片对象的onload函数来进行判断。

// Create an image.
myImage = new Image();
myImage.onload = function() {
// Draw image.
}
myImage.src = ‘image.png’;

 

在下面的所有例子中,我们的图片源将会使用这个256×256尺寸的大猩猩。

大猩猩图片源

 

基本绘画

在最基本的画图操作中,你需要的只是希望图像出现处的位置(x和y坐标)。图像的位置是相对于其左上角来判断的。使用这种方法,图像可以简单的以其原尺寸被画在画布上。

drawImage(image, x, y)

var canvas = document.getElementById(‘myCanvas’);
var ctx = canvas.getContext(’2d’);
ctx.drawImage(myImage, 50, 50);
ctx.drawImage(myImage, 125, 125);
ctx.drawImage(myImage, 210, 210);

 

基本画布绘图输出

缩放及调整尺寸

改变图像的尺寸,你需要使用重载的drawImage函数,提供给它希望的宽度和高度参数。

drawImage(image, x, y, width, height)

var canvas = document.getElementById(‘myCanvas’);
var ctx = canvas.getContext(’2d’);
ctx.drawImage(myImage, 50, 50, 100, 100);
ctx.drawImage(myImage, 125, 125, 200, 50);
ctx.drawImage(myImage, 210, 210, 500, 500);

 

这个例子演示了如何画一个比原图小的图像,一个不同长宽比的图像和一个比原图大的图像的方法。

画布裁剪图像

 

图像裁剪

最后一个drawImage方法的功用是对图像进行裁剪。

drawImage(image,
sourceX,
sourceY,
sourceWidth,
sourceHeight,
destX,
destY,
destWidth,
destHeight)

 

参数很多,但基本上你可以把它想成从原图中取出一个矩形区域,然后把它画到画布上目标区域里。

画布裁剪例子

 

 

 

 

var canvas = document.getElementById(‘myCanvas’);
var ctx = canvas.getContext(’2d’);
ctx.drawImage(myImage, 0, 0, 50, 50, 25, 25, 100, 100);
ctx.drawImage(myImage, 125, 125, 100, 100, 125, 125, 150, 150);
ctx.drawImage(myImage, 80, 80, 100, 100, 250, 250, 220, 220);

 

画布裁剪

这些就是HTML5中的canvas(画布)标记里进行绘图和处理图像的基本操作。绘图只是canvas能提供的功能之一,将来我们会发布更多的关于这方面的教程,会介绍关于这个标记的更多的特征和功能。如果你有任何的问题和想法,请在下面评论的写出来。

 

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



为什么要使用 Go 语言,Go 语言的优势在哪里?

$
0
0
我尝试来回答你几个问题:
1、Go有什么优势
  • 可直接编译成机器码,不依赖其他库,glibc的版本有一定要求,部署就是扔一个文件上去就完成了。
  • 静态类型语言,但是有动态语言的感觉,静态类型的语言就是可以在编译的时候检查出来隐藏的大多数问题,动态语言的感觉就是有很多的包可以使用,写起来的效率很高。
  • 语言层面支持并发,这个就是Go最大的特色,天生的支持并发,我曾经说过一句话,天生的基因和整容是有区别的,大家一样美丽,但是你喜欢整容的还是天生基因的美丽呢?Go就是基因里面支持的并发,可以充分的利用多核,很容易的使用并发。
  • 内置runtime,支持垃圾回收,这属于动态语言的特性之一吧,虽然目前来说GC不算完美,但是足以应付我们所能遇到的大多数情况,特别是Go1.1之后的GC。
  • 简单易学,Go语言的作者都有C的基因,那么Go自然而然就有了C的基因,那么Go关键字是25个,但是表达能力很强大,几乎支持大多数你在其他语言见过的特性:继承、重载、对象等。
  • 丰富的标准库,Go目前已经内置了大量的库,特别是网络库非常强大,我最爱的也是这部分。
  • 内置强大的工具,Go语言里面内置了很多工具链,最好的应该是gofmt工具,自动化格式化代码,能够让团队review变得如此的简单,代码格式一模一样,想不一样都很困难。
  • 跨平台编译,如果你写的Go代码不包含cgo,那么就可以做到window系统编译linux的应用,如何做到的呢?Go引用了plan9的代码,这就是不依赖系统的信息。
  • 内嵌C支持,前面说了作者是C的作者,所以Go里面也可以直接包含c代码,利用现有的丰富的C库。
2、Go适合用来做什么
  • 服务器编程,以前你如果使用C或者C++做的那些事情,用Go来做很合适,例如处理日志、数据打包、虚拟机处理、文件系统等。
  • 分布式系统,数据库代理器等
  • 网络编程,这一块目前应用最广,包括Web应用、API应用、下载应用、
  • 内存数据库,前一段时间google开发的groupcache,couchbase的部分组建
  • 云平台,目前国外很多云平台在采用Go开发,CloudFoundy的部分组建,前VMare的技术总监自己出来搞的apcera云平台。
3、Go成功的项目
nsq:bitly开源的消息队列系统,性能非常高,目前他们每天处理数十亿条的消息
docker:基于lxc的一个虚拟打包工具,能够实现PAAS平台的组建。
packer:用来生成不同平台的镜像文件,例如VM、vbox、AWS等,作者是vagrant的作者
skynet:分布式调度框架
Doozer:分布式同步工具,类似ZooKeeper
Heka:mazila开源的日志处理系统
cbfs:couchbase开源的分布式文件系统
tsuru:开源的PAAS平台,和SAE实现的功能一模一样
groupcache:memcahe作者写的用于Google下载系统的缓存系统
god:类似redis的缓存系统,但是支持分布式和扩展性
gor:网络流量抓包和重放工具
以下是一些公司,只是一小部分:
下面列出来了一些使用的用户
GoUsers - go-wiki - A list of organizations that use Go.
4、Go还存在的缺点
以下缺点是我自己在项目开发中遇到的一些问题:
  • Go的import包不支持版本,有时候升级容易导致项目不可运行,所以需要自己控制相应的版本信息
  • Go的goroutine一旦启动之后,不同的goroutine之间切换不是受程序控制,runtime调度的时候,需要严谨的逻辑,不然goroutine休眠,过一段时间逻辑结束了,突然冒出来又执行了,会导致逻辑出错等情况。
  • GC延迟有点大,我开发的日志系统伤过一次,同时并发很大的情况下,处理很大的日志,GC没有那么快,内存回收不给力,后来经过profile程序改进之后得到了改善。
  • pkg下面的图片处理库很多bug,还是使用成熟产品好,调用这些成熟库imagemagick的接口比较靠谱


最后还是建议大家学习Go,这门语言真的值得大家好好学习,因为它可以做从底层到前端的任何工作。

学习Go的话欢迎大家通过我写的书来学习,我已经开源在github:
astaxie/build-web-application-with-golang · GitHub

还有如果你用来做API开发或者网络开发,那么我做的开源框架beego也许适合你,可以适当的来学习一下:
astaxie/beego · GitHub

— 完 —
本文作者: asta谢

【知乎日报】 你都看到这啦,快来点我嘛 Σ(▼□▼メ)

此问题还有 17 个回答,查看全部。
延伸阅读:
为什么 Go 语言的 hello world 输出的是汉字「Hello, 世界」?
Scala 是一门怎样的语言,具有哪些优势?

利用keepalived构建高可用MySQL-HA(转)

$
0
0

关于MySQL-HA,目前有多种解决方案,比如heartbeat、drbd、mmm、共享存储,但是它们各有优缺点。heartbeat、drbd配置较为复杂,需要自己写脚本才能实现MySQL自动切换,对于不会脚本语言的人来说,这无疑是一种脑裂问题;对于mmm,生产环境中很少有人用,且mmm 管理端需要单独运行一台服务器上,要是想实现高可用,就得对mmm管理端做HA,这样无疑又增加了硬件开支;对于共享存储,个人觉得MySQL数据还是放在本地较为安全,存储设备毕竟存在单点隐患。使用MySQL双master+keepalived是一种非常好的解决方案,在MySQL-HA环境中,MySQL互为主从关系,这样就保证了两台MySQL数据的一致性,然后用keepalived实现虚拟IP,通过keepalived自带的服务监控功能来实现MySQL故障时自动切换。

下面,我把即将上线的一个生产环境中的架构与大家分享一下,看一下这个架构中,MySQL-HA是如何实现的,环境拓扑如下

  1. MySQL-VIP:192.168.1.200  
  2. MySQL-master1:192.168.1.201  
  3. MySQL-master2:192.168.1.202  
  4.  
  5. OS版本:CentOS 5.4  
  6. MySQL版本:5.0.89  
  7. Keepalived版本:1.1.20 

一、MySQL master-master配置

1、修改MySQL配置文件

两台MySQL均如要开启binlog日志功能,开启方法:在MySQL配置文件[MySQLd]段中加上log-bin=MySQL-bin选项

两台MySQL的server-ID不能一样,默认情况下两台MySQL的serverID都是1,需将其中一台修改为2即可

2、将192.168.1.201设为192.168.1.202的主服务器

在192.168.1.201上新建授权用户

  1. MySQL> grant replication slave on *.* to 'replication'@'%' identified by 'replication';  
  2. Query OK, 0 rows affected (0.00 sec)  
  3.  
  4. MySQL> show master status;  
  5. +------------------+----------+--------------+------------------+  
  6. | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |  
  7. +------------------+----------+--------------+------------------+  
  8. | MySQL-bin.000003 |      374 |              |                  |   
  9. +------------------+----------+--------------+------------------+  
  10. 1 row in set (0.00 sec) 

在192.168.1.202上将192.168.1.201设为自己的主服务器

  1. MySQL> change master to master_host='192.168.1.201',master_user='replication',master_password='replication',master_log_file='MySQL-bin.000003',master_log_pos=374;  
  2. Query OK, 0 rows affected (0.05 sec)  
  3.  
  4. MySQL> start slave;  
  5. Query OK, 0 rows affected (0.00 sec)  
  6.  
  7. MySQL> show slave status\G  
  8. *************************** 1. row ***************************  
  9.              Slave_IO_State: Waiting for master to send event  
  10.                 Master_Host: 192.168.1.201  
  11.                 Master_User: replication  
  12.                 Master_Port: 3306  
  13.               Connect_Retry: 60  
  14.             Master_Log_File: MySQL-bin.000003  
  15.         Read_Master_Log_Pos: 374  
  16.              Relay_Log_File: MySQL-master2-relay-bin.000002  
  17.               Relay_Log_Pos: 235  
  18.       Relay_Master_Log_File: MySQL-bin.000003  
  19.            Slave_IO_Running: Yes  
  20.           Slave_SQL_Running: Yes  
  21.             Replicate_Do_DB:   
  22.         Replicate_Ignore_DB:   
  23.          Replicate_Do_Table:   
  24.      Replicate_Ignore_Table:   
  25.     Replicate_Wild_Do_Table:   
  26. Replicate_Wild_Ignore_Table:   
  27.                  Last_Errno: 0  
  28.                  Last_Error:   
  29.                Skip_Counter: 0  
  30.         Exec_Master_Log_Pos: 374  
  31.             Relay_Log_Space: 235  
  32.             Until_Condition: None  
  33.              Until_Log_File:   
  34.               Until_Log_Pos: 0  
  35.          Master_SSL_Allowed: No  
  36.          Master_SSL_CA_File:   
  37.          Master_SSL_CA_Path:   
  38.             Master_SSL_Cert:   
  39.           Master_SSL_Cipher:   
  40.              Master_SSL_Key:   
  41.       Seconds_Behind_Master: 0  
  42. 1 row in set (0.00 sec) 

3、将192.168.1.202设为192.168.1.201的主服务器

在192.168.1.202上新建授权用户

  1. MySQL> grant replication slave on *.* to 'replication'@'%' identified by 'replication';  
  2. Query OK, 0 rows affected (0.00 sec)  
  3.  
  4. MySQL> show master status;  
  5. +------------------+----------+--------------+------------------+  
  6. | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |  
  7. +------------------+----------+--------------+------------------+  
  8. | MySQL-bin.000003 |      374 |              |                  |   
  9. +------------------+----------+--------------+------------------+  
  10. 1 row in set (0.00 sec) 

在192.168.1.201上,将192.168.1.202设为自己的主服务器

  1. MySQL> change master to master_host='192.168.1.202',master_user='replication',master_password='replication',master_log_file='MySQL-bin.000003',master_log_pos=374;  
  2. Query OK, 0 rows affected (0.05 sec)  
  3.  
  4. MySQL> start slave;  
  5. Query OK, 0 rows affected (0.00 sec)  
  6.  
  7. MySQL> show slave status\G  
  8. *************************** 1. row ***************************  
  9.              Slave_IO_State: Waiting for master to send event  
  10.                 Master_Host: 192.168.1.202  
  11.                 Master_User: replication  
  12.                 Master_Port: 3306  
  13.               Connect_Retry: 60  
  14.             Master_Log_File: MySQL-bin.000003  
  15.         Read_Master_Log_Pos: 374  
  16.              Relay_Log_File: MySQL-master1-relay-bin.000002  
  17.               Relay_Log_Pos: 235  
  18.       Relay_Master_Log_File: MySQL-bin.000003  
  19.            Slave_IO_Running: Yes  
  20.           Slave_SQL_Running: Yes  
  21.             Replicate_Do_DB:   
  22.         Replicate_Ignore_DB:   
  23.          Replicate_Do_Table:   
  24.      Replicate_Ignore_Table:   
  25.     Replicate_Wild_Do_Table:   
  26. Replicate_Wild_Ignore_Table:   
  27.                  Last_Errno: 0  
  28.                  Last_Error:   
  29.                Skip_Counter: 0  
  30.         Exec_Master_Log_Pos: 374  
  31.             Relay_Log_Space: 235  
  32.             Until_Condition: None  
  33.              Until_Log_File:   
  34.               Until_Log_Pos: 0  
  35.          Master_SSL_Allowed: No 
  36.          Master_SSL_CA_File:   
  37.          Master_SSL_CA_Path:   
  38.             Master_SSL_Cert:   
  39.           Master_SSL_Cipher:   
  40.              Master_SSL_Key:   
  41.       Seconds_Behind_Master: 0  
  42. 1 row in set (0.00 sec) 

4、MySQL同步测试

如上述均正确配置,现在任何一台MySQL上更新数据都会同步到另一台MySQL,MySQL同步在此不再演示

二、keepalived安装及配置

1、192.168.1.201服务器上keepalived安装及配置

安装keepalived

  1. #tar zxvf keepalived-1.1.20.tar.gz  
  2. #cd keepalived-1.1.20  
  3. #./configure --prefix=/usr/local/keepalived --with-kernel-dir=/usr/src/kernels/2.6.18-164.el5-i686  
  4. #make && make install 

配置keepalived

我们自己在新建一个配置文件,默认情况下keepalived启动时会去/etc/keepalived目录下找配置文件

  1. #mkdir /etc/keepalived  
  2. #vi /etc/keepalived/keepalived.conf  
  3. ! Configuration File for keepalived  
  4. global_defs {  
  5.      notification_email {  
  6.      luwenju@live.cn  
  7.      }  
  8.      notification_email_from luwenju@live.cn  
  9.      smtp_server 127.0.0.1  
  10.      smtp_connect_timeout 30  
  11.      router_id MySQL-ha  
  12.      }  
  13.  
  14. vrrp_instance VI_1 {  
  15.      state BACKUP   #两台配置此处均是BACKUP  
  16.      interface eth0  
  17.      virtual_router_id 51  
  18.      priority 100   #优先级,另一台改为90  
  19.      advert_int 1  
  20.      nopreempt  #不抢占,只在优先级高的机器上设置即可,优先级低的机器不设置  
  21.      authentication {  
  22.      auth_type PASS  
  23.      auth_pass 1111  
  24.      }  
  25.      virtual_ipaddress {  
  26.      192.168.1.200  
  27.      }  
  28.      }  
  29.  
  30. virtual_server 192.168.1.200 3306 {  
  31.      delay_loop 2   #每个2秒检查一次real_server状态  
  32.      lb_algo wrr   #LVS算法  
  33.      lb_kind DR    #LVS模式  
  34.      persistence_timeout 60   #会话保持时间  
  35.      protocol TCP  
  36.      real_server 192.168.1.201 3306 {  
  37.      weight 3  
  38.      notify_down /usr/local/MySQL/bin/MySQL.sh  #检测到服务down后执行的脚本  
  39.      TCP_CHECK {  
  40.      connect_timeout 10    #连接超时时间  
  41.      nb_get_retry 3       #重连次数  
  42.      delay_before_retry 3   #重连间隔时间  
  43.      connect_port 3306   #健康检查端口  
  44.      }  
  45.      } 

编写检测服务down后所要执行的脚本

  1. #vi /usr/local/MySQL/bin/MySQL.sh  
  2. #!/bin/sh  
  3. pkill keepalived  
  4. #chmod +x /usr/local/MySQL/bin/MySQL.sh 

注:此脚本是上面配置文件notify_down选项所用到的,keepalived使用notify_down选项来检查real_server的服务状态,当发现real_server服务故障时,便触发此脚本;我们可以看到,脚本就一个命令,通过pkill keepalived强制杀死keepalived进程,从而实现了MySQL故障自动转移。另外,我们不用担心两个MySQL会同时提供数据更新操作,因为每台MySQL上的keepalived的配置里面只有本机MySQL的IP+VIP,而不是两台MySQL的IP+VIP

启动keepalived

  1. #/usr/local/keepalived/sbin/keepalived –D  
  2. #ps -aux | grep keepalived 

测试

找一台局域网PC,然后去ping  MySQL的VIP,这时候MySQL的VIP是可以ping的通的

停止MySQL服务,看keepalived健康检查程序是否会触发我们编写的脚本

2、192.168.1.202上keepalived安装及配置

安装keepalived

  1. #tar zxvf keepalived-1.1.20.tar.gz  
  2. #cd keepalived-1.1.20  
  3. #./configure --prefix=/usr/local/keepalived --with-kernel-dir=/usr/src/kernels/2.6.18-164.el5-i686  
  4. #make && make install 

配置keepalived

这台配置和上面基本一样,但有三个地方不同:优先级为90、无抢占设置、real_server为本机IP

  1. #mkdir /etc/keepalived  
  2. #vi /etc/keepalived/keepalived.conf  
  3. ! Configuration File for keepalived  
  4. global_defs {  
  5.      notification_email {  
  6.      luwenju@live.cn  
  7.      }  
  8.      notification_email_from luwenju@live.cn  
  9.      smtp_server 127.0.0.1  
  10.      smtp_connect_timeout 30  
  11.      router_id MySQL-ha  
  12.      }  
  13.  
  14. vrrp_instance VI_1 {  
  15.      state BACKUP  
  16.      interface eth0  
  17.      virtual_router_id 51  
  18.      priority 90  
  19.      advert_int 1  
  20.      authentication {  
  21.      auth_type PASS  
  22.      auth_pass 1111  
  23.      }  
  24.      virtual_ipaddress {  
  25.      192.168.1.200  
  26.      }  
  27.      }  
  28.  
  29. virtual_server 192.168.1.200 3306 {  
  30.      delay_loop 2  
  31.      lb_algo wrr  
  32.      lb_kind DR  
  33.      persistence_timeout 60  
  34.      protocol TCP  
  35.      real_server 192.168.1.202 3306 {  
  36.      weight 3  
  37.      notify_down /usr/local/MySQL/bin/MySQL.sh  
  38.      TCP_CHECK {  
  39.      connect_timeout 10  
  40.      nb_get_retry 3  
  41.      delay_before_retry 3  
  42.      connect_port 3306  
  43.      }  
  44.      } 

编写检测服务down后所要执行的脚本

  1. #vi /usr/local/MySQL/bin/MySQL.sh  
  2. #!/bin/sh  
  3. pkill keepalived  
  4. #chmod +x /usr/local/MySQL/bin/MySQL.sh  
  5.  
  6. 启动keepalived  
  7. #/usr/local/keepalived/sbin/keepalived –D  
  8. #ps -aux | grep keepalived 

测试

停止MySQL服务,看keepalived健康检查程序是否会触发我们编写的脚本

三、测试

MySQL远程登录测试

我们找一台安装有MySQL客户端的windows,然后登录VIP,看是否能登录,在登录之两台MySQL服务器都要授权允许从远程登录

  1. MySQL> grant all privileges on *.* to 'root'@'%' identified by '123456';  
  2. Query OK, 0 rows affected (0.00 sec)  
  3.  
  4. MySQL> flush privileges;  
  5. Query OK, 0 rows affected (0.00 sec) 

使用客户端登录VIP测试

  1. C:\MySQL\bin>MySQL.exe -uroot -p123456 -h192.168.1.200 -P3306  
  2. Welcome to the MySQL monitor.  Commands end with ; or \g.  
  3. Your MySQL connection id is 224  
  4. Server version: 5.0.89-log Source distribution  
  5.  
  6. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.  
  7.  
  8. MySQL> 

● keepalived故障转移测试

※在windows客户端一直去ping  VIP,然后关闭192.168.1.201上的keepalived,正常情况下VIP就会切换到192.168.1.202上面去

※开启192.168.1.201上的keepalived,关闭192.168.1.202上的keepalived,看是否能自动切换,正常情况下VIP又会属于192.168.1.201

注:keepalived切换速度还是非常块的,整个切换过程只需1-3秒

● MySQL故障转移测试

※在192.168.1.201上关闭MySQL服务,看VIP是否会切换到192.168.1.202上

※开启192.168.1.201上的MySQL和keepalived,然后关闭192.168.1.202上的MySQL,看VIP是否会切换到192.168.1.201上

下面是用windows客户端连接的MySQL的VIP,在切换时我执行了一个MySQL查询命令,从执行show databases到显示出结果时间为3-5秒(大家可以看到上面有个错误提示,不过不用担心,因为我们的keepalived切换大概为3秒左右,这3秒左右VIP是谁都不属于的)

  1. MySQL> show databases;  
  2. ERROR 2006 (HY000): MySQL server has gone away  
  3. No connection. Trying to reconnect...  
  4. Connection id:    592  
  5. Current database: *** NONE ***  
  6.  
  7. +--------------------+  
  8. | Database           |  
  9. +--------------------+  
  10. | information_schema |  
  11. | MySQL              |  
  12. | test               |  
  13. +--------------------+  
  14. 3 rows in set (9.01 sec) 

后话:世间万事万物,都不具备绝对的完美,就像上面的MySQL-HA一样,keepalived只能做到对3306的健康检查,但是做不到比如像MySQL复制中的slave-SQL、slave-IO进程的检查。所以要想做到一些细致的健康检查,还得需要借助额外的监控工具,比如nagios,然后用nagios实现短信、邮件报警,从而能够有效地解决问题。

【编辑推荐】

  1. MySQL数据库集群进行正确配置步骤
  2. MySQL 集群在Server1与Server2上如何安装MySQL
  3. MySQL集群配置
  4. MySQL集群自动安装脚本
  5. MySQL触发器如何正确使用


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



nginx+keepalived实现双机热切的高可用

$
0
0

Nginx+keepalived热切换高可用

一 keepalived简介

keepalived是一个类似于layer3, 4 & 7交换机制的软件,也就是我们平时说的第3层、第4层和第7层交换。

Keepalived的作用是检测web服务器的状态,如果有一台web服务器死机,或工作出现故障,Keepalived将检测到,并将有故障的web服务器从系统中剔除,当web服务器工作正常后Keepalived自动将web服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的web服务器。

为了方便测试清关闭防火墙 service iptables stop 

二 安装

1,本次采用的是centos5.10 两台机器 (master_slave)架构master 192.168.235.101,

slave 192.168.235.102

2,准备编译环境 yum -y install gcc gcc+ gcc-c++ openssl openssl-devel pcre pcre-devel

3,下载软件源代码包

Nginx1.7.0 最新版

wget  http://nginx.org/download/nginx-1.7.0.tar.gz

keepalived 1.2.12最新版

wget  http://www.keepalived.org/software/keepalived-1.2.12.tar.gz

4,编译安装源代码

a. Nginx的安装(两台机器进行相同的安装即可)

解压nginx

tar xf nginx-1.7.0

cd nginx1.7.0

./configure

make && make install

如果没有报错证明安装成功

测试

/usr/local/nginx/sbin/nginx –t

[root@keepMaster opt]# /usr/local/nginx/sbin/nginx -t

nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok

nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

表示成功

b. keepalived安装

tar xf keepalived-1.2.12.tar.gz

cd keepalived-1.2.12

./configure

make && make install

cp /usr/local/etc/rc.d/init.d/keepalived /etc/init.d/

cp /usr/local/etc/sysconfig/keepalived /etc/sysconfig/

chmod +x /etc/init.d/keepalived

chkconfig –add keepalived

chkconfig keepalived on

mkdir /etc/keepalived

ln –s /usr/local/sbin/keepalived /usr/sbin

三 配置

Keepalived的配置

a在master机器(192.168.235.101中的配置)

vi /etc/keepalived/ keepalived.config //这个文件默认是不存在的

使用vi 加入以下的配置

global_defs {

   notification_email {

     admin@centos.bz

   }

   notification_email_from keepalived@domain.com

   smtp_server 127.0.0.1

   smtp_connect_timeout 30

   router_id LVS_DEVEL

}

vrrp_script chk_http_port {

                script "/opt/nginx_pid.sh" ############该脚本我们在后面书写

                interval 2

                weight 2

}

vrrp_instance VI_1 {

    state MASTER        ############ 辅机为 BACKUP

    interface eth0

    virtual_router_id 51

    mcast_src_ip 192.168.235.101

    priority 102                  ########### 权值要比 back 高

    advert_int 1

    authentication {

        auth_type PASS

        auth_pass 1111

    }

track_script { 

        chk_http_port ### 执行监控的服务 

        }

    virtual_ipaddress {

      192.168.235.100 ############ 此处的虚拟IP 地址即 我们web所                                                      ####要访问的IP地址  必须在同一网段

    }

}

 

 

b 配置slave(192.168.235.102)

vi /etc/keepalived/ keepalived.config //这个文件默认是不存在的

使用vi 加入以下的配置

 

global_defs {

   notification_email {

     admin@centos.bz

   }

   notification_email_from keepalived@domain.com

   smtp_server 127.0.0.1

   smtp_connect_timeout 30

   router_id LVS_DEVEL

}

vrrp_script chk_http_port {

                script "/opt/nginx_pid.sh" ############该脚本我们在后####面书写

                interval 2

                weight 2

}

vrrp_instance VI_1 {

    state BACKUP  

    interface eth0

    virtual_router_id 51

    mcast_src_ip 192.168.235.102

    priority 102                  ########### 权值要比 back 低

    advert_int 1

    authentication {

        auth_type PASS

        auth_pass 1111

    }

track_script { 

        chk_http_port ### 执行监控的服务 

        }

    virtual_ipaddress {

      192.168.235.100 ############ 此处的虚拟IP 地址即 我们web所                                                      ####要访问的IP地址  必须在同一网段

    }

}

 

c 分别在master slave上建立nginx的监控脚本文件

vi /opt/nginx_pid.sh

 

输入以下配置

#!/bin/bash

A=`ps –C nginx –no-header |wc -l`

if [$A –eq 0];then

/usr/local/nginx/sbin/nginx

sleep 3

if[`ps –C nginx –no-header |wc -l` -eq 0];then

killall keepalived

fi

fi

四 测试

<!--[if !supportLists]-->1. <!--[endif]-->分别在mster和slave上启动nginx  /usr/local/nginx/sbin/nginx

<!--[if !supportLists]-->2. <!--[endif]-->先启动master(101)上的 keepalived  service keepalived start 再启动slave(102)

<!--[if !supportLists]-->3. <!--[endif]-->在master上敲入 ip a

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000

   link/ether 00:0c:29:c2:74:15 brd ff:ff:ff:ff:ff:ff

    inet 192.168.235.101/24 brd 192.168.235.255 scope global eth0

    inet 192.168.235.110/32 scope global eth0 //虚拟IP成功了 使用该地址访问 

   inet6 fe80::20c:29ff:fec2:7415/64 scope link

       valid_lft forever preferred_lft forever

此时在slave输入ip a 是看不到虚拟ip的

<!--[if !supportLists]-->4. <!--[endif]-->在master上关闭nginx  /usr/local/nginx/sbin/nginx –s stop 输入ip a 

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000

   link/ether 00:0c:29:c2:74:15 brd ff:ff:ff:ff:ff:ff

    inet 192.168.235.101/24 brd 192.168.235.255 scope global eth0 

   inet6 fe80::20c:29ff:fec2:7415/64 scope link

       valid_lft forever preferred_lft forever

虚拟IP地址没有了

此时在slave下输入ip a 

看到inet 192.168.235.110/32 scope global eth0 说明成功了

下面是我测试机器

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000

    link/ether 00:0c:29:e3:a2:2b brd ff:ff:ff:ff:ff:ff

    inet 192.168.235.102/24 brd 192.168.235.255 scope global eth0

    inet 192.168.235.110/32 scope global eth0 //看到这个 表示切换成功 秒级别

    inet6 fe80::20c:29ff:fee3:a22b/64 scope link

       valid_lft forever preferred_lft forever

<!--[if !supportLists]-->5. <!--[endif]-->重新启动master 上的nginx,keepalived会自动切换到master 

<!--[if !supportLists]-->6. <!--[endif]-->经测试在master意外断电的情况下任然不影响切换,并在master成功修复,重新启动成功之后,slave会自动切换到master

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



hadoop编程:分析CSDN注册邮箱分布情况

$
0
0

hadoop编程:分析CSDN注册邮箱分布情况


本文博客链接: http://blog.csdn.net/jdh99,作者:jdh,转载请注明.


环境:

主机:Ubuntu10.04

hadoop版本:1.2.1

开发工具:eclipse4.4.0


说明:

要求:原始数据共6428632条,分析不同邮箱的注册情况,并按使用人数从大到小排序。

分析:hadoop自带一个排序,是按key值来进行排序的。要按值(value)进行排序,需要二次排序。

步骤:

1.job1:统计不同注册邮箱的使用人数,用默认的key值排序,保存在HDFS系统中

2.job2:对job1的输出进行二次排序,按值从大到小排序


结果输出:

使用人数在1W以上的邮箱共有24个:

qq.com    1976196
163.com    1766927
126.com    807895
sina.com    351596
yahoo.com.cn    205491
hotmail.com    202948
gmail.com    186843
sohu.com    104736
yahoo.cn    87048
tom.com    72365
yeah.net    53295
21cn.com    50710
vip.qq.com    35119
139.com    29207
263.net    24779
sina.com.cn    19156
live.cn    18920
sina.cn    18601
yahoo.com    18454
foxmail.com    16432
163.net    15176
msn.com    14211
eyou.com    13372
yahoo.com.tw    10810


源代码:


JOB1:统计不同注册邮箱的人数

CsdnData.java

package com.bazhangkeji.hadoop;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;

public class CsdnData 
{
	public static void main(String[] args) throws Exception 
	{
		Configuration conf = new Configuration();
		String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
		if (otherArgs.length != 2) 
		{
			System.err.println("Usage: csdndata <in> <out>");
			System.exit(2);
		}
		Job job = new Job(conf, "csdndata");
		job.setJarByClass(CsdnData.class);
		job.setMapperClass(MapData.class);
		job.setReducerClass(ReducerData.class); 
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);
		FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
		FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
		System.exit(job.waitForCompletion(true) ? 0 : 1);
  	}
}


MapData.java

package com.bazhangkeji.hadoop;
import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Mapper.Context;

public class MapData extends Mapper<Object, Text, Text, IntWritable>
{
	IntWritable one = new IntWritable(1);
  	Text word = new Text();

  	public void map(Object key, Text value, Context context) throws IOException, InterruptedException 
	{
  		StringBuffer str_in = new StringBuffer();
		StringBuffer str_out = new StringBuffer();
		int index = 0;
		
		//初始化字符串
		str_in.setLength(0);
		str_out.setLength(0);
		str_in.append(value.toString());
		
		//获得邮箱的起始位置
		index = str_in.toString().lastIndexOf('@');
		if (index != -1)
		{
			word.set(str_in.toString().substring(index + 1).trim().toLowerCase());
  			context.write(word, one);
		}
  	}
}


ReducerData.java

package com.bazhangkeji.hadoop;
import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.Reducer.Context;

public class ReducerData extends Reducer<Text,IntWritable,Text,IntWritable> 
{
	IntWritable result = new IntWritable();

  	public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException 
	{
    	int sum = 0;
    	for (IntWritable val : values) 
		{
      		sum += val.get();
    	}
    	result.set(sum);
    	context.write(key, result);
  	}
}


JOB2:对job1的输出进行二次排序,按值从大到小排序

SortSecond.java

package com.bazhangkeji.hadoop2;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;

public class SortSecond 
{
	public static void main(String[] args) throws Exception 
	{
		Configuration conf = new Configuration();
		String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
		if (otherArgs.length != 2) 
		{
			System.err.println("Usage: csdndata <in> <out>");
			System.exit(2);
		}
		Job job = new Job(conf, "sortsecond");
		job.setJarByClass(SortSecond.class);
		job.setMapperClass(MapSecond.class);
		job.setReducerClass(ReduceSecond.class); 
		job.setSortComparatorClass(SortMy.class); //设置自定义二次排序策略
		
		job.setOutputKeyClass(KeyMy.class);
		job.setOutputValueClass(IntWritable.class);
		
		FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
		FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
		System.exit(job.waitForCompletion(true) ? 0 : 1);
  	}
}


MapSecond.java

package com.bazhangkeji.hadoop2;
import java.io.IOException;
import java.util.StringTokenizer;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Mapper.Context;

public class MapSecond extends Mapper<LongWritable, Text, KeyMy, IntWritable>
{
	IntWritable one = new IntWritable(1);
  	Text word = new Text();
  	KeyMy keymy = new KeyMy();

  	public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException 
	{
  		String str_in = value.toString();
  		int index = 0;
  		
  		index = str_in.indexOf('\t');
  		if (value.toString().length() > 3 && index != -1)
  		{
	  		String str1 = str_in.substring(0, index);
	  		String str2 = str_in.substring(index + 1);
	  		if (str1.length() != 0 && str2.length() != 0)
	  		{
	  			one.set(Integer.parseInt(str2));
	  			word.set(str1);
				keymy.setFirstKey(word);
				keymy.setSecondKey(one);
	  			context.write(keymy, one);
	  		}
  		}
  	}
}


ReduceSecond.java

package com.bazhangkeji.hadoop2;
import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.Reducer.Context;

public class ReduceSecond extends Reducer<KeyMy,IntWritable,Text,IntWritable> 
{
	IntWritable result = new IntWritable();

  	public void reduce(KeyMy key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException 
	{
    	context.write(key.getFirstKey(), key.getSecondKey());
  	}
}


KeyMy.java

package com.bazhangkeji.hadoop2;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.WritableComparable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 自定义组合键
 */
public class KeyMy implements WritableComparable<KeyMy>{
    private static final Logger logger = LoggerFactory.getLogger(KeyMy.class);
    private Text firstKey;
    private IntWritable secondKey;
    public KeyMy() {
        this.firstKey = new Text();
        this.secondKey = new IntWritable();
    }
    public Text getFirstKey() {
        return this.firstKey;
    }
    public void setFirstKey(Text firstKey) {
        this.firstKey = firstKey;
    }
    public IntWritable getSecondKey() {
        return this.secondKey;
    }
    public void setSecondKey(IntWritable secondKey) {
        this.secondKey = secondKey;
    }
    @Override
    public void readFields(DataInput dateInput) throws IOException {
        // TODO Auto-generated method stub
        this.firstKey.readFields(dateInput);
        this.secondKey.readFields(dateInput);
    }
    @Override
    public void write(DataOutput outPut) throws IOException {
        this.firstKey.write(outPut);
        this.secondKey.write(outPut);
    }
    /**
     * 自定义比较策略
     * 注意:该比较策略用于 mapreduce的第一次默认排序,也就是发生在map阶段的sort小阶段,
     * 发生地点为环形缓冲区(可以通过io.sort.mb进行大小调整)
     */
    @Override
    public int compareTo(KeyMy KeyMy) {
        logger.info("-------KeyMy flag-------");
        return this.firstKey.compareTo(KeyMy.getFirstKey());
    }
}


SortMy.java

package com.bazhangkeji.hadoop2;

import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 自定义二次排序策略
 */
public class SortMy extends WritableComparator {
    private static final Logger logger = LoggerFactory.getLogger(SortMy.class);
    public SortMy() {
        super(KeyMy.class,true);
    }
    @Override
    public int compare(WritableComparable KeyMyOne,
            WritableComparable KeyMyOther) 
    {
        logger.info("---------enter SortMy flag---------");
        KeyMy c1 = (KeyMy) KeyMyOne;
        KeyMy c2 = (KeyMy) KeyMyOther;
        return c2.getSecondKey().get()-c1.getSecondKey().get();//0,负数,正数
    }
}

参考资料:

1.《hadoop权威指南》

2.   http://zengzhaozheng.blog.51cto.com/8219051/1379271



作者:jdh99 发表于2014/7/8 15:48:05 原文链接
阅读:64 评论:0 查看评论

邮件-最低成本的监控

$
0
0
我们有一个服务器集群,每台机器上都有日志需要观察,但天天登录机器下载日志非常繁琐,为此我们想了一个办法每天发邮件将日志发送至指定的邮箱,这样我们只要每天打开邮箱,就可以接收到所有服务器的日志。
 
1.安装
msmtp-1.4.28.tar.bz2
tar jxvf msmtp-1.4.28.tar.bz2
cd msmtp-1.4.28
./configure  --prefix=/webserver/msmtp  --sysconfdir=/webserver/msmtp/etc
make  && make  install
 
2.配置
vi /root/.msmtprc
account default
host smtp.126.com
port 25
from 发送邮箱地址
auth login
tls off
user 发送邮箱地址
password 发送邮箱密码
 
3.yum install mutt
 
4.配置mutt
vi  /root/.muttrc 
set sendmail="/webserver/msmtp/bin/msmtp"
set use_from=yes
set realname="xxx@126.com"
set editor="vim"
 
5.发送邮件测试
mutt -s "catalina-log" 接收邮箱地址 </root/context.txt -a /home/catalina.log
 
6.配置定时器
30 8 * * * mutt -s "138-log" 接收邮箱地址 </root/context.txt -a /home/catalina.log


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



CSRF 攻击的应对之道

$
0
0

CSRF 攻击的应对之道

 

牛 刚, 软件工程师, IBM
童 强国, 高级软件工程师, IBM

 

简介: CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,该攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在并未授权的情况下执行在权限保护之下的操作,有很大的危害性。然而,该攻击方式并不为大家所熟知,很多网站都有 CSRF 的安全漏洞。本文首先介绍 CSRF 的基本原理与其危害性,然后就目前常用的几种防御方法进行分析,比较其优劣。最后,本文将以实例展示如何在网站中防御 CSRF 的攻击,并分享一些开发过程中的最佳实践。

发布日期: 2011 年 2 月 24 日 
级别: 中级 
访问情况 : 12744 次浏览 
评论: 5 ( 查看 |  添加评论 - 登录)

平均分 4星 总共 65 评分的平均分(您的评分是 5 星)

 

CSRF 背景与介绍

CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一。其他安全隐患,比如 SQL 脚本注入,跨站域脚本攻击等在近年来已经逐渐为众人熟知,很多网站也都针对他们进行了防御。然而,对于大多数人来说,CSRF 却依然是一个陌生的概念。即便是大名鼎鼎的 Gmail, 在 2007 年底也存在着 CSRF 漏洞,从而被黑客攻击而使 Gmail 的用户造成巨大的损失。

CSRF 攻击实例

CSRF 攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在并未授权的情况下执行在权限保护之下的操作。比如说,受害者 Bob 在银行有一笔存款,通过对银行的网站发送请求 http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 可以使 Bob 把 1000000 的存款转到 bob2 的账号下。通常情况下,该请求发送到网站后,服务器会先验证该请求是否来自一个合法的 session,并且该 session 的用户 Bob 已经成功登陆。黑客 Mallory 自己在该银行也有账户,他知道上文中的 URL 可以把钱进行转帐操作。Mallory 可以自己发送一个请求给银行:http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory。但是这个请求来自 Mallory 而非 Bob,他不能通过安全认证,因此该请求不会起作用。这时,Mallory 想到使用 CSRF 的攻击方式,他先自己做一个网站,在网站中放入如下代码: src=”http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory ”,并且通过广告等诱使 Bob 来访问他的网站。当 Bob 访问该网站时,上述 url 就会从 Bob 的浏览器发向银行,而这个请求会附带 Bob 浏览器中的 cookie 一起发向银行服务器。大多数情况下,该请求会失败,因为他要求 Bob 的认证信息。但是,如果 Bob 当时恰巧刚访问他的银行后不久,他的浏览器与银行网站之间的 session 尚未过期,浏览器的 cookie 之中含有 Bob 的认证信息。这时,悲剧发生了,这个 url 请求就会得到响应,钱将从 Bob 的账号转移到 Mallory 的账号,而 Bob 当时毫不知情。等以后 Bob 发现账户钱少了,即使他去银行查询日志,他也只能发现确实有一个来自于他本人的合法请求转移了资金,没有任何被攻击的痕迹。而 Mallory 则可以拿到钱后逍遥法外。

CSRF 攻击的对象

在讨论如何抵御 CSRF 之前,先要明确 CSRF 攻击的对象,也就是要保护的对象。从以上的例子可知,CSRF 攻击是黑客借助受害者的 cookie 骗取服务器的信任,但是黑客并不能拿到 cookie,也看不到 cookie 的内容。另外,对于服务器返回的结果,由于浏览器同源策略的限制,黑客也无法进行解析。因此,黑客无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。所以,我们要保护的对象是那些可以直接产生数据改变的服务,而对于读取数据的服务,则不需要进行 CSRF 的保护。比如银行系统中转账的请求会直接改变账户的金额,会遭到 CSRF 攻击,需要保护。而查询余额是对金额的读取操作,不会改变数据,CSRF 攻击无法解析服务器返回的结果,无需保护。

 

回页首

当前防御 CSRF 的几种策略

在业界目前防御 CSRF 攻击主要有三种策略:验证 HTTP Referer 字段;在请求地址中添加 token 并验证;在 HTTP 头中自定义属性并验证。下面就分别对这三种策略进行详细介绍。

验证 HTTP Referer 字段

根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。在通常情况下,访问一个安全受限页面的请求来自于同一个网站,比如需要访问 http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory,用户必须先登陆 bank.example,然后通过点击页面上的按钮来触发转账事件。这时,该转帐请求的 Referer 值就会是转账按钮所在的页面的 URL,通常是以 bank.example 域名开头的地址。而如果黑客要对银行网站实施 CSRF 攻击,他只能在他自己的网站构造请求,当用户通过黑客的网站发送请求到银行时,该请求的 Referer 是指向黑客自己的网站。因此,要防御 CSRF 攻击,银行网站只需要对于每一个转账请求验证其 Referer 值,如果是以 bank.example 开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果 Referer 是其他网站的话,则有可能是黑客的 CSRF 攻击,拒绝该请求。

这种方法的显而易见的好处就是简单易行,网站的普通开发人员不需要操心 CSRF 的漏洞,只需要在最后给所有安全敏感的请求统一增加一个拦截器来检查 Referer 的值就可以。特别是对于当前现有的系统,不需要改变当前系统的任何已有代码和逻辑,没有风险,非常便捷。

然而,这种方法并非万无一失。Referer 的值是由浏览器提供的,虽然 HTTP 协议上有明确的要求,但是每个浏览器对于 Referer 的具体实现可能有差别,并不能保证浏览器自身没有安全漏洞。使用验证 Referer 值的方法,就是把安全性都依赖于第三方(即浏览器)来保障,从理论上来讲,这样并不安全。事实上,对于某些浏览器,比如 IE6 或 FF2,目前已经有一些方法可以篡改 Referer 值。如果 bank.example 网站支持 IE6 浏览器,黑客完全可以把用户浏览器的 Referer 值设为以 bank.example 域名开头的地址,这样就可以通过验证,从而进行 CSRF 攻击。

即便是使用最新的浏览器,黑客无法篡改 Referer 值,这种方法仍然有问题。因为 Referer 值会记录下用户的访问来源,有些用户认为这样会侵犯到他们自己的隐私权,特别是有些组织担心 Referer 值会把组织内网中的某些信息泄露到外网中。因此,用户自己可以设置浏览器使其在发送请求时不再提供 Referer。当他们正常访问银行网站时,网站会因为请求没有 Referer 值而认为是 CSRF 攻击,拒绝合法用户的访问。

在请求地址中添加 token 并验证

CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。

这种方法要比检查 Referer 要安全一些,token 可以在用户登陆后产生并放于 session 之中,然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。 而对于 POST 请求来说,要在 form 的最后加上 <input type=”hidden” name=”csrftoken” value=”tokenvalue”/>,这样就把 token 以参数的形式加入请求了。但是,在一个网站中,可以接受请求的地方非常多,要对于每一个请求都加上 token 是很麻烦的,并且很容易漏掉,通常使用的方法就是在每次页面加载时,使用 javascript 遍历整个 dom 树,对于 dom 中所有的 a 和 form 标签后加入 token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的 html 代码,这种方法就没有作用,还需要程序员在编码时手动添加 token。

该方法还有一个缺点是难以保证 token 本身的安全。特别是在一些论坛之类支持用户自己发表内容的网站,黑客可以在上面发布自己个人网站的地址。由于系统也会在这个地址后面加上 token,黑客可以在自己的网站上得到这个 token,并马上就可以发动 CSRF 攻击。为了避免这一点,系统可以在添加 token 的时候增加一个判断,如果这个链接是链到自己本站的,就在后面添加 token,如果是通向外网则不加。不过,即使这个 csrftoken 不以参数的形式附加在请求之中,黑客的网站也同样可以通过 Referer 来得到这个 token 值以发动 CSRF 攻击。这也是一些用户喜欢手动关闭浏览器 Referer 功能的原因。

在 HTTP 头中自定义属性并验证

这种方法也是使用 token 并进行验证,和上一种方法不同的是,这里并不是把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。

然而这种方法的局限性非常大。XMLHttpRequest 请求通常用于 Ajax 方法中对于页面局部的异步刷新,并非所有的请求都适合用这个类来发起,而且通过该类请求得到的页面不能被浏览器所记录下,从而进行前进,后退,刷新,收藏等操作,给用户带来不便。另外,对于没有进行 CSRF 防护的遗留系统来说,要采用这种方法来进行防护,要把所有请求都改为 XMLHttpRequest 请求,这样几乎是要重写整个网站,这代价无疑是不能接受的。

 

回页首

Java 代码示例

下文将以 Java 为例,对上述三种方法分别用代码进行示例。无论使用何种方法,在服务器端的拦截器必不可少,它将负责检查到来的请求是否符合要求,然后视结果而决定是否继续请求或者丢弃。在 Java 中,拦截器是由 Filter 来实现的。我们可以编写一个 Filter,并在 web.xml 中对其进行配置,使其对于访问所有需要 CSRF 保护的资源的请求进行拦截。

在 filter 中对请求的 Referer 验证代码如下


清单 1. 在 Filter 中验证 Referer
 // 从 HTTP 头中取得 Referer 值
 String referer=request.getHeader("Referer"); 
 // 判断 Referer 是否以 bank.example 开头
 if((referer!=null) &&(referer.trim().startsWith(“bank.example”))){ 
    chain.doFilter(request, response); 
 }else{ 
    request.getRequestDispatcher(“error.jsp”).forward(request,response); 
 } 

以上代码先取得 Referer 值,然后进行判断,当其非空并以 bank.example 开头时,则继续请求,否则的话可能是 CSRF 攻击,转到 error.jsp 页面。

如果要进一步验证请求中的 token 值,代码如下


清单 2. 在 filter 中验证请求中的 token
 HttpServletRequest req = (HttpServletRequest)request; 
 HttpSession s = req.getSession(); 

 // 从 session 中得到 csrftoken 属性
 String sToken = (String)s.getAttribute(“csrftoken”); 
 if(sToken == null){ 

    // 产生新的 token 放入 session 中
    sToken = generateToken(); 
    s.setAttribute(“csrftoken”,sToken); 
    chain.doFilter(request, response); 
 } else{ 

    // 从 HTTP 头中取得 csrftoken 
    String xhrToken = req.getHeader(“csrftoken”); 

    // 从请求参数中取得 csrftoken 
    String pToken = req.getParameter(“csrftoken”); 
    if(sToken != null && xhrToken != null && sToken.equals(xhrToken)){ 
        chain.doFilter(request, response); 
    }else if(sToken != null && pToken != null && sToken.equals(pToken)){ 
        chain.doFilter(request, response); 
    }else{ 
        request.getRequestDispatcher(“error.jsp”).forward(request,response); 
    } 
 } 

首先判断 session 中有没有 csrftoken,如果没有,则认为是第一次访问,session 是新建立的,这时生成一个新的 token,放于 session 之中,并继续执行请求。如果 session 中已经有 csrftoken,则说明用户已经与服务器之间建立了一个活跃的 session,这时要看这个请求中有没有同时附带这个 token,由于请求可能来自于常规的访问或是 XMLHttpRequest 异步访问,我们分别尝试从请求中获取 csrftoken 参数以及从 HTTP 头中获取 csrftoken 自定义属性并与 session 中的值进行比较,只要有一个地方带有有效 token,就判定请求合法,可以继续执行,否则就转到错误页面。生成 token 有很多种方法,任何的随机算法都可以使用,Java 的 UUID 类也是一个不错的选择。

除了在服务器端利用 filter 来验证 token 的值以外,我们还需要在客户端给每个请求附加上这个 token,这是利用 js 来给 html 中的链接和表单请求地址附加 csrftoken 代码,其中已定义 token 为全局变量,其值可以从 session 中得到。


清单 3. 在客户端对于请求附加 token
 function appendToken(){ 
    updateForms(); 
    updateTags(); 
 } 

 function updateForms() { 
    // 得到页面中所有的 form 元素
    var forms = document.getElementsByTagName('form'); 
    for(i=0; i<forms.length; i++) { 
        var url = forms[i].action; 

        // 如果这个 form 的 action 值为空,则不附加 csrftoken 
        if(url == null || url == "" ) continue; 

        // 动态生成 input 元素,加入到 form 之后
        var e = document.createElement("input"); 
        e.name = "csrftoken"; 
        e.value = token; 
        e.type="hidden"; 
        forms[i].appendChild(e); 
    } 
 } 

 function updateTags() { 
    var all = document.getElementsByTagName('a'); 
    var len = all.length; 

    // 遍历所有 a 元素
    for(var i=0; i<len; i++) { 
        var e = all[i]; 
        updateTag(e, 'href', token); 
    } 
 } 

 function updateTag(element, attr, token) { 
    var location = element.getAttribute(attr); 
    if(location != null && location != '' '' ) { 
        var fragmentIndex = location.indexOf('#'); 
        var fragment = null; 
        if(fragmentIndex != -1){ 

            //url 中含有只相当页的锚标记
            fragment = location.substring(fragmentIndex); 
            location = location.substring(0,fragmentIndex); 
        } 
		
        var index = location.indexOf('?'); 

        if(index != -1) { 
            //url 中已含有其他参数
            location = location + '&csrftoken=' + token; 
        } else { 
            //url 中没有其他参数
            location = location + '?csrftoken=' + token; 
        } 
        if(fragment != null){ 
            location += fragment; 
        } 
        element.setAttribute(attr, location); 
    } 
 } 

在客户端 html 中,主要是有两个地方需要加上 token,一个是表单 form,另一个就是链接 a。这段代码首先遍历所有的 form,在 form 最后添加一隐藏字段,把 csrftoken 放入其中。然后,代码遍历所有的链接标记 a,在其 href 属性中加入 csrftoken 参数。注意对于 a.href 来说,可能该属性已经有参数,或者有锚标记。因此需要分情况讨论,以不同的格式把 csrftoken 加入其中。

如果你的网站使用 XMLHttpRequest,那么还需要在 HTTP 头中自定义 csrftoken 属性,利用 dojo.xhr 给 XMLHttpRequest 加上自定义属性代码如下:


清单 4. 在 HTTP 头中自定义属性
 var plainXhr = dojo.xhr; 

 // 重写 dojo.xhr 方法
 dojo.xhr = function(method,args,hasBody) { 
    // 确保 header 对象存在
    args.headers = args.header || {}; 
		
    tokenValue = '<%=request.getSession(false).getAttribute("csrftoken")%>'; 
    var token = dojo.getObject("tokenValue"); 
    // 把 csrftoken 属性放到头中
    args.headers["csrftoken"] = (token) ? token : "  "; 
    return plainXhr(method,args,hasBody); 
 }; 

这里改写了 dojo.xhr 的方法,首先确保 dojo.xhr 中存在 HTTP 头,然后在 args.headers 中添加 csrftoken 字段,并把 token 值从 session 里拿出放入字段中。

 

回页首

CSRF 防御方法选择之道

通过上文讨论可知,目前业界应对 CSRF 攻击有一些克制方法,但是每种方法都有利弊,没有一种方法是完美的。如何选择合适的方法非常重要。如果网站是一个现有系统,想要在最短时间内获得一定程度的 CSRF 的保护,那么验证 Referer 的方法是最方便的,要想增加安全性的话,可以选择不支持低版本浏览器,毕竟就目前来说,IE7+, FF3+ 这类高版本浏览器的 Referer 值还无法被篡改。

如果系统必须支持 IE6,并且仍然需要高安全性。那么就要使用 token 来进行验证,在大部分情况下,使用 XmlHttpRequest 并不合适,token 只能以参数的形式放于请求之中,若你的系统不支持用户自己发布信息,那这种程度的防护已经足够,否则的话,你仍然难以防范 token 被黑客窃取并发动攻击。在这种情况下,你需要小心规划你网站提供的各种服务,从中间找出那些允许用户自己发布信息的部分,把它们与其他服务分开,使用不同的 token 进行保护,这样可以有效抵御黑客对于你关键服务的攻击,把危害降到最低。毕竟,删除别人一个帖子比直接从别人账号中转走大笔存款严重程度要轻的多。

如果是开发一个全新的系统,则抵御 CSRF 的选择要大得多。笔者建议对于重要的服务,可以尽量使用 XMLHttpRequest 来访问,这样增加 token 要容易很多。另外尽量避免在 js 代码中使用复杂逻辑来构造常规的同步请求来访问需要 CSRF 保护的资源,比如 window.location 和 document.createElement(“a”) 之类,这样也可以减少在附加 token 时产生的不必要的麻烦。

最后,要记住 CSRF 不是黑客唯一的攻击手段,无论你 CSRF 防范有多么严密,如果你系统有其他安全漏洞,比如跨站域脚本攻击 XSS,那么黑客就可以绕过你的安全防护,展开包括 CSRF 在内的各种攻击,你的防线将如同虚设。

 

回页首

总结与展望

可见,CSRF 是一种危害非常大的攻击,又很难以防范。目前几种防御策略虽然可以很大程度上抵御 CSRF 的攻击,但并没有一种完美的解决方案。一些新的方案正在研究之中,比如对于每次请求都使用不同的动态口令,把 Referer 和 token 方案结合起来,甚至尝试修改 HTTP 规范,但是这些新的方案尚不成熟,要正式投入使用并被业界广为接受还需时日。在这之前,我们只有充分重视 CSRF,根据系统的实际情况选择最合适的策略,这样才能把 CSRF 的危害降到最低。


参考资料

学习

讨论



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



常见Oracle HINT的用法

$
0
0

      在SQL语句优化过程中,我们经常会用到hint,现总结一下在SQL优化过程中常见Oracle HINT的用法:

  1. /*+ALL_ROWS*/

表明对语句块选择基于开销的优化方法,并获得最佳吞吐量,使资源消耗最小化.

例如:

SELECT /*+ALL+_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO=’SCOTT’;

  2. /*+FIRST_ROWS*/

表明对语句块选择基于开销的优化方法,并获得最佳响应时间,使资源消耗最小化.

例如:

SELECT /*+FIRST_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO=’SCOTT’;

  3. /*+CHOOSE*/

表明如果数据字典中有访问表的统计信息,将基于开销的优化方法,并获得最佳的吞吐量;

表明如果数据字典中没有访问表的统计信息,将基于规则开销的优化方法;

例如:

SELECT /*+CHOOSE*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO=’SCOTT’;

  4. /*+RULE*/

表明对语句块选择基于规则的优化方法.

例如:

SELECT /*+ RULE */ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO=’SCOTT’;

  5. /*+FULL(TABLE)*/

表明对表选择全局扫描的方法.

例如:

SELECT /*+FULL(A)*/ EMP_NO,EMP_NAM FROM BSEMPMS A WHERE EMP_NO=’SCOTT’;

  6. /*+ROWID(TABLE)*/

提示明确表明对指定表根据ROWID进行访问.

例如:

SELECT /*+ROWID(BSEMPMS)*/ * FROM BSEMPMS WHERE ROWID>=’AAAAAAAAAAAAAA’

AND EMP_NO=’SCOTT’;

  7. /*+CLUSTER(TABLE)*/

提示明确表明对指定表选择簇扫描的访问方法,它只对簇对象有效.

例如:

SELECT /*+CLUSTER */ BSEMPMS.EMP_NO,DPT_NO FROM BSEMPMS,BSDPTMS

WHERE DPT_NO=’TEC304′ AND BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;

  8. /*+INDEX(TABLE INDEX_NAME)*/

表明对表选择索引的扫描方法.

例如:

SELECT /*+INDEX(BSEMPMS SEX_INDEX) USE SEX_INDEX BECAUSE THERE ARE FEWMALE BSEMPMS */ FROM BSEMPMS WHERE SEX=’M';

  9. /*+INDEX_ASC(TABLE INDEX_NAME)*/

表明对表选择索引升序的扫描方法.

例如:

SELECT /*+INDEX_ASC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO=’SCOTT’;

  10. /*+INDEX_COMBINE*/

为指定表选择位图访问路经,如果INDEX_COMBINE中没有提供作为参数的索引,将选择出位图索引的布尔组合方式.

例如:

SELECT /*+INDEX_COMBINE(BSEMPMS SAL_BMI HIREDATE_BMI)*/ * FROM BSEMPMS

WHERE SAL<5000000 AND HIREDATE

  11. /*+INDEX_JOIN(TABLE INDEX_NAME)*/

提示明确命令优化器使用索引作为访问路径.

例如:

SELECT /*+INDEX_JOIN(BSEMPMS SAL_HMI HIREDATE_BMI)*/ SAL,HIREDATE

FROM BSEMPMS WHERE SAL<60000;

  12. /*+INDEX_DESC(TABLE INDEX_NAME)*/

表明对表选择索引降序的扫描方法.

例如:

SELECT /*+INDEX_DESC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='SCOTT';

  13. /*+INDEX_FFS(TABLE INDEX_NAME)*/

对指定的表执行快速全索引扫描,而不是全表扫描的办法.

例如:

SELECT /*+INDEX_FFS(BSEMPMS IN_EMPNAM)*/ * FROM BSEMPMS WHERE DPT_NO='TEC305';

  14. /*+ADD_EQUAL TABLE INDEX_NAM1,INDEX_NAM2,...*/

提示明确进行执行规划的选择,将几个单列索引的扫描合起来.

例如:

SELECT /*+INDEX_FFS(BSEMPMS IN_DPTNO,IN_EMPNO,IN_SEX)*/ * FROM BSEMPMS WHERE EMP_NO='SCOTT' AND DPT_NO='TDC306';

  15. /*+USE_CONCAT*/

对查询中的WHERE后面的OR条件进行转换为UNION ALL的组合查询.

例如:

SELECT /*+USE_CONCAT*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';

  16. /*+NO_EXPAND*/

对于WHERE后面的OR 或者IN-LIST的查询语句,NO_EXPAND将阻止其基于优化器对其进行扩展.

例如:

SELECT /*+NO_EXPAND*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';

  17. /*+NOWRITE*/

禁止对查询块的查询重写操作.

  18. /*+REWRITE*/

可以将视图作为参数.

  19. /*+MERGE(TABLE)*/

能够对视图的各个查询进行相应的合并.

例如:

SELECT /*+MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELET DPT_NO

,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) V WHERE A.DPT_NO=V.DPT_NO

AND A.SAL>V.AVG_SAL;

  20. /*+NO_MERGE(TABLE)*/

对于有可合并的视图不再合并.

例如:

SELECT /*+NO_MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELECT DPT_NO,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) V WHERE A.DPT_NO=V.DPT_NO AND A.SAL>V.AVG_SAL;

  21. /*+ORDERED*/

根据表出现在FROM中的顺序,ORDERED使ORACLE依此顺序对其连接.

例如:

SELECT /*+ORDERED*/ A.COL1,B.COL2,C.COL3 FROM TABLE1 A,TABLE2 B,TABLE3 C WHERE A.COL1=B.COL1 AND B.COL1=C.COL1;

  22. /*+USE_NL(TABLE)*/

将指定表与嵌套的连接的行源进行连接,并把指定表作为内部表.

例如:

SELECT /*+ORDERED USE_NL(BSEMPMS)*/ BSDPTMS.DPT_NO,BSEMPMS.EMP_NO,BSEMPMS.EMP_NAM FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;

  23. /*+USE_MERGE(TABLE)*/

将指定的表与其他行源通过合并排序连接方式连接起来.

例如:

SELECT /*+USE_MERGE(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;

  24. /*+USE_HASH(TABLE)*/

将指定的表与其他行源通过哈希连接方式连接起来.

例如:

SELECT /*+USE_HASH(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;

  25. /*+DRIVING_SITE(TABLE)*/

强制与ORACLE所选择的位置不同的表进行查询执行.

例如:

SELECT /*+DRIVING_SITE(DEPT)*/ * FROM BSEMPMS,DEPT@BSDPTMS WHERE BSEMPMS.DPT_NO=DEPT.DPT_NO;

  26. /*+LEADING(TABLE)*/

将指定的表作为连接次序中的首表.

  27. /*+CACHE(TABLE)*/

当进行全表扫描时,CACHE提示能够将表的检索块放置在缓冲区缓存中最近最少列表LRU的最近使用端

例如:

SELECT /*+FULL(BSEMPMS) CAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;

  28. /*+NOCACHE(TABLE)*/

当进行全表扫描时,CACHE提示能够将表的检索块放置在缓冲区缓存中最近最少列表LRU的最近使用端

例如:

SELECT /*+FULL(BSEMPMS) NOCAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;

  29. /*+APPEND*/

直接插入到表的最后,可以提高速度.

insert /*+append*/ into test1 select * from test4 ;

  30. /*+NOAPPEND*/

通过在插入语句生存期内停止并行模式来启动常规插入.

insert /*+noappend*/ into test1 select * from test4 ;

 

提示(hint)从Oracle7中引入,目的是弥补基于成本优化器的缺陷。提示通常用来改变SQL执行计划,提高执行效率。

 

  1.使用提示需要遵循的原则

    1)仔细检查提示语法。尽量使用完整注释语法/*+ hint */

    2)使用表别名。如果在查询中指定了表别名,那么提示必须也使用表别名。例如:select /*+ index(e,dept_idx) */ * from emp e;

    3)不要在提示中使用模式名称:如果在提示中指定了模式的所有者,那么提示将被忽略。例如:

select /*+ index(scott.emp,dept_idx) */ * from emp;

    4)检验提示。如果提示指定了不可用的访问路径,那么这个提示将被忽略。

  2.导致提示无效的条件:

提示

 

被忽略的条件

cluster

与非簇表一同使用

 

hash

与非簇表一同使用

 

hash_aj

不存在子查询

 

index

指定的索引不存在

 

index_combine

不存在位图索引

 

merge_aj

不存在子查询

 

parallel

调用的不是TABLE ACCESS FULL计划

 

push_subq

不存在子查询

 

star

事实表中存在不恰当的索引

 

use_concat

在where子句中不存在多个or条件

 

use_nl

表中不存在索引

 

  3几种主要的优化模式:

    1)all_rows:all_rows是基于成本的优化方法,目的是提供整体最佳的吞吐量和最小的资源消耗。all_rows提示倾向使用全表扫描,而且不适用于OLTP数据库。使用all_rows提示应该保障查询中涉及的表和索引拥有使用analyze命令分析得到的统计资料。 

    2)rule:rule提示使Oracle为查询提供基于规则的优化模式。在怀疑CBO生成了非优化的执行计划时,通常首先尝试使用rule提示。Rule提示忽略表和索引的统计资料,并且使用基本的试探法生成执行计划。

    3)first_rows:这个提示是基于成本的优化方法,目的是提供最快的反应时间。使用first_rows提示应该保障查询中涉及的表和索引拥有使用analyze命令分析得到的统计资料。

  4.表的连接提示

     1)use_hash提示

use_hash 提示对指定的表进行散列连接。散列连接是Oracle用以驱动表(最小的表)向RAM区中装载记录的方法,RAM区由HASH_AREA_SIZE定义。散列连接适合中间结果比较大的情况。使用散列连接时,HASH_AREA_SIZE对速度影响非常大,如果驱动表不能一次装入内存,那么需要使用TEMP表空间,这种情况下速度比较慢。这个参数可以在session级别动态修改,需要进行散列连接时可以临时增大,速度可能显著增加。

     2)use_merge 提示

use_merge 提示强制执行一个排序合并操作。排序合并操作通常与并行查询结合使用,因为排序合并操作倾向于全表扫描。该提示适合于生成大型结果集的查询。

     3)use_nl:

use_nl提示将强制对目标表执行嵌套循环连接。use_nl提示很少用于SQL调整,因为CBO和RBO更倾向于使用循环嵌套连接。

     4)star提示

star 提示强制使用星型查询计划。前提是查询中至少三个表,而且在事实表中存在恰当的索引

  5.表反连接提示

SQL反连接是指在语句中包含NOT IN 或者NOT EXISTS子句时执行的操作。

     1)merge_aj

在使用全表访问比索引访问更好的情况下,可以在NOT IN子查询中使用merge_aj提示以便执行反连接。

     2)hash_aj

hash_aj 提示放在NOT IN 子查询中用来希望执行散列连接时,执行散列反连接。

hash_aj和merge_aj要求子查询列非空。

  6INDEX提示

     1)INDEX提示简介:

INDEX提示被用于显示指定表名或表名与索引。如果只指定了表名,那么优化器将使用表中的"最优"索引。在永久优化SQL语句中,建议指定表和索引。

     2)index_join 提示

index_join 提示明确要求优化器使用索引连接来作为访问路径。

     3)and_equal 提示

and_equal 提示可以使多个非唯一的索引合并索引,并且使这些索引操作时就象单个连续索引一样。该提示如果被应用,在查询计划中显示的是AND-EQUAL

     4)index_asc 提示

index_asc 提示使用升序索引。这是默认的优化器行为

     5)no_index 提示

该提示忽略索引存在,类似full

     6)index_combine提示

index_combine 提示用来强制使用位图索引作为表的访问路径。

     7)index_ffs提示

索引快速完全扫描可以在不访问任何记录的情况下完成查询。

     8)use_concat提示

use_concat提示要求为所有的OR条件使用UNION ALL执行计划,并将这个查询重新书写为多个查询。如果在WHERE子句中存在大量OR条件,可以考虑使用use_concat提示。

  7.总结

     1)因为提示放在注释中,所以如果提示通现存的执行计划不兼容,或者提示不正确,有可能被忽略。

     2)在使用RBO时,可以通过提示将指定的查询更改为CBO。切记要对查询中涉及的所有表和索引进行分析

     3)在使用CBO的时候,可以通过添加RULE提示或者FIRST_ROWS提示来开始调整一个可以的SQL语句

     4)提示可以在子查询中使用,但是外部查询的提示不会带入子查询。

     5)如果在查询计划中发现卡笛尔积(CARTESIAN),则要尽量解决。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Hibernate调试——定位查询源头

$
0
0

为什么有时Hibernate会在程序某一部分生成一条指定sql查询?这个问题让人很难立刻理解,当处理不是我们本人编写的代码时更是如此。

本文将展示如何配置来产生Hibernate查询操作的日志。通过这些日志和一些小技巧来找出这些指定的查询为什么及在何处被执行。

Hibernate查询日志格式

Hibernate内建的查询日志格式如下:

select /* load your.package.Employee */ this_.code, ... 
from employee this_ 
where this_.employee_id=?

TRACE 12-04-2014@16:06:02  BasicBinder - binding parameter [1] as [NUMBER] - 1000

为什么Hibernate不能记载最终的查询日志?

需要注意的是,Hibernate只记录从它发送到JDBC的准备语句(prepared statement)及参数。准备语句使用“?”作为查询参数的占位符,这些参数的实际值被记录在准备语句的下方。

这些准备语句和最终发送到数据库的sql语句是不同的,对于这些最终的查询操作Hibernate无法记录。出现这种情况的原因是Hibernate只知道它发送给JDBC的准备语句和参数,实际的查询是由JDBC构建并发送给数据库的。

为了产生实际查询的日志,像log4jdbc这种工具是必不可少的,这里不会讨论如何使用log4jdbc。

如何找到原始查询操作

上述的可记录查询包含一条标注,在大多数情况下它可以标识某条起始查询语句。如果一条查询是由加载引起的,那么标注便是/*load your.entity.Name*/。如果是一条命名查询,那么标注则包含查询的名称。

如果它是一个对应许多延迟加载的查询,标注则会包含对应类的名称和引发该操作的属性值等。

设置Hibernate的查询日志

为了获得查询日志,需要将如下标签加入会话工厂的配置文件中:

<bean id= "entityManagerFactory" >
  ...<property name="jpaProperties" ><props><prop key="hibernate.show_sql" >true</ prop><prop key="hibernate.format_sql" >true</ prop><prop key="hibernate.use_sql_comments">true</prop></props></property>

上面的示例展示了Spring实体管理工厂的配置。下面是对一些标签的解释:

  • show_sql:激活查询日志功能。
  • format_sql:优雅地输出Sql。
  • use_sql_comments:添加一条解释型标注。

为了记录查询语句的参数信息,log4j或者相对应的信息是需要的。

<logger name="org.hibernate.type"><level value="trace" /></logger >

如果上述功能都不能运行

在大多数情况下,use_sql_comments创建的标注是足够用来标识查询的起始。但如果这还不够,我们可以标识和数据表名相关联的查询返回的实体,并在返回的实体构造函数中设置断点。

如果一个实体没有构造函数,我们可以创建一个构造函数并把断点设置在super()函数调用中。

@Entity
public class Employee {
    public Employee() {
        super(); // put the breakpoint here
    }
    ...
}

设置断点后,跳转到包含程序堆栈信息的Debug界面并从头到尾执行一遍。这样在调用栈中将会出现查询操作在何处被创建。

相关文章


获取google IP段方法

$
0
0
Google越来越难访问了,多半是修改hosts,然而IP也不是总是可用的,可能被GFW封闭了,也可能是Google IP变动了。总之,体验很差。然而,搞技术的,靠国内的破逼搜索,能搜索到个毛线。 要想访问Google可以看下这篇文章介绍的方法《google搜索被屏蔽的完美解决方法》。 下面来说说如何获取Google IP段的方法,非常简单。就一条命名。如下所示:
nslookup -q=TXT _netblocks.google.com 8.8.8.8
Windows和Linux平台下都可以执行。 win下: google-1 linux下: google-2

由人工智能管理的港铁

$
0
0
港铁公司运营香港地铁跻身全世界最出色地铁系统之列,它的准时抵达记录高达99.9%,超过了伦敦和纽约地铁。数百公里长的繁忙地铁系统需要大量人力维护,港铁公司平均每周有多达1万人执行2600项维护工作,工人们工作都是提前规划好的,由人工智能进行管理。港铁公司计划将人工智能推广到它在其它城市运营的地铁系统,其中北京有可能第一个采用。人工智能由香港城市大学计算机科学系的副教授陈汉伟设计,他指出,在人工智能采用之前,公司需要召开一个由五到六名不同领域专家组成的规划会议,相当混乱,而现在计划直接屏幕上呈现。人工智能根据整个地铁系统的仿真模型,寻找必要工程维护工作的最佳规划,发现人类无法发现的机会。它产生的规划仍然需要获得人类批准,而意料之外的紧急修复工作需要手动加入,系统会重新规划不太重要的任务。它还会检查维护计划是否符合法律规定,比如维护工作在居民区产生的噪音必须低于一定水平。人工智能包含了人类多年的经验知识,将专业知识制定成规则,它本身是基于遗传算法。






开发者应了解的一些SQL优化准则(转)

$
0
0

 

下面介绍一些开发者在 数据库操作中要注意的 SQL编码准则。虽然本文不能覆盖所有的准则,但还是希望能给开发者带来些许帮助。下面就来看看在编码实践中哪些应该做,哪些不应该做。

  1.  在长时间运行的查询和短查询中使用事务
  如果预期有一个长时间运行的查询,并且有大量的数据输出时,开发者就应该在BEGIN TRAN 和END TRAN之间使用事务。
  这样事务会在缓冲区缓存为独立事务,并会被分配特定内存,以此来提高处理速度。
  2.  不要使用SELECT *
  如果使用SELECT * 来选择表中的所有记录,那么一些不必要的记录也被读取、缓存,增加了磁盘的I/O和内存消耗。
  3.  避免在WHERE子句中使用显式或隐式函数,比如Convert ()
  4.  避免在触发器中执行长时间的操作
  5.  适当使用临时表和表变量
  当结果集较小的时候,请尽量使用表变量;当结果集相当大时,使用临时表。
  6.  使用连接(JOIN)代替子查询(Sub-Queries)
  子查询通常作为内联代码来使用,而连接(JOIN)则作为表来使用,这样速度会更快。所以,应尽量避免在连接中使用子查询。
  7.  连接条件中表的顺序
  在连接条件中,应尽量首先使用较小的表,然后逐步使用较大的表。
  8.  循环优化
  如果操作在循环内部没有任何影响,那么应尽量将操作放到循环外面,这样可以减少不必要的重复 工作。因为,SQL Server优化器不会自动识别这种低效率的代码,更不会自动优化(其他一些语言的编译器可以)。
  9.  参数探测
  不要在正执行的SP(存储过程)中使用SP参数,这样会导致参数探测(Parameter Sniffing)。应该在声明和设置后再使用SP参数。由于这个原因,SP的行为在每次运行期间都不相同。
  10.  当使用条件语句时,可以使用Index(索引)Hint(提示)
  比如在SQL Server 2008中,可以使用Index hint,也可以使用fixed plan hint强制在查询中使用hint,以提高运行速度。
  11.  在声明中明确指定存储过程中数据类型的大小
  开发者随机声明数据类型的大小是不可取的,如:Varchar (500)。这在执行时会在缓冲区中增加不必要的预留空间。
  12.  在查询中有效利用MAXDOP(最大并行度)设置
  询问数据库管理员关于四核CPU可用性的设置,包括内存的设置,然后适当使用hint,可以有效改善查询速度。
  13.  SQL Server 2008中的GROUPING SETS
  如果数据库服务器为SQL Server 2008,那么可以在所有的Unions中使用Grouping Set来代替Group By。这样在Union中重新进行group by排序时,优化器不会每次都制定一个计划。
  14.  当发生死锁时,总是使用With (nolock) 和With (rowlock)


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



xmpp 服务端和客户端的安装使用

$
0
0

最近要采用xmpp 协议搭建 web 聊天室。初步研究了一下。

xmpp 是一个基于xml的协议,所用的用户验证,通讯,查询等操作传输的都是xml。

ejabberd 是服务端实现 支持集群部署。

客户端实现有很多,基于java的sdk是 smack,基于js的这里采用 candy-chat (http://candy-chat.github.io/candy/)

psi 是一个跨平台的 基于xmpp的客户端实现。安装好ejabberd后再安装psi,然后连接ejabberd进行验证。

聊天室匿名登录 要添加配置 

{host_config, "10.153.74.191", [{auth_method, [internal, anonymous]}]}.



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Wibbitz:把文字转换为视频,让读新闻变得更简单

$
0
0

  现在人们要想不阅读新闻真的是没有借口了。人们再也不能抱怨字体太小,或者是看过第一段之后感觉乏味。如果读新闻还是不够轻松的话。一家以色列公司Wibbitz找到一种方式,可以更加的简单,就是把文字转换成视频,来突出显示重要的信息。

  这个技术此前只适用于智能手机,公司6月24日宣布了这个被描述为“网络上的播放键”的产品推出了PC浏览器插件。

  Wibbitz 使用基于文本生成的实用算法来分析文字,并且解读有趣的信息,使用智能语言处理技术,将它们转换成高度吸引人的视频文件;只要点击一下按钮,文章就可以变成轻松消化的视频,内含图片,声音片段,信息图表,和文字转语音技术合成的声音。

  我在Wibbitz 浏览器插件当中对一篇文章使用了这个插件。插件生成的片段我听了一下,这个视频基本上只是大声的读出文章,并且展示了文章里提到的重要人物,地点和事情的照片。有时候会展示阅读文章时相关的视频内容。然而他们会把长度限制在四十秒之内,所以文章读完之前这个视频就结束了。

  把文章改编成广播剧

  这个技术从去年六月份开始推出移动应用,每天会产生一万段视频。根据公司的公告所示,他们转换成视频的新闻条目,是根据他们合伙的顶级网上新闻网站所提供的内容。新的网页插件现在支持接近二百个主流的新闻来源,包括雅虎新闻,纽约时报,英国广播公司,卫报和其他。

  用户下载插件之后在浏览器地址栏当中会出现一个播放按钮,如果播放按钮是蓝色的,这意味着这个页面支持Wibbitz公司的技术。整个文章正文部分会被转换成四十秒钟的视频。公司表示, 这个技术一项主要的进步,是允许出版商和品牌节省传统方法的花费和人工努力来生成视频。

  公司联合创始人和CEO Zohar Dayan在声明当中表示:“我们创造一种技术,这不仅扩大了视频制造的过程的规模。而且也是对上下文敏感的。这种视频体验根据用户的设备类型来剪裁,可以适配用户的屏幕大小和方向。我们对自己的技术有很大的信心,在未来将会加入智能电视,甚至可穿戴设备这样的支持。”

  在移动平台上使用Wibbitz 的时候——没有人愿意在小屏幕上阅读长文章——使用它在实际的新闻网站上阅读,只不过是把文章从头开始念一遍,而且,用户没有办法完全通过查看四十秒钟的视频来读懂一篇五百个单词的文章。但是,如果他只是从头往下阅读文章,直到四十秒钟结束,那么用户至少会获得文章当中最重要的信息。很多人可以直接从这个应用生成的视频当中获取信息,但是如果你是那种很懒的,只是希望有人给你读文章的人,那你应该会失望的;你最好自己去阅读文字本身。

  Wibbitz 是2011年由Zohar Dayan 和Yotam Cohen两人共同创建的,曾经从英特尔资本等机构获得三百万美元融资。在今年二月份,他们在巴塞罗那的世界移动通信大会上获得了最佳产品和服务的奖项。(译:dio)

  下载插件: Chrome  Firefox

Viewing all 15843 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>