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

使用 Redis 来存储 Apache Tomcat 7 的 Session

$
0
0

使用  Redis 服务器来存储Session非常有优势。首先它是一个NOSQL数据,第二它很容易扩展使用。 This kind of setup would lead to a clear understanding of how  Redis can behave as cache as well as a session storing system. In order to do this the instructions are as follows :-

  1. Download  Redis and build by the following commands (This includes downloading it too)
    wget http://download.redis.io/redis-stable.tar.gz
    tar xvzf redis-stable.tar.gz
    cd redis-stable
    make
  2. Start  Redis using command( Redis Directory refers to the directory where  Redis is built)
    cd RedisDirectory/src
    ./redis-server --port 6379
  3. Get the latest version of  Apache Tomcat 7
  4. Download the latest version for  JEDIS (A Redis Java Client),  Tomcat Redis Session Manager (Redis-backed non-sticky session store for Apache Tomcat) and  Apache Commons Pool .
  5. Copy all the above files into the lib folder of the  Apache Tomcat 7 installation directory.
  6. Add the lines mentioned below in the context.xml of your  Apache Tomcat 7 (Or on the context block of server.xml if applicable).Edit the Configurations as your settings. In out case the port number to be configured is 6279.
    1<Valve  className="com.radiadesign.catalina.session.RedisSessionHandlerValve"  />
    2<Manager  className="com.radiadesign.catalina.session.RedisSessionManager"
    3host="localhost"  <!-- optional: defaults to "localhost" -->
    4port="6379" <!-- optional: defaults to "6379" -->
    5database="0" <!-- optional: defaults to "0" -->
    6maxInactiveInterval="60" <!-- optional: defaults to "60" (in seconds) -->  />
  7. Now restart your  Apache Tomcat 7 normally and now you would see that the sessions are being created in the Redis Rather than on Tomcat.

Thats it. Now you have your  Apache Tomcat 7 storing all the sessions in  Redis and it also takes care about the different aspects of sessions.

 

web.xml中的配置是有效的,即使是context.xml总配置maxInactiveInterval默认60秒,只要web.xml中的sessionConfig配置30分钟,则session的失效时间还是30分钟。



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


ITeye推荐




常用社交网络(SNS、人人网、新浪微博)动态新闻(feed、新鲜事、好友动态)系统浅析

$
0
0
原文地址:http://blog.csdn.net/sunmenggmail/article/details/8472546


http://blog.renren.com/GetEntry.do?id=781732878&owner=232930872

最近见几个朋友都在说人人网新鲜事排序的问题,恰巧对这方面也较感兴趣,于是打算顺便把手头收集到的资料梳理学习一下。由于本人也只是新手,很多内容仅仅是参阅资料后的个人猜测与纸上谈兵故难免存有错误与纰漏,感谢大家指正。

一、 什么是feed

“Feed,本意是“饲料、饲养、(新闻的)广播等”,RSS订阅的过程中会用到的“Feed”,便是在这个意义上进行引申,表示这是用来接收该信息来源更新的接口。”----摘自百度百科。

      要说严格的feed定义与解释又得吧啦吧啦说一大堆无趣的话,通俗点说feed系统就是当你登陆进对应网站后:阅读器收到的一篇篇新文章、人人网上看到的一件件新鲜事、新浪微博上推到你面前的一条条新围脖、QQ空间中好友的一桩桩新动态等等。

二、 怎样得到feed

      feed的获取方式主要有两种:push(推)以及pull(拉),简单说来正如他们字面意思一样。

      push就是当一条feed产生后交到分发器,它再去查找用户关系明确出谁应该看到这条feed,再push到这些用户的feed列表中(新鲜事、好友动态),用手机短信来比喻的话就是收件箱里存收到的feed,发件箱里存发出的feed,产生一条feed就是把它“推”到所有粉丝或好友的收件箱中,而查看的话直接访问自己的收件箱就OK,我们可以明显地看到这种情况下通过前置计算(或者叫offline computation)提前准备好用户的feed信息,在取数据时无需多余的计算开销;但相反在分发的过程中会产生大量的计算,尤其是类似于姚晨这种的明星级人物(1400多万的粉丝真的不是开玩笑)发送一条围脖会产生巨大的数据分发量(新浪微薄的具体解决方案貌似是异步发送,具体方案超出本文范围,感兴趣的同学可以去看TimYang的博客看看)。总体来说push的特征是取轻、发重。使用push方式发送的例图如下所示:



图2-1 push发feed例图

      pull则相反,当一个用户登录到网站后,业务逻辑系统会到feed列表里去查找用户应该看到的feed,用户的feed渲染系统再把它们pull出来。还是手机短信的例子,pull中发表feed就是把它存入自己的发件箱,用户查看feed的时候就去读取所有关注对象的发件箱把内容“拉”进自己的收件箱。“拉”方案的优点是随需计算(或者叫 online computation)节约存储空间,但相对的缺点也很明显过大的计算量影响feed数据的读取速度,尤其是峰值时段(新浪的号称1亿多用户可不是开玩笑的),相对于push来说pull的特征是取重、发轻。使用pull方式取的例图如下所示:



图2-2 pull模式取feed例图

      针对push和pull的优缺点,实际项目中一般采用混合模式。发布的时候push给热点用户,再把feed存入热点cache当没收到push的用户登陆后可以到cache里快速pull出相关feed;用户可以先收到push的新feed消息,当想看以前的消息时再去pull出相关的feed。

三、 如何表示feed

       每个平台有各式各样的feed消息,考虑到feed消息最终会展示到平台自身、扩展应用以及客户端上,所以对feed格式统一成某种规范而不是发布者随意输出最终展示的文字。同时对图片、视频以及连接等都统一定义。Facebook的实现方式是这样的:

feed是自描述的,即它不是由生产者决定最终格式,也不是前端决定。而是通过template机制来进行。
template在平台中可以由开发者注册,注册时需要定义字段及最终展示样式,如
“{*actor*} 在***游戏中升到 {*credit*} 级”

发布的feed内容仅包含字段数据,也就是变量的值,json格式。
“{"credit": "80"}”

前端需要显示feed时候调用feed模板,再替换字段得到feed内容
“Tim 在***游戏中升到 80 级”

模板需要定义两个,模板标题及模板内容(展示feed详细内容),前端根据需要决定只显示标题还是全部都显示。
“target”, “actor”是系统保留字段,代表目标对象和当前用户,{*actor*}必须放在模板标题开始位置。
“images”, “flash”, “mp3″, “video” 是系统保留字段,无需在模板中定义。但这些内容只会在详细feed界面输出。即只要feed内容里面有这个字段值,界面就会自动显示。
facebook文档中没有规定feed长度限制。
每个开发者最多只能注册100个模板。
四、 有效组织feed信息

      现在的网络是信息大爆炸的展示场,为了避免让用户淹没在杂乱的feed海洋中,如何有效组织这些feed信息就是各大平台技术较量的一大战场,当平台在后台为用户准备好了属于他的原始feed信息后,当然各家平台针对自己业务的特点会有不同的方案,但大致都要经过以下几个步骤才能变成最终展示给用户的形态:

1、   聚合:

       根据feed信息的访问频率可能会存在不同的服务器存储区域中。借用淘宝网核心系统专家余峰对各存储区与读书的比喻加以修改就是:

       “CPU访问L0就像是你读手边的一本书,访问L1就像从书桌拿一本书,L2是从书架拿一本书,L3是从客厅桌子上拿一本书,访问主存就像骑车去社区图书馆拿一本书。”

以下图新浪微博cache设计图为例:



图4-1、新浪微博cache结构图

       feed信息依据自身特性分布于各级存储中,必须要把他们汇聚到一起。汇聚并不单纯只是不同级别存储位置的汇聚,还有牵扯到业务数据的汇聚,比如来自不同feed信息源的汇聚如:来自平台本身,开放应用,第三方外部网站等业务流的汇聚。

2、   去重

      很多时候有些feed信息是有重复性的,比如A发表了一篇日志,他的好友B和C看后很喜欢选择分享。当这条feed如果出现在B与C的共同好友里就会出现重复feed信息。面对这个问题不同的社交平台针对自己的业务类型选择了不同的处理方式,即使是同一个平台也在对这种行为采取了不同的应对策略。经过简单测试后猜测结果:

                           表4-1 不同平台feed去重说明

平台名称

说明

新浪微博

基于微薄轻传播媒体理论,不覆盖重复的分享feed记录下分享传输的轨迹

人人网

早期

不处理重复分享,单纯按时间排序feed,时间早的自然在feed列表中淘汰

曾经

消除重复feed信息,只显示最新一条,但在feed信息的下方显示拥有相似分享的连接,点这里可以看到其他的相似分享以及消重的个数

现在

有选择的处理重复分享,按现有高级排序方法对feed排序

QQ空间

旧版

不处理重复分享,根据旧版汇聚在一个大的用户最新动态之中

新版

不处理重复分享,根据新版方式排序在feed列表里

经过测试发现:

       对于新浪微博来说由于他的定位是一种媒体工具,所以关注的点是信息的传输,故而并未太注重去重的分析。他只是单纯的记录信息传输过程新产生的信息,可以说针对同一条信息每一次不同人的转发都是在完善这条媒体信息。

       人人网由于是这里面最纯粹的社交网络,馈赠型经济驱动下的社交网络更关注的是如何产生分享这一行为,如何激励用户的互动是他首要关注的目标。因而我们可以看出人人网对于去重的技术关注较其他平台更深,其自身去重的方法也在不断修改,一开始确实是没有关注到相关问题,在用户数量增加后,对于一些热点分享就会出现很严重的重复feed,当用户登陆网站会发现排在feed列表前的都是重复的热点信息。针对这点人人推出了消除重复feed的功能,将许多相似分享合并到一个feed中只显示最新的分享条目。但这样一来有些用户发现自己分享的东西,只能在好友feed中生存一小段时间,一旦有共同好友也分享这条信息就会覆盖掉自己的分享。这样一来用户的分享不能被其他人完全看到,抑制了馈赠型经济的产生条件。好在之后随着推荐引擎的技术发展解决了这个问题,人人网feed排序方式的改进,完全可以避免热点信息重复出现在feed列表的前面,而且可以有针对性的将你的分享自动投递到你期望的读者面前,具体的排序方案在下一节会详细分析。

      QQ空间作为腾讯QQ IM的一个扩展并不算一个纯粹的社交分享型网络,他只是完善QQ IM不能触及的一些方面,使用户更加全面的了解IM交流的对象。他更像是一个用户的展示平台,所以重复分享现象在QQ空间中并不严重。QQ空间最主要的展示内容都是围绕着该用户的一些信息故不太会经常产生过度重复的分享内容。

      从趋势上看随着排序算法更加智能化的发展,重复的热点消息问题会得到更有的解决。

3、   排序

       feed排序算法可以说是SNS发展最重要的技术之一,也是各家平台的核心技术。由于看过的相关资料较少,以下的一些分析仅仅是个人纸上谈兵的一点浅析,欢迎大家指正。

不同平台有着不同的排序方案(经测试和个人使用总结):

                               表4-2 不同平台排序说明

平台名称

说明

新浪微博

单纯按照feed发生时间排序

人人网

早期

按feed发布时间顺序排序

曾经

有段时间相册是按最后评论时间排序

现在

综合的推荐引擎方式排序

QQ空间

旧版

将用户最近feed信息框入一个大的feed中,按用户最新动态时间对这个大feed整体排序

新版

类似于新浪微薄的单纯按照feed发生时间排序

       如前所述由于新浪微薄的定位是媒体功能,所以它的时效性要求较高。再加上原来140个字的限制都表明他轻量级的信息定位应该更专心于时效性,故新浪采取产生时间排序无可厚非,微博的原理是假设有价值的微博会不断被转发从而反复出现在用户的feed列表中,但这种方法也产生了微博的“15分钟定理”——如果一条微博在最初的15分钟内没有被大量关注与转发则它会被汪洋“博”海所淹没,最终传播不了多远。根据长尾理论可能存在一些小型的细分用户群体话题存在,于是一些有价值的微博由于初期没有被发现,或是作者的粉丝过少,都有可能导致作品无法传播开去。个人预测新浪也可能会引入推荐引擎系统,通过对用户兴趣和微博数据库的深度数据挖掘进行微博推荐,在时间单维排序的基础上加入兴趣维度。

       单看国内SNS人人网现在的排序系统貌似是最复杂的(facebook的算法智能与变态程度就不考虑了)。根据人人网张铁安在《程序员》与CSDN举办的TUP第二期上的演讲以及个人使用情况来看,首先人人网会对用户的资料和行为进行挖掘然后按兴趣分类生成兴趣向量,再根据用户与好友的互动行为挖掘生成社会关系向量。当一条feed由好友产生了会挖掘这条消息的兴趣分类向量,该分类向量与你的兴趣分类向量计算距离得到兴趣权值,再通过作者与你的社交向量做计算得出关系权值。最终一条feed的排序权值会来自于最少以下几个方面【生成时间、消息热度(最近活跃程度)、兴趣权值、关系权值、商业权值】。其中商业权值应该是针对一些商业推广活动类feed,比如说你参加了在人人网做广告的某些活动,系统会将这条feed发送给你的好友面前,可能根据广告主付钱多少采取不同的权值会在好友的feed列表前面排列很久。

       QQ空间在前面已经分析过是针对QQ IM的一个拓展,他的核心是展示用户信息平台,所以他较老版本的排序是以某一用户的最新动态排序,然后将该用户最近的动态打包合并到一个大的feed中向用户展示。他的核心是针对于一个用户的信息产生的。在新版QQ空间中可能是为了和面向媒体的腾讯微博以及面向社交的朋友网进行整合,修改成面对单一每条feed的排序。

       总体来说SNS由于业务类型的不同,各个平台针对产生feed的这一行为的关注点各有不同,进而导致了排序行为的不同。新浪微博这种媒体平台关注的是feed本身的内容以及由转发与评论引出的新内容;人人网这种纯粹社交网络关注的是由feed内容引发的用户间的互动行为;而QQ空间这种应为是辅助与其他产品的工具,所以他关注的是被辅助的产品自身所需求的特点。

4、   渲染

       渲染阶段比较易于理解,通过第三部分的描述我们了解到feed最初是由变量名与变量值组成的,渲染就是通过将feed套入对应模版并依据之前几个步骤经过聚合、去重、排序后的结果最终生成为用户所看到的feed列表。

五、 架构简介

       这部分只是简单给大家展示下新浪和人人网的系统架构,具体技术和原理个人还正在学习中(就不说出来丢人了)

新浪微博的功能性架构图如下所示:



                        图5-1 新浪微博功能架构

       这个是新浪微博的第三代架构图了,首先在最底层是实现一些存储同步等基础性需求。再上面是平台面向业务的服务与提供应用的服务,最上层是作为第三方开发的API提供给app开发者。

其系统架构如下图:



                               图5-2 微博系统架构

       具体技术方案我自己也在学习中,这里就不再多说了(就不出来丢人了…),有兴趣大家一起探讨学习吧。

人人网系统架构图:



                         图5-3 人人网feed系统架构图

       简单说一下这图,笑脸表示某个用户很开心写了篇日志,是先交给分发器(Dispatcher),经过一些处理后发往三个不同的地方,第一个是newsfeed这是完整的一个feed信息与索引;第二个是minifeed这是一个feed的短摘要信息,你在人人网上看到的某个用户写了篇日志的新鲜事,在它的标题下面会有一小段摘要,这个短摘要就是minifeed;第三是要把新鲜事本身cache起来,会把feed发到集群里面最后进行存储持久化。

六、 总结

       好久没写这么长篇的东西了,终于写完了。由于个人水平有限系统架构部分写的有点略显薄弱,欢迎大家一起讨论,争取以后有机会把这部分单独完善重写一篇。

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


ITeye推荐



MySQL与Oracle的大小写问题

$
0
0

前段时间,维护的一个应用在Oracle测试时出错,该应用原来已经在MySQL上完成测试。通过查找定位,原因是字段名大小写问题。下面与大家分离一下MySQL与Oracle在大小写处理上的区别:

MySQL

1、在Windows下,数据库名、表名、字段名不区分大小写。
2、大Linux/Unix下,数据库名、表名区分大小写,字段名不区分大小写。
3、编辑/etc/my.cnf,设置lower_case_table_names可以让MySQL是否区分表名的大小写。

0:区分大小写;
1:不区分大小写。

 

Oracle

1、在Oracle中,如果字段名称被双引号("")包裹,Oracle会区分大小写;
2、如果字段名称没有被双引号("")包裹,则全部转换成大写来执行。
3、如果表结构设计时,字段名称使用了数据库的保留字,SQL中的字段名称必须用双引号("")包裹,以避免SQL语句执行出错。不建议用数据库的保留字来做表名和字段名。

以下SQL语句在Oracle中执行时,字段 stat_time, interval 没有被双引号("")包裹,不区分大小写: 
insert into smsc_flow(stat_time,interval,"MODULEID","SMSCNO","ICPNO","MT_OK","MT_FAIL","MT_DELAY","MO_OK","MO_FAIL","STATUS_OK","STATUS_FAIL","SUCCESS_STATUS","COUNT") values('20101010112',1,'MT001',1,1,1,1,1,1,1,1,1,1,1) 

以下SQL语句在Oracle中执行时,字段 stat_time, interval 被双引号("")包裹,全部转换成大写执行: 
insert into smsc_flow("stat_time","interval","MODULEID","SMSCNO","ICPNO","MT_OK","MT_FAIL","MT_DELAY","MO_OK","MO_FAIL","STATUS_OK","STATUS_FAIL","SUCCESS_STATUS","count") values('20101010111','20101010111',1,'MT001',1,1,1,1,1,1,1,1,1,1,1) 

 



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


ITeye推荐



为什么越来越多的网站域名不加 www 了?

$
0
0
这个问题我琢磨过很久,分享一下心得。
1、不加www有哪些好处和坏处?
不加 www 的裸域名好处主要是域名更加简短、容易记忆。坏处就多了,讲几个主要的技术原因:
  • 裸域名只能绑定 DNS 的 A 记录,不能绑定 CNAME 记录。也就是说你不能把裸域设定为另外域名的别名。很多时候这对管理不是很方便,特别是使用第三方托管服务的时候。如果第三方迁移服务器导致 IP 地址变更,你必须自己去更改 DNS 的 A 记录。

    比如你的个人博客采用 Tumblr 的服务,如果使用裸域,你需要手动将你域名的 A 地址指向 Tumblr 指定的 IP 地址。Tumblr 如果迁移了机房,所有通过这种方式设定个人域名的用户都必须更改自己的 DNS 才能继续使用,否则服务就会中断。使用子域名的 CNAME 记录就相对简单很多,只需要将 www 子域名的 CNAME 字段指向 http://domains.tumblr.com 这个域名,之后如果 Tumblr 更改 IP 地址,他们只需要重新设置 http://domains.tumblr.com 这个域名的 A 记录,而无需要求每个用户去更改 DNS 记录。

    这个技术上的限制导致许多大型的第三方服务商不支持使用裸域。典型的如 Google 的服务,现在都不能使用裸域。Google 的服务用户基数大,不得不采用 DNS 级别的分布式,使用到的 IP 地址太多,而且变动大。让用户绑定 A 记录的话不利于负载均衡,维护起来也是几乎不可能完成的任务。同理,大部分 CDN 也不支持裸域。
  • 裸域的 cookie 的作用范围太大。假如知乎也采用裸域,那么知乎所有 cookie 的作用范围就包括 http://zhihu.com下的所有子域名。也就是说访问 http://foo.zhihu.comhttp://bar.zhihu.com的时候都会带上 http://zhihu.com裸域页面设置的 cookie。从安全、隐私、可扩展性、以及管理的角度而言,这对很多大型网站来说是不可接受的。
  • URL 的正则匹配,如果带 www 前缀的并且以 .com/.net/.org 结尾的,通常成功的机会要大很多。这个你会在许多文本编辑器里面遇到。如果 URL 不是 www 开头,并且也不是三大顶级域名结尾的,匹配成功的概率就要小很多。这是使用过程中有时候会让人很抓狂的点,重不重要全看你的用途和场合了。

另外一点非技术上的考量:用 www 子域名的好处体现在线下的环境,比如户外广告、报纸杂志、语音广播、语音电话等使用场合,www 这个前缀(不管是视觉的还是听觉的)能够很明确的提醒受众,这个信息片段是一个网站。有人会说加上 http:// 前缀也能解决这个问题,但现在随着以 Chrome/Safari 为首的浏览器都开始在地址栏里隐藏 http:// 协议前缀了,普通用户对于 http:// 这几个字符的理解会越来越模糊,所以如果是线下的话,保留 www 这个 visual/vocal cue 还是有一定意义的。

总的来说对于大访问量或多子域名的网站来说,不建议使用裸域。小流量或子域名少的网站的话就看个人爱好了。我挺喜欢裸域的。最近几年流行起来的「单页网页应用」(Single Page Web App) 也是以采用裸域的居多,Twitter 算是一例。

2、去掉www是否会影响网站的SEO(主要是排名和收录)?(前提是过去有加www)
早先裸域刚开始流行的时候确实有传闻说不利于 SEO,但现在看来似乎并无任何问题。如果有的话也是搜索引擎的 bug,给他们提一下他们应该会很乐意去改。Google 的站长工具里面有工具可以帮助你做 URL 迁移的,可以有效的解决这个问题,再配合下一部分的跳转,不用担心对 SEO 有任何负面影响。
3、用什么方式去跳转最好?(如301)
不管你决定使用还是不使用裸域,最好不要在同时保留 www 和非 www 前缀的 URL,这样既不方便用户的浏览器区分访问历史,也会对你做访问统计带来不少麻烦。最佳的方式是采用 301 跳转,并且跳转的时候保留 URL 里域名后的全部内容。比如,如果你决定使用裸域 http://example.com,那么请务必将
http://www.example.com/foo/bar?spam=egg
301 跳转到
http://example.com/foo/bar?spam=egg
去。或者反过来,如果你决定不使用裸域,那么请务必将
http://example.com/foo/bar?spam=egg
301 跳转到
http://www.example.com/foo/bar?spam=egg

这样的跳转需要在 web 服务器里单独配置,很多 DNS 管理界面提供的简单的跳转到新域名的根目录无法实现这样的功能(仅仅跳到 http://example.com/ ),对用户体验和搜索引擎 SEO 而言都是非常糟糕的。

下面给出如何在 nginx 里面实现上述的跳转:
# redirect http(s)://www.example.com to http(s)://example.com
server {
    server_name www.example.com;
    return 301 $scheme://example.com$request_uri;
}

# redirect http(s)://example.com to http(s)://www.example.com
server {
    server_name example.com;
    return 301 $scheme://www.$host$request_uri;
}


— 完 —
本文作者: Rio

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

此问题还有 20 个回答,查看全部。
延伸阅读:
做一个网站,网址加www好还是不加好,为什么?
网站域名中一定要包含 www 吗?

大型新闻网站点击量的技术方案咨询

$
0
0

网友提问:

1.问题主题

如何记录用户发表的文章的点击量

2.问题补充描述

当并发非常小的时候可以直接存在这个文章表里面,叫一个click_count,但是如果网站的访问量很高,那这样数据库肯定要累死,各位大牛有什么好的解决办法么?

8.5.3

mysqlops回答:

我们只讨论访问量很高的情况,例如:每天1亿及以上PV的新闻网站,建议做法可以分为2种方式:

1.使用缓存系统,比如Redis非常适合做计数器,异步的方式同步到MySQL数据库 或者Redis直接持久化的方式;

2.变相直接更新数据库的方式,每个应用程序服务都有一个自己内部的全局计数器,默认每隔10秒或者缓存计数达到10则一次性更新同步到数据库中,也即采用延迟非实时更新的方式,同时把计数器单独保存到一张表中,设计成父子表的模式;

上述2种方式作者都曾实施过,典型的场景是微博类的统计量太大,故采用第一种方式;阿里旺旺的新闻网站则采用第二种方式。

MySQL 5.6 查询优化器新特性的“BUG”

$
0
0

最近碰到一个慢SQL问题,解决过程有点小曲折,和大家分享下。 SQL本身不复杂,表结构、索引也比较简单,不过个别字段存在于多个索引中。

CREATE TABLE `pre_forum_post` (
  `pid` int(10) unsigned NOT NULL,
  `fid` mediumint(8) unsigned NOT NULL DEFAULT '0',
  `tid` mediumint(8) unsigned NOT NULL DEFAULT '0',
  `first` tinyint(1) NOT NULL DEFAULT '0',
  `author` varchar(40) NOT NULL DEFAULT '',
  `authorid` int(10) unsigned NOT NULL DEFAULT '0',
  `subject` varchar(80) NOT NULL DEFAULT '',
  `dateline` int(10) unsigned NOT NULL DEFAULT '0',
  `message` mediumtext NOT NULL,
  `useip` varchar(15) NOT NULL DEFAULT '',
  `invisible` tinyint(1) NOT NULL DEFAULT '0',
  `anonymous` tinyint(1) NOT NULL DEFAULT '0',
  `usesig` tinyint(1) NOT NULL DEFAULT '0',
  `htmlon` tinyint(1) NOT NULL DEFAULT '0',
  `bbcodeoff` tinyint(1) NOT NULL DEFAULT '0',
  `smileyoff` tinyint(1) NOT NULL DEFAULT '0',
  `parseurloff` tinyint(1) NOT NULL DEFAULT '0',
  `attachment` tinyint(1) NOT NULL DEFAULT '0',
  `rate` smallint(6) NOT NULL DEFAULT '0',
  `ratetimes` tinyint(3) unsigned NOT NULL DEFAULT '0',
  `status` int(10) NOT NULL DEFAULT '0',
  `tags` varchar(255) NOT NULL DEFAULT '0',
  `comment` tinyint(1) NOT NULL DEFAULT '0',
  `replycredit` int(10) NOT NULL DEFAULT '0',
  `position` int(8) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`tid`,`position`),
  UNIQUE KEY `pid` (`pid`),
  KEY `fid` (`fid`),
  KEY `displayorder` (`tid`,`invisible`,`dateline`),
  KEY `first` (`tid`,`first`),
  KEY `new_auth` (`authorid`,`invisible`,`tid`),
  KEY `idx_dt` (`dateline`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;"root@localhost Fri Aug  1 11:59:56 2014 11:59:56 [test]>show table status like 'pre_forum_post'\G
*************************** 1. row ***************************
           Name: pre_forum_post
         Engine: MyISAM
        Version: 10
     Row_format: Dynamic
           Rows: 23483977
 Avg_row_length: 203
    Data_length: 4782024708
Max_data_length: 281474976710655
   Index_length: 2466093056
      Data_free: 0
 Auto_increment: 1
    Create_time: 2014-08-01 11:00:56
    Update_time: 2014-08-01 11:08:49
     Check_time: 2014-08-01 11:12:23
      Collation: utf8_general_ci
       Checksum: NULL
 Create_options: 
        Comment: 


mysql> show index from pre_forum_post;
+----------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table          | Non_unique | Key_name     | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| pre_forum_post |          0 | PRIMARY      |            1 | tid         | A         |      838713 |     NULL | NULL   |      | BTREE      |         |               |
| pre_forum_post |          0 | PRIMARY      |            2 | position    | A         |    23483977 |     NULL | NULL   |      | BTREE      |         |               |
| pre_forum_post |          0 | pid          |            1 | pid         | A         |    23483977 |     NULL | NULL   |      | BTREE      |         |               |
| pre_forum_post |          1 | fid          |            1 | fid         | A         |        1470 |     NULL | NULL   |      | BTREE      |         |               |
| pre_forum_post |          1 | displayorder |            1 | tid         | A         |      838713 |     NULL | NULL   |      | BTREE      |         |               |
| pre_forum_post |          1 | displayorder |            2 | invisible   | A         |      869776 |     NULL | NULL   |      | BTREE      |         |               |
| pre_forum_post |          1 | displayorder |            3 | dateline    | A         |    23483977 |     NULL | NULL   |      | BTREE      |         |               |
| pre_forum_post |          1 | first        |            1 | tid         | A         |      838713 |     NULL | NULL   |      | BTREE      |         |               |
| pre_forum_post |          1 | first        |            2 | first       | A         |     1174198 |     NULL | NULL   |      | BTREE      |         |               |
| pre_forum_post |          1 | new_auth     |            1 | authorid    | A         |     1806459 |     NULL | NULL   |      | BTREE      |         |               |
| pre_forum_post |          1 | new_auth     |            2 | invisible   | A         |     1956998 |     NULL | NULL   |      | BTREE      |         |               |
| pre_forum_post |          1 | new_auth     |            3 | tid         | A         |    11741988 |     NULL | NULL   |      | BTREE      |         |               |
| pre_forum_post |          1 | idx_dt       |            1 | dateline    | A         |    23483977 |     NULL | NULL   |      | BTREE      |         |               |
+----------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

我们来看下这个SQL的执行计划:

mysql> explain select * from pre_forum_post where tid=7932612 and `invisible` in('0','-2') order by dateline  limit 15\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: pre_forum_post
         type: index
possible_keys: PRIMARY,displayorder,first          key: idx_dt
      key_len: 4
          ref: NULL         rows: 14042        Extra: Using where

可以看到执行计划比较奇怪,从几个可选的索引中,最终选择了 idx_dt,结果悲剧了,这个SQL执行耗时很长:

mysql> select * from pre_forum_post where tid=7932612 and `invisible` in('0','-2') order by dateline  limit 15;
15 rows in set (26.78 sec)

看下MySQL的会话状态值:Handler_read_next

| Handler_read_next          | 17274153 |

从1700多万数据中选取15条记录,结果可想而知,非常慢。 我们强制指定比较靠谱的索引再看下:

mysql> explain select * from pre_forum_post force index(displayorder) where tid=7932612 and `invisible` in('0','-2') order by dateline  limit 15\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: pre_forum_post
         type: range
possible_keys: displayorder          key: displayorder      key_len: 4
          ref: NULL         rows: 46131
        Extra: Using index condition; Using filesort

看下实际执行的耗时:

mysql> select * from pre_forum_post force index(displayorder) where tid=7932612 and `invisible` in('0','-2') order by dateline  limit 15;
15 rows in set (0.08 sec)

尼玛,怎么可以这么快,查询优化器未免太坑爹了吧。 再看下MySQL的会话状态值:Handler_read_next

| Handler_read_next          | 31188 |

和不强制索引的情况相比,差了553倍! 所幸,5.6以上除了EXPLAIN外,还支持OPTIMIZER_TRACE,我们来观察下两种执行计划的区别,发现不强制指定索引时的执行计划有诈,会在最后判断到 ORDER BY 子句时,修改执行计划:

          {\"reconsidering_access_paths_for_index_ordering": {\"clause": "ORDER BY",\"index_order_summary": {\"table": "`pre_forum_post`",\"index_provides_order": true,\"order_direction": "asc",\"index": "idx_dt",\"plan_changed": true,\"access_type": "index_scan"\              } /* index_order_summary */\
            } /* reconsidering_access_paths_for_index_ordering */\

而在前面analyzing_range_alternatives和considered_execution_plans阶段,都认为其他几个索引也是可选择的,直到这里才给强X了,你Y的… 看起来像是MySQL 5.6查询优化器的bug了,GOOGLE了一下,还真发有人已经反馈过类似的问题: MySQL bug 70245: incorrect costing for range scan causes optimizer to choose incorrect index

看完才发现,其实不是神马BUG,而是原来从5.6开始,增加了一个选项叫 eq_range_index_dive_limit 的高级货,这货大概的用途是: 在较多等值查询(例如多值的IN查询)情景中,预估可能会扫描的记录数,从而选择相对更合适的索引,避免所谓的index dive问题。

当面临下面两种选择时:

1、索引代价较高,但结果较为精确;
2、索引代价较低,但结果可能不够精确;

简单说,选项  eq_range_index_dive_limit 的值设定了 IN列表中的条件个数上线,超过设定值时,会将执行计划分支从 1 变成 2。

该值默认为 10,但社区众多人反馈较低了,因此在5.7版本后,将默认值调整为 200了。

不过,今天我们这里的案例却是想反的,因为优化器选择了看似代价低但精确的索引,实际却选择了更低效的索引。 因此,我们需要将其阈值调低,尝试设置  eq_range_index_dive_limit = 2 后(上面的例子中,IN条件里有2个值),再看下新的查询计划:

mysql> set eq_range_index_dive_limit = 2;

mysql> explain select * from pre_forum_post where tid=7932612 and `invisible` in('0','-2') order by dateline  limit 15\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: pre_forum_post
         type: range
possible_keys: PRIMARY,displayorder,first          key: displayorder
      key_len: 4
          ref: NULL         rows: 54        Extra: Using index condition; Using filesort

卧槽,预估扫描记录数又降了557倍,相比最开始降了接近32万倍! 在这个案例中,虽然通过修改选项  eq_range_index_dive_limit 的阈值可以达到优化效果,但事实上更靠谱的做法是:直接删除 idx_dt索引。 是的,没错,删除这个垃圾 重复索引,因为实际上这个索引的用处不大,够坑爹吧~~

参考资料:
http://blog.163.com/li_hx/blog/static/18399141320147521735442/
http://mysqlserverteam.com/you-asked-for-it-new-default-for-eq_range_index_dive_limit/https://www.facebook.com/note.php?note_id=10151533648715933http://bugs.mysql.com/bug.php?id=70586http://bugs.mysql.com/bug.php?id=67980http://bugs.mysql.com/bug.php?id=70331

我的面试程序员的经验:一个能一网打尽的技术问题

$
0
0

常见的招聘过程

我之前的主要工作是参与招聘并进行技术面试,招聘的总过程如下:

1. HR所进行的面试:判断候选人是不是一个连环杀手或精神病。

2. 技术专家进行的面试:判断候选人是不是一个优秀的程序员。

3. 大老板进行的面试:判断候选人愿意接受多少报酬。

我面试过两种类型的人:实习生和准员工。实习生只需要经历以上第二条步骤即可,其他人则需要经历所有的步骤。在那个公司工作的两年多时间里,我进行了超过200次技术面试,这对我来说是一种丰富的学习经历,我逐步弄清了这一过程的实质。这里有一个很重要的前提,请你记住,在法国你不能轻易解雇一个人,雇佣了一个错误的家伙,你就等着抱憾终身吧。找出最好的候选人极为关键,不能犯任何错误,这是一个繁琐的过程,但我乐在其中。

特别专业的中彩票式技术问题

在2008年,我进行了我的第一次技术面试,当时,公司已经有了一套工作流程供我参照:面试时间1小时,候选人有30分钟时间回答15个测试问题,之后我们会花15分钟时间讨论他们的回答,外加15分钟时间回答关于工作方面的问题。我很快就意识到这样的问卷是多么的糟糕,我的意思是,你竭尽全力也找不出比它更坑爹的东西了。我们公司里大概有50%的项目都是使用Java编写的,所以测试题就非常专注于Java,其中包含了5个琐碎的问题,紧接着是10个关于特定Java框架的极难问题,比如我们经常使用的问题有:

类和对象的区别。

Struts 2中的execAndwait拦截器的用途是什么?

见鬼的是,甚至是我自己都无法解释这些问题或再补充点什么,每一次面试我都祈祷候选人不会用这些问题来反问我!对一个面试官来说,这很讽刺,不是吗?无论如何我还是会快速浏览一下他们的回答(2-5分钟),之后将时间放在讨论他们的简历上,这浪费了很多时间,于是我决定改进一下。我上网比较了成百上千个面试问题,那时我相信我们必须在测试中放置正确的问题,才能展示一个人才的真正优秀之处,正所谓“好马配好鞍”。

宽泛的、怎么回答都对的技术问题

经过大约一个月的研究,我已经在网上找遍了各种问题,提炼出最好的50个问题,我认为它们都是好问题,因为用任何语言都能回答它们,同时难度也是平稳提升的。我将这50个问题打散,组成5套10大题,随机分发。

示例:

单例是什么?你什么时候会用它/不用它?

这问题好多了吧,我觉得显而易见的,一个给力的问题通常会得到一个给力的回答作为回报,我实践了几个星期,但是不知何故这并不完全奏效,我觉得我已经做的很好了,但结果却并不怎么好。是的,这些问题能够测试出一个人是否熟悉编程理论,然而最终我对此人能否编程依然一无所知,直到最后我也不确定用这种方法招聘员工能比用以前那种粗糙的struts 2问卷好多少。我想了很多,我意识到这其中有两个巨大的问题:

1. 问题太泛了,如果不专注于某一种语言,我无法讨论诸如SQL,前端细节等话题。

2. 问题太短了,10个泛泛而谈的问题涉及面太窄,我没法通过其他方式判断此人是否是优秀的程序员。

我需要的是更多的问题,并且这些问题必须针对候选人所申请的工作内容。

面试问题宝典:10万个为什么

事情逐渐有点失控了,当时我继续深入研究,并创建了一个全自动化的测试工具(在一个实习生的帮助下):测试经理(QM)。这个工具使招聘过程变得完美:在初次面试后,HR会选择三个与工作描述相关的话题,之后工具会自动生成一组多项选择题,其中包含3*20=60个随机但具体的问题,其难度符合测试者的经验水准。

示例:

(javascript)
var i = 0;
function a(){
  var i = 2; 
  i++;
} 
a(); 
alert(i);    =>    0 ? 2 ? 3 ?

之后,工具会绘制一个小图表,产生并发送邮件给HR,直接显示结果,而不是一堆无用的指标。这是我多么为之骄傲的工具!我急切盼望着有候选人能够测试这套系统!我坐在HR旁边,在内部系统上观察候选人选择某些答案后的实时分数。QM使我们所有的工作都变得更容易了,看上去非常完美,直到在我们自己的开发人员上测试它时……

好吧,情况比我们想象中的更为离奇,我们之中许多优秀的开发人员会获得和被我拒绝的那些人一样的分数,这才是正解,QM被证明是无效的!我花费了很多时间建立这个工具,同时也花费了很多时间认识到我犯了一个巨大的错误:我们希望对结果进行自动化处理,这迫使我们只能设置选择题。用户只需要选择一个答案,因而问题最后大多演变成了技巧性问题,最终的结果是我们根本没有测试软件开发的技能!要面对这副窘境非常艰难,但最后我还是承认这个工具产生了反作用,展示了错误的印象。

面试时让程序员去编码

8个月过去了,我做了更多的研究,视察了一些美国公司筛选候选人的过程,这时候我决定去追求另一种方法:只需编码。这是程序员得到报酬的原因啊,所以为什么不直接展示给我看他们是怎样写代码的呢?你会觉得这很合乎逻辑……在经历了前几个月的教训后,现在测试变得很简单:我会给出三个算法题,你需要在30分钟内解决它们。候选人可以任意选择语言,并使用一台电脑作答(无法连接网络)。这些都是网上能找到的经典问题:其中一个算法题通常涉及字符串操作(比如在一句句子中逆置单词),另一个问题涉及循环(比如计算斐波那契数列),最后一个问题涉及集合(比如列表排序)。

示例:

print out digits 1-100.
for multiples of 3, print out foo.
for multiples of 5, print out bar.
for multiples of both 3 and 5, print out foobar.

所有事情都变得更清晰,更美好了。我可以很直观地看到谁在代码中缩进、注释、遵循约定、寻找解决方案,等等。我可以据此判断这个人在过去的编程量,此外,通过与他们讨论问题的答案也能获得很多信息。我觉得候选人对这些测试题应该会感觉良好,因为我已经试图解除他们所有的压力,他们可以从容作答,选择他们想用的任何一门语言,征求建议,等等。

起初,我对结果感到很振奋,并继续执行了几个月,然而再一次的,我意识到我遗漏了些什么……好像有些事不对劲……事实上我确实可以依靠这种方式找出能解决算法问题的人,但他们真的是我所要寻找的优秀程序员吗?请你思考一下,一个程序员的水平是不是由他能否解决一个数学问题所定义的?是不是由他能否写出复杂度为O(n log n) 而非 O(n^2)的排序所决定的?

能够一网打尽的面试问题

我很清楚的记得,当我初学编程时,windows 3.1还未问世,QBasic语言是搭载在MSDOS 5.0上的,它包含自带的帮助信息,其中有所有的函数和关键字,像一本完美的离线手册。至今我还记得那时候编程的独特感受,萦绕在我心头,每一次我敲击F5,看到我写的程序在我眼前执行,每一行代码,每一个提示,甚至是颜色,或难以解决的问题……我简直是在天堂。我记得我在每一条命令前添加行号,用可怕的GOTO填满我的代码,同时每天又能学到很多令人振奋的新东西。我热爱编程,我会夜以继日地编写游戏、解决问题,并展示给我父母和朋友。时光飞逝,我从QBasic到pascal到vb,通过2400bps的调制解调器和家庭电话线路,为我们的BBS(Atomic BBS)编写游戏。我并不优秀,好吧事实上我的代码相当糟糕!但我热爱它!!我不能失去它……我猜有些人在他们第一次驾驶飞机、驾驶船只、吸食大麻、吃in n out(译注:美国一家汉堡快餐店)时会感受到他们的肾上腺素涌出的感觉,对我来说,那就是编程、编译和运行。25年前我获得了这种感受,至今它从未离我而去,我为编程而生,我永远都是程序员。

我始终相信,一个热爱编程的人不会只在工作中编程,在家中他们也会继续创造乐趣,这是一种爱好。多少次,我在工作中因为蛋疼的Eclipse而感到失望,只能在我回家后,写Ruby on Rails代码寻找快乐,放松身心!

回到上一个话题,在一年的尝试和失败后,我完全放弃了技术测试。我会坐在候选人身边,花5到10分钟阅读和点评他的简历,不问任何问题,之后我会翻过简历,看着候选人的眼睛问道: “我们剩下大概30分钟时间,你能告诉我你所编写过的最成功的项目的情况吗?”

这个简单、独特和客观的问题是关键。一些人会含糊地回答他们之前的工作或学校的项目,而另一些人会突然变得生龙活虎,尽管一开始他们还有点放不开,他们会热情激昂的谈论他们编写的游戏、制作的站点、贡献的开源项目、开发的工具,他们会很骄傲的展示给我看。我时常会被他们的侃侃而谈吸引和着迷,继而询问他们这些喜爱的项目的所有细节,他们的话匣子打开了,讲述了他们所攻克的技术难题,加上一些小小的个人情怀,仿佛这就是他们的孩子。还有一点无法令人忘怀:我仿佛可以看到他们眼中的光芒,仿佛可以看到他们小时候编译和运行第一个hello world程序的情景,很快,我意识到了我们的共同点,我们都是程序员。

他们中的绝大多数人没有接触过struts或其它我们正在使用的指定框架,然而当他们一进入工作后,他们总是会成为金牌程序员。他们学习快速,能写出更好的代码,他们用创造力和正能量激励着其他人,他们是真正的程序员。

oracle的jdbc驱动

$
0
0
oracle的jdbc驱动主要有下面三类:

  1、JDBC OCI: oci是oracle call interface的缩写,此驱动类似于传统的ODBC 驱动。因为它需要Oracle Call Interface and Net8,所以它需要在运行使用此驱动的JAVA程序的机器上安装客户端软件,其实主要是用到orcale客户端里以dll方式提供的oci和服务器配置。

  2、JDBC Thin: thin是for thin client的意思,这种驱动一般用在运行在WEB浏览器中的JAVA程序。它不是通过OCI or Net8,而是通过Java sockets进行通信,是纯java实现的驱动,因此不需要在使用JDBC Thin的客户端机器上安装orcale客户端软件,所以有很好的移植性,通常用在web开发中。

  3、JDBC KPRB: 这种驱动由直接存储在数据库中的JAVA程序使用,如Java Stored Procedures 、triggers、Database JSP's。因为是在服务器内部使用,他使用默认或当前的会话连接来访数据库,不需要用户名密码等,也不需要数据库url。

  在应用开发的时候,通常是用前面两种方式,下面是数据库url的写法:

  jdbc:oracle:thin:@server ip: service

  jdbc:oracle:oci:@service

  看来oci的还更加简洁,ip可以省掉不写了,这是因为oci驱动通过客户端的native java methods来条用c library方式来访问数据库服务器,使用到了客户端的net manager里的数据库服务配置。

  因为oci方式最终与数据库服务器通信交互是用的c library库,理论上性能优于thin方式,据说主要是体现在blob字段的存取上。

  开发oracle经常用到的 pl sql dev使用的估计是oci方式,需要安装客户端,但也可以不安装,但是要抽出其中的oci相关的dll即jar包、注册环境变量、配置侦听文件等,详细步骤可参考这个链接http://blog.csdn.net/shenyc/archive/2009/10/22/4713991.aspx。

  oracle在10g之后提供了精简客户端,安装的过程应该包括上面的那些工作。

  How does one connect with the JDBC OCI Driver?

  One must have Net8 (SQL*Net) installed and working before attempting to use one of the OCI drivers.

  Code: [Copy to clipboard]

  import java.sql.*;

  class dbAccess {

  public static void main (String args []) throws SQLException

  {

  try {

  Class.forName ("oracle.jdbc.driver.OracleDriver");

  } catch (ClassNotFoundException e) {

  e.printStackTrace();

  }

  Connection conn = DriverManager.getConnection

  ("jdbc:oracle:oci8:@ORA1", "scott", "tiger");

  // or oci9 @Service, userid, password

  Statement stmt = conn.createStatement();

  ResultSet rset = stmt.executeQuery (

  "select BANNER from SYS.V_$VERSION"

  );

  while (rset.next())

  System.out.println (rset.getString(1)); // Print col 1

  stmt.close();

  }

  }

  How does one connect with the JDBC KPRB Driver?

  One can obtain a handle to the default or current connection (KPRB driver) by calling the OracleDriver.defaultConenction() method. Please note that you do not need to specify a database URL, username or password as you are already connected to a database session. Remember not to close the default connection. Closing the default connection might throw an exception in future releases of Oracle.

  import java.sql.*;

  Code: [Copy to clipboard]

  class dbAccess {

  public static void main (String args []) throws SQLException

  {

  Connection conn = (new

  oracle.jdbc.driver.OracleDriver()).defaultConnection();

  Statement stmt = conn.createStatement();

  ResultSet rset = stmt.executeQuery (

  "select BANNER from SYS.V_$VERSION"

  );

  while (rset.next())

  System.out.println (rset.getString(1)); // Print col 1

  stmt.close();

  }

  }
原文:http://oracle.chinaitlab.com/exploiture/805352.html

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


ITeye推荐




对于初学者 哪几种编程语言最值得你去学习

$
0
0

有几个人曾经问我,有哪些最有用或最好的编程语言适宜学习?

HTML/CSS不谈,我认为答案取决于你想通过编程来做什么。

几种编程语言简述

  • 对只用一种语言来构建某个项目的情况而言,Javascript和它的框架是非常有用的。
  • Angular.js可以控制展示给用户的网站前端。
  • Node.js将作为管理网站所有内容的网络服务器。
  • Express.js在前端和服务器两者之间运行,对信息的来去进行导向。

MongoDB作为存储中心,用于存储从用户处获取的数据。MEAN(Mongo/Express/Angular/Node)——一个有助于构建你在一个Web应用中所需要的一切东西的结构化框架——是一套目前被大量初创公司所喜好的编程语言。这是一个完备的组件,它包含了基于单一语言来Web化地构建所有事物的技术。

我一直在用 Egghead.ioScotch.io来及时更新我的Angular.js和MEAN技术。Egghead的网站内容主要集中于有序地组织的教学视频。Scotch有一些关于构建Web应用的整个流程的很棒的图表,包括下面这张解释MEAN的图表:

它们都有很棒的教学视频,教你如何构建精巧的应用,例如基本的搜索引擎和新的表单验证方式(以确保若你创建输入表单,人们的实际输入将是有效标准下的值)。通过使用Angular.js,你可以使一个网站动画化并使它“动”起来,而不需要过多的设置。这是非常精巧的一种方式。

Python可读性和灵活性都非常好,最近它成为大学院校在教授计算机专业时选择的 入门语言之一。

对于玩转数据以及用它的共通模块来完成各种你以前认为不可能的精巧工作来说——例如抓取完整的网页和进行高级科学计算,Python都是绝佳的。我是在 Learn Python上开始学习Python的,它非常符合我“ 边做边学”的学习风格。

Java(以及其他更紧密地与计算机硬件交互的语言)对人类理解来说会稍难一些,尽管对于帮助你理解“代码的实际运行过程”和“程序员与计算机的交互过程”的绝大部分知识而言,它们是很棒的。

Java也用于在安卓系统上的移动应用开发,而这将会是一直有需求的领域。

如果我们想简捷地将知识转换成金钱,那么,我曾见到业界对IOS开发者有着大量需求,而开发IOS应用所使用的 Objective-CSwift也并不那么难学习。

Ruby——尤其是当它与Rails一起使用时——是一种因其并不陡峭的学习曲线而被大量初创公司在初创时所用的语言(事实上,有一本叫 Children’s Bookfor Ruby的书)。

我自己正在学习Python以处理数据;学习Javascript和MEAN以创建Web应用;以及学习Java以对计算机科学有更深入的理解与构建移动应用。我认为这是一个可用的平衡良好的编程语言组合。

我已经有了一组 学习清单和资源,用来帮助你我学习构建杰出作品所需要的东西。但实际上,这些都不是最好的编程语言。

最值得学习的编程语言——以及如何着手去学习它

学习如何像一个程序员那样思考——学习如何通过简洁的代码来用数学解决问题,绝对是最适宜学习的东西。编程语言会演进,它们会改变,它们会衰落并不再受欢迎;一个编程语言社群会变化成另外一个。

现在伟大的Web应用可能会在几十年后被淘汰。不会改变的是对人们的逻辑思考和问题解决能力的需求——以及将它们变成在机器上运作的一个更简单的自动化流程的需求。

你可以寄希望于一个事实,那就是随着时代不断发展,如果你训练解决问题的技能,你将能够找到最适合你的语言,以及获得你建立伟大事业所需要的知识与金钱。

我已经开通了 Project Euler,它提供一系列与数学和逻辑相关的编程问题。我在 Codecademy workspace上尝试用Python构造整洁的代码来解决这些问题。这是一名Google招聘官所提到过的“一个学习编程的很好的训练步骤”,而我并不怀疑这一点。我感到我的大脑更敏锐了,并对我的能力更充满自信——不仅仅是编程能力,还有思考能力。

最适宜学习的编程语言最终还是位于编程核心的逻辑,数学和问题解决能力。“什么是最适宜学习的编程语言”这个问题并无对错,只要你理解编程的基础价值何在。

CentOS 下通过 JavaCPP 调用 FFMPEG

$
0
0

1. Java 与 FFMPEG

   FFMPEG 是一个广泛用于媒体处理的库,在Java的世界里,处理视频的能力相当弱,于是有很大需求需要Java 调用 FFMPEG。

  Java 调用C 的方式有很多,可以用最原始的JNI方式,也可以JNA方式,还可以是命令行。

   采用命令行的方式比较简单,不过有很大局限性,尤其是涉及到 视频的处理和分析的时候,比如要取出某个packet,然后进行处理。

 
    这里介绍的是用JavaCPP 调用 ffmpeg 库的方式,而不是命令行模式。

     JavaCPP的源码在这里:https://github.com/bytedeco/javacpp 


  基于JavaCPP的项目   

1)JavaCV,是做图形图像的,有人脸识别、增强现实AR 等开源算法,非常不错

    项目主页是:https://github.com/bytedeco/javacv 
  
 2)JavaAV 封装了FFMPEG的java 接口

    项目主页:https://github.com/hoary/JavaAV


2.  JavaCPP Presets


  为了方便使用,JavaCPP下面有个presets项目,主页是 https://github.com/bytedeco/javacpp-presets,将一些常用的项目都编译好了以方便使用。
    集成的项目包括:
• OpenCV 2.4.9 http://opencv.org/downloads.html
• FFmpeg 2.3.x http://ffmpeg.org/download.html
• FlyCapture 2.6.x http://ww2.ptgrey.com/sdk/flycap
• libdc1394 2.1.x or 2.2.x http://sourceforge.net/projects/libdc1394/files/
• libfreenect 0.5 https://github.com/OpenKinect/libfreenect
• videoInput 0.200 https://github.com/ofTheo/videoInput/tree/update2013
• ARToolKitPlus 2.3.0 https://launchpad.net/artoolkitplus
• flandmark 1.07 http://cmp.felk.cvut.cz/~uricamic/flandmark/#download
• FFTW 3.3.4 http://www.fftw.org/download.html
• GSL 1.16 http://www.gnu.org/software/gsl/#downloading
• LLVM 3.4.2 http://llvm.org/releases/download.html
• Leptonica 1.71 http://www.leptonica.org/download.html
• Tesseract 3.03-rc1 https://code.google.com/p/tesseract-ocr/

    包含的平台有:   android-arm, android-x86, linux-x86, linux-x86_64, macosx-x86_64, windows-x86, windows-x86_64,

  但需要注意:这里的 linux-x86_64 是 基于Fedora 平台的,无法在 CentOS下使用。


3. 在CentOS下编译
  1)安装JDK
  2)安装 apache-maven 2/3

  3) 安装 gcc,gcc+,gcc-c++,yasm

     yum -y install yasm gcc+ gcc-c++


  4)  获取源码

     git clone http://github.com/bytedeco/javacpp-presets
  5)  编译 ffmpeg 


     cd javacpp-presets/
    ./cppbuild.sh -platform linux-x86_64 install ffmpeg 


  6) 编译 jni 和 相关jar


      mvn install --projects ffmpeg

 
   7) 编译完成后,会在 ffmpeg 的lib上生成相关 .so ,javacpp-presets/targets 下生成相关的jar


4. 测试
   1) 将相关的 .so 复制到 /lib64 目录下,或者通过 -Djava.library.path 指定到.so 所在目录
   2) 拷贝一个实例用来测试,  

   

public class Tutorial01 {

	static void SaveFrame(AVFrame pFrame, int width, int height, int iFrame)
			throws IOException {
		// Open file
		OutputStream stream = new FileOutputStream("frame" + iFrame + ".ppm");

		// Write header
		stream.write(("P6\n" + width + " " + height + "\n255\n").getBytes());

		// Write pixel data
		BytePointer data = pFrame.data(0);
		byte[] bytes = new byte[width * 3];
		int l = pFrame.linesize(0);
		for (int y = 0; y < height; y++) {
			data.position(y * l).get(bytes);
			stream.write(bytes);
		}

		// Close file
		stream.close();
	}

	public static void main(String[] args) throws IOException {
		AVFormatContext pFormatCtx = new AVFormatContext(null);
		int i, videoStream;
		AVCodecContext pCodecCtx = null;
		AVCodec pCodec = null;
		AVFrame pFrame = null;
		AVFrame pFrameRGB = null;
		AVPacket packet = new AVPacket();
		int[] frameFinished = new int[1];
		int numBytes;
		BytePointer buffer = null;

		AVDictionary optionsDict = null;
		SwsContext sws_ctx = null;

		if (args.length < 1) {
			args = new String[] { "/root/test.ts" };
			// System.out.println("Please provide a movie file");
			// System.exit(-1);
		}
		// Register all formats and codecs
		av_register_all();

		// Open video file
		if (avformat_open_input(pFormatCtx, args[0], null, null) != 0) {
			System.exit(-1); // Couldn't open file
		}

		// Retrieve stream information
		if (avformat_find_stream_info(pFormatCtx, (PointerPointer) null) < 0) {
			System.exit(-1); // Couldn't find stream information
		}

		// Dump information about file onto standard error
		av_dump_format(pFormatCtx, 0, args[0], 0);

		// Find the first video stream
		videoStream = -1;
		for (i = 0; i < pFormatCtx.nb_streams(); i++) {
			if (pFormatCtx.streams(i).codec().codec_type() == AVMEDIA_TYPE_VIDEO) {
				videoStream = i;
				break;
			}
		}
		if (videoStream == -1) {
			System.exit(-1); // Didn't find a video stream
		}

		// Get a pointer to the codec context for the video stream
		pCodecCtx = pFormatCtx.streams(videoStream).codec();

		// Find the decoder for the video stream
		pCodec = avcodec_find_decoder(pCodecCtx.codec_id());
		if (pCodec == null) {
			System.err.println("Unsupported codec!");
			System.exit(-1); // Codec not found
		}
		// Open codec
		if (avcodec_open2(pCodecCtx, pCodec, optionsDict) < 0) {
			System.exit(-1); // Could not open codec
		}

		// Allocate video frame
		pFrame = av_frame_alloc();

		// Allocate an AVFrame structure
		pFrameRGB = av_frame_alloc();
		if (pFrameRGB == null) {
			System.exit(-1);
		}

		// Determine required buffer size and allocate buffer
		numBytes = avpicture_get_size(AV_PIX_FMT_RGB24, pCodecCtx.width(),
				pCodecCtx.height());
		buffer = new BytePointer(av_malloc(numBytes));

		sws_ctx = sws_getContext(pCodecCtx.width(), pCodecCtx.height(),
				pCodecCtx.pix_fmt(), pCodecCtx.width(), pCodecCtx.height(),
				AV_PIX_FMT_RGB24, SWS_BILINEAR, null, null,
				(DoublePointer) null);

		// Assign appropriate parts of buffer to image planes in pFrameRGB
		// Note that pFrameRGB is an AVFrame, but AVFrame is a superset
		// of AVPicture
		avpicture_fill(new AVPicture(pFrameRGB), buffer, AV_PIX_FMT_RGB24,
				pCodecCtx.width(), pCodecCtx.height());

		// Read frames and save first five frames to disk
		i = 0;
		while (av_read_frame(pFormatCtx, packet) >= 0) {
			// Is this a packet from the video stream?
			if (packet.stream_index() == videoStream) {
				// Decode video frame
				avcodec_decode_video2(pCodecCtx, pFrame, frameFinished, packet);

				// Did we get a video frame?
				if (frameFinished[0] != 0) {
					// Convert the image from its native format to RGB
					sws_scale(sws_ctx, pFrame.data(), pFrame.linesize(), 0,
							pCodecCtx.height(), pFrameRGB.data(),
							pFrameRGB.linesize());

					// Save the frame to disk
					if (++i <= 5) {
						SaveFrame(pFrameRGB, pCodecCtx.width(),
								pCodecCtx.height(), i);
					}
				}
			}

			// Free the packet that was allocated by av_read_frame
			av_free_packet(packet);
		}

		// Free the RGB image
		av_free(buffer);
		av_free(pFrameRGB);

		// Free the YUV frame
		av_free(pFrame);

		// Close the codec
		avcodec_close(pCodecCtx);

		// Close the video file
		avformat_close_input(pFormatCtx);

		System.exit(0);
	}
}

4)运行实例,得到输出结果:
   
Input #0, mpegts, from '/root/test.ts':
  Duration: 00:03:20.02, start: 0.056778, bitrate: 1455 kb/s
  Program 1
    Metadata:
      service_name    : jVideo
      service_provider: jTeam
    Stream #0:0[0x100]: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p, 960x540 [SAR 1:1 DAR 16:9], 25 fps, 25 tbr, 90k tbn, 50 tbc
    Stream #0:1[0x101]: Audio: aac ([15][0][0][0] / 0x000F), 44100 Hz, stereo, fltp, 66 kb/s



作者:maoxiang 发表于2014-8-5 17:38:32 原文链接
阅读:86 评论:0 查看评论

交换机和路由器的区别

$
0
0

       交换机,又叫做交换式集线器,可以简单的理解为把一些电脑连接在一起组成一个局域网。而路由器和交换机的区别很明显,它的作用在于连接不同的网段并且找到网络中数据传输最合适的路径,但二者也并不是完全无联系的。下面来为大家解释交换机和路由器的区别。

路由器和交换机的区别一:交换机是一根网线上网,但是大家上网是分别拨号,各自使用自己的宽带,大家上网没有影响。而路由器比交换机多了一个虚拟拨号功能,通过同一台路由器上网的电脑是共用一个宽带账号,大家上网要相互影响。

路由器和交换机的区别二:交换机工作在中继层,交换机根据MAC地址寻址。路由器工作在网络层,根据IP地址寻址,路由器可以处理TCP/IP协议,而交换机不可以。

路由器和交换机的区别三:交换机可以使连接它的多台电脑组成局域网,如果还有代理服务器的话还可以实现同时上网功能而且局域网所有电脑是共享它的带宽速率的,但是交换机没有路由器的自动识别数据包发送和到达地址的功能。路由器可以自动识别数据包发送和到达的地址,路由器相当于马路上的警察,负责交通疏导和指路的。

路由器和交换机的区别四: 举几个例子,路由器是小邮局,就一个地址(IP), 负责一个地方的收发(个人电脑,某个服务器,所以你家上网要这个东西),交换机是省里的大邮政中心,负责由一个地址给各个小地方的联系。简单的说路由器专管入网,交换机只管配送,路由路由就是给你找路让你上网的,交换机只负责开门,交换机上面要没有路由你是上不了网的。

路由器和交换机的区别五:路由器提供了防火墙的服务。路由器仅仅转发特定地址的数据包,不传送不支持路由协议的数据包传送和未知目标网络数据包的传送,从而可以防止广播风暴。

作者:wu20093346 发表于2014-8-5 15:49:08 原文链接
阅读:101 评论:0 查看评论

前微软雇员解释为何Windows 9应该免费升级

$
0
0
前微软雇员解释为何Windows 9应该免费升级
  毫无疑问,Windows 9 将是微软操作系统业务的下一个重大卖点,有关这个全新操作系统的传闻一直从未间断。近期,资深微软观察者 Mary Jo Foley 更是透露,微软可能会向 Windows XP/Vista/7 等用户免费开放 Windows 9 版本,从而借此鼓励更多用户升级,提高新一代操作系统的市场份额。  对于这一传闻,前微软优秀工程师兼总经理、现任 True Mountain 集团总裁哈尔·贝伦森(Hal Berenson)近日提出了自己的看法,他认为微软提供 Windows 9 免费升级的做法是完全有道理的,因为这将有助于推动用户向新一代操作系统转移,而这并不会影响到微软的 OS 业务。  哈尔在他的博客中表示,微软免费开放 Windows 9 版本升级的做法并不稀奇,因为该公司在 Windows 业务中的摇钱树主要依赖企业版,这一方面的收入从未受到影响。从微软此前的财报中可以看到,Windows 业务的收入绝大部分主要来源于两方面,分别为微软向 OEM 厂商新设备收取的 Windows 授权费以及和企业签订的批量授权协议。因此,微软免费向普通消费用户提供新系统升级,对营收影响其实不大。  哈尔提到,与消费级用户不同的是,企业版 Windows 9 还将会继续维持相对稳定的售价,并且同时向客户的操作系统提供免费软件的许可证,为企业级合作伙伴提供安全保障。  哈尔还补充道,企业版 Threshold(Windows 9?)可能不会免费,当然,除非客户已经通过软件保障计划来支付相关费用,否则该版本不会免费。同时,OEM 版本将会继续向小尺寸(9 英寸以下)设备免费授权,目前这一块已经不是 Windows 业务的重要部分。  如此看来,微软免费开放新系统升级将是一个有效的方式来提高新系统的采用率,虽然这可能会降低财务报告的“美观度”,但这个影响在短期内是可以忽略的。如果这一免费计划能够顺利实施并不断增长系统采用率,那么微软 Windows 业务的长期发展将会是积极可观的。  目前,业界盛传 Windows 9 系统有望在明年四月份推出,微软现在正在加紧开发进度,希望通过推出多项新的功能及特性,为新系统带来重大更新,从而能够在面世时一炮打响。  此外,近期还有传 Windows 9 在正式发布之前将会出现两个不同的测试版,其中之一可能会在今年 10 —11 月份期间放出。目前有关 Windows 9 的新细节鲜有曝光,不过随着测试版的临近,相信未来将会不断出现更多消息。
阅读全文


hibernate调用返回游标的存储过程

$
0
0
注:原创作品,转载请注明出处。


    上篇博文介绍的是hibernate调用返回单值的存储过程,本片博文说的是hibernate调用返回游标的存储过程。
    此此扁博文的存储过程的功能相当于是jdbc调用select 的作用。

1,创建oracle中的包,并在该包中创建的游标类型。
---创建oracle的程序包,在该包中创建一个游标类型
--该类型在存储过程中用来生命输出参数的类型
create or replace package pkg_return_list
as
  type list_cursor is ref cursor;
end pkg_return_list;


2,创建oracle存储过程,该存储过程有唯一的输出参数。
--创建从java程序调用的存储过程,注意该存储过程的唯一输出
--参数的类型是个游标类型。
create or replace procedure pro_return_list(p_cursor out pkg_return_list.list_cursor)
as
begin
  open p_cursor for select * from TBL_ADDRESS;
end pro_return_list;



3,java调用存储过程的主程序。
package com.supan.test;
import com.supan.dao.imp.UserDaoImp;
public class hibernate1
{
	public static void main(String[] args)
	{
		UserDaoImp udi = new UserDaoImp();
		udi.callprocedureOfCursor();
	}
}


4,java的dao层方法
	//调用返回游标的存储过程
	public void callprocedureOfCursor()
	{
		//没有spring的注入,只有人工苦逼的注册sessionFactory属性
		Configuration cof = new Configuration().configure();
		this.setSessionFactory(cof.buildSessionFactory());
		
		//定义存放结果的结果map
		final Map<String,String> result = new HashMap<String, String>();
		getHibernateTemplate().execute(new HibernateCallback<Object>()
		{
			@Override
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException
			{
				session.doWork(new Work()
				{
					@Override
					public void execute(Connection conn) throws SQLException
					{
						CallableStatement proc = null;
						ResultSet rs = null;
						try
						{
							proc = conn.prepareCall("{call pro_return_list(?)}");
							//注意:这里是注册输出参数的类型
							proc.registerOutParameter(1, oracle.jdbc.OracleTypes.CURSOR);
							
							//执行存储过程
							proc.execute();
							
							//获取存储过程的输出参数
							rs = (ResultSet)proc.getObject(1);
							
							while(rs.next())
							{
								//注意访问结果集是从索引位置1开始的,而不是0
								System.out.println(rs.getLong(1));
								System.out.println(rs.getString(2));
								System.out.println(rs.getString(3));
								System.out.println(rs.getString(4));
								System.out.println("----------------");
							}
						}
						catch(Exception e)
						{
							//logger.error("访问数据库失败");
							e.printStackTrace();
						}
						finally
						{
							if(null != proc)
							{
								try
								{
									proc.close();
								} catch (Exception e2)
								{
									//logger.error(close proc happend error);
									e2.printStackTrace();
								}
							}
						}
					}
				});
				return null;
			}
		});
}


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


ITeye推荐



用iftop监控网络流量

$
0
0

iftop是很有用的工具,下面的命令监控了我的笔记本的无线网卡

iftop -i wlan0

比如我现在播放乐视一个视频,iftop显示的信息:



基本说明:

1. 屏幕主要部分都是表示两个机器之间的数据传送,有箭头表示方向,右边三个数值分别是过去2秒,10秒和40秒的平均流量。

2  左下角的TX 表示发出的数据,RX表示收到的数据,

 cum表示总流量, peak表示对应的峰值, Total就不用解释了。


作者:sheismylife 发表于2014-8-5 22:48:12 原文链接
阅读:89 评论:0 查看评论

PM必看:用户场景神器—故事板

$
0
0

大多数产品经理都知道说用户体验设计,但是对于用户角色模型和用户场景却了解较少,而在产品设计中“设计故事板”能够直观地体现出用户和产品使用情境。

那么,故事板是什么呢?故事板,起源于动画行业。在电影电视中,故事板的作用是来安排剧情中的重要镜头。他们相当于一个可视化的剧本。故事板展示了各个镜头之间的关系,以及他们是如何串联起来,给观众一个完整的体验。

现在,“故事板”在产品设计过程中也被广泛的采用,虽然产品设计故事板和动画、影视制作故事板都是用一系列的图片和语言组成的视觉表现形式,但是之间的所表达的信息和目标用户却是不一样的。我们在做“产品设计故事板”的目的是让产品设计师在特定产品使用情境下全面理解用户和产品之间的交互关系。

故事板的主要形式有:

文字故事板

我们要描述一个好的用户场景,需要对用户使用这个产品的过程有一个基本的了解,还需要对用户角色和使用情景有所设想。好的用户情景,可以贯穿整个产品设计的过程,模拟现实的用户操作和交互方式,用于产品的可用性评估。

使用简单的语言描述人物角色、情境及用户使用情景,尽量避免不要给出具体的用户行为和交互动作。

合理的文字故事板应该注意以下几个方面:

  1. 确定角色,多个角色做多个故事板;
  2. 确定确实必须完成的目标;
  3. 确定故事的出发点或事件;
  4. 明确角色信息及关注点;
  5. 确定故事板的数量,取决于人物角色和目标数量;
  6. 书写故事,从触发到结束;

 

 

用户Tina:性别、年龄、职业、收入、学历、使用习惯、使用网络、关键特征、目标以及故事描述,这些作为人物角色模型的故事板信息内容。

图片故事板

对于交互设计师而言,图形故事板是最快让他人获取自己的想法的最佳手段。通过图形故事板,用户就像在看电影一样,融入到情景当中。

通过反思各个场景的的事件,提醒团队该注意哪些方面,反思交互效果,能够让他人通过看得见的方式面面加以注释。

在图形故事板中,用户通过一连串的用户行为,连接成一个完整的用户场景。

(故事板就是一个用户场景)

(事件/行为是具体的人和系统的交互行为,它将人、物和环境结合起来,构成了整个故事的内容)

(把角色放在某个场景里,讲一个故事。故事中应该包括对问题的研究和自己的想法)

如果着重研究线下任务,则故事板中线下场景居多,如关注屏幕任务则会展现界面居多。当然如果完全关注屏幕任务就是我们的线框图和原型了。

故事板关注的是屏幕任务和线下任务结合的边缘地带。

故事板是传统交互设计方法的重要补充工具,平时我们的原型设计仅仅局限于屏幕环境的设计,忽略了屏幕之外的使用情境,通过故事板绘制的关键使用场景有利于我们理解屏幕之外的用户目标和动机。其实有经验的设计师会在产品设计初期假想一些应用情境,只不过他们没有画在纸上而已。

另外,故事板不仅仅是设计师头脑中假想情境的具象化,他还可以使一些模糊的用户需求更加具象更有说服力,在设计沟通的过程中能发挥巨大的作用。

故事板揭示了用户与产品的各个交互行为,他可以让pm像用户一样,融入到用户的使用情景当中;又可以以一个旁观者的状态,观看全局,反思和总结使用场景的问题及真伪。

本文由人人都是产品经理团队@易卿收集整理,转载请注明来源


(关注更多人人都是产品经理观点,参与微信互动(微信搜索“人人都是产品经理”或“woshipm”)


使用JProfiler解决Java应用程序内存溢出问题实例

$
0
0

    前段时间基于OpenJms部署了一个消息中间件服务器,通过主题订阅模式在各个消息节点之间传递信息,但是某个类型的消息节点长时间运行后出现了内存溢出问题,最后使用JProfiler的基本线程监测功能找到问题所在,并且进行解决。

 

Java 版本 java version "1.7.0_40"

JProfiler 版本 v8.0.7

 

 

1、 打开JProfiler,选择New Session


 

2、 点击Attach,按ok,以便从本地JVM中选择正在运行的java程序

 

3、 选择session,这里可以看到本机运行的所有java程序,我的应用ID为844,因此,选择844,点击ok

 

4、 可以看到JProfiler正在尝试连接到该java应用

 

5、 选择Instrumention,可以使用到所有的JProfiler特性,但是对于JVM的读取可能存在一定的风险使得java应用出现异常。

 

6、 设置完成后,直接ok

 

7、 主界面上可以看到当前产生的对象,占用的内存比例

 

8、 选择左侧栏的Telemetries,可以看到当前的内存分配和使用情况

 

9、 选择左侧栏下面的Threads,可以看到当前的线程执行情况,如图可以看到,当前的运行线程(绿色部分),阻塞线程(红色部分),网络或IO线程(蓝色部分),等待线程(橙色部分)。此时节点未接受任何消息,因此线程和内存都处于平稳运行状态。


 

10、 有任务消息下达时,线程数猛增

 

11、 具体可以看到左侧栏Threads页面下面的线程数量

 

12、 慢慢的,随着消息数量的增多,处理线程越来越多,几乎是直线上涨

 
 
 13、 至此,可以看到线程的数量在不断增多,而并不是之前预想的一旦一个线程完成就立即释放资源,因此极有可能是这里导致了内存溢出。


 
 14、 Java自己的内存回收GC正在努力工作,回收我忘记释放的资源。

 

15、 很小的程序,由于内存没有处理好,cpu占用却不小

 

16、 由于GC的关系,内存占用呈现出锯齿状,但由于资源未释放,总内存使用量一直在随时间增大。

此处为展示完全,因此没有达到内存溢出的地步。

 

17、 经过对代码的分析,实际的执行消息处理类是单例模式,代码的意图是创建唯一的一个线程池,然后接收到消息后就将任务塞到线程池中执行。但是实际的实现却是,每接收到一个任务就创建了一个线程池,并且任务执行完后并没有回收线程池,因此导致了这个结果。

pool = Executors.newScheduledThreadPool(cnf.getServerType());

 

将其修改为

if (pool == null) {

    pool = Executors.newScheduledThreadPool(5);

}

 

 

18、 修改之后,线程的运行情况如图,已经不再重复创建无用的线程池,而是稳定在一定数量的线程,平稳运行。

 

 线程运行情况

 

CPU占用率 

 

 

GC的活跃情况 


 

 

内存占用情况

 

 

 



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


ITeye推荐



OAuth 2.0 工作流程

$
0
0

原文链接:http://www-01.ibm.com/support/knowledgecenter/SSELE6_8.0.0.3/com.ibm.ammob.doc_8.0.0.3/config/concept/con_oauth20_workflow.html%23con_oauth20_workflow?lang=zh

两种认证模式区别见:http://stackoverflow.com/questions/7522831/what-is-the-purpose-of-the-implicit-grant-authorization-type-in-oauth-2

OAuth 2.0 工作流程

IBM Security Access Manager 中的 OAuth 2.0 支持使 OAuth 客户机能够通过四种不同的方式获取对受保护资源的访问权。

OAuth 2.0 工作流程

Security Access Manager for Mobile 支持下列 OAuth 2.0 工作流程。

授权代码流程(适用于有服务器端的应用)

授权代码授予类型适用于那些向授权服务器进行认证时可以对其客户机凭证进行保密的 OAuth 客户机。例如,在安全服务器上实现的客户机。作为基于重定向的流程,OAuth 客户机必须能够与资源所有者的用户代理进行交互。它还必须能够通过重定向接收来自授权服务器的传入请求。

授权代码工作流程图包括下列步骤:

  1. OAuth 客户机会在将资源所有者的用户代理定向到授权端点时启动流程。OAuth 客户机包括其客户机标识、所请求的作用域、本地状态以及重定向 URI。在准予或拒绝访问之后,授权服务器会将用户代理发回到重定向 URI。
  2. 授权服务器通过用户代理对资源所有者进行认证,并确定资源所有者是准予还是拒绝访问请求。
  3. 如果资源所有者准予访问,那么 OAuth 客户机将使用先前提供的重定向 URI 将用户代理重定向回 OAuth 客户机。重定向 URI 包括授权代码以及 OAuth 客户机先前提供的所有本地状态。
  4. OAuth 客户机通过令牌端点从授权服务器请求访问令牌。OAuth 客户机使用其客户机凭证进行认证,并包括上一步中接收到的授权代码。OAuth 客户机还提供了用于获取授权代码以进行验证的重定向 URI。
  5. 授权服务器验证客户机凭证和授权代码。此服务器还将确保接收到的重定向 URI 与步骤 3 中用于重定向客户机的 URI 相匹配。如果有效,那么授权服务器将使用访问令牌进行回应。

授权服务器可以是资源服务器,也可以是另一实体。单个授权服务器可以发放多个资源服务器接受的访问令牌。

使用刷新令牌的授权代码流程

使用刷新令牌的授权代码工作流程图包括下列步骤:

  1. OAuth 客户机通过使用其客户机凭证向授权服务器进行认证并出示权限授予来请求访问令牌。
  2. 授权服务器验证客户机凭证和权限授予。如果有效,那么授权服务器将发放访问令牌和刷新令牌。
  3. OAuth 客户机通过出示访问令牌向资源服务器发出访问受保护资源的请求。
  4. 资源服务器验证访问令牌。如果访问令牌有效,那么资源所有者将为该请求提供服务。
  5. 重复步骤 3 和 4,直到访问令牌到期。如果 OAuth 客户机知道访问令牌已到期,请跳至步骤 7。否则,OAuth 客户机将发出访问另一个受保护资源的请求。
  6. 如果访问令牌无效,那么资源服务器将返回错误。
  7. OAuth 客户机通过使用其客户机凭证向授权服务器进行认证并出示刷新令牌来请求新的访问令牌。
  8. 授权服务器验证客户机凭证和刷新令牌,并且在这些凭证和令牌有效的情况下发放新的访问令牌和新的刷新令牌。

隐式授予流程

隐式授予类型适用于那些无法对其用于向授权服务器认证的客户机凭证进行保密的客户机。例如用户代理中的客户机应用程序,这些应用程序通常使用 JavaScript 之类的脚本语言在浏览器中进行实现。

作为基于重定向的流程,OAuth 客户机必须能够与资源所有者的用户代理(通常是 Web 浏览器)进行交互。OAuth 客户机还必须能够通过重定向接收来自授权服务器的传入请求。

隐式授予工作流程图包含下列步骤:

  1. OAuth 客户机通过将资源所有者的用户代理定向到授权端点来启动流程。OAuth 客户机包括其客户机标识、所请求的作用域、本地状态以及重定向 URI。在准予或拒绝访问之后,授权服务器会将用户代理发回到重定向 URI。
  2. 授权服务器通过用户代理对资源所有者进行认证,并确定资源所有者是准予还是拒绝访问请求。
  3. 如果资源所有者准予访问,那么授权服务器将使用先前提供的重定向 URI 将用户代理重定向回客户机。重定向 URI 将访问令牌包括在 URI 片段中。
  4. 用户代理通过向 Web 服务器发出不包含该片段的请求来按重定向指示信息进行操作。用户代理将在本地保留片段信息。
  5. Web 服务器返回一个 Web 页面,此页面通常是包含嵌入式脚本的 HTML 文档。此 Web 页面将访问完全重定向 URI,其中包括用户代理所保留的片段。它还可以抽取该片段中包含的访问令牌及其他参数。
  6. 用户代理在本地运行 Web 服务器提供的脚本,这将抽取访问令牌并将其传递到客户机。

资源所有者密码凭证流程

资源所有者密码凭证授予类型适用于资源所有者与客户机之间具有信任关系的情况。例如,资源所有者可以是 OAuth 客户机的计算机操作系统,也可以是具有高级别特权的应用程序。

只有当 OAuth 客户机已获取资源所有者的凭证时,您才能使用此授予类型。此授予类型还可以通过将存储的凭证转换为访问令牌,对使用直接认证方案的现有客户机进行迁移。

资源所有者密码凭证工作流程图包括下列步骤:

  1. 资源所有者为客户机提供其用户名和密码。
  2. OAuth 客户机通过令牌端点从授权服务器请求访问令牌。OAuth 客户机使用其客户机凭证进行认证,并包括从资源所有者接收到的凭证。
  3. 在验证资源所有者凭证和客户机凭证之后,授权服务器发放访问令牌和(可选)刷新令牌。

客户机凭证流程

当 OAuth 客户机仅使用其客户机凭证请求访问令牌时,将使用客户机凭证流程。此流程适用于下列其中一种情况:

  • OAuth 客户机请求在其控制下访问受保护资源。
  • OAuth 客户机请求访问其他受保护资源,而该资源的授权先前已通过授权服务器进行安排。

客户机凭证工作流程图包括下列步骤:

  1. OAuth 客户机通过使用其客户机凭证进行认证来从令牌端点请求访问令牌。
  2. 在验证客户机凭证之后,授权服务器发放访问令牌。

用户代理在Authorization Code下为web server端服务,在Implicit_Grant下可以为浏览器



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


ITeye推荐



cordova与ios native code交互的原理

$
0
0

很早以前写了一篇博客,总结cordova插件怎么调用到原生代码: cordova调用过程,不过写得太水,基本没有提到原理。最近加深了一点理解,重新补充说明一下

js调用native

下面是我们产品中的代码片段:

datePicker.show(options, function (date) {
    var month = date.getMonth() + 1;
    callback(null, date.getFullYear() + "-" + month + "-" + date.getDate());
});

cordova插件最终表现出来的都是js接口,并且调用者完全不需要知道自己在调用一个cordova插件

但是在任何cordova js方法内部,最后一定会调用cordova.exec函数:

cordova.exec(successCallback, errorCallback, "DatePicker", "show", []);

然后就进入了关键的cordova.exec函数,这是cordova框架的js端的最后一环,就是由它完成对ios native的调用

在exec函数里,首先会判断平台,可能是android,ios或者wp,其他平台本文省略,如果是ios平台,cordova会采用以下2种方式的一种,来与ios native code交互

通过iframe

cordova.exec往当前的html中插入一个不可见的iframe,从而向UIWebView请求加载一个特殊的URL,这个URL里当然就包含了要调用的native plugin的类名,方法名,参数,回调函数等信息

接下来,由于被请求加载URL,于是UIWebViewDelegate的这个方法被调用:

- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType

这里就进入了native侧,从request里就拿到了js端传过来的信息,然后调用到native plugin

通过XHR

另一种方式,cordova.exec里直接发起一个XHR请求,被native侧的NSURLProtocol拦截,于是调用到这个native方法:

+ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest

也进入了native侧,然后以同样的方式调用到native plugin

在2种方式中,cordova会优先选择XHR方式,只有当XHR方式不可用时,才会使用iframe的方式。不过无论怎么样,这2种方法都为从js到native打开了一条通道,剩下的就是传递参数和路由的问题了

native调用js

另一条通道就简单的多,因为iOS提供了原生支持,所以不需要想特别的办法。即通过UIWebView的这个方法:

- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

看一下cordova框架native侧的代码,我去掉了注释和无关代码:

- (void)evalJsHelper:(NSString*)js
{
    if (![NSThread isMainThread] || !_commandQueue.currentlyExecuting) {
        [self performSelectorOnMainThread:@selector(evalJsHelper2:) withObject:js waitUntilDone:NO];
    } else {
        [self evalJsHelper2:js];
    }
}

- (void)evalJsHelper2:(NSString*)js
{
    NSString* commandsJSON = [_viewController.webView stringByEvaluatingJavaScriptFromString:js];
}

可以看到,正是通过UIWebView提供的这个方法完成的,但是,一定执行在main thread

同步和异步的问题

从上面的分析可以发现,从js调用native,2种方式都必定是异步的。而从native回到js,却是一个同步的方法,而且是跑在主线程里

调用cordova插件的代码,对返回值的处理一定要放在回调函数里,因为结果是异步返回的。同时,回调函数的执行时间不能太长,否则会阻塞native主线程

参考

本文参考了以下2篇文章,都写得很好:

iOS版PhoneGap原理分析

浅析Cordova for iOS




作者:kyfxbl 发表于2014-8-6 18:00:57 原文链接
阅读:0 评论:0 查看评论

面试笔试常考的mysql 数据库操作group by

$
0
0

IT 面试中,数据库的相关问题基本上属于必考问题,而其中关于sql语句也是经常考察的一个重要知识点。


下面介绍下sql语句中一个比较重要的操作group by,他的重要行一方面体现在他的理解困难度,一方面体现应用中的长见性。


首先,给出一个studnet学生表:

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) DEFAULT NULL,
  `sex` tinyint(1) DEFAULT '0',
  `score` int(10) NOT NULL,
  `dept` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 


添加一些测试数据:


mysql> select * from student where id<10;
+----+------+------+-------+---------+
| id | name | sex  | score | dept    |
+----+------+------+-------+---------+
|  1 | a    |    1 |    90 | dev     |
|  2 | b    |    1 |    90 | dev     |
|  3 | b    |    0 |    88 | design  |
|  4 | c    |    0 |    60 | sales   |
|  5 | c    |    0 |    89 | sales   |
|  6 | d    |    1 |   100 | product |
+----+------+------+-------+---------+



给出需求,写出sql:

给出各个部门最高学生的分数。

要想得到各个部门学生,首先就要分组,按照部门把他们分组,然后在各个部门中找到分数最高的就可以了。


所以sql语句为:

mysql> select *, max(score) as max  from student group by dept order by name;
+----+------+------+-------+---------+------+
| id | name | sex  | score | dept    | max  |
+----+------+------+-------+---------+------+
|  1 | a    |    1 |    90 | dev     |   90 |
|  3 | b    |    0 |    88 | design  |   88 |
|  4 | c    |    0 |    60 | sales   |   89 |
|  6 | d    |    1 |   100 | product |  100 |
+----+------+------+-------+---------+------+
4 rows in set (0.00 sec)


这只是个简单的例子,我们可以再把这个例子复杂化,比如分数最高的必须是女生,即sex列值必须为1才挑选出,这时的sql语句应该为:

mysql> select *,max(score) as max from student group by dept having sex='1' order by name;
+----+------+------+-------+---------+------+
| id | name | sex  | score | dept    | max  |
+----+------+------+-------+---------+------+
|  1 | a    |    1 |    90 | dev     |   90 |
|  6 | d    |    1 |   100 | product |  100 |
+----+------+------+-------+---------+------+
2 rows in set (0.46 sec)


这里我们没有用where语句而是用了having,这里简单说明一下,因为我们的条件是在分组后进行的,其实分组前挑选出sex='1',然后再按照dept部门分组,也是可行的,这里就要看题目是怎么要求的:

mysql> select *,max(score) as max from student where sex='1' group by dept order by name;
+----+------+------+-------+---------+------+
| id | name | sex  | score | dept    | max  |
+----+------+------+-------+---------+------+
|  1 | a    |    1 |    90 | dev     |   90 |
|  6 | d    |    1 |   100 | product |  100 |
+----+------+------+-------+---------+------+
2 rows in set (0.05 sec)


查询出的结果时一致的,如果把选择条件改为必须部门所有人的分数之和大于150才能把分数最高的部门的人列出来,这里就必须使用having了,因为 having 里面可以使用聚合函数sum,并且也必须分完组我们才能得到这个组的总分数,才能比较是否该值大于150:

mysql> select *,max(score) as max from student   group by dept having sum(score)>150 order by name;
+----+------+------+-------+---------+------+
| id | name | sex  | score | dept    | max  |
+----+------+------+-------+---------+------+
|  1 | a    |    1 |    90 | dev     |   90 |
|  6 | d    |    1 |   100 | product |  100 |
+----+------+------+-------+---------+------+
2 rows in set (0.00 sec)


额外增加一个例子,比如我要选出不重复的部门,我们可以使用

mysql> select distinct dept from student;
+---------+
| dept    |
+---------+
| dev     |
| design  |
| sales   |
| product |
+---------+
4 rows in set (0.02 sec)


但是如果我们还要列出他的id等一些其他信息,我们如果这样:

mysql> select name,distinct dept from student;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'distinct dept from student' at line 1

这是不行的,因为distinct只能放到开始位置,如果:

mysql> select distinct  dept,name from student;
+---------+------+
| dept    | name |
+---------+------+
| dev     | a    |
| dev     | b    |
| design  | b    |
| sales   | c    |
| product | d    |
| product | m    |
+---------+------+
6 rows in set (0.00 sec)


为什么没有达到预期的效果,因为distinct 作用到了2个字段上,这时,我们就需要groub by 出场了。


mysql> select  dept,name from student group by dept;
+---------+------+
| dept    | name |
+---------+------+
| design  | b    |
| dev     | a    |
| product | d    |
| sales   | c    |
+---------+------+
4 rows in set (0.00 sec)


按照dept分组,自然就达到去重的目的了。所以有时候如果我们碰到了一个问题很难解决,比如用distinct去重,并带上其他列值,我们就需要尝试换个思路,可能答案自然就找到了。








作者:fujiafeihudui 发表于2014-8-6 16:38:46 原文链接
阅读:15 评论:0 查看评论

你需要立刻突破的十大编程禁忌

$
0
0

程序员在编程的时候难免会犯错误,但如果不从错误中吸取教训,那么习惯成自然,你会经常犯错的。从错误中不断的学习,锻炼好的行为习惯有助于事业上的稳定。这就是我们如何将小麦从糟糠中区别出来以及如何避免编程禁忌的绝佳经验。此外,最重要的就是可以为客户带来更好的用户体验。

1. 不提升非技术技能

我们认为非技术技能是项目成功的主要因素。这些非技术技能也可以称之为“软技能”,总体上来说,它已经被公司证明为能够驾驭企业和客户之间的长期商业关系,因此也能决定公司的成长发展路径。一些关键的软技能指标包括:

a.纪律——这是最重要的特征之一,缺乏纪律,最终会让这个开发团队在开发能力上“缺乏自信”。解决这一问题的矫正方法就是每天制定详细的to-do清单:兑现你的承诺、完成你开始做的事情、避免多重任务,因为这些往往会让你的生活产生混乱。

b.顾客的声音——不把客户置于决策的核心地位只会跟你们业务的原始目的相冲突。如果客户不高兴,即使你拥有世界上一流的专业知识和资源也不会起什么作用。保持符合客户期望的解决方案、及时交付才能体现出项目的真正价值。

c.沟通——尤其是当客户和供应商并不在同一地点的时候,明确而及时的沟通是填补服务空白的极好措施。主要集中在这三个方面你就能克服问题——进行主题讨论、清晰表达、干脆简洁。

d.了解需求——在整个开发生命周期过程中,决定成功和失败的之间的一个至关重要的区别将会给人留下深刻的印象。通过最初的头脑风暴法了解问题状态,以及后续的交货程序,这其中都要和客户完美配合。只有这样,客户才会赞赏你的工作,给你好评。

2. 对编码不理智

古人云:善泅者溺,善骑者堕。但估计绝大多数的程序员都认为自己的编程技术绝对的牛。而同样真实的是,每一个代码,让不同的程序员去实现的话都会不可避免地发现它所存在的缺陷。所以说,只有通过在一个项目上的合作,程序员之间必然有的摩擦才能证明谁是最好的。健康的竞争是好事,但它不应该成为一个本来可以成功的项目的负担。

另一个创意阻碍是无法将预定义的模板使用在对你有利的开发项目里。 几乎所有的编程语言有一个很好的在线/内置的代码片段存储库,可以修补代码,防止重新编程。然而,如果因为不理解需求或缺乏接触各种可用库/模板的话,这就意味着程序员最终会无意间将一开始就创建的代码付之东流。这不仅增加了开发时间,也提高了总体成本。另外一点就是,发布了的代码已经经过了质量检测,所以只有将它用作模板才能发挥它更大的价值。

3. 不一定什么都要被理解

如果你是刚调到这个团队来的编程人员,对于手头的工作并不是很熟悉,那该怎么办?肯定是先看一些前任留下来的工作计划,要是他写的详细倒也没什么,如果写的不详细,估计会让你更加的挠头。

因此,推己及人,在需要交代的工作上,最好是把任务写的尽可能的详细。这么做也是非常现实的原因:能够把编程问题解决掉,最好是保证使用解释性的语言和英语发音来表示变量。一些基本的指针可以让你的程序更容易被理解,包括:

  • a. 把所有参数、引用、方法和变量名称尽可能接近英语表达。保持文件名简短但有助于理解的功能。
  • b. 使用++包装文字是一个好办法,能让代码和注释更加清晰。
  • c. 将编写的程序保持在一个连续的流程上,尤其是在使用OOP基础上的语言:C#、C 和 C++。
  • d. 对于不同的代码块使用不同的描述名称。

4. 不使用经过验证的工具和技术

程序员的好坏从他使用的编程工具和调试工具上就能看出。在异常情况的跟踪上,下面就是程序员经常会出现的常见错误。

  • 对一些可能会对其它代码有影响的常见案例进行捕捉,处理这些比较常见的异常情况(而不是特殊的异常)意味着无意中除除掉了会抑制整个程序的残留部分,因此并不会影响他人的代码。
  • 也许程序员可能带有恶意的意图来捕捉所有的异常情况,但即使是捕捉到了也不实施采取措施,这就是常说的“虚假安全阀”,这种异常处理手段是对整个软件的稳定和安全的一种妥协方式。

5. 糟糕的控制版本

在任何涉及多个团队的项目里,当谈到版本控制的时候不去介绍使用最佳实践都是一个十足的罪过。版本控制的目的是确保由一个人执行的编辑或修订不去影响另一个人的工作。

版本控制不仅有助于将由两个或两个以上的程序员的编辑工作合并到一起,还有助于跟踪程序的更改历史。所以说,任何开发团队都应该做一些好的改进措施以确保强大的版本控制,这其中就包括:

  • 为每个解决方案创建一个“逻辑单元”
  • 给解决方案制定描述性的名称
  • 确保你所使用的都是最先进的文件
  • 频繁的向团队分享你所做的各种改变

6. 拥有最新信息的个人代表不了团队


这是相对有趣的一点,所有的商业产品都想要以自身的敏捷技术和产品文化来给客户留下深刻的印象, 但是现实中很少有厂商会花时间去磨练他们员工在介绍产品特点上的技能。许多公司只是简单地提供了一些基本的培训,并且抱希望与员工在真实的日常项目里学到更多的技能。所以部门经理和项目的直接领导可以通过以下两个办法来提高员工的业绩:

  • 一旦有新员工加入,就立刻强制安排他参加专业培训,让他知道他的角色是用来干什么的,尽早产生创造力。例如一个测试人与加入之后,就应该向他介绍编程的理念,之后将培训重点放到测试实践上,而不是继续阐述编程的重要性。
  • 现阶段的技术的进化程度比以往任何时候都要快,,所以要记住,定期培训是必不可少的,这是在给团队创造价值。例如一个Web 设计师需要知道响应式设计,提供给设计师大量的用户日常使用的移动设备的不断扩张的样品,希望他们能获得灵感。

7. 不恰当的测试


测试作为整个系统开发生命周期(Systems Development Life Cycle,简称SDLC)的重要一个要素,通常不需要开发团队给出太惊人的结果。 但是如果在测试环节没有付出恰当的、相应的努力的话,这是说不过去的。下面的一些方法或许对你的测试团队有用,至少在你们交付产品的时候能够给用户一个好的交代。

  • 单元测试
  • 实物模型
  • 综合测试

8. 注意安全漏洞

有的时候在软件开发过程中,就会遇见如下这样的安全漏洞:

A、不同组件之间意想不到的交互作用:a、输入不正确的验证信息;b、SQL资料隐码攻击;c、跨网站指令码;d、命令植入攻击;e、跨站请求伪造(CSRF);

B、难以实施的资源管理,包括:a、不尊重可用内存缓冲区;b、对外控制;c、使用有潜在危险的功能;

9. 和客户交流


最初的合同签订后,开发公司通常会忘记每天与客户进行产品上的信息交互,以至于在交货的时候还需要进行升级。 两大关键的交流点可以让你和客户保持更好的、更长的关系

  • 在客户开问之前,开发方应该和客户进行交流沟通。
  • 和客户保持周期性的交流。

10. 避免标准实践面临的迫在眉睫的最后期限

通常情况下项目都会遇到进度延误的现象。 然而,这不是说你有理由去偷工减料或者是在开发或测试阶段耍花招,未经测试的模块绝对是一个隐患,会让你的开发团队名誉受损的。一个更好的方法来管理延迟是提前告知客户并且积极执行延迟计划。只要延期的理由是有效的,客户应该会理解,也会给你额外的时间来解决这个问题。

显然,在项目的最后期限内,急急忙忙完成编程的质量肯定不是特比保险,所以在交付之后开发团队整体上会花更多的时间和努力来进行跟踪维护,这样的成本也是很巨大的,最好的办法就在一开始就制定完美的执行计划。项目再造所耗费的资源或许是项目本身的成本的好几倍,任何一个公司宁愿花更多的时间在初始开发上,这样最终的产品一定会符合SDLC标准,并在缺陷和不良问题上有足够的话语权。对于顾客来说,时效性不能以牺牲质量为代价,永远都不能。

Viewing all 15843 articles
Browse latest View live


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